From 1acb9c2d13f03a9a55a2e456230940a93881cab5 Mon Sep 17 00:00:00 2001 From: Omar Elrefaei <17922991+Omar-Elrefaei@users.noreply.github.com> Date: Wed, 31 Aug 2022 03:35:53 -0400 Subject: [PATCH 01/34] Simplify trivial ifelse cases. Fix JuliaSymbolics/Symbolics.jl#170 --- src/simplify_rules.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/simplify_rules.jl b/src/simplify_rules.jl index 9ebfb5ee..5845adea 100644 --- a/src/simplify_rules.jl +++ b/src/simplify_rules.jl @@ -54,6 +54,7 @@ let @rule(real(~x::_isreal) => ~x) @rule(imag(~x::_isreal) => zero(symtype(~x))) @rule(ifelse(~x::is_literal_number, ~y, ~z) => ~x ? ~y : ~z) + @rule(ifelse(~x, ~y, ~y) => ~y) ] TRIG_EXP_RULES = [ From 71676f6a02f46ad91f2ae824ebb5722bbb65f742 Mon Sep 17 00:00:00 2001 From: Manuel Date: Mon, 19 Dec 2022 10:20:59 +0100 Subject: [PATCH 02/34] `promote_symtype` for `Base.literal_pow` Add a special method to infer the symtype of a `Base.literal_pow` expression. Should fix https://github.com/JuliaSymbolics/Symbolics.jl/issues/455 --- src/methods.jl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/methods.jl b/src/methods.jl index 2a878efa..deec3ce0 100644 --- a/src/methods.jl +++ b/src/methods.jl @@ -133,6 +133,9 @@ function Base.literal_pow(::typeof(^), x::Symbolic, ::Val{p}) where {p} T = symtype(x) T <: Number ? Base.:^(x, p) : error_f_symbolic(rem2pi, T) end +function promote_symtype(::typeof(Base.literal_pow), _, ::Type{T}, ::Type{Val{S}}) where{T<:Number,S} + return promote_symtype(^, T, typeof(S)) +end promote_symtype(::Any, T) = promote_type(T, Real) for f in monadic From fe5a640643ac76431d89577f8f52ef682c998f98 Mon Sep 17 00:00:00 2001 From: Miles Cranmer Date: Mon, 16 Jan 2023 15:19:38 -0500 Subject: [PATCH 03/34] Add unittest for LiteralReal simplification --- test/rulesets.jl | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/rulesets.jl b/test/rulesets.jl index 0964a600..1b1bc1fb 100644 --- a/test/rulesets.jl +++ b/test/rulesets.jl @@ -54,6 +54,18 @@ end @test simplify(Term(zero, [x + 2])) == 0 end +@testset "LiteralReal" begin + @syms x1::LiteralReal x2::LiteralReal + s = cos(x1 * 3.2) - x2 * 5.8 + x2 * 1.2 + @eqtest s == cos(x1 * 3.2) - x2 * 5.8 + x2 * 1.2 + + # Prevents automatic simplification: + @eqtest s != cos(x1 * 3.2) - x2 * 4.6 + + # However, manual simplification should still work: + @eqtest simplify(s) == cos(3.2(x1^1)) - 4.6x2 +end + @testset "boolean" begin @syms a::Real b c From 4a7bd79a054a3dbfba4ad8478d87ea7b2de78cd8 Mon Sep 17 00:00:00 2001 From: Miles Cranmer Date: Mon, 16 Jan 2023 15:21:08 -0500 Subject: [PATCH 04/34] Correct simplification test --- test/rulesets.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/rulesets.jl b/test/rulesets.jl index 1b1bc1fb..82ed5564 100644 --- a/test/rulesets.jl +++ b/test/rulesets.jl @@ -60,7 +60,7 @@ end @eqtest s == cos(x1 * 3.2) - x2 * 5.8 + x2 * 1.2 # Prevents automatic simplification: - @eqtest s != cos(x1 * 3.2) - x2 * 4.6 + @eqtest s != cos(3.2(x1^1)) - 4.6x2 # However, manual simplification should still work: @eqtest simplify(s) == cos(3.2(x1^1)) - 4.6x2 From 769851ff3e6d6ee0635ba822f1f0169bceb4ce2c Mon Sep 17 00:00:00 2001 From: Arno Strouwen Date: Thu, 9 Feb 2023 05:09:56 +0100 Subject: [PATCH 05/34] [skip ci] update readme for new docs --- README.md | 42 +++++++++++++++++++----------------------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index d97f4c23..f0b46bbf 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,22 @@ -

SymbolicUtils.jl

- -

- - CI - - - - - -

