how to convert Reduce output to Julia symbolic expression?

Viewed 160

In Julia, I use Reduce package to do integration since Julia Symbolics.jl does not have integrate command in it.

But I am not able to figure how to convert the output (the anti derivative) to Julia symbolic expression so I can use it inside Julia symbolic, and not using the Reduce package.

This is what I tried

               _
   _       _ _(_)_     |  Documentation: https://docs.julialang.org
  (_)     | (_) (_)    |
   _ _   _| |_  __ _   |  Type "?" for help, "]?" for Pkg help.
  | | | | | | |/ _` |  |
  | | |_| | | | (_| |  |  Version 1.7.1 (2021-12-22)
 _/ |\__'_|_|_|\__'_|  |  Official https://julialang.org/ release
|__/                   |

julia> using Symbolics    
julia> using Reduce    
julia> @variables x
1-element Vector{Num}:
 x

#use Reduce to do the integration
julia> output = :(int(sin(x),x)) |> rcall
:(-(cos(x)))

 julia> typeof(output)
 Expr

Now, I want to use the output -cos(x) as symbolic expression using Julia Symbolics. How to convert it?

Now it gives an error as is

julia> Symbolics.simplify(output^2)
ERROR: MethodError: no method matching ^(::Expr, ::Int64)
Closest candidates are:
  ^(::Union{AbstractChar, AbstractString}, ::Integer) at G:\nabbasi\data\CDROM\JULIA_language\julia-1.7.1-win64\julia-1.7.1\share\julia\base\strings\basic.jl:721
  ^(::LinearAlgebra.Diagonal, ::Integer) at G:\nabbasi\data\CDROM\JULIA_language\julia-1.7.1-win64\julia-1.7.1\share\julia\stdlib\v1.7\LinearAlgebra\src\diagonal.jl:196
  ^(::LinearAlgebra.Diagonal, ::Real) at G:\nabbasi\data\CDROM\JULIA_language\julia-1.7.1-win64\julia-1.7.1\share\julia\stdlib\v1.7\LinearAlgebra\src\diagonal.jl:195
  ...
Stacktrace:
 [1] literal_pow(f::typeof(^), x::Expr, #unused#::Val{2})
   @ Base .\intfuncs.jl:325
 [2] top-level scope
   @ REPL[9]:1

This is the same output, but now it is Julia Symbolics expression:

julia> f=-cos(x)
-cos(x)

julia> typeof(f)
Num

julia> Symbolics.simplify( f^2)
cos(x)^2

How does one convert output of Reduce expression to Julia Symbolics expression, may be for further processing?

It is possible to convert the Reduce output to string

julia> g=string(output)
"-(cos(x))"

But do not know how to convert the above string now to Symbolics expression. parse does not work. May be there is different command?

References

Reduce Julia package https://docs.juliahub.com/Reduce/wEGBP/1.2.5/

Julia Symbolics package https://symbolics.juliasymbolics.org/dev/

1 Answers

So it looks like these two packages (Reduce and Symbolics) are using rather different formats for their symbolic expressions. Reduce is using Julia's native Expr type, the same type used to represent Julia code itself, while Symbolics is using an approach where calling a function on one or more special variable designated with @variables will return an object that records the operations performed on those variables:

julia> using Reduce, Symbolics

julia> a = :(-(cos(x)))
:(-(cos(x)))

julia> @variables x;

julia> b = -(cos(x))
-cos(x)

julia> typeof(a)
Expr

julia> typeof(b)
Num

To see and compare what is actually inside these two different objects a and b, we can use Julia's very useful dump function:

julia> dump(a)
Expr
  head: Symbol call
  args: Array{Any}((2,))
    1: Symbol -
    2: Expr
      head: Symbol call
      args: Array{Any}((2,))
        1: Symbol cos
        2: Symbol x

julia> dump(b)
Num
  val: SymbolicUtils.Mul{Real, Int64, Dict{Any, Number}, Nothing}
    coeff: Int64 -1
    dict: Dict{Any, Number}
      slots: Array{UInt8}((16,)) UInt8[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00]
      keys: Array{Any}((16,))
        1: #undef
        2: #undef
        3: #undef
        4: #undef
        5: #undef
        ...
        12: #undef
        13: SymbolicUtils.Term{Real, Nothing}
          f: cos (function of type typeof(cos))
          arguments: Array{SymbolicUtils.Sym{Real, Base.ImmutableDict{DataType, Any}}}((1,))
            1: SymbolicUtils.Sym{Real, Base.ImmutableDict{DataType, Any}}
              name: Symbol x
              metadata: Base.ImmutableDict{DataType, Any}
                parent: Base.ImmutableDict{DataType, Any}
                key: Symbolics.VariableSource <: Any
                value: Tuple{Symbol, Symbol}
          metadata: Nothing nothing
          hash: Base.RefValue{UInt64}
            x: UInt64 0xd47301bdec8cde1c
        14: #undef
        15: #undef
        16: #undef
      vals: Array{Number}((16,)) Number[#undef, #undef, #undef, #undef, #undef, #undef, #undef, #undef, #undef, #undef, #undef, #undef, 1, #undef, #undef, #undef]
      ndel: Int64 0
      count: Int64 1
      age: UInt64 0x0000000000000001
      idxfloor: Int64 13
      maxprobe: Int64 0
    sorted_args_cache: Base.RefValue{Any}
      x: Array{Any}((2,))
        1: Int64 -1
        2: SymbolicUtils.Term{Real, Nothing}
          f: cos (function of type typeof(cos))
          arguments: Array{SymbolicUtils.Sym{Real, Base.ImmutableDict{DataType, Any}}}((1,))
            1: SymbolicUtils.Sym{Real, Base.ImmutableDict{DataType, Any}}
              name: Symbol x
              metadata: Base.ImmutableDict{DataType, Any}
                parent: Base.ImmutableDict{DataType, Any}
                key: Symbolics.VariableSource <: Any
                value: Tuple{Symbol, Symbol}
          metadata: Nothing nothing
          hash: Base.RefValue{UInt64}
            x: UInt64 0xd47301bdec8cde1c
    hash: Base.RefValue{UInt64}
      x: UInt64 0x0000000000000000
    metadata: Nothing nothing

and they are indeed quite different.

That's not to say that you can't use the two packages together though. Since an Expr is the type used to represent Julia code as data, all you need to do is evaluate that Expr to obtain the same result as if you had called the functions therein (going the other way would be another matter, though!). So we can write:

julia> output = :(int(sin(x),x)) |> rcall
:(-(cos(x)))

julia> output = eval(output)
-cos(x)

julia> Symbolics.simplify(output^2)
cos(x)^2

Alternatively, you could add the ^2 to output while keeping it in Expr form by using one of Julia's many ways of manipulating Exprs, and only later eval it when you need to pass it to Symbolics:

julia> output = :(int(sin(x),x)) |> rcall
:(-(cos(x)))

julia> output_squared = :($output^2) # interpolate one Expr into another
:((-(cos(x))) ^ 2)

julia> Symbolics.simplify(eval(output_squared))
cos(x)^2

There may more performant approaches of which I am unaware, since I have not extensively used either package, and using eval is arguably a bit of a hack. But, this should work without error for any case where the functions in your Reduce expression are defined Julia functions that Symbolics knows about (and as long as you're evaluating them in the right scope).

Related