juliasymbolics / symbolicutils.jl Goto Github PK
View Code? Open in Web Editor NEWSymbolic expressions, rewriting and simplification
Home Page: https://docs.sciml.ai/SymbolicUtils/stable/
License: Other
Symbolic expressions, rewriting and simplification
Home Page: https://docs.sciml.ai/SymbolicUtils/stable/
License: Other
julia> @syms α β γ δ
(α, β, γ, δ)
julia> isequal(α + 1, α + 1)
true
julia> α + 1 == α + 1
true
# but
julia> isequal((((1 / β - 1) + δ) / γ) ^ (1 / (γ - 1)), (((1 / β - 1) + δ) / γ) ^ (1 / (γ - 1)))
true
julia> (((1 / β - 1) + δ) / γ) ^ (1 / (γ - 1)) == (((1 / β - 1) + δ) / γ) ^ (1 / (γ - 1))
(((1 / β - 1) + δ) / γ) ^ (1 / (γ - 1)) == (((1 / β - 1) + δ) / γ) ^ (1 / (γ - 1))
julia> using SymbolicUtils
julia> @vars x
(x,)
julia> simplify(1x + 3x)
((3 * x) + x)
julia> SymbolicUtils.SIMPLIFY_RULES[11](1x + 3x)
(4 * x)
At first I thought this might have been caused by my recent modification to RuleSet
, but I checked out an old commit and that was not the problem.
let
@syms a b c d; Random.seed!(123);
function random_term(len; atoms, funs, fallback_atom=1)
xs = rand(atoms, len)
while length(xs) > 1
xs = map(Iterators.partition(xs, 2)) do xy
x = xy[1]; y = get(xy, 2, fallback_atom)
rand(funs)(x, y)
end
end
xs[]
end
ex = random_term(1000; atoms=[a, b, c, d, 1, 2.0], funs=[+, *, /])
#@btime simplify($ex, threaded=true, fixpoint=false)
simplify(ex, fixpoint=false)
end;
(posted by @MasonProtter)
Some theories are:
(try changing sorted_args to:)
function sort_args(f, args)
if length(args) < 2
return Term(f, args)
elseif length(args) == 2
x, y = args
return Term(f, x <ₑ y ? [x,y] : [y,x])
end
args = args isa Tuple ? [args...] : args
try
Term(f, sort(args, lt=<ₑ))
catch err
foreach(showraw, args) # added this try catch, that's the change
end
end
then you can see:
Internal error: encountered unexpected error in runtime:
StackOverflowError()
unknown function (ip: 0x7f0b15cbf833)
unknown function (ip: 0x7f0b15cc8a44)
unknown function (ip: 0x7f0b15c62e94)
unknown function (ip: 0x7f0b15c59637)
jl_matching_methods at /usr/bin/../lib/libjulia.so.1 (unknown line)
unknown function (ip: 0x7f0b08b913a0)
unknown function (ip: 0x7f0b08b9bcc4)
unknown function (ip: 0x7f0b08b9e6bc)
unknown function (ip: 0x7f0b08b9e862)
unknown function (ip: 0x7f0b08b9fb54)
unknown function (ip: 0x7f0b08ba2c4b)
unknown function (ip: 0x7f0b08ba84f1)
unknown function (ip: 0x7f0b08c158ac)
unknown function (ip: 0x7f0b08c16afc)
unknown function (ip: 0x7f0b08c173e4)
unknown function (ip: 0x7f0b08c1758f)
unknown function (ip: 0x7f0b15c60bf5)
unknown function (ip: 0x7f0b15c615a4)
jl_apply_generic at /usr/bin/../lib/libjulia.so.1 (unknown line)
foreach at ./abstractarray.jl:1919
sort_args at /home/shashi/.julia/dev/SymbolicUtils/src/simplify.jl:260
#92 at /home/shashi/.julia/dev/SymbolicUtils/src/rule_dsl.jl:169
macro expansion at /home/shashi/.julia/dev/SymbolicUtils/src/SymbolicUtils.jl:13 [inlined]
#25 at /home/shashi/.julia/dev/SymbolicUtils/src/rule_dsl.jl:38
loop at /home/shashi/.julia/dev/SymbolicUtils/src/matchers.jl:215
#19 at /home/shashi/.julia/dev/SymbolicUtils/src/matchers.jl:220
segment_matcher at /home/shashi/.julia/dev/SymbolicUtils/src/matchers.jl:193
loop at /home/shashi/.julia/dev/SymbolicUtils/src/matchers.jl:219
#19 at /home/shashi/.julia/dev/SymbolicUtils/src/matchers.jl:220
literal_matcher at /home/shashi/.julia/dev/SymbolicUtils/src/matchers.jl:129
loop at /home/shashi/.julia/dev/SymbolicUtils/src/matchers.jl:219
term_matcher at /home/shashi/.julia/dev/SymbolicUtils/src/matchers.jl:224
Rule at /home/shashi/.julia/dev/SymbolicUtils/src/rule_dsl.jl:36
#_#31 at /home/shashi/.julia/dev/SymbolicUtils/src/rule_dsl.jl:267
Any##kw at /home/shashi/.julia/dev/SymbolicUtils/src/rule_dsl.jl:252
unknown function (ip: 0x7f0aef12678c)
#_#31 at /home/shashi/.julia/dev/SymbolicUtils/src/rule_dsl.jl:275
Any##kw at /home/shashi/.julia/dev/SymbolicUtils/src/rule_dsl.jl:252
unknown function (ip: 0x7f0aef12678c)
#_#31 at /home/shashi/.julia/dev/SymbolicUtils/src/rule_dsl.jl:275
Any##kw at /home/shashi/.julia/dev/SymbolicUtils/src/rule_dsl.jl:252
unknown function (ip: 0x7f0aef12678c)
#_#31 at /home/shashi/.julia/dev/SymbolicUtils/src/rule_dsl.jl:275
Any##kw at /home/shashi/.julia/dev/SymbolicUtils/src/rule_dsl.jl:252
unknown function (ip: 0x7f0aef12678c)
#_#31 at /home/shashi/.julia/dev/SymbolicUtils/src/rule_dsl.jl:275
Any##kw at /home/shashi/.julia/dev/SymbolicUtils/src/rule_dsl.jl:252
unknown function (ip: 0x7f0aef12678c)
#_#31 at /home/shashi/.julia/dev/SymbolicUtils/src/rule_dsl.jl:275
Any##kw at /home/shashi/.julia/dev/SymbolicUtils/src/rule_dsl.jl:252
unknown function (ip: 0x7f0aef12678c)
#_#31 at /home/shashi/.julia/dev/SymbolicUtils/src/rule_dsl.jl:275
Any##kw at /home/shashi/.julia/dev/SymbolicUtils/src/rule_dsl.jl:252
unknown function (ip: 0x7f0aef12678c)
#_#31 at /home/shashi/.julia/dev/SymbolicUtils/src/rule_dsl.jl:275
Any##kw at /home/shashi/.julia/dev/SymbolicUtils/src/rule_dsl.jl:252
unknown function (ip: 0x7f0aef12678c)
#_#31 at /home/shashi/.julia/dev/SymbolicUtils/src/rule_dsl.jl:275
Any##kw at /home/shashi/.julia/dev/SymbolicUtils/src/rule_dsl.jl:252
unknown function (ip: 0x7f0aef12678c)
#_#31 at /home/shashi/.julia/dev/SymbolicUtils/src/rule_dsl.jl:275
Any##kw at /home/shashi/.julia/dev/SymbolicUtils/src/rule_dsl.jl:252
unknown function (ip: 0x7f0aef12678c)
Jameson thinks this could be real.
I tried the exprs shown in the failing rule applications, but they simplify just fine.
Hello,
Two small suggestions for the website:
_assets/favicon.png
or to remove this line: SymbolicUtils.jl/_layout/head.html
Line 12 in fd84074
publish
(since the commit messages are franklin-update
), but you don't have to because you also use the github action which builds your website so you could:
__site
and put it in your .gitignore
git push
your changes and let the github action do the restsin(~x)::oftype(Number)
basically the predicate must be run on the matched term itself.matcher
in the macro). Can also allow a Token
type for users to write custom matchers for some rules.*(~~x::hasrepeats)
should not search all possible sub args because it keeps failing. There's only one possible match: all the arguments.Break()
-- meaning, do not continue looking for more matches.ComeBack()
which means keep trying larger matches and if one fails, come back to me.@prule sin(~x)^2 + cos(~x)^2 => 1
this should search for all pairs within +
. Equivalent to +(a~~, cos(~x)^2, b~~, sin(~x)^2, c~~)
(notice it got sorted). What's a good name for these? Fixed in #12 (called @acrule
)I come across from slack, here is what our use case would look like in Yao
In our next coming DSL for quantum programs, we are using a custom IR node that is defined using some custom Node type inside a Expr
: https://github.com/QuantumBFS/YaoIR.jl/blob/master/src/compiler/ir.jl
the use case for SymbolicUtils is to perform pattern matching for quantum circuits, and replace SymEngine on symbolic calculation inside quantum circuits.
an example of the program would look like:
using YaoIR
using YaoArrayRegister
using FFTW
using Test
@device function qft(n::Int)
1 => H
for k in 2:n
@ctrl k 1 => shift(2π / 2^k)
end
if n > 1
2:n => qft(n - 1)
end
end
you can get the IR object using
julia> ir = @code_qast qft(3)
QASTCode(
name=qft
arguments=[:n]
free_variables=[:n]
strict_mode=nothing
code=begin
#= REPL[5]:2 =#
Locations(1) => H
#= REPL[5]:3 =#
for k = 2:n
#= REPL[5]:4 =#
@ctrl CtrlLocations(k) Locations(1) => shift((2π) / 2 ^ k)
end
#= REPL[5]:7 =#
if n > 1
#= REPL[5]:8 =#
Locations(2:n) => qft(n - 1)
end
end)
and what the actual AST would look like
julia> dump(ir.code)
Expr
head: Symbol block
args: Array{Any}((6,))
1: LineNumberNode
line: Int64 2
file: Symbol REPL[5]
2: YaoIR.GateLocation
location: Locations{Int64}
storage: Int64 1
gate: Symbol H
3: LineNumberNode
line: Int64 3
file: Symbol REPL[5]
4: Expr
head: Symbol for
args: Array{Any}((2,))
1: Expr
head: Symbol =
args: Array{Any}((2,))
1: Symbol k
2: Expr
head: Symbol call
args: Array{Any}((3,))
1: Symbol :
2: Int64 2
3: Symbol n
2: Expr
head: Symbol block
args: Array{Any}((2,))
1: LineNumberNode
line: Int64 4
file: Symbol REPL[5]
2: YaoIR.Control
ctrl_location: Expr
head: Symbol call
args: Array{Any}((2,))
gate: YaoIR.GateLocation
location: Locations{Int64}
gate: Expr
5: LineNumberNode
line: Int64 7
file: Symbol REPL[5]
6: Expr
head: Symbol if
args: Array{Any}((2,))
1: Expr
head: Symbol call
args: Array{Any}((3,))
1: Symbol >
2: Symbol n
3: Int64 1
2: Expr
head: Symbol block
args: Array{Any}((2,))
1: LineNumberNode
line: Int64 8
file: Symbol REPL[5]
2: YaoIR.GateLocation
location: Expr
head: Symbol call
args: Array{Any}((2,))
gate: Expr
head: Symbol call
args: Array{Any}((2,))
The other use case is matching graphs, we are working on zx calculus for quantum circuit simplification this summer as GSoC: https://github.com/QuantumBFS/ZX.jl basically this is a undirect multigraph: https://github.com/QuantumBFS/Multigraphs.jl (the ZX package needs a few refactor recently, so, unfortunately, I cannot give you a runnable example for now)
Tho, the pattern matching algorithm for ZX can be more specific, but it still shares a lot of things with tree pattern match, e.g the outside loop of simplify
. I sketched an implementation of simplify
here: QuantumBFS/ZXCalculus.jl#5
One place where this package could really stand out is using Julia's scheduler and Dagger to really allow for scaling symbolic algorithms in a highly parallel way. In large expressions, there should be tons of opportunities for splitting the job effectively, so it would be nice to see this target the large scale case. One of the things that really got me to Julia was symbolic performance, and this could blow everything else out of the water!
It may be good to separate simplification into different phases
so that basically 1) is done first, and 2) is done next, replace big terms with symbols (or use a dict) and go back to 1) and replace symbols back with terms stored in the dict. Optionally 2) can be switched out with other phases for e.g. to reduce round off errors.
Something like #55 could be in 2.
Our RuleSets already implements this separation, I think. We need to use it for this. Contexts can be used to switch them on or off, or order and cycle them.
Maybe this might work:
Deploy script checks to see if there is any changes to files in a docs
directory since the last push,
then checks out website
branch and does:
git checkout feature-branch -- website/
cp website/* .
# build & deploy
cc @tlienart you might find this interesting haha
Every time we add new functionality, ModelingToolkit needs to wrap it just to call to_mtk
. Not fully sure what a solution would look like. Maybe we can use contexts here to invoke a conversion at the end of simplify.
This is like a "first pr" level issue in the broader problem space of context-based simplification.
RuleSet([a => 1])(expr)
can replace a
in expr
with 1. But a much more efficient substitute
function would just walk the tree and use a Dict -- that way it can substitute many exprs at once.
Failed to apply rule (*)(~(~x)) ^ ~y => (*)(map((a->begin
#= /home/travis/build/JuliaSymbolics/SymbolicUtils.jl/src/rulesets.jl:22 =#
pow(a, ~y)
end), ~(~x))...) on expression ((70.9781323261056 * conj(e)) ^ c)
@YingboMa I find the implementation of ^
and pow
etc very confusing! Let me know if you figure this out.
If you could provide a table with the available and missing features, people can make extensions easier. For example, something like the following will let others know to work on what features:
Type | Feature | |
---|---|---|
+-*/ | Number | done |
+-*/ | Array | missing |
integration | Number | missing |
derivative | Number | done |
I mentioned this in the announcement post, however, I thought it's worth opening an issue, so it is not lost.
I can see why Chris went with identity(0)
in MTK,
But I really just want to use 0
or 1
Base.zero(::Type{<:Symbolic{T}}) where {T<:Number} = zero(T)
Base.one(::Type{<:Symbolic{T}}) where {T<:Number} = one(T)
But Base has code like this: :(
_qreltype(::Type{T}) where T = typeof(zero(T)/sqrt(abs2(one(T))))
As seen in ModelingToolkit:
-1.0 * one(u₈ˏ₈ˏ₃) + -1.0 * one(1.0 + -1u₈ˏ₈ˏ₃ + u₈ˏ₈ˏ₁ * u₈ˏ₈ˏ₂) * one(u₈ˏ₈ˏ₃)
should be zero
julia> @fun a()
a()::Number
julia> @fun b()
b()::Number
julia> simplify(a() + b() + a())
(a() + b() + a())
The recursion in promote_symtype
appears to cause an error when calling a symbolic function with more than 2 arguments.
julia> @syms f(a,b,c)
(f(::Number, ::Number, ::Number)::Number,)
julia> f(1,2,3)
ERROR: f(::Number, ::Number, ::Number)::Number takes 3 arguments; 2 arguments given
Stacktrace:
[1] error(::String) at ./error.jl:33
[2] promote_symtype(::SymbolicUtils.Sym{SymbolicUtils.FnType{Tuple{Number,Number,Number},Number}}, ::Type{T} where T, ::Type{T} where T) at /home/david/.julia/packages/SymbolicUtils/Jm3HG/src/types.jl:166
[3] rec_promote_symtype(::SymbolicUtils.Sym{SymbolicUtils.FnType{Tuple{Number,Number,Number},Number}}, ::Type{T} where T, ::Type{T} where T, ::Type{T} where T) at /home/david/.julia/packages/SymbolicUtils/Jm3HG/src/methods.jl:35
[4] term(::SymbolicUtils.Sym{SymbolicUtils.FnType{Tuple{Number,Number,Number},Number}}, ::Int64, ::Vararg{Int64,N} where N; type::Nothing) at /home/david/.julia/packages/SymbolicUtils/Jm3HG/src/types.jl:290
[5] term(::SymbolicUtils.Sym{SymbolicUtils.FnType{Tuple{Number,Number,Number},Number}}, ::Int64, ::Vararg{Int64,N} where N) at /home/david/.julia/packages/SymbolicUtils/Jm3HG/src/types.jl:289
[6] (::SymbolicUtils.Sym{SymbolicUtils.FnType{Tuple{Number,Number,Number},Number}})(::Int64, ::Vararg{Int64,N} where N) at /home/david/.julia/packages/SymbolicUtils/Jm3HG/src/types.jl:147
[7] top-level scope at REPL[3]:1
julia> term(f, 1,2,3; type=Number)
f(1, 2, 3)
Groebner basis finding is a pretty essential computation in a lot of cases. Needed for things like SciML/ModelingToolkit.jl#337
Some symbolic systems want to carry more type info in their symbols or other non-type contextual information. I think a good direction would be to support a Context
object that can hold a dictionary of predicates and assorted info which would be fed into a RuleSet
. I'm imagining something like
@syms x::Real y::Integer
ctx = @context(x => (x > 3,), y => (y != 0, y < 10))
simplify(sqrt(x - 3)^2/y , context=ctx)
The example in the interface section seems like a feature that I expect to see from SymbolicUtils by default (simplifying ex = 1 + (:x - 2)
)
The interface example is better to be a real-world problem (something missing form SymbolicUtils.jl
)
https://juliasymbolics.github.io/SymbolicUtils.jl/interface/#example
Currently, I don't think there's an easy way yo get back to the repository from the website. It'd be good to have a big obvious link at the top or something.
With #78 Mike showed
julia> D(f, x, n) = n == 0 ? f(x) : ForwardDiff.derivative(x -> D(f, x, n-1), x)
D (generic function with 1 method)
julia> f(x) = D(sin, x, 5)
f (generic function with 1 method)
julia> ir = XLA.@code_typed f(0.5);
julia> @syms x;
julia> SymbolicUtils.irterm(ir, [x])
cos(x)
It works because XLATools unrolls struct access and only retains hardware numbers. We can have a Mjolnir primitive set which de-structures structs (link) so we can call it with ForwardDiff and possibly other packages which use structs of number types.
Currently, we set a random seed in the test suite so that they are deterministic. It'd be good if we had a bot that cloned master
once a day and opened a PR with a new random seed. This way we can continue to discover weird edge cases with the fuzz tests but also have things be (mostly) deterministic.
If someone knows their way around github actions, we would really appreciate your help on this!
Currently, we make a lot of tacit assumptions about the commutativity of multiplication, but I think it'd be good if we could handle non-commutative number-systems like quaternions gracefully.
One way to do it would be to make something like
iscommutative(::typeof(*), x::Number) = true
iscommutative(::typeof(*), x::Symbolic{<:Number}) = true
iscommutative(::typeof(*), x::Quaternion) = false
iscommutative(::typeof(*), x::Symbolic{<: Quaternion}) = false
iscommutative(f) = x -> iscommutative(f, x)
and then you would have rules like
@rule (~x * ~y::iscommutative(*) * ~x::iscommutative(*)) => ~x^2 * y
The fuzzer found this interesting case:
Test failed for expression
((c ^ 44) ^ -66.20537843708486) = 0.0
Simplified to:
(c ^ -2913.0366512317337) = Errored(DomainError(-2.517537638236398, "Exponentiation yielding a complex result requires a complex argument.\nReplace x^y with (x+0im)^y, Complex(x)^y, or similar."))
On inputs:
Any[c] = [-2.517537638236398]
in general
(x^1/2)^2
is +-x
(x^2)^(1//2)
is x
.
Right now we use operators defined on the Term
type in our rules (example).
We should have a @term f(x, y)
macro which expands to:
term(ctx, f, x, y, type=promote_symtype(ctx, f, symtype(x), symtype(y))
This could allow custom contexts to define term
and promote_symtype
methods to specify how terms in other packages are to be constructed so as to avoid conversion back and forth if we used it in our rule mechanism. This would complete the interfacing mechanism.
@vars a b c d
expr1 = (d * (a * (a * (c / (c * (b * (b / (a / (a / b)))))))))
expr2 = (d * (b * (a * (d / (b * (c * (a / (c * (a * b)))))))))
simplify(expr1-2expr2+expr1+expr2)
Error showing value of type SymbolicUtils.Term{Number}:
ERROR: Failed to apply rule (*)(~(~(x::!issortedₑ))) => sort_args(*, ~(~x)) on expression (2 * (1 ^ -1) * a * b * (d ^ 2))
Stacktrace:
[1] (::RuleSet)(::SymbolicUtils.Term{Number}; depth::Int64) at /home/shashi/.julia/dev/SymbolicUtils/src/rule_dsl.jl:229
[2] (::SymbolicUtils.var"#32#33"{Int64,RuleSet})(::SymbolicUtils.Term{Number}) at /home/shashi/.julia/dev/SymbolicUtils/src/rule_dsl.jl:221
[3] iterate at ./generator.jl:47 [inlined]
caused by [exception 1]
TypeError: non-boolean (Nothing) used in boolean context
Stacktrace:
[1] issorted(::SubArray{Any,1,Array{Any,1},Tuple{UnitRange{Int64}},true}, ::Base.Order.Lt{typeof(SymbolicUtils.:<ₑ)}) at ./sort.jl:62
[2] #issorted#1 at ./sort.jl:91 [inlined]
[3] issortedₑ at /home/shashi/.julia/dev/SymbolicUtils/src/simplify.jl:117 [inlined]
[4] #66 at ./operators.jl:880 [inlined]
[5] (::SymbolicUtils.var"#segment_matcher#18"{SymbolicUtils.Segment{Base.var"#66#67"{typeof(SymbolicUtils.issortedₑ)}}})(::SymbolicUtils.LL{Array{Any,1}}, ::SymbolicUtils.MatchDict{NamedTuple{(:x,),Tuple{Int64}},Tuple{Base.RefValue{Any}}}, ::SymbolicUtils.var"#19#22"{SymbolicUtils.LL{Array{Any,1}},SymbolicUtils.LL{Tuple{SymbolicUtils.var"#literal_matcher#16"{typeof(*)},SymbolicUtils.var"#segment_matcher#18"{SymbolicUtils.Segment{Base.var"#66#67"{typeof(SymbolicUtils.issortedₑ)}}}}}}) at /home/shashi/.julia/dev/SymbolicUtils/src/matchers.jl:173
[6] (::SymbolicUtils.var"#loop#21"{SymbolicUtils.var"#25#26"{SymbolicUtils.var"#44#71"}})
We should make a benchmarking script that people can run locally to look for performance regressions. Ideally, it should deterministically generate a big hairy expression and then simplify it.
A good starting point to build on would be
using SymbolicUtils, Random
Random.seed!(1)
let
@syms a b c d
expr1 = foldr((x,y)->rand([*, /, +])(x,y), rand([a,b,c,d,1,2], 20))
SymbolicUtils.@timerewrite simplify(expr1)
end
but we should include more algebraic and trigonometric functions. I find with this expression, on the current master branch, I need to run it two or three times before the reported times stabilize, so there seems to be a lot of recompilation going on and we don't necessarily want to measure that in the script.
Bonus points if someone can make the script run on both master
and the current branch (if that branch is not master
) for easy comparisons.
I think we should remove those rules, as they could be very precarious (see the example below). Defining non-equivalence relations on floating-point numbers can be quite dangerous.
julia> using SymbolicUtils
julia> @vars a::Integer x::Real y::Number
(a, x, y)
julia> simplify(tan(x + (2π + 1e-11) * a))
tan(x)
julia> tan(pi)
-1.2246467991473532e-16
julia> tan(pi + (2π + 1e-11) * 10^11)
1.5573370942459988
Allow symbolic conditions in rules:
@rule abs(~x) => ~x >= 0 ? ~x : -~x
# In a Bool ruleset:
@rule ~x >= 0 => isnonnegative(~x, @CTX) # isnonnegative returns Union{Bool, Nothing}
This should be lowered such that if the truth value of ~x >= 0
cannot be ascertained, then it would leave the input expression unchanged.
We can potentially allow expressions with if conditions. Maybe just by adding an ifelse
method.
@YingboMa
suggested benchmarking factorized_W in https://github.com/SciML/DiffEqProblemLibrary.jl/blob/master/src/ode/ode_simple_nonlinear_prob.jl
It would be nice if PkgBenchmark can detect a Project.toml in the benchmark folder.
In some test of RuleSet
a deprecated simplify
function was called,
┌ Warning: `simplify(x, rules::RuleSet; kwargs...)` is deprecated, use `simplify(x, rules = rules; kwargs...)` instead.
│ caller = ip:0x0
└ @ Core :-1
The tests still pass, I just want to point it out to you guys
I suspect that most functions which are called in the process of simplify
should be marked with @nospecialize
, otherwise the compiler is going to be doing a ton of unnecessary work generating specialized methods every time it looks into the arguments
of a term
since they're not parameterized on the type of arguments
or operation
.
It'll take a lot of work, but it'd be worthwhile to just go through and test out which functions need this by doing a lot of benchmarking.
julia> @syms a(t) b(t) t
(a(::Number)::Number, b(::Number)::Number, t)
julia> a(t) * -b(t) + b(t) * a(t)
(b(t) * a(t)) + (-1 * a(t) * b(t))
Found with https://github.com/SciML/ModelingToolkit.jl/issues/256
Fuzzing is a great exploratory tool, but I don't like it's non-determinism effects our test coverage. It'd be nice if we could run the fuzzing tests though something like Github actions separately from Travis. That way we still get the interesting new fuzz-test results, but the randomness won't effect out badges.
Hi,
Thanks for working on this cool package, I've been wanting to do symbolic stuff in Julia instead of Mathematica for a long time, so I am excited about this.
It seems that the fixpoint
example from the docs (see below) is outdated and returns an error due to the fact that fixpoint
requires now 3 arguments (the last one I guess allows to apply rules according to context or something like this, but it is currently undocumented).
using SymbolicUtils: fixpoint
fixpoint(rset, 2 * (w+w+α+β))
@HarrisonGrodin suggested integrating with z3 for constraint solving. This could be a great way to do #67 !
Currently, if I write
julia> using SymbolicUtils, Test; @vars a b;
julia> a * b * a
((a ^ 2) * b)
julia> @test a * b * a == a^2 * b
Test Failed at REPL[33]:1
Expression: a * b * a == a ^ 2 * b
Evaluated: ((a ^ 2) * b) == ((a ^ 2) * b)
ERROR: There was an error during testing
Because we simplify the output of Base.show
, errors testsets tricky to debug. notice it says Evaluated: ((a ^ 2) * b) == ((a ^ 2) * b)
One can use showraw
to see what the actual representation is, but I think maybe we shouldn't be simplifying on show
.
It would be nice to be able to compute the inverse of functions.
#82 type of issues are scary, need to make sure every branch in the comparison operator is tested.
We currently have
https://github.com/shashi/SymbolicUtils.jl/blob/46f737893be66a67111cb5224ac4ed5e1d7d1bea/src/symbolic.jl#L31-L34
and
https://github.com/shashi/SymbolicUtils.jl/blob/46f737893be66a67111cb5224ac4ed5e1d7d1bea/src/symbolic.jl#L73-L77
Couldn't we just do
struct Variable{T} <: Symbolic{T}
name::Symbol
Variable(name, ::Type{T}) where {T} = new{T}(name)
end
and
struct Term{T} <: Symbolic{T}
f::Any
arguments::Any
Term(f, ::Type{T}, arguments) where {T} = new{T}(f, arguments)
end
instead?
https://coveralls.io/builds/30341877/source?filename=src%2Frulesets.jl
shows it is empty
I wonder if we can place a LineNumberNode
in the rules macro and that could potentially allow julia to know which rule was used?
We really want
Variable{T} <: T
But can't have it easily.
Some approaches:
@vars a::AbstractMatrix b::Real c::Complex
should generate different Variable types with the right super type. This reminds me of the NamedTuples.jl from Julia 0.6.A resolution of this should fix #8
Is this intended behaviour?
using SymbolicUtils
@vars x
r1 = rewriter([@rule ~x => ~x + 1])
r2 = rewriter( @rule ~x => ~x + 1 )
julia> r1(x)
x
julia> r2(x)
(1 + x)
If nobody beats me to it, I'd like to write up a README file sometime soon.
This looks like a good spot to implement equation solving. Similar to https://docs.sympy.org/latest/modules/solvers/solvers.html .
I think maybe we should do a bit less fuzz tests, maybe 5000 instead of 10,000? It seems that sometimes, if Travis is being slow, the tests will stall out because it doesn't get any output for too long. Alternatively, we could try printing more output.
We only use a partial order right now, which causes expressions to be not always isequal
after simplification. We make sure this does not stop some rules from getting applied, but it does make tests others have written fail from time to time for no reason. see for example https://travis-ci.org/github/SciML/ModelingToolkit.jl/builds/684905351#L569
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.