@@ -653,4 +653,229 @@ public void testMPSolver_setHintAndSolverGetters() {
653653
654654 assertFalse (solver .setNumThreads (4 ));
655655 }
656+
657+ private void runSolveWithHint (MPSolver .OptimizationProblemType problemType ) {
658+ if (!MPSolver .supportsProblemType (problemType )) {
659+ return ;
660+ }
661+ final MPSolver solver = new MPSolver ("testSolveWithHint" , problemType );
662+ assertNotNull (solver );
663+
664+ final double infinity = MPSolver .infinity ();
665+ final MPVariable x = solver .makeIntVar (0.0 , infinity , "x" );
666+ final MPVariable y = solver .makeIntVar (0.0 , infinity , "y" );
667+
668+ // Maximize x + 10 * y.
669+ final MPObjective objective = solver .objective ();
670+ objective .setCoefficient (x , 1 );
671+ objective .setCoefficient (y , 10 );
672+ objective .setMaximization ();
673+
674+ // x + 7 * y <= 17.5.
675+ final MPConstraint c0 = solver .makeConstraint (-infinity , 17.5 , "c0" );
676+ c0 .setCoefficient (x , 1 );
677+ c0 .setCoefficient (y , 7 );
678+
679+ // x <= 3.5.
680+ final MPConstraint c1 = solver .makeConstraint (-infinity , 3.5 , "c1" );
681+ c1 .setCoefficient (x , 1 );
682+
683+ // Provide a feasible hint to guide the solver.
684+ solver .setHint (new MPVariable [] {x , y }, new double [] {2.0 , 1.0 });
685+
686+ assertEquals (MPSolver .ResultStatus .OPTIMAL , solver .solve ());
687+ // Optimal: x = 3, y = 2, obj = 3 + 20 = 23.
688+ // With x <= 3.5 and integer, x max is 3. Then
689+ // x + 7*y <= 17.5 → 3 + 7*y <= 17.5 → 7*y <= 14.5 → y <= 2, so y = 2.
690+ assertThat (objective .value ()).isWithin (NUM_TOLERANCE ).of (23.0 );
691+ assertThat (x .solutionValue ()).isWithin (NUM_TOLERANCE ).of (3.0 );
692+ assertThat (y .solutionValue ()).isWithin (NUM_TOLERANCE ).of (2.0 );
693+ }
694+
695+ @ Test
696+ public void testMPSolver_solveWithHint () {
697+ runSolveWithHint (MPSolver .OptimizationProblemType .CBC_MIXED_INTEGER_PROGRAMMING );
698+ runSolveWithHint (MPSolver .OptimizationProblemType .SCIP_MIXED_INTEGER_PROGRAMMING );
699+ runSolveWithHint (MPSolver .OptimizationProblemType .SAT_INTEGER_PROGRAMMING );
700+ runSolveWithHint (MPSolver .OptimizationProblemType .GUROBI_MIXED_INTEGER_PROGRAMMING );
701+ }
702+
703+ private void runSolveWithAndWithoutHint (
704+ MPSolver .OptimizationProblemType problemType , boolean useHint ) {
705+ if (!MPSolver .supportsProblemType (problemType )) {
706+ return ;
707+ }
708+ final MPSolver solver =
709+ new MPSolver ("testSolveWithAndWithoutHint" , problemType );
710+ assertNotNull (solver );
711+
712+ final double infinity = MPSolver .infinity ();
713+ final MPVariable x = solver .makeIntVar (0.0 , infinity , "x" );
714+ final MPVariable y = solver .makeIntVar (0.0 , infinity , "y" );
715+ final MPVariable z = solver .makeIntVar (0.0 , infinity , "z" );
716+
717+ // Maximize 10*x + 6*y + 4*z.
718+ final MPObjective objective = solver .objective ();
719+ objective .setCoefficient (x , 10 );
720+ objective .setCoefficient (y , 6 );
721+ objective .setCoefficient (z , 4 );
722+ objective .setMaximization ();
723+
724+ // x + y + z <= 100.
725+ final MPConstraint c0 = solver .makeConstraint (-infinity , 100.0 );
726+ c0 .setCoefficient (x , 1 );
727+ c0 .setCoefficient (y , 1 );
728+ c0 .setCoefficient (z , 1 );
729+
730+ // 10*x + 4*y + 5*z <= 600.
731+ final MPConstraint c1 = solver .makeConstraint (-infinity , 600.0 );
732+ c1 .setCoefficient (x , 10 );
733+ c1 .setCoefficient (y , 4 );
734+ c1 .setCoefficient (z , 5 );
735+
736+ // 2*x + 2*y + 6*z <= 300.
737+ final MPConstraint c2 = solver .makeConstraint (-infinity , 300.0 );
738+ c2 .setCoefficient (x , 2 );
739+ c2 .setCoefficient (y , 2 );
740+ c2 .setCoefficient (z , 6 );
741+
742+ if (useHint ) {
743+ // Provide a feasible hint: (x=30, y=70, z=0) satisfies all constraints:
744+ // 30 + 70 + 0 = 100 <= 100
745+ // 10*30 + 4*70 + 5*0 = 300 + 280 = 580 <= 600
746+ // 2*30 + 2*70 + 6*0 = 60 + 140 = 200 <= 300
747+ solver .setHint (new MPVariable [] {x , y , z }, new double [] {30.0 , 70.0 , 0.0 });
748+ }
749+
750+ assertEquals (MPSolver .ResultStatus .OPTIMAL , solver .solve ());
751+ // Same problem as runLinearSolver with integer variables:
752+ // optimal: x=33, y=67, z=0, obj = 10*33 + 6*67 + 4*0 = 330 + 402 = 732.
753+ assertThat (objective .value ()).isWithin (NUM_TOLERANCE ).of (732.0 );
754+ assertThat (x .solutionValue ()).isWithin (NUM_TOLERANCE ).of (33.0 );
755+ assertThat (y .solutionValue ()).isWithin (NUM_TOLERANCE ).of (67.0 );
756+ assertThat (z .solutionValue ()).isWithin (NUM_TOLERANCE ).of (0.0 );
757+ }
758+
759+ @ Test
760+ public void testMPSolver_solveWithAndWithoutHint () {
761+ for (MPSolver .OptimizationProblemType solverType :
762+ new MPSolver .OptimizationProblemType [] {
763+ MPSolver .OptimizationProblemType .CBC_MIXED_INTEGER_PROGRAMMING ,
764+ MPSolver .OptimizationProblemType .SCIP_MIXED_INTEGER_PROGRAMMING ,
765+ MPSolver .OptimizationProblemType .SAT_INTEGER_PROGRAMMING ,
766+ MPSolver .OptimizationProblemType .GUROBI_MIXED_INTEGER_PROGRAMMING ,
767+ }) {
768+ // Solve without hint.
769+ runSolveWithAndWithoutHint (solverType , /*useHint=*/ false );
770+ // Solve with hint.
771+ runSolveWithAndWithoutHint (solverType , /*useHint=*/ true );
772+ }
773+ }
774+
775+ private void runSolveWithBadHint (MPSolver .OptimizationProblemType problemType ) {
776+ if (!MPSolver .supportsProblemType (problemType )) {
777+ return ;
778+ }
779+ final MPSolver solver =
780+ new MPSolver ("testSolveWithBadHint" , problemType );
781+ assertNotNull (solver );
782+
783+ final double infinity = MPSolver .infinity ();
784+ final MPVariable x = solver .makeIntVar (0.0 , infinity , "x" );
785+ final MPVariable y = solver .makeIntVar (0.0 , infinity , "y" );
786+
787+ // Maximize x + 10 * y.
788+ final MPObjective objective = solver .objective ();
789+ objective .setCoefficient (x , 1 );
790+ objective .setCoefficient (y , 10 );
791+ objective .setMaximization ();
792+
793+ // x + 7 * y <= 17.5.
794+ final MPConstraint c0 = solver .makeConstraint (-infinity , 17.5 , "c0" );
795+ c0 .setCoefficient (x , 1 );
796+ c0 .setCoefficient (y , 7 );
797+
798+ // x <= 3.5.
799+ final MPConstraint c1 = solver .makeConstraint (-infinity , 3.5 , "c1" );
800+ c1 .setCoefficient (x , 1 );
801+
802+ // Provide a very suboptimal but still feasible hint:
803+ // (x=0, y=0) yields obj = 0 + 10*0 = 0, but the true optimum is 23.
804+ // If the solver blindly accepted the hint and stopped early, it would
805+ // return obj=0. Verifying obj=23 proves the solver continued searching
806+ // past the hint and found the true optimum.
807+ solver .setHint (new MPVariable [] {x , y }, new double [] {0.0 , 0.0 });
808+
809+ assertEquals (MPSolver .ResultStatus .OPTIMAL , solver .solve ());
810+ // Optimal: x = 3, y = 2, obj = 3 + 20 = 23.
811+ assertThat (objective .value ()).isWithin (NUM_TOLERANCE ).of (23.0 );
812+ assertThat (x .solutionValue ()).isWithin (NUM_TOLERANCE ).of (3.0 );
813+ assertThat (y .solutionValue ()).isWithin (NUM_TOLERANCE ).of (2.0 );
814+ }
815+
816+ @ Test
817+ public void testMPSolver_solveWithBadHint () {
818+ runSolveWithBadHint (
819+ MPSolver .OptimizationProblemType .CBC_MIXED_INTEGER_PROGRAMMING );
820+ runSolveWithBadHint (
821+ MPSolver .OptimizationProblemType .SCIP_MIXED_INTEGER_PROGRAMMING );
822+ runSolveWithBadHint (
823+ MPSolver .OptimizationProblemType .SAT_INTEGER_PROGRAMMING );
824+ runSolveWithBadHint (
825+ MPSolver .OptimizationProblemType .GUROBI_MIXED_INTEGER_PROGRAMMING );
826+ }
827+
828+ private void runSolveWithInfeasibleHint (
829+ MPSolver .OptimizationProblemType problemType ) {
830+ if (!MPSolver .supportsProblemType (problemType )) {
831+ return ;
832+ }
833+ final MPSolver solver =
834+ new MPSolver ("testSolveWithInfeasibleHint" , problemType );
835+ assertNotNull (solver );
836+
837+ final double infinity = MPSolver .infinity ();
838+ final MPVariable x = solver .makeIntVar (0.0 , infinity , "x" );
839+ final MPVariable y = solver .makeIntVar (0.0 , infinity , "y" );
840+
841+ // Maximize x + 10 * y.
842+ final MPObjective objective = solver .objective ();
843+ objective .setCoefficient (x , 1 );
844+ objective .setCoefficient (y , 10 );
845+ objective .setMaximization ();
846+
847+ // x + 7 * y <= 17.5.
848+ final MPConstraint c0 = solver .makeConstraint (-infinity , 17.5 , "c0" );
849+ c0 .setCoefficient (x , 1 );
850+ c0 .setCoefficient (y , 7 );
851+
852+ // x <= 3.5.
853+ final MPConstraint c1 = solver .makeConstraint (-infinity , 3.5 , "c1" );
854+ c1 .setCoefficient (x , 1 );
855+
856+ // Provide an infeasible hint: (x=10, y=10) violates both constraints:
857+ // x + 7*y = 10 + 70 = 80 > 17.5
858+ // x = 10 > 3.5
859+ // The solver should detect infeasibility and ignore the hint, solving
860+ // the problem normally and finding the true optimum.
861+ solver .setHint (new MPVariable [] {x , y }, new double [] {10.0 , 10.0 });
862+
863+ assertEquals (MPSolver .ResultStatus .OPTIMAL , solver .solve ());
864+ // Optimal: x = 3, y = 2, obj = 3 + 20 = 23.
865+ assertThat (objective .value ()).isWithin (NUM_TOLERANCE ).of (23.0 );
866+ assertThat (x .solutionValue ()).isWithin (NUM_TOLERANCE ).of (3.0 );
867+ assertThat (y .solutionValue ()).isWithin (NUM_TOLERANCE ).of (2.0 );
868+ }
869+
870+ @ Test
871+ public void testMPSolver_solveWithInfeasibleHint () {
872+ runSolveWithInfeasibleHint (
873+ MPSolver .OptimizationProblemType .CBC_MIXED_INTEGER_PROGRAMMING );
874+ runSolveWithInfeasibleHint (
875+ MPSolver .OptimizationProblemType .SCIP_MIXED_INTEGER_PROGRAMMING );
876+ runSolveWithInfeasibleHint (
877+ MPSolver .OptimizationProblemType .SAT_INTEGER_PROGRAMMING );
878+ runSolveWithInfeasibleHint (
879+ MPSolver .OptimizationProblemType .GUROBI_MIXED_INTEGER_PROGRAMMING );
880+ }
656881}
0 commit comments