Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Symbolics.jl | Interfacing - building an expression tree to use custom rules #687

Closed
Audrius-St opened this issue Aug 3, 2022 · 5 comments

Comments

@Audrius-St
Copy link

Audrius-St commented Aug 3, 2022

Hello,

I would like to use the SymbolicUtils composing rewriter Fixpoint(rw) which

returns a rewriter which applies rw repeatedly until there are no changes to be made

to apply a custom rule to a Symbolics.jl expression until nothing is returned.

However, rereading the SymbolicUtils.jl and TermInterface.jl documentation it is not clear to me how to do this - specifically how to set up the interface between Symbolics.jl and SymbolicUtils.jl.

The MNWE is

# test_symbolics_main.jl

using Symbolics

function main()

    @variables x, t
    @variables Φ(x, t)

    Dx = Differential(x)
    D2x = Dx * Dx

    Dt = Differential(t)

    # The rule that I would like to apply
    r = @rule Dt(Dx(Dx(~Φ))) => Dx(Dx(Dt(~Φ)))

    # The test expression to which I would like to apply the rule
    ∂2Φ∂t2 = Dt(Dx(Dx(Φ))) - 2*Dt(Φ)*Φ

    # The rewrite I would like to perform
    γ2 = Fixpoint(r)(∂2Φ∂t2)

    # The problem
    @show istree(∂2Φ∂t2) # returns "false" . Not an expression tree.

end

begin
    main()

end

The SymbolicUtils.jl documentation states

In particular, you should define methods from TermInterface.jl for an expression tree type T with symbol types S to work with SymbolicUtils.jl

and the TermInterface.jl documentation states

You should define the following methods for an expression tree type T with symbol types S to work with TermInterface.jl . . .

istree(x::T) or istree(x::Type{T})

Without an example, it is not clear to me, a Symbolics neophyte, what is meant and how to proceed.

In the above MNWE,
should @variables be replaced by @syms?
should ∂2Φ∂t2 be defined as ∂2Φ∂t2::Symbolics.Term?

@Audrius-St
Copy link
Author

Audrius-St commented Aug 7, 2022

After rereading the Symbolics.jl documentation,

Supported types and dispatch in Symbolics
. . .
User-facing APIs in Symbolics always take wrapped objects like Num, they are then internally unwrapped for expression tree manipulation.
. . .

I thought that I had found the solution.

function main()

    @variables x, t
    @variables Φ(x, t)

    Dx = Differential(x)
    D2x = Dx * Dx

    Dt = Differential(t)

    # The test rewrite rule that I would like to apply
    pd_rule = @rule Dt(Dx(Dx(~Φ))) => Dx(Dx(Dt(~Φ)))

    # The test expression to which I would like to apply the rule
    ∂2Φ∂t2 = Dt(Dx(Dx(Φ))) - 2*Dt(Φ)*Φ

    # Unwrap the expression to expose the expression tree
    ∂2Φ∂t2_tree = Symbolics.value.(∂2Φ∂t2)

    @show istree(∂2Φ∂t2_tree)
    @show ∂2Φ∂t2_tree
    @show typeof(∂2Φ∂t2_tree)

    # Test of the rewrite rule
    @show γ2 = pd_rule(∂2Φ∂t2)

end

begin
    main()

end

which generates the output

julia> include("test_symbolics_main.jl")
istree(∂2Φ∂t2_tree) = true
∂2Φ∂t2_tree = Differential(t)(Differential(x)(Differential(x)(Φ(x, t)))) - 2Differential(t)(Φ(x, t))*Φ(x, t)
typeof(∂2Φ∂t2_tree) = SymbolicUtils.Add{Real, Int64, Dict{Any, Number}, Nothing}
pd_rule(∂2Φ∂t2_tree) = nothing

So pd_rule(∂2Φ∂t2_tree) returns nothing .
Puzzled.

@bowenszhu
Copy link
Member

bowenszhu commented Aug 8, 2022

If a @rule fails to match the matcher pattern with the top-level operation of the input expression, it returns nothing.

Chain(itr) returns the original input expression, if the rewriters do not change the input expression.

∂2Φ∂t2_tree = Dt(Dx(Dx(Φ))) - 2*Dt(Φ)*Φ is a tree-like

      -
   /     \
Dt        *
|       / | \
Dx     2  Dt Φ
|         |
Dx        Φ
|
Φ

The matcher Dt(Dx(Dx(~Φ))) of the rule you would like to apply pd_rule = @rule Dt(Dx(Dx(~Φ))) => Dx(Dx(Dt(~Φ))) cannot be matched with the top-level operation - of the tree-like variable ∂2Φ∂t2_tree.

In your case, the composite rewriters Prewalk and Postwalk are helpful, which do pre- or post-order traversal for the expression tree.

Please try the following

using Symbolics, SymbolicUtils.Rewriters
@variables x, t
@variables Φ(x, t)
Dx = Differential(x)
D2x = Dx * Dx
Dt = Differential(t)
pd_rule = @rule Dt(Dx(Dx(~Φ))) => Dx(Dx(Dt(~Φ)))
∂2Φ∂t2 = Dt(Dx(Dx(Φ))) - 2 * Dt(Φ) * Φ
∂2Φ∂t2_tree = Symbolics.value(∂2Φ∂t2)
γ2 = Postwalk(Chain([pd_rule]))(∂2Φ∂t2_tree)

result:

Differential(x)(Differential(x)(Differential(t)(Φ(x, t)))) - 2Differential(t)(Φ(x, t))*Φ(x, t)

See also the documentation of Composing Rewriters in SymbolicUtils.jl
https://symbolicutils.juliasymbolics.org/rewrite/#composing_rewriters

@Audrius-St
Copy link
Author

Audrius-St commented Aug 8, 2022

Wonderful. Thank you for your detailed explanation and solution.

With a test of composition

   @variables x, t 
   @variables Φ(x, t) 

   Dx = Differential(x) 
   D2x = Dx ∘ Dx 

   Dt = Differential(t) 

   pd_rule = @rule Dt(Dx(Dx(~Φ))) => Dx(Dx(Dt(~Φ))) 

   ∂2Φ∂t2 = Dt(D2x(Φ)) - 2 * Dt(Φ) * Φ 
   ∂2Φ∂t2_tree = Symbolics.value(∂2Φ∂t2) 

   γ2 = Symbolics.Postwalk(Symbolics.Chain([pd_rule]))(∂2Φ∂t2_tree)
   @show γ2

gives the same desired result:

γ2 = Differential(x)(Differential(x)(Differential(t)(Φ(x, t)))) - 2Differential(t)(Φ(x, t))*Φ(x, t)

However, including the same code in a function

# test_rewrite_main.jl

using Symbolics 

function main()

# The above code is inserted here

end

begin
    main()
end

give the unexpected result

γ2 = Differential(t)(Differential(x)(Differential(x)(Φ(x, t)))) - 2Differential(t)(Φ(x, t))*Φ(x, t)

the rule and rewrite operations seem to have been ignored.
This makes me question my understanding of Symbolics.jl and of Julia as I thought placing code in functions is considered good form.

@bowenszhu
Copy link
Member

I guess that's an issue for SymbolicUtils.jl term rewriting. Opened a separate issue JuliaSymbolics/SymbolicUtils.jl#460

@Audrius-St
Copy link
Author

I guess that's an issue for SymbolicUtils.jl term rewriting. Opened a separate issue JuliaSymbolics/SymbolicUtils.jl#460

Understood. Thank you for investigating and opening the new issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants