Skip to content

Commit 6b06224

Browse files
committed
Feasible and Infeasible solution callbacks
1 parent e7e7c5a commit 6b06224

File tree

2 files changed

+131
-53
lines changed

2 files changed

+131
-53
lines changed

docs/src/apireference.md

+5-4
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,10 @@ submit
7777
List of submittables
7878

7979
```@docs
80+
RejectSolution
8081
LazyConstraint
8182
HeuristicSolution
83+
UserCut
8284
```
8385

8486
## Model Interface
@@ -129,8 +131,8 @@ Silent
129131
TimeLimitSec
130132
RawParameter
131133
AbstractCallback
132-
LazyCallback
133-
HeuristicCallback
134+
FeasibleSolutionCallback
135+
InfeasibleSolutionCallback
134136
```
135137

136138
List of attributes useful for optimizers
@@ -229,8 +231,7 @@ Calls to `get` and `set` should include as an argument a single `VariableIndex`
229231
VariableName
230232
VariablePrimalStart
231233
VariablePrimal
232-
VariablePrimalAtIntegerNode
233-
VariablePrimalAtFractionalNode
234+
CallbackVariablePrimal
234235
```
235236

236237
### Constraints

src/attributes.jl

+126-49
Original file line numberDiff line numberDiff line change
@@ -407,36 +407,71 @@ end
407407

408408
## Submittables
409409

410+
"""
411+
RejectSolution(callback_data)
412+
413+
Reject feasible solution provided by [`CallbackVariablePrimal`](@ref).
414+
415+
This can be submitted only from the [`FeasibleSolutionCallback`](@ref). The
416+
field `callback_data` is a solver-specific callback type that is passed as the
417+
argument to the feasible solution callback.
418+
"""
419+
struct RejectSolution{CBDT} <: AbstractSubmittable
420+
callback_data::CBDT
421+
end
422+
410423
"""
411424
LazyConstraint(callback_data)
412425
413-
Lazy constraint `func`-in-`set` submitted as a tuple `(func, set)`. The optimal
426+
Lazy constraint `func`-in-`set` submitted as `func, set`. The optimal
414427
solution returned by [`VariablePrimal`](@ref) will satisfy all lazy
415428
constraints that have been submitted.
416429
417-
This can be submitted only from the lazy callback (that is, the function set to
418-
[`LazyCallback`](@ref)). The field `callback_data` is a solver-specific
419-
callback type that is passed as the argument to the lazy callback.
430+
This can be submitted only from the [`FeasibleSolutionCallback`](@ref). The
431+
field `callback_data` is a solver-specific callback type that is passed as the
432+
argument to the feasible solution callback.
420433
"""
421-
struct LazyConstraint <: AbstractSubmittable
422-
callback_data::Any
434+
struct LazyConstraint{CBDT} <: AbstractSubmittable
435+
callback_data::CBDT
423436
end
424437

