@@ -11,6 +11,7 @@ import DiffOpt
1111import HiGHS
1212import Ipopt
1313import SCS
14+ import LinearAlgebra
1415import MathOptInterface as MOI
1516
1617const ATOL = 1e-3
@@ -337,6 +338,98 @@ function test_jump_api()
337338 return
338339end
339340
341+ function test_forward_wrappers_non_parametric ()
342+ # Tests set_forward_objective_function and set_forward_constraint_function
343+ # overloads on a non-parametric model.
344+ #
345+ # min x s.t. x + y == 1, x >= 0, y >= 0
346+ # Solution: x=0, y=1
347+ # Perturb constraint RHS: x + y + ϵ == 1
348+ # Sensitivity should be dy/dϵ = -1
349+ # since the slack goes to y and x is at its lower bound (active).
350+ model = JuMP. direct_model (DiffOpt. diff_optimizer (HiGHS. Optimizer))
351+ set_silent (model)
352+ @variable (model, x >= 0 )
353+ @variable (model, y >= 0 )
354+ @constraint (model, c1, x + y == 1 )
355+ @objective (model, Min, 1.0 * x)
356+ optimize! (model)
357+ @test value (x) ≈ 0.0 atol = ATOL
358+ @test value (y) ≈ 1.0 atol = ATOL
359+
360+ DiffOpt. set_forward_objective_function (model, 0.0 )
361+ DiffOpt. set_forward_constraint_function (model, c1, 1.0 )
362+ DiffOpt. forward_differentiate! (model)
363+ dy = DiffOpt. get_forward_variable (model, y)
364+ @test dy ≈ - 1.0 atol = ATOL
365+
366+ DiffOpt. empty_input_sensitivities! (model)
367+ DiffOpt. set_forward_constraint_function (model, c1, 0.0 * x + 1.0 )
368+ DiffOpt. forward_differentiate! (model)
369+ dy2 = DiffOpt. get_forward_variable (model, y)
370+ @test dy2 ≈ dy atol = ATOL
371+ return
372+ end
373+
374+ function test_forward_vector_constraint_wrappers ()
375+ # Tests set_forward_constraint_function overloads for vector constraints
376+ # using conic_diff_model (direct_model + diff_optimizer bridges differently).
377+ model = DiffOpt. conic_diff_model (SCS. Optimizer)
378+ set_silent (model)
379+ @variable (model, x)
380+ @variable (model, y)
381+ @constraint (model, c_eq, x + y == 1 )
382+ @constraint (model, c_nn, [1.0 * y, 1.0 * x] in MOI. Nonnegatives (2 ))
383+ @objective (model, Min, 1.0 * x)
384+ optimize! (model)
385+ @test value (x) ≈ 0.0 atol = ATOL
386+ @test value (y) ≈ 1.0 atol = ATOL
387+
388+ DiffOpt. set_forward_constraint_function (model, c_nn, [0.0 , 0.0 ])
389+ DiffOpt. set_forward_constraint_function (model, c_eq, 1.0 )
390+ DiffOpt. forward_differentiate! (model)
391+ dy = DiffOpt. get_forward_variable (model, y)
392+ @test dy ≈ - 1.0 atol = ATOL
393+
394+ DiffOpt. empty_input_sensitivities! (model)
395+ DiffOpt. set_forward_constraint_function (model, c_nn, [0.0 * x, 0.0 * x])
396+ DiffOpt. set_forward_constraint_function (model, c_eq, 1.0 )
397+ DiffOpt. forward_differentiate! (model)
398+ dy2 = DiffOpt. get_forward_variable (model, y)
399+ @test dy2 ≈ dy atol = ATOL
400+ return
401+ end
402+
403+ function test_forward_psd_matrix_wrapper ()
404+ # Tests set_forward_constraint_function with AbstractMatrix{<:AbstractJuMPScalar}
405+ # for PSD cone constraints. Uses a non-parametric model since
406+ # ForwardConstraintFunction is blocked on parametric models.
407+ model = Model (() -> DiffOpt. diff_optimizer (SCS. Optimizer))
408+ set_silent (model)
409+ @variable (model, x)
410+ @objective (model, Min, - x)
411+ @constraint (
412+ model,
413+ con,
414+ LinearAlgebra. Symmetric ([1.0 - x 0.0 ; 0.0 x]) in PSDCone (),
415+ )
416+ optimize! (model)
417+ @test value (x) ≈ 1.0 atol = ATOL
418+
419+ perturbation = [1.0 + 0.0 * x 0.0 * x; 0.0 * x 0.0 * x]
420+ DiffOpt. set_forward_constraint_function (model, con, perturbation)
421+ DiffOpt. forward_differentiate! (model)
422+ @test DiffOpt. get_forward_variable (model, x) ≈ 1.0 atol = ATOL
423+
424+ bad = [0.0 * x 1.0 * x; 0.0 * x 0.0 * x]
425+ @test_throws ErrorException DiffOpt. set_forward_constraint_function (
426+ model,
427+ con,
428+ bad,
429+ )
430+ return
431+ end
432+
340433end # module
341434
342435TestJuMPWrapper. runtests ()
0 commit comments