StackOverflowError, MutableArithmetics jump variables

Hello, I am making a tool to find fastest possible lap time for racing car, I am trying to use same function for initial value problem solving and also for optimisation in julia. The function describes behavior of a car. I have a rotation matrix to rotate a wheel, steering angle is therefore union of float and optimization variable. I am getting this error when I added steering of wheels to the optimisation.

     [1] operate(::typeof(*), A::Matrix{NonlinearExpression}, B::Vector{Union{Float64, NonlinearExpression, VariableRef, AffExpr}})
       @ MutableArithmetics ~/.julia/packages/MutableArithmetics/PcwmY/src/implementations/LinearAlgebra.jl:427
     [2] mul(::Matrix{NonlinearExpression}, ::Vector{Union{Float64, NonlinearExpression, VariableRef, AffExpr}})
       @ MutableArithmetics ~/.julia/packages/MutableArithmetics/PcwmY/src/shortcuts.jl:58
     [3] *(a::Matrix{NonlinearExpression}, b::Vector{Union{Float64, NonlinearExpression, VariableRef, AffExpr}})
       @ MutableArithmetics ~/.julia/packages/MutableArithmetics/PcwmY/src/dispatch.jl:369--- the above 3 lines are repeated 26660 more times ---

and this is the minimal working example that causes the same error.

using JuMP, Ipopt

model = JuMP.Model(Ipopt.Optimizer)
v = Vector{Union{Float64, JuMP.VariableRef, JuMP.AffExpr, JuMP.NonlinearExpression}}(undef, 4)

v[1] = 1.0          
v[2] = @NLexpression(model, sin(8))  # NonlinearExpression           

steering = @NLexpression(model, 0.5)
rot = [@NLexpression(model, sin(1)) @NLexpression(model, sin(2)) ;
        @NLexpression(model, sin(3)) @NLexpression(model, sin(4))]

rot*v

My actual code is here SLapSim.jl/src/components/wheelAssembly.jl at main · ceserik/SLapSim.jl · GitHub ( I didnt push version that causes this error yet)
I am new to julia so most likely I didnt find the proper way to do what i want (use same function for optimization and initial value problem solving). Does Modelling Toolkit have better ways to deal with this or what is the proper way to do it? I also found similar issue here Stack overflow error with @SDconstraint

Hi @Ceserik, you’re using the legacy @NL API. This does not support linear algebra. Use the new API, which is just removing @NL, and changing NonlinearExpression to JuMP.NonlinearExpr.

See the nonlinear documentation here: Nonlinear Modeling · JuMP

The legacy nonlinear API is documented at Nonlinear Modeling (Legacy) · JuMP

I assume you’ve just minimised your example because it has no variables, but here’s what you can do with the new API:

julia> using JuMP

julia> model = Model()
A JuMP Model
├ solver: none
├ objective_sense: FEASIBILITY_SENSE
├ num_variables: 0
├ num_constraints: 0
└ Names registered in the model: none

julia> @variable(model, x[1:2, 1:2])
2×2 Matrix{VariableRef}:
 x[1,1]  x[1,2]
 x[2,1]  x[2,2]

julia> @variable(model, y)
y

julia> v = [1.0, sin(y)]
2-element Vector{NonlinearExpr}:
 +(1.0)
 sin(y)

julia> rot = sin.(x)
2×2 Matrix{NonlinearExpr}:
 sin(x[1,1])  sin(x[1,2])
 sin(x[2,1])  sin(x[2,2])

julia> rot * v
2-element Vector{NonlinearExpr}:
 (+(0.0) + (sin(x[1,1]) * +(1.0))) + (sin(x[1,2]) * sin(y))
 (+(0.0) + (sin(x[2,1]) * +(1.0))) + (sin(x[2,2]) * sin(y))
1 Like

Hello, thanks for your answer. In my actual code I am using new API, I created minimal example using legacy API by mistake. And I forgot to use actual rotation matrix. I am sorry for not describing my issue better. I have written new minimal example, that is closer to what I am actually running. The rotation works fine for Float64 but not for JuMP expressions.

using JuMP

