@@ -810,27 +810,77 @@ class SolutionData(StrictModel):
810810 )
811811
812812
813+ # LP termination status values
814+ # NOTE: These must match LPTerminationStatus from
815+ # cuopt.linear_programming.solver.solver_wrapper
816+ # We cannot import them directly because it triggers CUDA/RMM initialization
817+ # before the server has configured memory management.
818+ # See test_termination_status_enum_sync() in test_lp.py to ensure these stay in sync.
819+ LP_STATUS_NAMES = frozenset (
820+ {
821+ "NoTermination" ,
822+ "NumericalError" ,
823+ "Optimal" ,
824+ "PrimalInfeasible" ,
825+ "DualInfeasible" ,
826+ "IterationLimit" ,
827+ "TimeLimit" ,
828+ "PrimalFeasible" ,
829+ }
830+ )
831+
832+ # MILP termination status values
833+ # NOTE: These must match MILPTerminationStatus from
834+ # cuopt.linear_programming.solver.solver_wrapper
835+ MILP_STATUS_NAMES = frozenset (
836+ {
837+ "NoTermination" ,
838+ "Optimal" ,
839+ "FeasibleFound" ,
840+ "Infeasible" ,
841+ "Unbounded" ,
842+ "TimeLimit" ,
843+ }
844+ )
845+
846+ # Combined set of all valid status names
847+ ALL_STATUS_NAMES = LP_STATUS_NAMES | MILP_STATUS_NAMES
848+
849+
850+ def validate_termination_status (v ):
851+ """Validate that status is a valid LP or MILP termination status name."""
852+ if v not in ALL_STATUS_NAMES :
853+ raise ValueError (
854+ f"status must be one of { sorted (ALL_STATUS_NAMES )} , got '{ v } '"
855+ )
856+ return v
857+
858+
813859class SolutionResultData (StrictModel ):
814- status : int = Field (
815- default = 0 ,
816- examples = [1 ],
817- description = (
818- "In case of LP : \n \n "
819- "0 - No Termination \n \n "
820- "1 - Optimal solution is available \n \n "
821- "2 - Primal Infeasible solution \n \n "
822- "3 - Dual Infeasible solution \n \n "
823- "4 - Iteration Limit reached \n \n "
824- "5 - TimeLimit reached \n \n "
825- "6 - Primal Feasible \n \n "
826- "---------------------- \n \n "
827- "In case of MILP/IP : \n \n "
828- "0 - No Termination \n \n "
829- "1 - Optimal solution is available \n \n "
830- "2 - Feasible solution is available \n \n "
831- "3 - Infeasible \n \n "
832- "4 - Unbounded\n \n "
833- ),
860+ status : Annotated [str , PlainValidator (validate_termination_status )] = (
861+ Field (
862+ default = "NoTermination" ,
863+ examples = ["Optimal" ],
864+ description = (
865+ "In case of LP : \n \n "
866+ "NoTermination - No Termination \n \n "
867+ "NumericalError - Numerical Error \n \n "
868+ "Optimal - Optimal solution is available \n \n "
869+ "PrimalInfeasible - Primal Infeasible solution \n \n "
870+ "DualInfeasible - Dual Infeasible solution \n \n "
871+ "IterationLimit - Iteration Limit reached \n \n "
872+ "TimeLimit - TimeLimit reached \n \n "
873+ "PrimalFeasible - Primal Feasible \n \n "
874+ "---------------------- \n \n "
875+ "In case of MILP/IP : \n \n "
876+ "NoTermination - No Termination \n \n "
877+ "Optimal - Optimal solution is available \n \n "
878+ "FeasibleFound - Feasible solution is available \n \n "
879+ "Infeasible - Infeasible \n \n "
880+ "Unbounded - Unbounded \n \n "
881+ "TimeLimit - TimeLimit reached \n \n "
882+ ),
883+ )
834884 )
835885 solution : SolutionData = Field (
836886 default = SolutionData (), description = ("Solution of the LP problem" )
@@ -896,7 +946,7 @@ class IncumbentSolution(StrictModel):
896946 "value" : {
897947 "response" : {
898948 "solver_response" : {
899- "status" : 1 ,
949+ "status" : "Optimal" ,
900950 "solution" : {
901951 "problem_category" : 0 ,
902952 "primal_solution" : [0.0 , 0.0 ],
@@ -925,7 +975,7 @@ class IncumbentSolution(StrictModel):
925975 "value" : {
926976 "response" : {
927977 "solver_response" : {
928- "status" : 2 ,
978+ "status" : "FeasibleFound" ,
929979 "solution" : {
930980 "problem_category" : 1 ,
931981 "primal_solution" : [0.0 , 0.0 ],
@@ -956,7 +1006,7 @@ class IncumbentSolution(StrictModel):
9561006 "value" : {
9571007 "response" : {
9581008 "solver_response" : {
959- "status" : 2 ,
1009+ "status" : "FeasibleFound" ,
9601010 "solution" : {
9611011 "problem_category" : 1 ,
9621012 "primal_solution" : [0.0 , 0.0 ],
0 commit comments