Using structures with JuMP

I am trying to solve a non-linear system re-writing it in a way that I can use optimization (basically the objective function is 1.0 and the constraints are the equations of the system).

However, these equations use user-defined function that have objects of type mutable structure as arguments. When I try to run the model I get the error “Unexpected stuct_params of type struct_params in non-linear expression”, where “struct_params” is the structure that I am talking about. A snippet of the code is provided below

function v_zeros_UI_exogeno!(pars,Psi,UI)
    pars::struct_params
    Psi::struct_smm
    UI::Float64
    
    b = (UI^(1-pars.sigma))/(1-pars.sigma)
    n = 0.5*b
    
    model = Model(Ipopt.Optimizer)
    
    @variable(model, v_ni >= 0)
    @variable(model, v_nf >= 0)
    @variable(model, v_bi >= 0)
    @variable(model, v_bf >= 0) 
    
    register(model, :gauss_cheby_integral_f, 4, gauss_cheby_integral_f, autodiff=true)
    register(model, :gauss_cheby_integral_i, 6, gauss_cheby_integral_i, autodiff=true)
    register(model, :complementar_cdf_f, 3, complementar_cdf_f, autodiff=true)
    register(model, :complementar_cdf_i, 5, complementar_cdf_i, autodiff=true)

    @objective(model, Min, 1.0)
    
    @NLconstraint(model, c1, v_ni == n + (Psi.alpha_f/(pars.r+pars.lambda_f))*gauss_cheby_integral_f(v_nf,v_max,pars,Psi) + 
    (Psi.alpha_i/(pars.r+pars.lambda_i))*gauss_cheby_integral_i(v_ni,v_max,pars,Psi,false,UI) + 
    (Psi.alpha_f/(pars.r+pars.lambda_f))*(v_nf - v_ni + (pars.lambda_f/pars.r)*(v_bf-v_ni))*complementar_cdf_f(pars,Psi,v_nf))
    
    @NLconstraint(model, c2, v_nf == ((pars.r+pars.lambda_f)/pars.r)*v_ni - (pars.lambda_f/pars.r)*v_bf)
    
    @NLconstraint(model, c3, v_bi == ((pars.r+pars.theta)/pars.r)*v_bf - (pars.theta/pars.r)*v_ni)
    
    @NLconstraint(model, c4, v_bf == b - (pars.theta/pars.r)*(v_bf - v_ni) + (Psi.alpha_f/(pars.r+pars.lambda_f))*gauss_cheby_integral_f(v_bf,v_max,pars,Psi) + 
    (Psi.alpha_i/(pars.r+pars.lambda_i+pars.theta))*gauss_cheby_integral_i(v_bi,v_max,pars,Psi,true,UI) + 
    (complementar_cdf_i(pars,Psi,true,v_bi,UI))*(((pars.theta*pars.lambda_i)/(pars.r+pars.lambda_i))*(v_ni/pars.r) - pars.theta*(v_bf/pars.r)) + 
    ((Psi.alpha_i*pars.theta)/((pars.r+pars.lambda_i+pars.theta)*(pars.r+pars.lambda_i)))*(gauss_cheby_integral_i(v_ni,v_max,pars,Psi,false,UI) + ((complementar_cdf_i(pars,Psi,true,v_ni,UI))-(complementar_cdf_i(pars,Psi,true,v_bi,UI)))*v_ni))
    
    optimize!(model)

    return [value(v_ni),value(v_nf),value(v_bi),value(v_bf)]
end

Hi @joaomcostab, welcome to the forum!

Last year we rewrote JuMP’s nonlinear interface, precisely to support structures like this.

See:

To update, change

register(model, :gauss_cheby_integral_f, 4, gauss_cheby_integral_f, autodiff=true)

to

@operator(model, op_gauss_cheby_integral_f, 4, gauss_cheby_integral_f)

Use op_gauss_cheby_integral_f instead of gauss_cheby_integral_f in the constraint macros, and change @NLconstraint to @constraint.

If you get stuck, post a reproducible example that we can copy and paste, and we can help fix any issues.

Thanks for the quick reply!

This almost got my problem solved. However, now I am having an issue with the moi_function. I have two mutable structures, struct_params and struct_smm. After making the changes suggested, it gives the error “MethodError: no method matching moi_function(::struct_smm)”.

Can you make a reproducible example? What is the full error message and stacktrace?

Note that your user defined function must take only JuMP variables and expressions as input. They must not take additonal arguments like your structs.

Thanks again for the reply.

I do not feel free to make a reproducible example because I am not full responsible for the code. To make a reproducible example, I would have to give parts of the code that my supervisor may not like.

However, I guess that your comment may solve the issue. I will change the functions to only take JuMP variables.

Again, thanks for the reply!

Try simplifying the code as much as possible. Delete unnecessary variables and constraints. Replace data with constants like 1.0. Rename sensitive variables and constraints. The goal is to have a piece of code that reproduces the error. We don’t need exactly the code you are running.

The change will be something like this. Instead of:

gauss_cheby_integral_f(v_nf,v_max,pars,Psi)

do

function jump_gauss_cheby_integral_f(v_nf, v_max)
    return gauss_cheby_integral_f(v_nf,v_max,pars,Psi)
end
@operator(model, op_gauss_cheby_integral_f, 2, jump_gauss_cheby_integral_f)

and * op_gauss_cheby_integral_f(v_nf, v_max) +.