Giter VIP home page Giter VIP logo

symbolicutils.jl's Introduction

SymbolicUtils.jl

Join the chat at https://julialang.zulipchat.com #sciml-bridged Global Docs

codecov Build Status Build status

ColPrac: Contributor's Guide on Collaborative Practices for Community Packages SciML Code Style

Tutorials and Documentation

For information on using the package, see the stable documentation. Use the in-development documentation 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 Symbolics.jl. If you want to build a crazy CAS for your weird Octonian algebras, you've come to the right place.

Symbols in SymbolicUtils carry type information. Operations on them propagate this information. A rule-based rewriting language 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 rules for expressions of numeric symbols and numbers. These can be remixed and extended for special purposes.

If you are a Julia package developer in need of a rule rewriting system for your own types, have a look at the interfacing guide.

"I don't want to read your manual, just show me some cool code"

julia> using SymbolicUtils

julia> SymbolicUtils.show_simplified[] = true

julia> @syms x::Real y::Real z::Complex f(::Number)::Real
(x, y, z, f(::Number)::Real)

julia> 2x^2 - y + x^2
(3 * (x ^ 2)) + (-1 * y)

julia> f(sin(x)^2 + cos(x)^2) + z
f(1) + z

julia> r = @rule sinh(im * ~x) => sin(~x)
sinh(im * ~x) => sin(~x)

julia> r(sinh(im * y))
sin(y)

julia> simplify(cos(y)^2 + sinh(im*y)^2, RuleSet([r]))
1

Citations

  • The pattern matcher is an adaption of the one by Gerald Jay Sussman (as seen in 6.945 at MIT), his use of symbolic programming in the book SICM inspired this package.
  • Rewrite.jl and Simplify.jl by Harrison Grodin also inspired this package.

symbolicutils.jl's People

Contributors

adamslc avatar arnostrouwen avatar blegat avatar bolognam avatar bowenszhu avatar chrisrackauckas avatar contradict avatar david-pl avatar dhairyalgandhi avatar eschnett avatar github-actions[bot] avatar grahamas avatar gronniger avatar jkosata avatar masonprotter avatar milescranmer avatar moble avatar musm avatar nmheim avatar pagnani avatar rafaelarutjunjan avatar ranocha avatar rikhuijzer avatar shashi avatar suavesito-olimpiada avatar tmigot avatar tymonkilich avatar vaibhavdixit02 avatar willow-ahrens avatar yingboma avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

symbolicutils.jl's Issues

[website] ignore __site & remove favicon

Hello,

Two small suggestions for the website:

  • the current favicon is the default favicon that ships with template and is a photo of an Iguana, it's a nice animal but probably not what you want. Easy way out is to replace the _assets/favicon.png or to remove this line:
    <link rel="icon" href="/assets/favicon.png">
  • at the moment you use 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:
    • remove __site and put it in your .gitignore
    • just git push your changes and let the github action do the rest

Phases of simplification

It may be good to separate simplification into different phases

  1. Simplification to make other simplification rules work well,
  2. Simplification for humans

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.

Simplify on show is kinda annoying

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.

"convert back" interface.

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.

Link back to repo from website

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.

Make it possible to trace struct code

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.

Rules infrastructure wishlist

  • sin(~x)::oftype(Number) basically the predicate must be run on the matched term itself.
  • Maybe the Segment and Slot types need not exist. A closure per token of the rule would serve the purpose (i.e. go straight to matcher in the macro). Can also allow a Token type for users to write custom matchers for some rules.
  • lone segments such as *(~~x::hasrepeats) should not search all possible sub args because it keeps failing. There's only one possible match: all the arguments.
  • If a segment appears twice in a rule, make it possible to use a faster longest common subsequence matched.
  • allow segment predicate to return Break() -- meaning, do not continue looking for more matches.
  • allow segment predicate to say ComeBack() which means keep trying larger matches and if one fails, come back to me.
  • partial rules that commute: @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)

fuzz test ordering

#82 type of issues are scary, need to make sure every branch in the comparison operator is tested.

Set random seed before fuzzing

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.

Error in symbolic functions with more than 2 arguments

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)

Multithreaded and parallel simplification

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!

Rules not being reached

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.

Why store the type of a term in a field?

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?

Interface with Mjolnir

Mjolnir's IR (and generally SSA IR) is a super set of Terms in SymbolicUtils. It could be pretty nice to use Mjolnir and SymbolicUtils together to represent and optimize code that may include branches, especially with #67 and #70.

substitution

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.

Write up README

If nobody beats me to it, I'd like to write up a README file sometime soon.

Supertype of Symbolic expressions

We really want

Variable{T} <: T

But can't have it easily.

Some approaches:

  • ReflectOn in Cassette
    • (or) Write a version of this without Cassette
  • @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

Equality of this expression does not simplify

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))

2π identity rules

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

Rules for `one` and `zero`

As seen in ModelingToolkit:

 -1.0 * one(u₈ˏ₈ˏ₃) + -1.0 * one(1.0 + -1u₈ˏ₈ˏ₃ + u₈ˏ₈ˏ₁ * u₈ˏ₈ˏ₂) * one(u₈ˏ₈ˏ₃)

should be zero

Fuzztests causing stalls

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.

Do an audit for which functions should be marked with @nospecialize

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.

failure in simplify

@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"}})

Untraceable StackOverflow.

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:

  • too much recursion (I doubt this)
  • julia bug

(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.

  • regular old stackoverflow

I tried the exprs shown in the failing rule applications, but they simplify just fine.

Interface for supplying additional information

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)

^ calls `isless`

  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.

custom term and matching graphs

I come across from slack, here is what our use case would look like in Yao

Custom Term

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,))

Matching Graphs

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

Move the documentation files to master

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

bot to periodically change random seed

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!

Make a benchmark script

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.

Table of available/missing features

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.

What should be `zero` and `one`

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))))

simplification of (a^m)^n

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.

Symbolic{Bool} and simplification

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.

Handle non-commutative numbers

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

`fixpoint` example from the documentation is not working

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+α+β))

Warning in test

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

More decoupling from Term

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.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.