@@ -283,7 +283,7 @@ julia> MOI.Bridges.runtests(
283
283
end,
284
284
)
285
285
Test Summary: | Pass Total Time
286
- Bridges.runtests | 32 32 0.8s
286
+ Bridges.runtests | 33 33 0.8s
287
287
```
288
288
"""
289
289
function runtests (args... ; kwargs... )
@@ -293,12 +293,60 @@ function runtests(args...; kwargs...)
293
293
return
294
294
end
295
295
296
+ # A good way to check that the linear mapping implemented in the setter of
297
+ # `ConstraintDual` is the inverse-adjoint of the mapping implemented in the
298
+ # constraint transformation is to check `get_fallback` for `DualObjectiveValue`.
299
+ # Indeed, it will check that the inner product between the constraint constants
300
+ # and the dual is the same before and after the bridge transformations.
301
+ # For this test to be enabled, the bridge should implement `supports`
302
+ # for `ConstraintDual` and implement `MOI.set` for `ConstraintDual`.
303
+ # Typically, this would be achieved using
304
+ # `Union{ConstraintDual,ConstraintDualStart}` for `MOI.get`, `MOI.set` and
305
+ # `MOI.supports`
306
+ function _test_dual (
307
+ Bridge:: Type{<:AbstractBridge} ,
308
+ input_fn:: Function ;
309
+ dual,
310
+ eltype,
311
+ model_eltype,
312
+ )
313
+ inner = MOI. Utilities. UniversalFallback (MOI. Utilities. Model {model_eltype} ())
314
+ mock = MOI. Utilities. MockOptimizer (inner)
315
+ model = _bridged_model (Bridge{eltype}, mock)
316
+ input_fn (model)
317
+ final_touch (model)
318
+ # Should be able to call final_touch multiple times.
319
+ final_touch (model)
320
+ # If the bridges does not support `ConstraintDualStart`, it probably won't
321
+ # support `ConstraintDual` so we skip these tests
322
+ list_of_constraints = MOI. get (model, MOI. ListOfConstraintTypesPresent ())
323
+ attr = MOI. ConstraintDual ()
324
+ for (F, S) in list_of_constraints
325
+ if ! MOI. supports (model, attr, MOI. ConstraintIndex{F,S})
326
+ # We need all duals for `DualObjectiveValue` fallback
327
+ # TODO except the ones with no constants, we could ignore them
328
+ return
329
+ end
330
+ for ci in MOI. get (model, MOI. ListOfConstraintIndices {F,S} ())
331
+ set = MOI. get (model, MOI. ConstraintSet (), ci)
332
+ MOI. set (model, MOI. ConstraintDual (), ci, _fake_start (dual, set))
333
+ end
334
+ end
335
+ model_dual =
336
+ MOI. Utilities. get_fallback (model, MOI. DualObjectiveValue (), eltype)
337
+ mock_dual =
338
+ MOI. Utilities. get_fallback (mock, MOI. DualObjectiveValue (), eltype)
339
+ # Need `atol` in case one of them is zero and the other one almost zero
340
+ Test. @test model_dual ≈ mock_dual atol = 1e-6
341
+ end
342
+
296
343
function _runtests (
297
344
Bridge:: Type{<:AbstractBridge} ,
298
345
input_fn:: Function ,
299
346
output_fn:: Function ;
300
347
variable_start = 1.2 ,
301
348
constraint_start = 1.2 ,
349
+ dual = constraint_start,
302
350
eltype = Float64,
303
351
model_eltype = eltype,
304
352
print_inner_model:: Bool = false ,
@@ -403,6 +451,11 @@ function _runtests(
403
451
Test. @testset " Test delete" begin # COV_EXCL_LINE
404
452
_test_delete (Bridge, model, inner)
405
453
end
454
+ if ! isnothing (dual)
455
+ Test. @testset " Test ConstraintDual" begin
456
+ _test_dual (Bridge, input_fn; dual, eltype, model_eltype)
457
+ end
458
+ end
406
459
return
407
460
end
408
461
0 commit comments