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/
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
I use lots of Int32s in my code because I have some large arrays of those. But for some x::Int32 we have typeof(x+1) == Int64 since numeric literals are Int64 by default (I have to use 64bit Julia to handle my arrays). The problem is, if I have some function f(x::Int32) then f(x+1) will method error. I don't want to implement a f(x::Int64) = f(convert(Int32, x)) for almost every function and want to use concrete types for type stability. Currently, I simply have expressions like x + Int32(1) all over my code which looks really cluttered. For other types we have shorthands, i.e., 1.f0 gives me a Float32 and big"1" a BigInt. Is there something similar for Int32?
Since you explicitly mention the big_str macro (big"") you can easily define a similar macro for Int32 (the same way the uint128_str and int128_str is defined):
macro i32_str(s)
parse(Int32, s)
end
julia> typeof(i32"1")
Int32
this might still clutter your code too much so alternatively you could exploit that a number followed by a name is multiplication:
struct i32 end
(*)(n, ::Type{i32}) = Int32(n)
julia> typeof(1i32)
Int32
You can make a macro to replace every literal integer with an Int32, a bit like what ChangePrecision.jl does for floats. A very quick first attempt is:
julia> macro literal32(ex)
esc(literal32(ex))
end;
julia> literal32(ex::Expr) = Expr(ex.head, literal32.(ex.args)...);
julia> literal32(i::Int) = Int32(i);
julia> literal32(z) = z; # ignore Symbol, literal floats, etc.
julia> #literal32 [1,2] .+ 3
2-element Vector{Int32}:
4
5
julia> #literal32 function fun(x::AbstractVector)
x[1] + 2 # both 1 and 2 are changed
end
fun (generic function with 1 method)
julia> fun(Int32[3,4]) |> typeof
Int32
One place this may have unexpected consequences is literal type parameters:
julia> #literal32([1,2,3]) isa Array{Int32,1}
true
julia> #literal32 [1,2,3] isa Array{Int32,1}
false
Another is that x^2 will not use Base.literal_pow, e.g. #literal32 Meta.#lower pi^2.
What if you say:
# Or, a::Int32 = 1
julia> a = Int32(1)
1
julia> b::Int32 = a+2
3
julia> typeof(b)
Int32
julia> f(b)
...
I want to have a curried version of a function. So, I write the code as follows:
f(x::Int64, y::Int64) = x + y
f(x::Int64) = (y::Int64) -> f(x, y)
But I am not sure if Julia considers this an example of a type-unstable definition. On the face of it, one of the methods returns an anonymous function, while another returns an Int64. Yet, when the curried version is applied, the final result is also an Int64.
So, my questions are:
Is this code type-stable?
If not, is there a way to have a curried version of a function without writing type-unstable code?
Thanks in advance.
Yes, it is.
According to the official doc, you can investigate it by using the #code_warntype macro:
julia> #code_warntype f(1, 5)
MethodInstance for f(::Int64, ::Int64)
from f(x::Int64, y::Int64) in Main at REPL[2]:1
Arguments
#self#::Core.Const(f)
x::Int64
y::Int64
Body::Int64
1 ─ %1 = (x + y)::Int64
└── return %1
The arguments of this function have the exact type Int64, and as we can see in the Body::Int64, the inferred return type function is Int64.
Furthermore, we have f(x) which is based on the type-stable function f(x, y):
julia> #code_warntype f(1)
MethodInstance for f(::Int64)
from f(x::Int64) in Main at REPL[15]:1
Arguments
#self#::Core.Const(f)
x::Int64
Locals
#3::var"#3#4"{Int64}
Body::var"#3#4"{Int64}
1 ─ %1 = Main.:(var"#3#4")::Core.Const(var"#3#4")
│ %2 = Core.typeof(x)::Core.Const(Int64)
│ %3 = Core.apply_type(%1, %2)::Core.Const(var"#3#4"{Int64})
│ (#3 = %new(%3, x))
└── return #3
Here as well, there's not any unstable defined parameter type.
Look at the following as an example of an unstable-typed function:
julia> unstF(X) = x*5
unstF (generic function with 1 method)
julia> #code_warntype unstF(1)
MethodInstance for unstF(::Int64)
from unstF(X) in Main at REPL[17]:1
Arguments
#self#::Core.Const(unstF)
X::Int64
Body::Any
1 ─ %1 = (Main.x * 5)::Any
└── return %1
If you try this in the REPL, you'll see the Any appears with a red color. Since we have the Body::Any (Any with the red color), we can conclude that the returned object by this function is a non-concrete type object. Because the compiler doesn't know what is the x (Note that the input is X). So the result can be Anything! So this function is type-unstable for (here, for integer inputs. Note that you should investigate the type-stability of your function by your desired input(s). E.g., #code_warntype f(5) and #code_warntype f(5.) should be observed if I can pass it Float64 or Int64 either).
This question already has answers here:
Julia: what does the "<:" symbol mean?
(2 answers)
Closed 2 years ago.
Can anyone help me with this code:
struct GenericPoint{T<:Real}
x::T
y::T
end
What is meaning of <: in {T<:Real} in Julialang?
Let me begin by saying that the punctuation page of the manual is a handy way to search for such operators, which are otherwise very difficult to look for using a search engine.
In the specific case of <:, we find this page, with the relevant documentation for essential operators.
There are (at least) 3 contexts where A <: B might be used, and in all of them this expresses the idea that A is a subtype of B.
as a predicate, A <: B returns true if and only if all values of type A are also of type B:
julia> Int <: Number
true
julia> Int <: AbstractString
false
in a type definition, this declares that the newly defined type is a subtype of an existing (abstract) type:
# `Foo` is declared to be a subtype of `Number`
struct Foo <: Number
end
as a type-parameter constraint (like in your example), T <: Real expresses the idea that type parameter T can be any subtype of Real:
julia> struct GenericPoint{T<:Real}
x::T
y::T
end
# Works because 1 and 2 are of type Int, and Int <: Real
julia> GenericPoint(1, 2)
GenericPoint{Int64}(1, 2)
# Does not work because "a" and "b" are of type String,
# which is not a subtype of Real
julia> GenericPoint("a", "b")
ERROR: MethodError: no method matching GenericPoint(::String, ::String)
Stacktrace:
[1] top-level scope at REPL[5]:1
Note that the use for type-parameter constraints is not restricted to the definition of parametric types, but also applies to function/method definitions:
julia> foo(x::Vector{T}) where {T<:Number} = "OK"
foo (generic function with 1 method)
# OK because:
# - [1,2,3] is of type Vector{Int}, and
# - Int <: Number
julia> foo([1, 2, 3])
"OK"
# Incorrect because:
# - ["a", "b", "c"] is of type Vector{String}, but
# - String is not a subtype of Number
julia> foo(["a", "b", "c"])
ERROR: MethodError: no method matching foo(::Array{String,1})
For example in the code below, x is defining the domain, but why is there the double dot between 0 and 4pi?
using ApproxFun
x=Fun(identity,0..4π)
.. is an operator (like e.g. +) but it does not have a default definition. You can define it to to whatever you want:
julia> ..(a, b) = println(a, ", ", b)
.. (generic function with 1 method)
julia> "hello" .. "world"
hello, world
The Julia package IntervalArithmetic uses it to construct an interval, e.g.
julia> using IntervalArithmetic
julia> 4..5
[4, 5]
julia> typeof(4..5)
Interval{Float64}
and I suspect this is what it is used for in your code example.
.. is not part of Julia, rather part of the packages used by ApproxFun.
It is used to represent intervals, see the code below
julia> u = 1..3
1..3
julia> dump(u)
Interval{:closed,:closed,Int64}
left: Int64 1
right: Int64 3
So this is just a convenience constructor for the Interval object, see:
julia> 1..3 === Interval{:closed,:closed,Int64}(1,3)
true
In Julia, you can declare an Int64, Bool or a Float64 and index it with 1.
julia> aa = 10
10
julia> typeof(10)
Int64
julia> aa[1]
10
julia> aa[0]
ERROR: BoundsError
Stacktrace:
[1] getindex(::Int64, ::Int64) at .\number.jl:78
[2] top-level scope at none:0
julia> aa[2]
ERROR: BoundsError
Stacktrace:
[1] getindex(::Int64, ::Int64) at .\number.jl:78
[2] top-level scope at none:0
Are there practical or theoretical reasons for this functionality to exist? I have never seen it in any other language I've used (Python, Ruby, Matlab, C++).
The reason is twofold:
Numbers are treated by Julia as 0-dimensional containers.
If you add 1 as a dimension index number in getindex then it is not an error, even if 1 is beyond the dimensionality of the container.
These two rules in combination lead to the behavior you describe. Here are some more examples of the same:
julia> a = 1
1
julia> b = [1,2,3]
3-element Array{Int64,1}:
1
2
3
julia> a[]
1
julia> a[1,1,1,1]
1
julia> b[2,1,1,1,1]
2
and note that standard functions defined for containers are defined for numbers and behave as for 0-dimensional objects, e.g.:
julia> size(a)
()
julia> axes(a)
()
There is an open PR that gives more details how omitted and extra indices work.