+# SymbolicUtils.jl + +[![Join the chat at https://julialang.zulipchat.com #sciml-bridged](https://img.shields.io/static/v1?label=Zulip&message=chat&color=9558b2&labelColor=389826)](https://julialang.zulipchat.com/#narrow/stream/279055-sciml-bridged) +[![Global Docs](https://img.shields.io/badge/docs-SciML-blue.svg)](https://docs.sciml.ai/SymbolicUtils/stable/) + +[![codecov](https://codecov.io/gh/JuliaSymbolics/SymbolicUtils.jl/branch/master/graph/badge.svg)](https://app.codecov.io/gh/JuliaSymbolics/SymbolicUtils.jl) +[![Build Status](https://github.com/JuliaSymbolics/SymbolicUtils.jl/workflows/CI/badge.svg)](https://github.com/JuliaSymbolics/SymbolicUtils.jl/actions?query=workflow%3ACI) +[![Build status](https://badge.buildkite.com/3db222e469784b365e4b45f2b0155d252cf0ae70fef708bfa1.svg?branch=master)](https://buildkite.com/julialang/symbolicutils-dot-jl) + +[![ColPrac: Contributor's Guide on Collaborative Practices for Community Packages](https://img.shields.io/badge/ColPrac-Contributor%27s%20Guide-blueviolet)](https://github.com/SciML/ColPrac) +[![SciML Code Style](https://img.shields.io/static/v1?label=code%20style&message=SciML&color=9558b2&labelColor=389826)](https://github.com/SciML/SciMLStyle) + + +## Tutorials and Documentation + +For information on using the package, +[see the stable documentation](https://symbolicutils.juliasymbolics.org/stable/). Use the +[in-development documentation](https://symbolicutils.juliasymbolics.org/dev/) for the version of +the documentation, which contains the unreleased features. SymbolicUtils.jl provides various utilities for symbolic computing. SymbolicUtils.jl is what one would use to build a Computer Algebra System (CAS). If you're looking for a complete CAS, similar to SymPy or Mathematica, see @@ -18,19 +25,8 @@ Octonian algebras, you've come to the right place. [Symbols in SymbolicUtils](https://symbolicutils.juliasymbolics.org/#creating_symbolic_expressions) carry type information. Operations on them propagate this information. [A rule-based rewriting language](https://symbolicutils.juliasymbolics.org/rewrite/#rule-based_rewriting) can be used to find subexpressions that satisfy arbitrary conditions and apply arbitrary transformations on the matches. The library also contains a set of useful [simplification](https://juliasymbolics.github.io/SymbolicUtils.jl/#simplification) rules for expressions of numeric symbols and numbers. These can be remixed and extended for special purposes. - If you are a Julia package develper in need of a rule rewriting system for your own types, have a look at the [interfacing guide](https://symbolicutils.juliasymbolics.org/interface/). -[**Go to the manual**](https://juliasymbolics.github.io/SymbolicUtils.jl/) - -SymbolicUtils.jl is on the general registry and can be added the usual way: -```julia -pkg> add SymbolicUtils -``` -or -```julia -julia> using Pkg; Pkg.add("SymbolicUtils") -``` ### "I don't want to read your manual, just show me some cool code" ```julia From 55f48e01ca8630abb7488c9ab78a38fe966db0de Mon Sep 17 00:00:00 2001 From: "Bowen S. Zhu" Date: Fri, 10 Feb 2023 03:19:27 -0500 Subject: [PATCH 06/34] Fix printing for term operators that could be both unary and binary Fix #512 --- src/types.jl | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/types.jl b/src/types.jl index 1ee2543c..b4133434 100644 --- a/src/types.jl +++ b/src/types.jl @@ -778,12 +778,14 @@ end function show_call(io, f, args) fname = istree(f) ? Symbol(repr(f)) : nameof(f) - binary = Base.isbinaryoperator(fname) - if binary - for (i, t) in enumerate(args) - i != 1 && print(io, " $fname ") - print_arg(io, t, paren=true) - end + len_args = length(args) + if Base.isunaryoperator(fname) && len_args == 1 + print(io, "$fname") + print_arg(io, first(args), paren=true) + elseif Base.isbinaryoperator(fname) && len_args == 2 + print_arg(io, first(args), paren=true) + print(io, " $fname ") + print_arg(io, last(args), paren=true) else if issym(f) Base.show_unquoted(io, nameof(f)) From 83dd375211886250f879162d66e0dd60c06a6314 Mon Sep 17 00:00:00 2001 From: Shashi Gowda Date: Tue, 21 Feb 2023 13:08:17 -0500 Subject: [PATCH 07/34] apply arithmetic operations in basic symbolics only if binary --- src/types.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types.jl b/src/types.jl index 1ee2543c..9160ead3 100644 --- a/src/types.jl +++ b/src/types.jl @@ -532,7 +532,7 @@ function basic_similarterm(t, f, args, stype; metadata=nothing) if T === nothing T = _promote_symtype(f, args) end - if stype <: Number && (f in (+, *) || (f in (/, ^) && length(args) == 2)) && all(x->symtype(x) <: Number, args) + if stype <: Number && (f in (/, ^, +, *) && length(args) == 2) && all(x->symtype(x) <: Number, args) res = f(args...) if res isa Symbolic @set! res.metadata = metadata From c3cd7b20e742134c3a6e48bb6e7faa03ffe8f4c2 Mon Sep 17 00:00:00 2001 From: Shashi Gowda Date: Tue, 21 Feb 2023 15:13:21 -0500 Subject: [PATCH 08/34] revert and fix basic_similarterm with isterm check --- src/types.jl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/types.jl b/src/types.jl index 9160ead3..f36eac5d 100644 --- a/src/types.jl +++ b/src/types.jl @@ -532,7 +532,9 @@ function basic_similarterm(t, f, args, stype; metadata=nothing) if T === nothing T = _promote_symtype(f, args) end - if stype <: Number && (f in (/, ^, +, *) && length(args) == 2) && all(x->symtype(x) <: Number, args) + if isterm(t) + Term{T}(f, args, metadata=metadata) + elseif stype <: Number && (f in (+, *) || (f in (/, ^) && length(args) == 2)) && all(x->symtype(x) <: Number, args) res = f(args...) if res isa Symbolic @set! res.metadata = metadata From cb1351d499ac39f3b7d137d4f3578ab89b379dd0 Mon Sep 17 00:00:00 2001 From: Yingbo Ma Date: Fri, 17 Mar 2023 15:20:05 -0400 Subject: [PATCH 09/34] Fix -x printing --- src/types.jl | 2 +- test/basics.jl | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/types.jl b/src/types.jl index 1ee2543c..4cc97efc 100644 --- a/src/types.jl +++ b/src/types.jl @@ -778,7 +778,7 @@ end function show_call(io, f, args) fname = istree(f) ? Symbol(repr(f)) : nameof(f) - binary = Base.isbinaryoperator(fname) + binary = Base.isbinaryoperator(fname) && length(args) > 1 if binary for (i, t) in enumerate(args) i != 1 && print(io, " $fname ") diff --git a/test/basics.jl b/test/basics.jl index 75e31060..23a5fdc2 100644 --- a/test/basics.jl +++ b/test/basics.jl @@ -1,4 +1,4 @@ -using SymbolicUtils: Symbolic, Sym, FnType, Term, Add, Mul, Pow, symtype, operation, arguments, issym, isterm, BasicSymbolic +using SymbolicUtils: Symbolic, Sym, FnType, Term, Add, Mul, Pow, symtype, operation, arguments, issym, isterm, BasicSymbolic, term using SymbolicUtils using IfElse: ifelse using Setfield @@ -173,6 +173,7 @@ end @syms a b c @test repr(a+b) == "a + b" @test repr(-a) == "-a" + @test repr(term(-, a; type = Real)) == "-(a)" @test repr(-a + 3) == "3 - a" @test repr(-(a + b)) == "-a - b" @test repr((2a)^(-2a)) == "(2a)^(-2a)" From 7a9f0c55a513f3350c9af5074de662127a6a9b4b Mon Sep 17 00:00:00 2001 From: Yingbo Ma Date: Fri, 17 Mar 2023 15:43:36 -0400 Subject: [PATCH 10/34] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 714c08c0..b6af1438 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "SymbolicUtils" uuid = "d1185830-fcd6-423d-90d6-eec64667417b" authors = ["Shashi Gowda"] -version = "1.0.3" +version = "1.0.4" [deps] AbstractTrees = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" From a3ed38be10fd9228a310763ae036f025659a9bda Mon Sep 17 00:00:00 2001 From: Shashi Gowda Date: Fri, 24 Mar 2023 14:38:25 -0400 Subject: [PATCH 11/34] call functions in basic_similarterm if output type is not LiteralReal. --- src/types.jl | 2 +- test/rulesets.jl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/types.jl b/src/types.jl index 39cc41c9..7a1ed403 100644 --- a/src/types.jl +++ b/src/types.jl @@ -532,7 +532,7 @@ function basic_similarterm(t, f, args, stype; metadata=nothing) if T === nothing T = _promote_symtype(f, args) end - if isterm(t) + if T <: LiteralReal Term{T}(f, args, metadata=metadata) elseif stype <: Number && (f in (+, *) || (f in (/, ^) && length(args) == 2)) && all(x->symtype(x) <: Number, args) res = f(args...) diff --git a/test/rulesets.jl b/test/rulesets.jl index 82306a5d..cd1ada54 100644 --- a/test/rulesets.jl +++ b/test/rulesets.jl @@ -63,7 +63,7 @@ end @eqtest s != cos(3.2(x1^1)) - 4.6x2 # However, manual simplification should still work: - @eqtest simplify(s) == cos(3.2(x1^1)) - 4.6x2 + @eqtest simplify(s) == simplify(cos(3.2x1) - 4.6x2) end @testset "boolean" begin From 1538cc5847b98d774c4ef3f92d3bf3367fa11394 Mon Sep 17 00:00:00 2001 From: Shashi Gowda Date: Fri, 24 Mar 2023 15:28:33 -0400 Subject: [PATCH 12/34] don't do clever printing when LiteralReal --- src/types.jl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/types.jl b/src/types.jl index fa08146a..c5ef4b5e 100644 --- a/src/types.jl +++ b/src/types.jl @@ -811,8 +811,9 @@ function show_term(io::IO, t) f = operation(t) args = arguments(t) - - if f === (+) + if symtype(t) <: LiteralReal + show_call(io, f, args) + elseif f === (+) show_add(io, args) elseif f === (*) show_mul(io, args) From ef02babc5db9d198ec1c6f72ff4dad8f436b0d8c Mon Sep 17 00:00:00 2001 From: Shashi Gowda Date: Mon, 10 Apr 2023 15:10:50 -0400 Subject: [PATCH 13/34] fix some printing tests --- test/basics.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/basics.jl b/test/basics.jl index 23a5fdc2..ad11ee90 100644 --- a/test/basics.jl +++ b/test/basics.jl @@ -173,7 +173,7 @@ end @syms a b c @test repr(a+b) == "a + b" @test repr(-a) == "-a" - @test repr(term(-, a; type = Real)) == "-(a)" + @test repr(term(-, a; type = Real)) == "-a" @test repr(-a + 3) == "3 - a" @test repr(-(a + b)) == "-a - b" @test repr((2a)^(-2a)) == "(2a)^(-2a)" @@ -280,8 +280,8 @@ end @testset "LiteralReal" begin @syms x::LiteralReal y::LiteralReal z::LiteralReal @test repr(x+x) == "x + x" - @test repr(x*x) == "x*x" - @test repr(x*x + x*x) == "x*x + x*x" + @test repr(x*x) == "x * x" + @test repr(x*x + x*x) == "(x * x) + (x * x)" for ex in [sin(x), x+x, x*x, x\x, x/x] @test typeof(sin(x)) <: BasicSymbolic{LiteralReal} end From effa3c71d7c7d38f6da6906d6ce850ed4d3f69e7 Mon Sep 17 00:00:00 2001 From: Shashi Gowda Date: Mon, 10 Apr 2023 16:18:40 -0400 Subject: [PATCH 14/34] patch release --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index b6af1438..73ab49d9 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "SymbolicUtils" uuid = "d1185830-fcd6-423d-90d6-eec64667417b" authors = ["Shashi Gowda"] -version = "1.0.4" +version = "1.0.5" [deps] AbstractTrees = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" From b2201e7f52960f5bee9803635cca86aeea899d1b Mon Sep 17 00:00:00 2001 From: Miles Cranmer Date: Sun, 7 May 2023 21:21:02 -0400 Subject: [PATCH 15/34] Automatically benchmark PRs --- .github/workflows/benchmark_pr.yml | 78 ++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 .github/workflows/benchmark_pr.yml diff --git a/.github/workflows/benchmark_pr.yml b/.github/workflows/benchmark_pr.yml new file mode 100644 index 00000000..20a5467b --- /dev/null +++ b/.github/workflows/benchmark_pr.yml @@ -0,0 +1,78 @@ +name: Benchmark a pull request + +on: + pull_request_target: + branches: + - master + +permissions: + pull-requests: write + +jobs: + generate_plots: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - uses: julia-actions/setup-julia@v1 + with: + version: "1.8" + - uses: julia-actions/cache@v1 + - name: Extract Package Name from Project.toml + id: extract-package-name + run: | + PACKAGE_NAME=$(grep "^name" Project.toml | sed 's/^name = "\(.*\)"$/\1/') + echo "::set-output name=package_name::$PACKAGE_NAME" + - name: Build AirspeedVelocity + env: + JULIA_NUM_THREADS: 2 + run: | + # Lightweight build step, as sometimes the runner runs out of memory: + julia -e 'ENV["JULIA_PKG_PRECOMPILE_AUTO"]=0; import Pkg; Pkg.add("AirspeedVelocity")' + julia -e 'ENV["JULIA_PKG_PRECOMPILE_AUTO"]=0; import Pkg; Pkg.build("AirspeedVelocity")' + - name: Add ~/.julia/bin to PATH + run: | + echo "$HOME/.julia/bin" >> $GITHUB_PATH + - name: Run benchmarks + run: | + echo $PATH + ls -l ~/.julia/bin + mkdir results + benchpkg ${{ steps.extract-package-name.outputs.package_name }} --rev="${{github.event.repository.default_branch}},${{github.event.pull_request.head.sha}}" --url=${{ github.event.repository.clone_url }} --bench-on="${{github.event.repository.default_branch}}" --output-dir=results/ --tune --exeflags="-O3 --threads=auto" + - name: Create plots from benchmarks + run: | + mkdir -p plots + benchpkgplot ${{ steps.extract-package-name.outputs.package_name }} --rev="${{github.event.repository.default_branch}},${{github.event.pull_request.head.sha}}" --npart=10 --format=png --input-dir=results/ --output-dir=plots/ + - name: Upload plot as artifact + uses: actions/upload-artifact@v2 + with: + name: plots + path: plots + - name: Create markdown table from benchmarks + run: | + benchpkgtable ${{ steps.extract-package-name.outputs.package_name }} --rev="${{github.event.repository.default_branch}},${{github.event.pull_request.head.sha}}" --input-dir=results/ --ratio > table.md + echo '### Benchmark Results' > body.md + echo '' >> body.md + echo '' >> body.md + cat table.md >> body.md + echo '' >> body.md + echo '' >> body.md + echo '### Benchmark Plots' >> body.md + echo 'A plot of the benchmark results have been uploaded as an artifact to the workflow run for this PR.' >> body.md + echo 'Go to "Actions"->"Benchmark a pull request"->[the most recent run]->"Artifacts" (at the bottom).' >> body.md + + - name: Find Comment + uses: peter-evans/find-comment@v2 + id: fcbenchmark + with: + issue-number: ${{ github.event.pull_request.number }} + comment-author: 'github-actions[bot]' + body-includes: Benchmark Results + + - name: Comment on PR + uses: peter-evans/create-or-update-comment@v3 + with: + comment-id: ${{ steps.fcbenchmark.outputs.comment-id }} + issue-number: ${{ github.event.pull_request.number }} + body-path: body.md + edit-mode: replace From dc4b9517e76b9c0914712d7ef2c60fe37979bb4d Mon Sep 17 00:00:00 2001 From: MilesCranmer Date: Sun, 7 May 2023 21:24:10 -0400 Subject: [PATCH 16/34] Specify dependencies of benchmarks --- benchmark/Project.toml | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 benchmark/Project.toml diff --git a/benchmark/Project.toml b/benchmark/Project.toml new file mode 100644 index 00000000..d02b4e6a --- /dev/null +++ b/benchmark/Project.toml @@ -0,0 +1,2 @@ +[deps] +Metatheory = "e9d8d322-4543-424a-9be4-0cc815abe26c" From 0bb36965dfcce6109bc0a3592d1ba7b6d155d157 Mon Sep 17 00:00:00 2001 From: Shashi Gowda Date: Tue, 13 Jun 2023 21:05:58 -0400 Subject: [PATCH 17/34] avoid applying canonicalizing rules on canonical form --- src/simplify_rules.jl | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/simplify_rules.jl b/src/simplify_rules.jl index bc1e216a..caac3426 100644 --- a/src/simplify_rules.jl +++ b/src/simplify_rules.jl @@ -7,14 +7,12 @@ the argument to the predicate satisfies `istree` and `operation(x) == f` is_operation(f) = @nospecialize(x) -> istree(x) && (operation(x) == f) let - PLUS_RULES = [ + CANONICALIZE_PLUS = [ @rule(~x::isnotflat(+) => flatten_term(+, ~x)) @rule(~x::needs_sorting(+) => sort_args(+, ~x)) @ordered_acrule(~a::is_literal_number + ~b::is_literal_number => ~a + ~b) @acrule(*(~~x) + *(~β, ~~x) => *(1 + ~β, (~~x)...)) - @acrule(*(~α, ~~x) + *(~β, ~~x) => *(~α + ~β, (~~x)...)) - @acrule(*(~~x, ~α) + *(~~x, ~β) => *(~α + ~β, (~~x)...)) @acrule(~x + *(~β, ~x) => *(1 + ~β, ~x)) @acrule(*(~α::is_literal_number, ~x) + ~x => *(~α + 1, ~x)) @@ -24,7 +22,12 @@ let @rule(+(~x) => ~x) ] - TIMES_RULES = [ + PLUS_DISTRIBUTE = [ + @acrule(*(~α, ~~x) + *(~β, ~~x) => *(~α + ~β, (~~x)...)) + @acrule(*(~~x, ~α) + *(~~x, ~β) => *(~α + ~β, (~~x)...)) + ] + + CANONICALIZE_TIMES = [ @rule(~x::isnotflat(*) => flatten_term(*, ~x)) @rule(~x::needs_sorting(*) => sort_args(*, ~x)) @@ -40,7 +43,7 @@ let ] - POW_RULES = [ + CANONICALIZE_POW = [ @rule(^(*(~~x), ~y::_isinteger) => *(map(a->pow(a, ~y), ~~x)...)) @rule((((~x)^(~p::_isinteger))^(~q::_isinteger)) => (~x)^((~p)*(~q))) @rule(^(~x, ~z::_iszero) => 1) @@ -120,12 +123,13 @@ let function number_simplifier() rule_tree = [If(istree, Chain(ASSORTED_RULES)), - If(is_operation(+), - Chain(PLUS_RULES)), - If(is_operation(*), - Chain(TIMES_RULES)), - If(is_operation(^), - Chain(POW_RULES))] |> RestartedChain + If(x -> !isadd(x) && is_operation(+)(x), + Chain(CANONICALIZE_PLUS)), + If(is_operation(+), Chain(PLUS_DISTRIBUTE)), # This would be useful even if isadd + If(x -> !ismul(x) && is_operation(*)(x), + Chain(CANONICALIZE_TIMES)), + If(x -> !ispow(x) && is_operation(^)(x), + Chain(CANONICALIZE_POW))] |> RestartedChain rule_tree end From 3ad4632c2443cd220904755189b4431ce4a4aa11 Mon Sep 17 00:00:00 2001 From: Shashi Gowda Date: Tue, 13 Jun 2023 21:17:18 -0400 Subject: [PATCH 18/34] multiplication rule to match previous behavior --- src/simplify_rules.jl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/simplify_rules.jl b/src/simplify_rules.jl index caac3426..ca5eaf35 100644 --- a/src/simplify_rules.jl +++ b/src/simplify_rules.jl @@ -35,13 +35,14 @@ let @rule(*(~~x::hasrepeats) => *(merge_repeats(^, ~~x)...)) @acrule((~y)^(~n) * ~y => (~y)^(~n+1)) - @ordered_acrule((~x)^(~n) * (~x)^(~m) => (~x)^(~n + ~m)) @ordered_acrule((~z::_isone * ~x) => ~x) @ordered_acrule((~z::_iszero * ~x) => ~z) @rule(*(~x) => ~x) ] + MUL_DISTRIBUTE = @ordered_acrule((~x)^(~n) * (~x)^(~m) => (~x)^(~n + ~m)) + CANONICALIZE_POW = [ @rule(^(*(~~x), ~y::_isinteger) => *(map(a->pow(a, ~y), ~~x)...)) @@ -126,6 +127,7 @@ let If(x -> !isadd(x) && is_operation(+)(x), Chain(CANONICALIZE_PLUS)), If(is_operation(+), Chain(PLUS_DISTRIBUTE)), # This would be useful even if isadd + If(is_operation(*), MUL_DISTRIBUTE), # Same If(x -> !ismul(x) && is_operation(*)(x), Chain(CANONICALIZE_TIMES)), If(x -> !ispow(x) && is_operation(^)(x), From 7169244e587d15936d08b07aa4be266ced418b30 Mon Sep 17 00:00:00 2001 From: Shashi Gowda Date: Tue, 13 Jun 2023 21:22:46 -0400 Subject: [PATCH 19/34] Similarly, a Pow rule --- src/simplify_rules.jl | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/simplify_rules.jl b/src/simplify_rules.jl index ca5eaf35..88a1a648 100644 --- a/src/simplify_rules.jl +++ b/src/simplify_rules.jl @@ -50,6 +50,9 @@ let @rule(^(~x, ~z::_iszero) => 1) @rule(^(~x, ~z::_isone) => ~x) @rule(inv(~x) => 1/(~x)) + ] + + POW_RULES = [ @rule(^(~x::_isone, ~z) => 1) ] @@ -127,11 +130,13 @@ let If(x -> !isadd(x) && is_operation(+)(x), Chain(CANONICALIZE_PLUS)), If(is_operation(+), Chain(PLUS_DISTRIBUTE)), # This would be useful even if isadd - If(is_operation(*), MUL_DISTRIBUTE), # Same If(x -> !ismul(x) && is_operation(*)(x), Chain(CANONICALIZE_TIMES)), + If(is_operation(*), MUL_DISTRIBUTE), If(x -> !ispow(x) && is_operation(^)(x), - Chain(CANONICALIZE_POW))] |> RestartedChain + Chain(CANONICALIZE_POW)), + If(is_operation(^), Chain(POW_RULES)), + ] |> RestartedChain rule_tree end From e1e3d6159f9dbf3369a4485267a7c3ece43f650f Mon Sep 17 00:00:00 2001 From: Brad Carman Date: Wed, 14 Jun 2023 21:49:34 +0200 Subject: [PATCH 20/34] UInt fix --- src/types.jl | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/types.jl b/src/types.jl index c5ef4b5e..09490650 100644 --- a/src/types.jl +++ b/src/types.jl @@ -223,22 +223,28 @@ Base.nameof(s::BasicSymbolic) = issym(s) ? s.name : error("None Sym BasicSymboli ## This is much faster than hash of an array of Any hashvec(xs, z) = foldr(hash, xs, init=z) + +const csym = typemax(UInt) +const cadd = typemax(UInt) - 1 +const csub = typemax(UInt) - 2 +const cdiv = typemax(UInt) - 3 +const cpow = typemax(UInt) - 4 function Base.hash(s::BasicSymbolic, salt::UInt) E = exprtype(s) if E === SYM - hash(nameof(s), salt ⊻ 0x4de7d7c66d41da43) + hash(nameof(s), salt ⊻ csym) elseif E === ADD || E === MUL - !iszero(salt) && return hash(hash(s, zero(UInt64)), salt) + !iszero(salt) && return hash(hash(s, zero(UInt)), salt) h = s.hash[] !iszero(h) && return h - hashoffset = isadd(s) ? 0xaddaddaddaddadda : 0xaaaaaaaaaaaaaaaa - h′= hash(hashoffset, hash(s.coeff, hash(s.dict, salt))) + hashoffset = isadd(s) ? cadd : csub + h′ = hash(hashoffset, hash(s.coeff, hash(s.dict, salt))) s.hash[] = h′ return h′ elseif E === DIV - return hash(s.num, hash(s.den, salt ⊻ 0x334b218e73bbba53)) + return hash(s.num, hash(s.den, salt ⊻ cdiv)) elseif E === POW - hash(s.exp, hash(s.base, salt ⊻ 0x2b55b97a6efb080c)) + hash(s.exp, hash(s.base, salt ⊻ cpow)) elseif E === TERM !iszero(salt) && return hash(hash(s, zero(UInt)), salt) h = s.hash[] From fd44a0dd6c8054bea4a24c94cd3328cb2e5e7c43 Mon Sep 17 00:00:00 2001 From: Brad Carman Date: Wed, 14 Jun 2023 22:52:29 +0200 Subject: [PATCH 21/34] implemented % UInt --- src/types.jl | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/types.jl b/src/types.jl index 09490650..2c68e1bb 100644 --- a/src/types.jl +++ b/src/types.jl @@ -224,12 +224,12 @@ Base.nameof(s::BasicSymbolic) = issym(s) ? s.name : error("None Sym BasicSymboli ## This is much faster than hash of an array of Any hashvec(xs, z) = foldr(hash, xs, init=z) -const csym = typemax(UInt) -const cadd = typemax(UInt) - 1 -const csub = typemax(UInt) - 2 -const cdiv = typemax(UInt) - 3 -const cpow = typemax(UInt) - 4 -function Base.hash(s::BasicSymbolic, salt::UInt) +const SYM_SALT = 0x4de7d7c66d41da43 % UInt +const ADD_SALT = 0xaddaddaddaddadda % UInt +const SUB_SALT = 0xaaaaaaaaaaaaaaaa % UInt +const DIV_SALT = 0x334b218e73bbba53 % UInt +const POW_SALT = 0x2b55b97a6efb080c % UInt +function Base.hash(s::BasiSYM_SALTbolic, salt::UInt) E = exprtype(s) if E === SYM hash(nameof(s), salt ⊻ csym) @@ -237,14 +237,14 @@ function Base.hash(s::BasicSymbolic, salt::UInt) !iszero(salt) && return hash(hash(s, zero(UInt)), salt) h = s.hash[] !iszero(h) && return h - hashoffset = isadd(s) ? cadd : csub + hashoffset = isadd(s) ? ADD_SALT : SUB_SALT h′ = hash(hashoffset, hash(s.coeff, hash(s.dict, salt))) s.hash[] = h′ return h′ elseif E === DIV - return hash(s.num, hash(s.den, salt ⊻ cdiv)) + return hash(s.num, hash(s.den, salt ⊻ DIV_SALT)) elseif E === POW - hash(s.exp, hash(s.base, salt ⊻ cpow)) + hash(s.exp, hash(s.base, salt ⊻ POW_SALT)) elseif E === TERM !iszero(salt) && return hash(hash(s, zero(UInt)), salt) h = s.hash[] From b93d74c17acb9a435506ef0f9238e7e366615b47 Mon Sep 17 00:00:00 2001 From: Brad Carman Date: Wed, 14 Jun 2023 22:53:27 +0200 Subject: [PATCH 22/34] typo fix --- src/types.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/types.jl b/src/types.jl index 2c68e1bb..501effdc 100644 --- a/src/types.jl +++ b/src/types.jl @@ -229,10 +229,10 @@ const ADD_SALT = 0xaddaddaddaddadda % UInt const SUB_SALT = 0xaaaaaaaaaaaaaaaa % UInt const DIV_SALT = 0x334b218e73bbba53 % UInt const POW_SALT = 0x2b55b97a6efb080c % UInt -function Base.hash(s::BasiSYM_SALTbolic, salt::UInt) +function Base.hash(s::BasicSymbolic, salt::UInt) E = exprtype(s) if E === SYM - hash(nameof(s), salt ⊻ csym) + hash(nameof(s), salt ⊻ SYM_SALT) elseif E === ADD || E === MUL !iszero(salt) && return hash(hash(s, zero(UInt)), salt) h = s.hash[] From 419f7c1ed55fec4693af440454c2617c4de8c209 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Wed, 5 Jul 2023 10:18:36 +0200 Subject: [PATCH 23/34] Update to MultivariatePolynomials v0.5 --- Project.toml | 4 ++-- docs/src/manual/representation.md | 2 +- page/representation.md | 2 +- src/SymbolicUtils.jl | 3 +-- src/polyform.jl | 5 ++--- 5 files changed, 7 insertions(+), 9 deletions(-) diff --git a/Project.toml b/Project.toml index 73ab49d9..acbc7fbe 100644 --- a/Project.toml +++ b/Project.toml @@ -32,10 +32,10 @@ Combinatorics = "1.0" ConstructionBase = "1.1" DataStructures = "0.18" DocStringExtensions = "0.8, 0.9" -DynamicPolynomials = "0.3, 0.4" +DynamicPolynomials = "0.5" IfElse = "0.1" LabelledArrays = "1.5" -MultivariatePolynomials = "0.3, 0.4" +MultivariatePolynomials = "0.5" NaNMath = "0.3, 1" Setfield = "0.7, 0.8, 1" SpecialFunctions = "0.10, 1.0, 2" diff --git a/docs/src/manual/representation.md b/docs/src/manual/representation.md index b9664abe..1c8dde52 100644 --- a/docs/src/manual/representation.md +++ b/docs/src/manual/representation.md @@ -23,7 +23,7 @@ $p / q$ is represented by `Div(p, q)`. The result of `*` on `Div` is maintainted Packages like DynamicPolynomials.jl provide representations that are even more efficient than the `Add` and `Mul` types mentioned above. They are designed specifically for multi-variate polynomials. They provide common algorithms such as multi-variate polynomial GCD. The restrictions that make it fast also mean some things are not possible: Firstly, DynamicPolynomials can only represent flat polynomials. For example, `(x-3)*(x+5)` can only be represented as `(x^2) + 15 - 8x`. Secondly, DynamicPolynomials does not have ways to represent generic Terms such as `sin(x-y)` in the tree. -To reconcile these differences while being able to use the efficient algorithms of DynamicPolynomials we have the `PolyForm` type. This type holds a polynomial and the mappings necessary to present the polynomial as a SymbolicUtils expression (i.e. by defining `operation` and `arguments`). The mappings constructed for the conversion are 1) a bijection from DynamicPolynomials PolyVar type to a Symbolics `Sym`, and 2) a mapping from `Sym`s to non-polynomial terms that the `Sym`s stand-in for. These terms may themselves contain PolyForm if there are polynomials inside them. The mappings are transiently global, that is, when all references to the mappings go out of scope, they are released and re-created. +To reconcile these differences while being able to use the efficient algorithms of DynamicPolynomials we have the `PolyForm` type. This type holds a polynomial and the mappings necessary to present the polynomial as a SymbolicUtils expression (i.e. by defining `operation` and `arguments`). The mappings constructed for the conversion are 1) a bijection from DynamicPolynomials Variable type to a Symbolics `Sym`, and 2) a mapping from `Sym`s to non-polynomial terms that the `Sym`s stand-in for. These terms may themselves contain PolyForm if there are polynomials inside them. The mappings are transiently global, that is, when all references to the mappings go out of scope, they are released and re-created. ```julia julia> @syms x y diff --git a/page/representation.md b/page/representation.md index fe6dc55f..b8932b5f 100644 --- a/page/representation.md +++ b/page/representation.md @@ -23,7 +23,7 @@ $p / q$ is represented by `Div(p, q)`. The result of `*` on `Div` is maintainted Packages like DynamicPolynomials.jl provide representations that are even more efficient than the `Add` and `Mul` types mentioned above. They are designed specifically for multi-variate polynomials. They provide common algorithms such as multi-variate polynomial GCD. The restrictions that make it fast also mean some things are not possible: Firstly, DynamicPolynomials can only represent flat polynomials. For example, `(x-3)*(x+5)` can only be represented as `(x^2) + 15 - 8x`. Secondly, DynamicPolynomials does not have ways to represent generic Terms such as `sin(x-y)` in the tree. -To reconcile these differences while being able to use the efficient algorithms of DynamicPolynomials we have the `PolyForm` type. This type holds a polynomial and the mappings necessary to present the polynomial as a SymbolicUtils expression (i.e. by defining `operation` and `arguments`). The mappings constructed for the conversion are 1) a bijection from DynamicPolynomials PolyVar type to a Symbolics `Sym`, and 2) a mapping from `Sym`s to non-polynomial terms that the `Sym`s stand-in for. These terms may themselves contain PolyForm if there are polynomials inside them. The mappings are transiently global, that is, when all references to the mappings go out of scope, they are released and re-created. +To reconcile these differences while being able to use the efficient algorithms of DynamicPolynomials we have the `PolyForm` type. This type holds a polynomial and the mappings necessary to present the polynomial as a SymbolicUtils expression (i.e. by defining `operation` and `arguments`). The mappings constructed for the conversion are 1) a bijection from DynamicPolynomials Variable type to a Symbolics `Sym`, and 2) a mapping from `Sym`s to non-polynomial terms that the `Sym`s stand-in for. These terms may themselves contain PolyForm if there are polynomials inside them. The mappings are transiently global, that is, when all references to the mappings go out of scope, they are released and re-created. ```julia julia> @syms x y diff --git a/src/SymbolicUtils.jl b/src/SymbolicUtils.jl index 934431b3..2655263b 100644 --- a/src/SymbolicUtils.jl +++ b/src/SymbolicUtils.jl @@ -40,8 +40,7 @@ include("matchers.jl") include("rewriters.jl") # Convert to an efficient multi-variate polynomial representation -import MultivariatePolynomials -const MP = MultivariatePolynomials +import MultivariatePolynomials as MP import DynamicPolynomials export expand include("polyform.jl") diff --git a/src/polyform.jl b/src/polyform.jl index 4da965ed..fa93e7f6 100644 --- a/src/polyform.jl +++ b/src/polyform.jl @@ -1,6 +1,5 @@ export PolyForm, simplify_fractions, quick_cancel, flatten_fractions using Bijections -using DynamicPolynomials: PolyVar """ PolyForm{T} <: Symbolic @@ -44,7 +43,7 @@ end Base.hash(p::PolyForm, u::UInt64) = xor(hash(p.p, u), trunc(UInt, 0xbabacacababacaca)) Base.isequal(x::PolyForm, y::PolyForm) = isequal(x.p, y.p) -# We use the same PVAR2SYM bijection to maintain the PolyVar <-> Sym mapping, +# We use the same PVAR2SYM bijection to maintain the MP.AbstractVariable <-> Sym mapping, # When all PolyForms go out of scope in a session, we allow it to free up memory and # start over if necessary const PVAR2SYM = Ref(WeakRef()) @@ -156,7 +155,7 @@ end function PolyForm(x, pvar2sym=get_pvar2sym(), sym2term=get_sym2term(), - vtype=DynamicPolynomials.PolyVar{true}; + vtype=MP.AbstractVariable; Fs = Union{typeof(+), typeof(*), typeof(^)}, recurse=false, metadata=metadata(x)) From 9fd654756c75e0034a17aa112e009a41e8c15724 Mon Sep 17 00:00:00 2001 From: Chris Elrod Date: Mon, 10 Jul 2023 14:22:30 -0400 Subject: [PATCH 24/34] optimize `isequal` --- src/types.jl | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/types.jl b/src/types.jl index c5ef4b5e..d49c9fa0 100644 --- a/src/types.jl +++ b/src/types.jl @@ -189,6 +189,15 @@ Base.isequal(::Symbolic, x) = false Base.isequal(x, ::Symbolic) = false Base.isequal(::Symbolic, ::Symbolic) = false coeff_isequal(a, b) = isequal(a, b) || ((a isa AbstractFloat || b isa AbstractFloat) && (a==b)) +function _allarequal(xs, ys)::Bool + N = length(xs) + length(ys) == N || return false + for n = 1:N + isequal(xs[n], ys[n]) || return false + end + return true +end + function Base.isequal(a::BasicSymbolic{T}, b::BasicSymbolic{S}) where {T,S} a === b && return true @@ -196,7 +205,9 @@ function Base.isequal(a::BasicSymbolic{T}, b::BasicSymbolic{S}) where {T,S} E === exprtype(b) || return false T === S || return false - + return _isequal(a, b, E)::Bool +end +function _isequal(a, b, E) if E === SYM nameof(a) === nameof(b) elseif E === ADD || E === MUL @@ -208,9 +219,7 @@ function Base.isequal(a::BasicSymbolic{T}, b::BasicSymbolic{S}) where {T,S} elseif E === TERM a1 = arguments(a) a2 = arguments(b) - isequal(operation(a), operation(b)) && - length(a1) == length(a2) && - all(isequal(l, r) for (l, r) in zip(a1, a2)) + isequal(operation(a), operation(b)) && _allarequal(a1, a2) else error_on_type() end @@ -223,7 +232,7 @@ Base.nameof(s::BasicSymbolic) = issym(s) ? s.name : error("None Sym BasicSymboli ## This is much faster than hash of an array of Any hashvec(xs, z) = foldr(hash, xs, init=z) -function Base.hash(s::BasicSymbolic, salt::UInt) +function Base.hash(s::BasicSymbolic, salt::UInt)::UInt E = exprtype(s) if E === SYM hash(nameof(s), salt ⊻ 0x4de7d7c66d41da43) From c6ffe2f1bd77d672bb74b7b0b57546d704e931c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Tue, 11 Jul 2023 09:01:30 +0200 Subject: [PATCH 25/34] Update src/polyform.jl --- src/polyform.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/polyform.jl b/src/polyform.jl index fa93e7f6..c12982a3 100644 --- a/src/polyform.jl +++ b/src/polyform.jl @@ -155,7 +155,7 @@ end function PolyForm(x, pvar2sym=get_pvar2sym(), sym2term=get_sym2term(), - vtype=MP.AbstractVariable; + vtype=DynamicPolynomials.Variable{ DynamicPolynomials.Commutative{DynamicPolynomials.CreationOrder},DynamicPolynomials.Graded{MP.LexOrder}}; Fs = Union{typeof(+), typeof(*), typeof(^)}, recurse=false, metadata=metadata(x)) From 9285d05d3d33a2b891da353f6da7d785a10b725a Mon Sep 17 00:00:00 2001 From: Yingbo Ma Date: Wed, 26 Jul 2023 17:04:14 -0400 Subject: [PATCH 26/34] Add NaNMath lowering --- src/code.jl | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/src/code.jl b/src/code.jl index 1eb72051..3873ebff 100644 --- a/src/code.jl +++ b/src/code.jl @@ -1,6 +1,6 @@ module Code -using StaticArrays, LabelledArrays, SparseArrays, LinearAlgebra +using StaticArrays, LabelledArrays, SparseArrays, LinearAlgebra, NaNMath export toexpr, Assignment, (←), Let, Func, DestructuredArgs, LiteralExpr, SetArray, MakeArray, MakeSparseArray, MakeTuple, AtIndex, @@ -96,7 +96,29 @@ Base.convert(::Type{Assignment}, p::Pair) = Assignment(pair[1], pair[2]) toexpr(a::Assignment, st) = :($(toexpr(a.lhs, st)) = $(toexpr(a.rhs, st))) -function_to_expr(op, args, st) = nothing +const NaNMathFuns = ( + :sin, + :cos, + :tan, + :asin, + :acos, + :acosh, + :atanh, + :log, + :log2, + :log10, + :lgamma, + :log1p, + :sqrt, +) +function function_to_expr(op, args, st) + (op isa Function && (name = nameof(op)) in NaNMathFuns) && return nothing + fun = GlobalRef(NaNMath, name) + args = map(Base.Fix2(toexpr, st), arguments(O)) + expr = Expr(:call, fun) + expr.args = args + return expr +end function function_to_expr(op::Union{typeof(*),typeof(+)}, O, st) out = get(st.rewrites, O, nothing) From 88e2e138263b63f9ddc115a7ddf63f79bab485cd Mon Sep 17 00:00:00 2001 From: Yingbo Ma Date: Wed, 26 Jul 2023 17:10:19 -0400 Subject: [PATCH 27/34] Fix typo and test --- src/code.jl | 6 +++--- test/code.jl | 4 ++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/code.jl b/src/code.jl index 3873ebff..52be9c85 100644 --- a/src/code.jl +++ b/src/code.jl @@ -111,12 +111,12 @@ const NaNMathFuns = ( :log1p, :sqrt, ) -function function_to_expr(op, args, st) - (op isa Function && (name = nameof(op)) in NaNMathFuns) && return nothing +function function_to_expr(op, O, st) + (op isa Function && (name = nameof(op)) in NaNMathFuns) || return nothing fun = GlobalRef(NaNMath, name) args = map(Base.Fix2(toexpr, st), arguments(O)) expr = Expr(:call, fun) - expr.args = args + append!(expr.args, args) return expr end diff --git a/test/code.jl b/test/code.jl index 636023a5..5675b878 100644 --- a/test/code.jl +++ b/test/code.jl @@ -189,6 +189,10 @@ test_repr(a, b) = @test repr(Base.remove_linenums!(a)) == repr(Base.remove_linen f = eval(toexpr(Func([a+b], [], a+b))) @test f(1) == 1 @test f(2) == 2 + + f = eval(toexpr(Func([a, b], [], sqrt(a - b)))) + @test isnan(f(0, 10)) + @test f(10, 2) ≈ sqrt(8) end let From 382a88ffd25eb0d725cca27c1819992b7afaea9a Mon Sep 17 00:00:00 2001 From: Yingbo Ma Date: Wed, 26 Jul 2023 17:17:45 -0400 Subject: [PATCH 28/34] Check function identity --- src/code.jl | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/src/code.jl b/src/code.jl index 52be9c85..6674da27 100644 --- a/src/code.jl +++ b/src/code.jl @@ -1,6 +1,6 @@ module Code -using StaticArrays, LabelledArrays, SparseArrays, LinearAlgebra, NaNMath +using StaticArrays, LabelledArrays, SparseArrays, LinearAlgebra, NaNMath, SpecialFunctions export toexpr, Assignment, (←), Let, Func, DestructuredArgs, LiteralExpr, SetArray, MakeArray, MakeSparseArray, MakeTuple, AtIndex, @@ -97,22 +97,23 @@ Base.convert(::Type{Assignment}, p::Pair) = Assignment(pair[1], pair[2]) toexpr(a::Assignment, st) = :($(toexpr(a.lhs, st)) = $(toexpr(a.rhs, st))) const NaNMathFuns = ( - :sin, - :cos, - :tan, - :asin, - :acos, - :acosh, - :atanh, - :log, - :log2, - :log10, - :lgamma, - :log1p, - :sqrt, + sin, + cos, + tan, + asin, + acos, + acosh, + atanh, + log, + log2, + log10, + lgamma, + log1p, + sqrt, ) function function_to_expr(op, O, st) - (op isa Function && (name = nameof(op)) in NaNMathFuns) || return nothing + op in NaNMathFuns || return nothing + name = nameof(op) fun = GlobalRef(NaNMath, name) args = map(Base.Fix2(toexpr, st), arguments(O)) expr = Expr(:call, fun) From 312b0ce4d656d9280e0b2df97b305615a91aff4f Mon Sep 17 00:00:00 2001 From: Yingbo Ma Date: Wed, 26 Jul 2023 17:22:38 -0400 Subject: [PATCH 29/34] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 73ab49d9..8aa24e84 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "SymbolicUtils" uuid = "d1185830-fcd6-423d-90d6-eec64667417b" authors = ["Shashi Gowda"] -version = "1.0.5" +version = "1.1.0" [deps] AbstractTrees = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" From 325b673565d6129df1e7d4b03a2d766b2abfd1fd Mon Sep 17 00:00:00 2001 From: Shashi Gowda Date: Thu, 27 Jul 2023 15:44:27 -0400 Subject: [PATCH 30/34] Update Project.toml bump --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 8ad0cbce..1782e8a6 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "SymbolicUtils" uuid = "d1185830-fcd6-423d-90d6-eec64667417b" authors = ["Shashi Gowda"] -version = "1.1.0" +version = "1.1.1" [deps] AbstractTrees = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" From 43262130b4297f79e5dd15dc17ced4c5ead94aab Mon Sep 17 00:00:00 2001 From: Yingbo Ma Date: Mon, 31 Jul 2023 18:12:13 -0400 Subject: [PATCH 31/34] Register NaNMath functions --- src/methods.jl | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/methods.jl b/src/methods.jl index bdd96512..17b21b39 100644 --- a/src/methods.jl +++ b/src/methods.jl @@ -1,3 +1,4 @@ +import NaNMath import SpecialFunctions: gamma, loggamma, erf, erfc, erfcinv, erfi, erfcx, dawson, digamma, trigamma, invdigamma, polygamma, airyai, airyaiprime, airybi, airybiprime, besselj0, @@ -12,9 +13,12 @@ const monadic = [deg2rad, rad2deg, transpose, asind, log1p, acsch, atand, sec, acscd, cot, exp2, expm1, atanh, gamma, loggamma, erf, erfc, erfcinv, erfi, erfcx, dawson, digamma, trigamma, invdigamma, polygamma, airyai, airyaiprime, airybi, - airybiprime, besselj0, besselj1, bessely0, bessely1, isfinite] + airybiprime, besselj0, besselj1, bessely0, bessely1, isfinite, + NaNMath.sin, NaNMath.cos, NaNMath.tan, NaNMath.asin, NaNMath.acos, + NaNMath.acosh, NaNMath.atanh, NaNMath.log, NaNMath.log2, + NaNMath.log10, NaNMath.lgamma, NaNMath.log1p, NaNMath.sqrt] -const diadic = [max, min, hypot, atan, mod, rem, copysign, +const diadic = [max, min, hypot, atan, NaNMath.atanh, mod, rem, copysign, besselj, bessely, besseli, besselk, hankelh1, hankelh2, polygamma, beta, logbeta] const previously_declared_for = Set([]) From 1189564c8b25235ad216d1d2572ec110549d8c53 Mon Sep 17 00:00:00 2001 From: Yingbo Ma Date: Mon, 31 Jul 2023 18:20:33 -0400 Subject: [PATCH 32/34] Fix tests --- test/code.jl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/code.jl b/test/code.jl index 5675b878..32d773cd 100644 --- a/test/code.jl +++ b/test/code.jl @@ -1,4 +1,5 @@ using Test, SymbolicUtils +using NaNMath using SymbolicUtils.Code using SymbolicUtils.Code: LazyState using StaticArrays @@ -84,10 +85,10 @@ test_repr(a, b) = @test repr(Base.remove_linenums!(a)) == repr(Base.remove_linen @test toexpr(SetArray(true, a, [x(t), AtIndex(9, b), c])).head == :macrocall test_repr(toexpr(LiteralExpr(:(let x=1, y=2 - $(sin(a+b)) + $(NaNMath.sin(a+b)) end))), :(let x = 1, y = 2 - $(sin)($(+)(a, b)) + $(NaNMath.sin)($(+)(a, b)) end)) test_repr(toexpr(MakeArray([a,b,a+b], :arr)), From 4f1540bd3aa20ef411795fe8c4532521dfb82a10 Mon Sep 17 00:00:00 2001 From: Yingbo Ma Date: Mon, 31 Jul 2023 20:02:37 -0400 Subject: [PATCH 33/34] Add nanmath as an option --- src/code.jl | 2 +- test/code.jl | 15 ++++++++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/code.jl b/src/code.jl index 6674da27..23ca867e 100644 --- a/src/code.jl +++ b/src/code.jl @@ -112,7 +112,7 @@ const NaNMathFuns = ( sqrt, ) function function_to_expr(op, O, st) - op in NaNMathFuns || return nothing + (get(st.rewrites, :nanmath, false) && op in NaNMathFuns) || return nothing name = nameof(op) fun = GlobalRef(NaNMath, name) args = map(Base.Fix2(toexpr, st), arguments(O)) diff --git a/test/code.jl b/test/code.jl index 32d773cd..1cf181a7 100644 --- a/test/code.jl +++ b/test/code.jl @@ -8,6 +8,8 @@ using SparseArrays using LinearAlgebra test_repr(a, b) = @test repr(Base.remove_linenums!(a)) == repr(Base.remove_linenums!(b)) +nanmath_st = Code.NameState() +nanmath_st.rewrites[:nanmath] = true @testset "Code" begin @syms a b c d e p q t x(t) y(t) z(t) @@ -84,11 +86,18 @@ test_repr(a, b) = @test repr(Base.remove_linenums!(a)) == repr(Base.remove_linen end) @test toexpr(SetArray(true, a, [x(t), AtIndex(9, b), c])).head == :macrocall + f = GlobalRef(NaNMath, :sin) test_repr(toexpr(LiteralExpr(:(let x=1, y=2 - $(NaNMath.sin(a+b)) + $(sin(a+b)) + end)), nanmath_st), + :(let x = 1, y = 2 + $(f)($(+)(a, b)) + end)) + test_repr(toexpr(LiteralExpr(:(let x=1, y=2 + $(sin(a+b)) end))), :(let x = 1, y = 2 - $(NaNMath.sin)($(+)(a, b)) + $(sin)($(+)(a, b)) end)) test_repr(toexpr(MakeArray([a,b,a+b], :arr)), @@ -191,7 +200,7 @@ test_repr(a, b) = @test repr(Base.remove_linenums!(a)) == repr(Base.remove_linen @test f(1) == 1 @test f(2) == 2 - f = eval(toexpr(Func([a, b], [], sqrt(a - b)))) + f = eval(toexpr(Func([a, b], [], sqrt(a - b)), nanmath_st)) @test isnan(f(0, 10)) @test f(10, 2) ≈ sqrt(8) end From 9f5873edb91265c79f4e2eeede0bb043c67c7ef9 Mon Sep 17 00:00:00 2001 From: Yingbo Ma Date: Mon, 31 Jul 2023 20:59:23 -0400 Subject: [PATCH 34/34] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 1782e8a6..1f50aa38 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "SymbolicUtils" uuid = "d1185830-fcd6-423d-90d6-eec64667417b" authors = ["Shashi Gowda"] -version = "1.1.1" +version = "1.2.0" [deps] AbstractTrees = "1520ce14-60c1-5f80-bbc7-55ef81b5835c"