425438
"""
426439
HeuristicSolution(callback_data)
427440
428-
Heuristically obtained integer-feasible solutions. The solution is given by a
429-
`Dict{VariableIndex, Float64}` mapping the variables to their solution value.
441+
Heuristically obtained feasible solution. The solution is submitted as
442+
`variables, values` where `values[i]` gives the value of `variables[i]`,
443+
similarly to [`set`](@ref).
430444
431-
This can be submitted only from the heuristic callback (that is, the function
432-
set to [`HeuristicCallback`](@ref)). The field `callback_data` is a
433-
solver-specific callback type that is passed as the argument to the heuristic
434-
callback.
445+
This can be submitted only from the [`InfeasibleSolutionCallback`](@ref). The
446+
field `callback_data` is a solver-specific callback type that is passed as the
447+
argument to the infeasible solution callback.
435448
436449
Note that the solver may silently reject the provided solution.
437450
"""
438-
struct HeuristicSolution <: AbstractSubmittable
439-
callback_data::Any
451+
struct HeuristicSolution{CBDT} <: AbstractSubmittable
452+
callback_data::CBDT
453+
end
454+
455+
"""
456+
UserCut(callback_data)
457+
458+
Constraint `func`-to-`set` suggested to render the solution given by
459+
[`CallbackVariablePrimal`](@ref) trivially infeasible. The solution is submitted
460+
as `func, set`. For instance, for a solution satisfying all constraints except
461+
[`SingleVariable`](@ref)-in-[`Integer`](@ref) constraints, a
462+
[`ScalarAffineFunction`](@ref)-in-[`LessThan`](@ref) may be submitted cut it
463+
off. Note that, as opposed to [`LazyConstraint`](@ref), the provided constraint
464+
cannot modify the feasible set, the constraint should be redundant, e.g., it
465+
may be a consequence of affine and integrality constraints.
466+
467+
This can be submitted only from the [`InfeasibleSolutionCallback`](@ref). The
468+
field `callback_data` is a solver-specific callback type that is passed as the
469+
argument to the infeasible solution callback.
470+
471+
Note that the solver may silently ignore the provided constraint.
472+
"""
473+
struct UserCut{CBDT} <: AbstractSubmittable
474+
callback_data::CBDT
440475
end
441476

