Solution
The Solution class contains the results of solving an optimization problem.
Accessing Optimal Values
Dictionary-Style Access
Get optimal values by variable name:
from optyx import Variable, Problem
x = Variable("x" , lb= 0 )
y = Variable("y" , lb= 0 )
solution = (
Problem()
.minimize(x** 2 + y** 2 )
.subject_to(x + y >= 1 )
.solve()
)
# Access by name
print (f"x* = { solution['x' ]:.4f} " )
print (f"y* = { solution['y' ]:.4f} " )
All Values
Get the full dictionary of optimal values:
{'x': 0.49999999999999994, 'y': 0.5}
Properties
.status
SolverStatus
Solution status
.objective_value
float
Optimal objective value
.values
dict[str, float]
All optimal variable values
.solve_time
float
Time to solve (seconds)
.iterations
int
Number of solver iterations
.message
str
Solver message
.mip_gap
float \| None
Relative optimality gap (MILP only)
.best_bound
float \| None
Best dual bound (MILP only)
.is_optimal
bool
True if status is OPTIMAL
.is_feasible
bool
True if status is OPTIMAL, MAX_ITERATIONS, or TERMINATED
Examples
from optyx import Variable, Problem
x = Variable("x" , lb= 0 )
y = Variable("y" , lb= 0 )
solution = (
Problem()
.minimize((x - 3 )** 2 + (y - 4 )** 2 )
.subject_to(x + y <= 5 )
.solve()
)
print (f"Status: { solution. status} " )
print (f"Objective: { solution. objective_value:.4f} " )
print (f"Solve time: { solution. solve_time:.4f} s" )
print (f"Iterations: { solution. iterations} " )
print (f"Message: { solution. message} " )
Status: SolverStatus.OPTIMAL
Objective: 2.0000
Solve time: 0.0006s
Iterations: 4
Message: Optimization terminated successfully
SolverStatus
The status indicates whether the solver found a valid solution:
from optyx import SolverStatus
SolverStatus.OPTIMAL
Converged to an optimal solution
SolverStatus.INFEASIBLE
No feasible solution exists
SolverStatus.UNBOUNDED
Objective can be improved indefinitely
SolverStatus.MAX_ITERATIONS
Reached iteration limit
SolverStatus.TERMINATED
Stopped early by callback or time limit
SolverStatus.FAILED
Solver encountered an error
SolverStatus.NOT_SOLVED
Problem has not been solved yet
Checking Status
from optyx import Variable, Problem, SolverStatus
x = Variable("x" , lb= 0 )
solution = (
Problem()
.minimize(x** 2 )
.subject_to(x >= 1 )
.solve()
)
if solution.status == SolverStatus.OPTIMAL:
print (f"Found optimal solution: x* = { solution['x' ]:.4f} " )
else :
print (f"Solver failed: { solution. message} " )
Found optimal solution: x* = 1.0000
Handling Failures
Infeasible Problem
from optyx import Variable, Problem, SolverStatus
x = Variable("x" , lb= 0 , ub= 1 )
# Impossible constraints
solution = (
Problem()
.minimize(x)
.subject_to(x >= 2 ) # But x ≤ 1!
.solve()
)
print (f"Status: { solution. status} " )
if solution.status != SolverStatus.OPTIMAL:
print (f"Problem: { solution. message} " )
Status: SolverStatus.INFEASIBLE
Problem: The problem is infeasible. (HiGHS Status 8: model_status is Infeasible; primal_status is None) No feasible solution exists.
Defensive Programming
from optyx import Variable, Problem, SolverStatus
def safe_solve(prob):
"""Solve with error handling."""
solution = prob.solve()
if solution.status == SolverStatus.OPTIMAL:
return solution
elif solution.status == SolverStatus.MAX_ITERATIONS:
print ("Warning: Reached iteration limit, solution may not be optimal" )
return solution
else :
raise RuntimeError (f"Solver failed: { solution. message} " )
x = Variable("x" , lb= 0 )
prob = Problem().minimize(x** 2 ).subject_to(x >= 1 )
try :
sol = safe_solve(prob)
print (f"x* = { sol['x' ]:.4f} " )
except RuntimeError as e:
print (e)
Complete Example
from optyx import Variable, Problem, SolverStatus
# Define problem
x = Variable("x" , lb= 0 )
y = Variable("y" , lb= 0 )
z = Variable("z" , lb= 0 )
solution = (
Problem("production" )
.maximize(40 * x + 30 * y + 50 * z) # Profit
.subject_to(2 * x + y + 3 * z <= 100 ) # Machine hours
.subject_to(x + 2 * y + z <= 80 ) # Labor hours
.subject_to(x + y + z <= 50 ) # Raw material
.solve()
)
print ("=" * 40 )
print ("PRODUCTION OPTIMIZATION RESULTS" )
print ("=" * 40 )
print (f"Status: { solution. status. name} " )
print (f"Solve time: { solution. solve_time* 1000 :.1f} ms" )
print ()
print ("Optimal Production:" )
print (f" Product X: { solution['x' ]:.2f} units" )
print (f" Product Y: { solution['y' ]:.2f} units" )
print (f" Product Z: { solution['z' ]:.2f} units" )
print ()
print (f"Maximum Profit: $ { solution. objective_value:.2f} " )
========================================
PRODUCTION OPTIMIZATION RESULTS
========================================
Status: OPTIMAL
Solve time: 1.5ms
Optimal Production:
Product X: 50.00 units
Product Y: -0.00 units
Product Z: 0.00 units
Maximum Profit: $2000.00
Serialization Methods
.to_dict()
Convert the solution to a plain dictionary.
data = solution.to_dict()
.to_json(path=None)
Serialize to JSON. Returns a string, or writes to path if provided.
json_str = solution.to_json() # returns string
solution.to_json("solution.json" ) # writes to file
Solution.from_json(json_str_or_path)
Reconstruct a Solution from a JSON string or file.
restored = Solution.from_json("solution.json" )
restored = Solution.from_json('{"status": "optimal", ...}' )
.print_vars()
Pretty-print status, objective, and all variable values.
SolverProgress
The SolverProgress dataclass is passed to your callback function at each solver iteration.
from optyx import SolverProgress
.iteration
int
Current iteration number
.objective_value
float
Current objective function value
.constraint_violation
float
Max constraint violation (0.0 if feasible)
.elapsed_time
float
Wall-clock seconds since solve started
.x
np.ndarray
Current variable values
def my_callback(progress: SolverProgress) -> bool | None :
print (f"Iter { progress. iteration} : obj= { progress. objective_value:.4f} " )
if progress.elapsed_time > 10 :
return True # stop early
return None # continue
solution = prob.solve(callback= my_callback)
See the Solver Callbacks example for a full walkthrough.