ModelingToolkit and normalize()

The following code fails:

using ModelingToolkit, OrdinaryDiffEq, PyPlot, LinearAlgebra

G_EARTH  = Float64[0.0, 0.0, -9.81]    # gravitational acceleration

# model
@parameters mass=1.0 c_spring=50.0 damping=0.5 l0=10.0
@variables t pos(t)[1:3]=[0.0, 0.0,  0.0]
@variables   vel(t)[1:3]=[0.0, 0.0, 50.0] 
@variables   acc(t)[1:3]=[0.0, 0.0, -9.81]
@variables unit_vector(t)[1:3]=[1.0, 0.0, 0.0]
@variables force(t) = 0.0 norm1(t) = l0
D = Differential(t)

eqs = vcat(D.(pos) ~ vel,
           D.(vel) ~ acc,
           norm1 ~ norm(pos),
           unit_vector ~ normalize(pos),
           force ~ norm1 * c_spring,
           acc    .~ G_EARTH)

@named sys = ODESystem(eqs, t)

with the message:

ERROR: LoadError: TypeError: non-boolean (Num) used in boolean context
Stacktrace:
 [1] copyto!(dest::OffsetArrays.OffsetVector{Num, Vector{Num}}, src::Symbolics.Arr{Num, 1})
   @ Base ./abstractarray.jl:1059
 [2] copymutable_oftype(A::Symbolics.Arr{Num, 1}, ::Type{Num})
   @ LinearAlgebra ~/.julia/juliaup/julia-1.10.0-rc2+0.x64.linux.gnu/share/julia/stdlib/v1.10/LinearAlgebra/src/LinearAlgebra.jl:404
 [3] normalize(a::Symbolics.Arr{Num, 1}, p::Int64)
   @ LinearAlgebra ~/.julia/juliaup/julia-1.10.0-rc2+0.x64.linux.gnu/share/julia/stdlib/v1.10/LinearAlgebra/src/generic.jl:1881
 [4] normalize(a::Symbolics.Arr{Num, 1})
   @ LinearAlgebra ~/.julia/juliaup/julia-1.10.0-rc2+0.x64.linux.gnu/share/julia/stdlib/v1.10/LinearAlgebra/src/generic.jl:1879
 [5] top-level scope
   @ ~/repos/Tethers.jl/src/Tether_03.jl:15
 [6] include
   @ ./client.jl:489 [inlined]
 [7] macro expansion
   @ ./timing.jl:279 [inlined]
 [8] top-level scope
   @ ./REPL[1]:1
in expression starting at /home/ufechner/repos/Tethers.jl/src/Tether_03.jl:15

It works if I comment the line:

 unit_vector ~ normalize(pos),

What is the best way to solve this issue?

You could replace normalize with pos ./ norm(pos) to get rid of the error.

The problem seems to be that isempty(::Symbolics.Arr{Num,1}) or in your case isempty(pos) (which is called inside normalize) returns a Num instead of a Bool. Not sure if that is a feature or a bug, I suspect it is a feature for other aspects.

1 Like

Created a bug report: normalize() fails with symbolic arguments · Issue #52465 · JuliaLang/julia · GitHub

Not sure if it is a bug in LinearAlgebra or in ModelingToolkit, though…

Extra question:
The term pos / norm(pos) works as good as your suggestion, pos ./ norm(pos).

When is the dot operator needed and when not?

It’s not a registered function, so it may need to wait until symbolic array registration merges (which is hopefully next week) to then be held lazy.

1 Like

Merges to which package, or to Julia itself?

Symbolics.jl. The PR is done but just missing tests.

2 Likes

It still crashes:

using ModelingToolkit, LinearAlgebra

@variables t pos(t)[1:3] = [0.0, 0.0,  10.0]
normalize(pos)

with:

julia> normalize(pos)
ERROR: TypeError: non-boolean (Num) used in boolean context
Stacktrace:
 [1] copyto!(dest::OffsetArrays.OffsetVector{Num, Vector{Num}}, src::Symbolics.Arr{Num, 1})
   @ Base ./abstractarray.jl:1059
 [2] copymutable_oftype(A::Symbolics.Arr{Num, 1}, ::Type{Num})
   @ LinearAlgebra ~/.julia/juliaup/julia-1.10.0-rc2+0.x64.linux.gnu/share/julia/stdlib/v1.10/LinearAlgebra/src/LinearAlgebra.jl:404
 [3] normalize(a::Symbolics.Arr{Num, 1}, p::Int64)
   @ LinearAlgebra ~/.julia/juliaup/julia-1.10.0-rc2+0.x64.linux.gnu/share/julia/stdlib/v1.10/LinearAlgebra/src/generic.jl:1881
 [4] normalize(a::Symbolics.Arr{Num, 1})
   @ LinearAlgebra ~/.julia/juliaup/julia-1.10.0-rc2+0.x64.linux.gnu/share/julia/stdlib/v1.10/LinearAlgebra/src/generic.jl:1879
 [5] top-level scope
   @ REPL[8]:1
(Mo) pkg> st
Status `~/repos/Mo/Project.toml`
  [961ee093] ModelingToolkit v8.74.0
  [0c5d862f] Symbolics v5.13.0

Shall I create a bug report for Modelingtoolkit?

Did you try registering it?

I do not understand what you mean… What shall I register, and how?

From docs I think: @register_symbolic f_fun(t)

Not working:

using ModelingToolkit, LinearAlgebra

@variables t pos(t)[1:3] = [0.0, 0.0, 10.0]

@register_symbolic LinearAlgebra.normalize(pos)

normalize(pos)

Message:

ERROR: LoadError: TypeError: non-boolean (Num) used in boolean context
Stacktrace:
 [1] copyto!(dest::OffsetArrays.OffsetVector{Num, Vector{Num}}, src::Symbolics.Arr{Num, 1})
   @ Base ./abstractarray.jl:1059
 [2] copymutable_oftype(A::Symbolics.Arr{Num, 1}, ::Type{Num})
   @ LinearAlgebra ~/.julia/juliaup/julia-1.10.0-rc2+0.x64.linux.gnu/share/julia/stdlib/v1.10/LinearAlgebra/src/LinearAlgebra.jl:404
 [3] normalize(a::Symbolics.Arr{Num, 1}, p::Int64)
   @ LinearAlgebra ~/.julia/juliaup/julia-1.10.0-rc2+0.x64.linux.gnu/share/julia/stdlib/v1.10/LinearAlgebra/src/generic.jl:1881
 [4] normalize(a::Symbolics.Arr{Num, 1})
   @ LinearAlgebra ~/.julia/juliaup/julia-1.10.0-rc2+0.x64.linux.gnu/share/julia/stdlib/v1.10/LinearAlgebra/src/generic.jl:1879
 [5] top-level scope
   @ ~/repos/Tethers.jl/mwes/mwe01.jl:11
 [6] include(fname::String)
   @ Base.MainInclude ./client.jl:489
 [7] top-level scope
   @ REPL[1]:1
in expression starting at /home/ufechner/repos/Tethers.jl/mwes/mwe01.jl:11

Is this any help:

julia> using Symbolics

julia> @variables t pos(t)[1:3]
2-element Vector{Any}:
 t
  (pos(t))[1:3]

julia> using LinearAlgebra

julia> @register_symbolic LinearAlgebra.normalize(v::AbstractArray, p::Real)

julia> normalize(pos)
LinearAlgebra.normalize(pos(t), 2)

?

I thought you were linking to the new @register_array_symbolic PR which is why you asked this the day after it merged? That gives the tool to register the array function normalize but someone needs to add it to the list of registered functions for it to be kept lazy.