@@ -53,6 +53,60 @@ function _get_exa_model(model)
5353 return be. optimizer. model. optimizer. model
5454end
5555
56+ function parametric_genopt_test ()
57+ # Same structure as quadrotor_test but with MOI parameters declared first,
58+ # which shifts MOI variable indices relative to ExaModels internal indices.
59+ # This tests that the GenOpt extension does NOT assume var_to_idx is the identity.
60+ container = GenOpt. ParametrizedArray
61+
62+ N = 3
63+ n = 9
64+ p = 4
65+ dt = 0.1
66+
67+ x0_val = zeros (n)
68+ xf = [1.0 , 0 , 0 , 1 , 0 , 0 , 0 , 0 , 0 ]
69+
70+ Q = zeros (n)
71+ Q[1 ] = 1.0 ; Q[4 ] = 1.0
72+ Qf = ones (n)
73+ R = ones (p)
74+
75+ g = 9.81
76+
77+ itr1 = [(i, j, xf[j]) for i in 1 : N for j in 1 : n if Q[j] != 0 ]
78+ itr2 = [(j, xf[j]) for j in 1 : n]
79+
80+ model = Model ()
81+
82+ # Parameters come first, shifting MOI variable indices by 2
83+ @variable (model, par1 in MOI. Parameter (2.0 ))
84+ @variable (model, par2 in MOI. Parameter (3.0 ))
85+
86+ @variable (model, x[1 : (N+ 1 ), 1 : n])
87+ @variable (model, u[1 : N, 1 : p])
88+
89+ @constraint (model, start[i in 1 : n], x[1 , i] == x0_val[i], container = container)
90+
91+ @constraint (model, [i in 1 : N], x[i+ 1 , 1 ] == x[i, 1 ] + x[i, 2 ] * dt, container = container)
92+ @constraint (model, [i in 1 : N], x[i+ 1 , 2 ] == x[i, 2 ] + (u[i, 1 ]) * dt, container = container)
93+ @constraint (model, [i in 1 : N], x[i+ 1 , 3 ] == x[i, 3 ] + x[i, 4 ] * dt, container = container)
94+ @constraint (model, [i in 1 : N], x[i+ 1 , 4 ] == x[i, 4 ] + (u[i, 2 ]) * dt, container = container)
95+ @constraint (model, [i in 1 : N], x[i+ 1 , 5 ] == x[i, 5 ] + x[i, 6 ] * dt, container = container)
96+ @constraint (model, [i in 1 : N], x[i+ 1 , 6 ] == x[i, 6 ] + (u[i, 3 ] - g) * dt, container = container)
97+ @constraint (model, [i in 1 : N], x[i+ 1 , 7 ] == x[i, 7 ] + u[i, 1 ] * dt, container = container)
98+ @constraint (model, [i in 1 : N], x[i+ 1 , 8 ] == x[i, 8 ] + u[i, 2 ] * dt, container = container)
99+ @constraint (model, [i in 1 : N], x[i+ 1 , 9 ] == x[i, 9 ] + u[i, 4 ] * dt, container = container)
100+
101+ @objective (model, Min,
102+ lazy_sum (0.5 * R[j] * (u[i, j]^ 2 ) for i in 1 : N, j in 1 : p) +
103+ lazy_sum (0.5 * Q[it[2 ]] * (x[it[1 ], it[2 ]] - it[3 ])^ 2 for it in itr1) +
104+ lazy_sum (0.5 * Qf[it[1 ]] * (x[N+ 1 , it[1 ]] - it[2 ])^ 2 for it in itr2),
105+ )
106+
107+ return model
108+ end
109+
56110function runtests ()
57111 @testset " GenOpt extension test" begin
58112 @testset " Quadrotor with ExaModels.Optimizer" begin
@@ -81,6 +135,19 @@ function runtests()
81135 itr_lengths = sort ([length (c. itr) for c in nonempty])
82136 @test itr_lengths == [3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 9 ]
83137 end
138+
139+ @testset " GenOpt with MOI parameters" begin
140+ # Tests that var_to_idx is not assumed to be the identity.
141+ # Parameters before variables shift MOI indices.
142+ model = parametric_genopt_test ()
143+
144+ set_optimizer (model, () -> ExaModels. Optimizer (MadNLP. madnlp))
145+ set_optimizer_attribute (model, " print_level" , MadNLP. ERROR)
146+ optimize! (model)
147+
148+ # Same problem as quadrotor, should give same objective
149+ @test isapprox (objective_value (model), 8.1797 , atol = 1e-3 )
150+ end
84151 end
85152end
86153
0 commit comments