model = JuMP.Model()
v = Vector{Union{Float64, JuMP.VariableRef, JuMP.AffExpr, JuMP.NonlinearExpression}}(undef, 2)
w = Vector{Union{Float64, JuMP.VariableRef, JuMP.AffExpr, JuMP.NonlinearExpression}}(undef, 2)
@variable(model,x[1:2])
@variable(model, angle)
v[1] = x[1]
v[2] = x[2]

w[1] = 1.0
w[2] = 2.0

function rotate(angle)
    out = [
        cos(angle) -sin(angle);
        -sin(angle) cos(angle)
    ]
    return out
end


b = rotate(pi/2) * w  #works fine
c = rotate(angle) * v #error

I get this error

ERROR: StackOverflowError:
Stacktrace:
     [1] operate(::typeof(*), A::Matrix{NonlinearExpr}, B::Vector{Union{Float64, NonlinearExpression, VariableRef, AffExpr}})
       @ MutableArithmetics ~/.julia/packages/MutableArithmetics/PcwmY/src/implementations/LinearAlgebra.jl:418
     [2] mul(::Matrix{NonlinearExpr}, ::Vector{Union{Float64, NonlinearExpression, VariableRef, AffExpr}})
       @ MutableArithmetics ~/.julia/packages/MutableArithmetics/PcwmY/src/shortcuts.jl:58
     [3] *(a::Matrix{NonlinearExpr}, b::Vector{Union{Float64, NonlinearExpression, VariableRef, AffExpr}})
       @ MutableArithmetics ~/.julia/packages/MutableArithmetics/PcwmY/src/dispatch.jl:369
     [4] operate(::typeof(*), A::Matrix{NonlinearExpr}, B::Vector{Union{Float64, NonlinearExpression, VariableRef, AffExpr}})
       @ MutableArithmetics ~/.julia/packages/MutableArithmetics/PcwmY/src/implementations/LinearAlgebra.jl:427
--- the above 3 lines are repeated 26659 more times ---
 [79982] mul(::Matrix{NonlinearExpr}, ::Vector{Union{Float64, NonlinearExpression, VariableRef, AffExpr}})
       @ MutableArithmetics ~/.julia/packages/MutableArithmetics/PcwmY/src/shortcuts.jl:58
 [79983] *(a::Matrix{NonlinearExpr}, b::Vector{Union{Float64, NonlinearExpression, VariableRef, AffExpr}})
       @ MutableArithmetics ~/.julia/packages/MutableArithmetics/PcwmY/src/dispatch.jl:369

How can I fix this?

By the way this function responsible for creating and solving the problem, I cannot include it all here because the car model is spread around many files

function findOptimalTrajectory(track::Track,car::Car,model::JuMP.Model)
    N = length(track.sampleDistances)

    #fill track parameters which are constant along track, should be automatized
    track.rho    = fill(track.rho[1],N)
    track.μ      = fill(track.μ[1],N)
    track.widthL = fill(track.widthL[1],N)
    track.widthR = fill(track.widthR[1],N)

    nStates =  Int(car.carParameters.nStates.value)
    #create inputs for car model #create instance of track parameters

    #determine sizes of inputs and states
    nControls = Int(round(car.carParameters.nControls.value))
    @variable(model,U[1:N-1,1:nControls])

    #stateInit = [fill(5,N),zeros(N),zeros(N),zeros(N),zeros(N),range(0,4,N)]'
    @variable(model,X[1:N,1:6])

    for i = 1:N
        set_start_value(X[i,1], 5.0)  # vx = 5
        set_start_value(X[i,2], 0.0)  # vy = 0
        set_start_value(X[i,3], pi/2)  # psi = 0
        set_start_value(X[i,4], 0.0)  # dpsi = 0
        set_start_value(X[i,5], 0.0)  # n = 0
        set_start_value(X[i,6], 4*(i-1)/(N-1))  # t = linspace(0,4,N)
        #println(4*(i-1)/(N-1))
    end

    
    s = track.sampleDistances#1:length(track.curvature)
    #t_final = X[end,6]


    for k = 1:N-1 # loop over control intervals
        x_next = X[k,:] .+ (s[k+1]-s[k]) .* carODE_path(car,track,k, U[k,:], X[k,:],model);
        @constraint(model,X[k+1,:] .== x_next)
    end

    #initial states
    #        [vx vy psi dpsi n t]
    #initial = [4; 0; 0; 0; 0; 0]
    #set initial states
    #@constraint(model,X[1,:] .== initial)

    @constraint(model,X[1:end,1] .>= 0) #vx
    @constraint(model,X[1,1] .== 5) # intial vx
    @constraint(model,X[1,2] .== 0) # intial vx
    @constraint(model,X[1,6] .>= 0) # final time
    @constraint(model,diff(X[:,6]) .>=0) #time goes forward


    @objective(model,Min,X[end,6])
    optimize!(model)

    # Plotting the results
    fig = Figure(layout = GridLayout(6, 1))
    
    # Create subplots for each variable
    labels = ["vx", "vy", "psi", "dpsi", "n", "t"]
    for (i, label) in enumerate(labels)
        ax = Axis(fig[i, 1], ylabel = label)
        lines!(ax, value.(X[:,i]), label=label)
    end
    display(GLMakie.Screen(), fig)  # This creates a new window
    return X, U