442477
## Optimizer attributes
@@ -525,43 +560,97 @@ attributes (i.e, the attributes `attr` such that `is_set_by_optimize(attr)`)
525560
may not be accessible from the callback hence trying to get result attributes
526561
might throw a [`OptimizeInProgress`](@ref) error.
527562
563+
Some solvers require a complete solution, others only partial solutions. It's up
564+
to you to provide the appropriate one. If in doubt, give a complete solution.
565+
528566
The value of the attribute should be a function taking only one argument, that
529567
is commonly called `callback_data`, that can be used for instance in
530-
[`LazyCallback`](@ref), [`HeuristicSolution`](@ref).
568+
[`FeasibleSolutionCallback`](@ref), [`InfeasibleSolutionCallback`](@ref).
531569
"""
532570
abstract type AbstractCallback <: AbstractOptimizerAttribute end
533571

534572
"""
535-
LazyCallback() <: AbstractCallback
573+
FeasibleSolutionCallback() <: AbstractCallback
536574
537-
The *lazy* callback can be used to submit [`LazyConstraint`](@ref) which are
538-
added on-demand at integer nodes in the branch and bound tree. Note that there
539-
is no guarantee that the callback is called at *every* integer primal solution.
575+
The callback can be used to reject a feasible primal solution by submitting
576+
[`RejectSolution`](@ref) and/or [`LazyConstraint`](@ref). Note that there
577+
is no guarantee that the callback is called at *every* feasible primal solution.
540578
541-
The solution at the integer node is accessed through
542-
[`VariablePrimalAtIntegerNode`](@ref). Trying to access other result
579+
The feasible primal solution is accessed through
580+
[`CallbackVariablePrimal`](@ref). Trying to access other result
543581
attributes will throw [`OptimizeInProgress`](@ref) as discussed in
544582
[`AbstractCallback`](@ref).
583+
584+
## Examples
585+
586+
```julia
587+
x = MOI.add_variables(optimizer, 8)
588+
MOI.submit(optimizer, MOI.FeasibleSolutionCallback(), callback_data -> begin
589+
sol = MOI.get(optimizer, MOI.CallbackVariablePrimal(callback_data), x)
590+
if MOI.supports(optimizer, MOI.RejectSolution(callback_data))
591+
# determine if it should be rejected only if it's supported as it may
592+
# be time consuming.
593+
if # should reject
594+
MOI.submit(optimizer, MOI.RejectSolution(callback_data))
595+
end
596+
end
597+
if MOI.supports(optimizer, MOI.LazyConstraint(callback_data))
598+
# determine the lazy constraint only if it's supported as it may be time
599+
# consuming.
600+
if # should add a lazy constraint
601+
func = # computes function
602+
set = # computes set
603+
MOI.submit(optimizer, MOI.LazyConstraint(callback_data), func, set)
604+
end
605+
end
606+
end
607+
```
545608
"""
546-
struct LazyCallback <: AbstractCallback end
609+
struct FeasibleSolutionCallback <: AbstractCallback end
547610

548611
"""
549-
HeuristicCallback() <: AbstractCallback
612+
InfeasibleSolutionCallback() <: AbstractCallback
550613
551-
The *heuristic* callback can be used to submit [`HeuristicSolution`](@ref) at
552-
fractional (i.e., non-integer) nodes in the branch and bound tree. Note that
553-
there is not guarantee that the callback is called *everytime* the solver has a
554-
fractional solution.
614+
The callback can be used to submit [`HeuristicSolution`](@ref) and/or
615+
[`UserCut`](@ref) given an infeasible primal solution.
616+
For instance, it may be called at fractional (i.e., non-integer) nodes in the
617+
branch and bound tree of a mixed-integer problem. Note that there is not
618+
guarantee that the callback is called *everytime* the solver has an infeasible
619+
solution.
555620
556-
The solution at the fractional node is accessed through
557-
[`VariablePrimalAtFractionalNode`](@ref). Trying to access other result
621+
The infeasible solution is accessed through
622+
[`CallbackVariablePrimal`](@ref). Trying to access other result
558623
attributes will throw [`OptimizeInProgress`](@ref) as discussed in
559624
[`AbstractCallback`](@ref).
560625
561-
Some solvers require a complete solution, others only partial solutions. It's up
562-
to you to provide the appropriate one. If in doubt, give a complete solution.
626+
## Examples
627+
628+
```julia
629+
x = MOI.add_variables(optimizer, 8)
630+
MOI.submit(optimizer, MOI.InfeasibleSolutionCallback(), callback_data -> begin
631+
sol = MOI.get(optimizer, MOI.CallbackVariablePrimal(callback_data), x)
632+
if MOI.supports(optimizer, MOI.HeuristicSolution(callback_data))
633+
# determine the heuristic solution only if it it's supported as it may
634+
# be time consuming.
635+
if # can find a heuristic solution
636+
values = # computes heuristic solution
637+
MOI.submit(optimizer, MOI.HeuristicSolution(callback_data), x,
638+
values)
639+
end
640+
end
641+
if MOI.supports(optimizer, MOI.UserCut(callback_data))
642+
# determine the user cut only if it's supported as it may be time
643+
# consuming.
644+
if # can find a user cut
645+
func = # computes function
646+
set = # computes set
647+
MOI.submit(optimizer, MOI.UserCut(callback_data), func, set)
648+
end
649+
end
650+
end
651+
```
563652
"""
564-
struct HeuristicCallback <: AbstractCallback end
653+
struct InfeasibleSolutionCallback <: AbstractCallback end
565654

566655
## Model attributes
567656

@@ -784,25 +873,13 @@ end
784873
VariablePrimal() = VariablePrimal(1)
785874

786875
"""
787-
VariablePrimalAtIntegerNode(callback_data)
788-
789-
A variable attribute for the assignment to some primal variable's value at the
790-
integer node from which the [`LazyCallback`](@ref) corresponding to
791-
`callback_data` was called.
792-
"""
793-
struct VariablePrimalAtIntegerNode <: AbstractVariableAttribute
794-
callback_data::Any
795-
end
796-
797-
"""
798-
VariablePrimalAtFractionalNode(callback_data)
876+
CallbackVariablePrimal(callback_data)
799877
800-
A variable attribute for the assignment to some primal variable's value at the
801-
fractional node from which the [`HeuristicCallback`](@ref) corresponding to
802-
`callback_data` was called.
878+
A variable attribute for the assignment to some primal variable's value during
879+
the callback identified by `callback_data`.
803880
"""
804-
struct VariablePrimalAtFractionalNode <: AbstractVariableAttribute
805-
callback_data::Any
881+
struct CallbackVariablePrimal{CBDT} <: AbstractVariableAttribute
882+
callback_data::CBDT
806883
end
807884

808885
"""

0 commit comments

Comments
 (0)