end

Okay, I can reproduce:

julia> using JuMP

julia> rotate(angle) = [cos(angle) -sin(angle); -sin(angle) cos(angle)]
rotate (generic function with 1 method)

julia> begin
           model = Model()
           @variable(model, x[1:2])
           @variable(model, angle)
           v = Union{Float64,JuMP.VariableRef}[x[1], x[2]]
           c = rotate(angle) * v
       end
ERROR: StackOverflowError:
Stacktrace:
 [1] operate(::typeof(*), A::Matrix{NonlinearExpr}, B::Vector{Union{Float64, VariableRef}})
   @ MutableArithmetics ~/.julia/packages/MutableArithmetics/PcwmY/src/implementations/LinearAlgebra.jl:418
 [2] mul(::Matrix{NonlinearExpr}, ::Vector{Union{Float64, VariableRef}})
   @ MutableArithmetics ~/.julia/packages/MutableArithmetics/PcwmY/src/shortcuts.jl:58

julia> begin
           model = Model()
           @variable(model, x[1:2])
           @variable(model, angle)
           v = Any[x[1], x[2]]
           c = rotate(angle) * v
       end
ERROR: StackOverflowError:
Stacktrace:
     [1] *(a::Matrix{NonlinearExpr}, b::Vector{Any})
       @ MutableArithmetics ~/.julia/packages/MutableArithmetics/PcwmY/src/dispatch.jl:369
     [2] operate(::typeof(*), A::Matrix{NonlinearExpr}, B::Vector{Any})
       @ MutableArithmetics ~/.julia/packages/MutableArithmetics/PcwmY/src/implementations/LinearAlgebra.jl:427
     [3] mul(::Matrix{NonlinearExpr}, ::Vector{Any})
       @ MutableArithmetics ~/.julia/packages/MutableArithmetics/PcwmY/src/shortcuts.jl:58--- the above 3 lines are repeated 26660 more times ---

I’m taking a look to see if we can fix this in MutableArithmetics. I understand the cause, but a fix is a bit tricky,

In the mean time, you can avoid the problem by ensuring that the matrix and vector have a single concrete type, like JuMP.NonlinearExpr

julia> using JuMP

julia> rotate(angle) = [cos(angle) -sin(angle); -sin(angle) cos(angle)]
rotate (generic function with 1 method)

julia> begin
           model = Model()
           @variable(model, x[1:2])
           @variable(model, angle)
           v = NonlinearExpr[x[1], x[2]]
           c = rotate(angle) * v
       end
2-element Vector{NonlinearExpr}:
 (+(0.0) + (cos(angle) * +(x[1]))) + (-(sin(angle)) * +(x[2]))
 (+(0.0) + (-(sin(angle)) * +(x[1]))) + (cos(angle) * +(x[2]))

Edit: upstream issue: StackOverflow with * between non-concrete types · Issue #336 · jump-dev/MutableArithmetics.jl · GitHub

1 Like

Thanks boss :smiley:

1 Like