Solvers Module
The solvers module provides solver interfaces and implementations for solving optimization problems. OptiX supports multiple solvers through a unified interface, allowing easy switching between different optimization engines.
Solver Factory
- solvers.solve(problem: OXCSPProblem, solver: str, **kwargs)[source]
Unified optimization problem solving interface with multi-solver support.
This function serves as the primary entry point for solving optimization problems within the OptiX framework, providing a standardized interface that abstracts away solver-specific implementation details while ensuring consistent problem setup, solving workflows, and solution extraction across different optimization engines and algorithmic approaches.
The function implements a comprehensive solving pipeline that automatically handles variable creation, constraint translation, objective function configuration, and solution extraction, enabling users to focus on problem modeling rather than solver-specific integration complexities.
- Solving Pipeline:
The function orchestrates a standardized solving workflow:
Solver Validation: Verifies solver availability and compatibility with the specified problem type and configuration parameters
Solver Instantiation: Creates solver instance with custom parameters and configuration options for performance tuning and behavior control
Variable Setup: Translates OptiX decision variables to solver-specific variable representations with proper bounds, types, and naming conventions
Constraint Translation: Converts OptiX constraints to native solver constraint formats with accurate coefficient handling and operator mapping
Special Constraint Handling: Processes advanced constraint types including multiplicative, division, modulo, and conditional constraints using solver-specific implementation strategies
Objective Configuration: Sets up optimization objectives for linear and goal programming problems with proper minimization/maximization handling
Solution Execution: Executes the core solving algorithm with progress monitoring and early termination capabilities
Result Extraction: Retrieves optimization results and translates them to standardized OptiX solution formats for consistent analysis
- Parameters:
problem (OXCSPProblem) – The optimization problem instance to solve. Must be a properly configured OptiX problem with defined variables, constraints, and (for LP/GP problems) objective functions. Supports constraint satisfaction problems (CSP), linear programming (LP), and goal programming (GP) formulations.
solver (str) – The identifier of the optimization solver to use for problem solving. Must match a key in the _available_solvers registry. Supported values include: - ‘ORTools’: Google’s open-source constraint programming solver - ‘Gurobi’: Commercial high-performance optimization solver Additional solvers may be available through plugin extensions.
**kwargs – Arbitrary keyword arguments passed directly to the solver constructor for custom parameter configuration. Enables solver-specific performance tuning, algorithmic customization, and behavior control. Common parameters include: - maxTime (int): Maximum solving time in seconds - solutionCount (int): Maximum number of solutions to enumerate - equalizeDenominators (bool): Enable fractional coefficient handling - use_continuous (bool): Enable continuous variable optimization - Additional solver-specific parameters as documented by each solver
- Returns:
A two-element tuple containing comprehensive solving results:
status (OXSolutionStatus): The termination status of the optimization process indicating solution quality and solver performance. Possible values: * OXSolutionStatus.OPTIMAL: Globally optimal solution found * OXSolutionStatus.FEASIBLE: Feasible solution found, optimality not guaranteed * OXSolutionStatus.INFEASIBLE: No feasible solution exists * OXSolutionStatus.UNBOUNDED: Problem is unbounded * OXSolutionStatus.TIMEOUT: Solver reached time limit * OXSolutionStatus.ERROR: Solving error occurred * OXSolutionStatus.UNKNOWN: Status cannot be determined
solver_obj (OXSolverInterface): The configured solver instance used for problem solving. Provides access to all found solutions through iteration protocols, individual solution access through indexing, and solver-specific diagnostic information through logging methods. The solver maintains complete solution history and enables detailed post-solving analysis and validation.
- Return type:
- Raises:
OXception – Raised when the specified solver is not available in the solver registry. This typically occurs when: - The solver name is misspelled or incorrect - The solver backend is not installed or properly configured - Required dependencies for the solver are missing - The solver registration failed during framework initialization
Additional solver-specific exceptions may be raised during the solving process –
and should be handled appropriately by calling code for robust error management. –
Example
Basic problem solving with default parameters:
from problem.OXProblem import OXCSPProblem from solvers.OXSolverFactory import solve # Create and configure problem problem = OXCSPProblem() x = problem.create_decision_variable("x", 0, 10) y = problem.create_decision_variable("y", 0, 10) problem.create_constraint([x, y], [1, 1], "<=", 15) # Solve with default OR-Tools configuration status, solver = solve(problem, 'ORTools') # Analyze results if status == OXSolutionStatus.OPTIMAL: print("Found optimal solution") for solution in solver: print(f"Variables: {solution.decision_variable_values}") elif status == OXSolutionStatus.INFEASIBLE: print("Problem has no feasible solution")
Advanced solving with custom parameters:
from problem.OXProblem import OXLPProblem from solvers.OXSolverFactory import solve # Create linear programming problem problem = OXLPProblem() x = problem.create_decision_variable("x", 0, 100) y = problem.create_decision_variable("y", 0, 100) problem.create_constraint([x, y], [1, 1], "<=", 150) problem.create_objective_function([x, y], [2, 3], "maximize") # Solve with custom Gurobi parameters status, solver = solve( problem, 'Gurobi', use_continuous=True, maxTime=3600, optimality_gap=0.01 ) # Access optimal solution if status == OXSolutionStatus.OPTIMAL: solution = solver[0] print(f"Optimal value: {solution.objective_function_value}") print(f"Variables: {solution.decision_variable_values}")
- Performance Considerations:
Solver instantiation overhead is minimized through efficient registry lookup
Problem setup is optimized for large-scale problems with thousands of variables
Memory usage scales linearly with problem size and solution enumeration
Parallel solving capabilities depend on individual solver implementations
- Solver Selection Guidelines:
OR-Tools: Recommended for constraint satisfaction, discrete optimization, and problems requiring advanced constraint types with good open-source support
Gurobi: Optimal for large-scale linear/quadratic programming requiring commercial-grade performance and advanced optimization algorithms
Custom Solvers: Consider for specialized problem domains or when specific algorithmic approaches are required for particular optimization scenarios
- Thread Safety:
The solve function creates independent solver instances for each call, ensuring thread safety for concurrent optimization operations. However, individual solver implementations may have their own thread safety considerations that should be reviewed for multi-threaded optimization scenarios.
Solver Interfaces
Base Interface
- class solvers.OXSolverInterface(**kwargs)[source]
Bases:
objectAbstract base class defining the standard interface for all optimization solver implementations.
This class establishes the fundamental contract that all concrete solver implementations must adhere to within the OptiX optimization framework. It provides a comprehensive template for integrating diverse optimization engines while maintaining consistent behavior, standardized method signatures, and uniform solution handling patterns.
The interface design follows the Template Method pattern, defining the overall algorithm structure for solving optimization problems while allowing subclasses to implement solver-specific details. This ensures consistent problem setup, solving workflows, and solution extraction across different optimization engines.
- Core Responsibilities:
Variable Management: Standardized creation and mapping of decision variables from OptiX problem formulations to solver-specific representations
Constraint Translation: Systematic conversion of OptiX constraints to native solver constraint formats with proper operator and coefficient handling
Objective Configuration: Setup of optimization objectives for linear and goal programming problems with support for minimization and maximization
Solution Extraction: Comprehensive retrieval of optimization results including variable values, constraint evaluations, and solver statistics
Parameter Management: Flexible configuration of solver-specific parameters for performance tuning and algorithmic customization
- Interface Methods:
The class defines both abstract methods (must be implemented by subclasses) and concrete methods (provide common functionality):
Abstract Methods (require implementation): - _create_single_variable(): Variable creation in solver-specific format - _create_single_constraint(): Constraint creation with proper translation - create_special_constraints(): Advanced constraint type handling - create_objective(): Objective function setup for optimization problems - solve(): Core solving algorithm execution and solution extraction - get_solver_logs(): Diagnostic and debugging information retrieval
Concrete Methods (provided by base class): - create_variable(): Orchestrates creation of all problem variables - create_constraints(): Manages setup of all standard constraints - Collection access methods for solution enumeration and analysis
- _parameters
Comprehensive dictionary storing solver-specific configuration parameters including algorithmic settings, performance tuning options, and behavioral controls. This enables flexible customization of solver behavior without modifying core implementation code.
- Type:
Parameters
- _solutions
Ordered collection of optimization solutions found during the solving process. Supports multiple solution enumeration for problems with multiple optimal or feasible solutions. Provides efficient access through indexing and iteration protocols.
- Type:
List[OXSolverSolution]
- Solving Workflow:
The standard solving process follows a well-defined sequence:
Problem Setup: Variable and constraint creation from OptiX problem definition
Solver Configuration: Parameter application and algorithmic customization
Objective Setup: Optimization direction and objective function configuration
Solution Process: Core solving algorithm execution with progress monitoring
Result Extraction: Solution data retrieval and status determination
Validation: Solution verification and constraint satisfaction checking
# Standard workflow implementation solver = ConcretesolverInterface(**parameters) solver.create_variable(problem) solver.create_constraints(problem) solver.create_special_constraints(problem) if isinstance(problem, OXLPProblem): solver.create_objective(problem) status = solver.solve(problem) # Access results for solution in solver: analyze_solution(solution)
- Extensibility Design:
The interface is designed to accommodate diverse optimization paradigms:
Linear Programming: Continuous optimization with linear constraints
Integer Programming: Discrete optimization with integer variables
Constraint Programming: Logical constraint satisfaction and enumeration
Goal Programming: Multi-objective optimization with priority levels
Heuristic Algorithms: Approximate optimization with custom algorithms
- Parameter Management:
Solver parameters enable fine-grained control over optimization behavior:
Algorithmic Parameters: Solver-specific algorithm selection and tuning
Performance Parameters: Time limits, memory limits, and precision settings
Output Parameters: Logging levels, solution enumeration, and debugging options
Problem-Specific Parameters: Customization for particular problem characteristics
- Solution Management:
The interface provides comprehensive solution handling capabilities:
Multiple Solutions: Support for enumeration of alternative optimal solutions
Solution Quality: Status tracking and optimality verification
Incremental Results: Progressive solution improvement tracking
Solution Comparison: Utilities for comparing and ranking multiple solutions
- Error Handling:
The interface defines consistent error handling patterns:
Implementation Errors: NotImplementedError for missing abstract methods
Parameter Validation: Custom exceptions for invalid solver parameters
Numerical Issues: Graceful handling of solver-specific numerical problems
Resource Limitations: Proper handling of memory and time limit violations
- Performance Considerations:
Solution storage uses efficient data structures for large solution sets
Parameter dictionaries provide O(1) configuration access
Iterator protocols enable memory-efficient solution enumeration
Abstract method design minimizes overhead in concrete implementations
- Example Implementation:
Basic structure for implementing a custom solver interface:
class CustomSolverInterface(OXSolverInterface): def __init__(self, **kwargs): super().__init__(**kwargs) self._native_solver = initialize_custom_solver() self._var_mapping = {} def _create_single_variable(self, var: OXVariable): native_var = self._native_solver.add_variable( name=var.name, lower_bound=var.lower_bound, upper_bound=var.upper_bound ) self._var_mapping[var.id] = native_var def solve(self, prb: OXCSPProblem) -> OXSolutionStatus: status = self._native_solver.solve() if status == 'optimal': solution = self._extract_solution() self._solutions.append(solution) return OXSolutionStatus.OPTIMAL return OXSolutionStatus.UNKNOWN
Note
Concrete implementations should carefully handle solver-specific exceptions and translate them to appropriate OXSolutionStatus values for consistent error reporting across the framework.
- __init__(**kwargs)[source]
Initialize the solver interface with optional parameters.
- Parameters:
**kwargs – Solver-specific parameters passed as keyword arguments.
- create_variable(prb: OXCSPProblem)[source]
Create all variables from the problem in the solver.
- Parameters:
prb (OXCSPProblem) – The problem containing variables to create.
- create_constraints(prb: OXCSPProblem)[source]
Create all regular constraints from the problem in the solver.
This method creates all constraints except those that are part of special constraints (which are handled separately).
- Parameters:
prb (OXCSPProblem) – The problem containing constraints to create.
- create_special_constraints(prb: OXCSPProblem)[source]
Create all special constraints from the problem in the solver.
- Parameters:
prb (OXCSPProblem) – The problem containing special constraints to create.
- Raises:
NotImplementedError – Must be implemented by subclasses.
- create_objective(prb: OXLPProblem)[source]
Create the objective function in the solver.
- Parameters:
prb (OXLPProblem) – The linear programming problem containing the objective function.
- Raises:
NotImplementedError – Must be implemented by subclasses.
- solve(prb: OXCSPProblem) OXSolutionStatus[source]
Solve the optimization problem.
- Parameters:
prb (OXCSPProblem) – The problem to solve.
- Returns:
The status of the solution process.
- Return type:
OXSolutionStatus
- Raises:
NotImplementedError – Must be implemented by subclasses.
- get_solver_logs() List[str | List[str]] | None[source]
Get solver-specific logs and debugging information.
- Returns:
Solver logs if available, None otherwise.
- Return type:
Optional[LogsType]
- Raises:
NotImplementedError – Must be implemented by subclasses.
- __getitem__(item) OXSolverSolution[source]
Get a solution by index.
- Parameters:
item – The index of the solution to retrieve.
- Returns:
The solution at the specified index.
- Return type:
OXSolverSolution
- __len__() int[source]
Get the number of solutions found.
- Returns:
The number of solutions in the solution list.
- Return type:
OR-Tools Solver
OR-Tools Solver Integration Module
This module provides comprehensive integration between the OptiX optimization framework and Google’s OR-Tools constraint programming solver. It implements the OptiX solver interface using OR-Tools’ CP-SAT engine to enable solving complex discrete optimization problems including constraint satisfaction, integer programming, and goal programming.
The module serves as a critical component of OptiX’s multi-solver architecture, offering high-performance constraint programming capabilities alongside other solver backends like Gurobi for different optimization scenarios.
- Architecture:
Solver Interface: Complete implementation of OXSolverInterface for OR-Tools
Constraint Programming: Leverages CP-SAT for discrete optimization excellence
Multi-Problem Support: Handles CSP, LP, and GP problem types seamlessly
Advanced Constraints: Supports complex non-linear constraint relationships
- Key Components:
OXORToolsSolverInterface: Primary solver implementation class providing complete integration with OR-Tools CP-SAT solver including variable management, constraint translation, objective handling, and solution extraction capabilities
- Solver Capabilities:
Variable Types: Boolean and bounded integer decision variables with automatic type detection based on variable bounds and mathematical properties
Linear Constraints: Full support for relational operators (=, <=, >=, <, >) with efficient constraint expression evaluation and validation
Special Constraints: Advanced non-linear relationships including:
Multiplicative: Product relationships between multiple variables
Division/Modulo: Integer division and remainder operations for discrete math
Summation: Explicit sum constraints for complex variable relationships
Conditional: If-then-else logic with indicator variables for decision modeling
Objective Functions: Optimization support for minimization and maximization with linear and goal programming objective types
Multi-Solution Enumeration: Configurable solution collection with callback mechanisms for exploring solution spaces and alternative optima
Performance Tuning: Comprehensive parameter configuration for time limits, solution counts, and algorithmic behavior customization
- Mathematical Features:
Float Coefficient Handling: Automatic denominator equalization for fractional weights enabling seamless integration of real-valued problem formulations
Integer Programming: Native support for discrete optimization with advanced branching and cutting plane algorithms from OR-Tools
Constraint Propagation: Sophisticated constraint propagation techniques for efficient problem space reduction and faster solving
- Configuration Parameters:
The solver accepts multiple parameters for fine-tuning performance and behavior:
equalizeDenominators (bool): Enables automatic conversion of float coefficients to integers using common denominator calculation, allowing OR-Tools to handle fractional weights in constraints and objectives. Default: False
solutionCount (int): Maximum number of solutions to enumerate during solving. Higher values enable comprehensive solution space exploration but increase computational overhead. Default: 1
maxTime (int): Maximum solving time in seconds before automatic termination. Prevents indefinite solving on computationally difficult problem instances. Default: 600 seconds (10 minutes)
- Integration Patterns:
The module follows OptiX’s standardized solver integration patterns for consistent usage across different solver backends:
from problem.OXProblem import OXCSPProblem, OXLPProblem from solvers.ortools import OXORToolsSolverInterface from solvers.OXSolverFactory import solve # Direct solver instantiation approach problem = OXCSPProblem() # ... configure problem variables and constraints ... solver = OXORToolsSolverInterface( equalizeDenominators=True, solutionCount=5, maxTime=300 ) solver.create_variables(problem) solver.create_constraints(problem) solver.create_special_constraints(problem) status = solver.solve(problem) # Factory pattern approach (recommended) problem = OXLPProblem() # ... configure problem ... status, solutions = solve(problem, "ORTools", equalizeDenominators=True, solutionCount=10, maxTime=600)
- Performance Considerations:
OR-Tools CP-SAT excels at discrete optimization problems with complex constraints
Integer variable domains should be bounded for optimal performance
Large solution enumeration (>100 solutions) may require increased time limits
Float coefficient conversion adds preprocessing overhead but enables broader compatibility
Special constraints leverage native CP-SAT primitives for efficient solving
- Compatibility:
Python Version: Requires Python 3.7 or higher for full feature support
OR-Tools Version: Compatible with OR-Tools 9.0+ constraint programming library
OptiX Framework: Fully integrated with OptiX problem modeling and solving architecture
Operating Systems: Cross-platform support on Windows, macOS, and Linux
- Use Cases:
This solver implementation is particularly well-suited for:
Scheduling and resource allocation problems with discrete time slots
Combinatorial optimization problems with complex constraint relationships
Integer programming formulations requiring advanced constraint types
Multi-objective optimization with goal programming approaches
Constraint satisfaction problems with large solution spaces requiring enumeration
Notes
For continuous optimization problems, consider using the Gurobi solver interface
Large-scale linear programming may benefit from specialized LP solver backends
Memory usage scales with problem size and solution enumeration requirements
Solver logs and debugging information available through get_solver_logs() method
- class solvers.ortools.OXORToolsSolverInterface(**kwargs)[source]
Bases:
OXSolverInterfaceConcrete implementation of OptiX solver interface using Google OR-Tools CP-SAT solver.
This class provides a comprehensive bridge between OptiX’s problem modeling framework and Google’s OR-Tools Constraint Programming solver. It handles the complete lifecycle of problem solving from variable and constraint creation through solution extraction and analysis.
The implementation leverages OR-Tools’ CP-SAT solver, which excels at discrete optimization problems including constraint satisfaction, integer programming, and mixed-integer programming. The class automatically handles type conversions, constraint translations, and solution callbacks to provide seamless integration with OptiX workflows.
- Key Capabilities:
Variable Management: Automatic creation and mapping of boolean and integer variables
Constraint Translation: Comprehensive support for linear and special constraint types
Multi-Solution Handling: Configurable solution enumeration with callback system
Parameter Configuration: Flexible solver parameter management for performance tuning
Solution Analysis: Complete solution data extraction including constraint violations
- Solver Parameters:
The class accepts various initialization parameters to customize solver behavior:
equalizeDenominators (bool): When True, enables automatic conversion of float coefficients to integers using common denominator calculation. This allows OR-Tools to handle fractional weights that would otherwise be rejected. Default: False
solutionCount (int): Maximum number of solutions to collect during enumeration. Higher values enable finding multiple feasible solutions but increase solving time. Default: 1
maxTime (int): Maximum solving time in seconds before termination. Prevents infinite solving on difficult instances. Default: 600 seconds (10 minutes)
- _model
The underlying OR-Tools CP-SAT model instance that stores all variables, constraints, and objectives for the optimization problem.
- Type:
CpModel
- _var_mapping
Bidirectional mapping from OptiX variable UUIDs to their corresponding OR-Tools variable objects for efficient lookup during solving.
- Type:
Dict[str, IntVar|BoolVar]
- _constraint_mapping
Mapping from OptiX constraint UUIDs to OR-Tools constraint objects for tracking and solution analysis purposes.
- Type:
Dict[str, Constraint]
- _constraint_expr_mapping
Mapping from constraint UUIDs to their mathematical expressions for solution value calculation.
- Type:
Dict[str, LinearExpr]
- Type Support:
Boolean Variables: Automatically detected from 0-1 bounds, mapped to BoolVar
Integer Variables: Bounded integer variables with custom ranges, mapped to IntVar
Linear Expressions: Sum of variables with integer or float coefficients
Special Constraints: Non-linear relationships handled through CP-SAT primitives
Example
Comprehensive solver setup and configuration:
# Create solver with advanced configuration solver = OXORToolsSolverInterface( equalizeDenominators=True, # Handle fractional coefficients solutionCount=10, # Find up to 10 solutions maxTime=1800 # 30-minute time limit ) # Setup problem solver.create_variables(problem) solver.create_constraints(problem) solver.create_special_constraints(problem) if isinstance(problem, OXLPProblem): solver.create_objective(problem) # Solve and analyze status = solver.solve(problem) if status == OXSolutionStatus.OPTIMAL: for i, solution in enumerate(solver): print(f"Solution {i+1}: {solution.decision_variable_values}") print(f"Objective: {solution.objective_function_value}") # Access solver statistics logs = solver.get_solver_logs()
Warning
OR-Tools CP-SAT requires integer coefficients for all constraints and objectives. When using float coefficients, the equalizeDenominators parameter must be enabled to perform automatic conversion, or an OXception will be raised during constraint creation.
Note
This implementation is optimized for discrete optimization problems. For continuous optimization or large-scale linear programming, consider using the Gurobi solver interface which may provide better performance for those problem types.
- class SolutionLimiter(max_solution_count: int, solver: OXORToolsSolverInterface, prb: OXCSPProblem)[source]
Bases:
CpSolverSolutionCallbackCallback class to limit the number of solutions found.
This class extends CpSolverSolutionCallback to control the number of solutions collected during the solving process.
- _solver
Reference to the solver interface.
- Type:
- _problem
The problem being solved.
- Type:
- __init__(max_solution_count: int, solver: OXORToolsSolverInterface, prb: OXCSPProblem)[source]
Initialize the solution limiter callback.
- Parameters:
max_solution_count (int) – Maximum number of solutions to collect.
solver (OXORToolsSolverInterface) – Reference to the solver interface.
prb (OXCSPProblem) – The problem being solved.
- __init__(**kwargs)[source]
Initialize the OR-Tools solver interface.
- Parameters:
**kwargs – Solver parameters. Supported parameters: - equalizeDenominators (bool): Use denominator equalization for float handling. - solutionCount (int): Maximum number of solutions to find. - maxTime (int): Maximum solving time in seconds.
- create_objective(prb: OXLPProblem)[source]
Create the objective function in the OR-Tools model.
- Parameters:
prb (OXLPProblem) – The linear programming problem containing the objective function.
- Raises:
OXception – If no objective function is specified or if float weights are used without denominator equalization enabled.
- create_special_constraints(prb: OXCSPProblem)[source]
Create all special constraints from the problem.
- Parameters:
prb (OXCSPProblem) – The problem containing special constraints.
- Raises:
OXception – If an unsupported special constraint type is encountered.
- get_solver_logs() List[str | List[str]] | None[source]
Get solver logs and debugging information.
- Returns:
Currently not implemented, returns None.
- Return type:
Optional[LogsType]
- solve(prb: OXCSPProblem) OXSolutionStatus[source]
Solve the optimization problem using OR-Tools CP-SAT solver.
- Parameters:
prb (OXCSPProblem) – The problem to solve.
- Returns:
The status of the solution process.
- Return type:
OXSolutionStatus
- Raises:
OXception – If the solver returns an unexpected status.
- class solvers.ortools.OXORToolsSolverInterface(**kwargs)[source]
Bases:
OXSolverInterfaceConcrete implementation of OptiX solver interface using Google OR-Tools CP-SAT solver.
This class provides a comprehensive bridge between OptiX’s problem modeling framework and Google’s OR-Tools Constraint Programming solver. It handles the complete lifecycle of problem solving from variable and constraint creation through solution extraction and analysis.
The implementation leverages OR-Tools’ CP-SAT solver, which excels at discrete optimization problems including constraint satisfaction, integer programming, and mixed-integer programming. The class automatically handles type conversions, constraint translations, and solution callbacks to provide seamless integration with OptiX workflows.
- Key Capabilities:
Variable Management: Automatic creation and mapping of boolean and integer variables
Constraint Translation: Comprehensive support for linear and special constraint types
Multi-Solution Handling: Configurable solution enumeration with callback system
Parameter Configuration: Flexible solver parameter management for performance tuning
Solution Analysis: Complete solution data extraction including constraint violations
- Solver Parameters:
The class accepts various initialization parameters to customize solver behavior:
equalizeDenominators (bool): When True, enables automatic conversion of float coefficients to integers using common denominator calculation. This allows OR-Tools to handle fractional weights that would otherwise be rejected. Default: False
solutionCount (int): Maximum number of solutions to collect during enumeration. Higher values enable finding multiple feasible solutions but increase solving time. Default: 1
maxTime (int): Maximum solving time in seconds before termination. Prevents infinite solving on difficult instances. Default: 600 seconds (10 minutes)
- _model
The underlying OR-Tools CP-SAT model instance that stores all variables, constraints, and objectives for the optimization problem.
- Type:
CpModel
- _var_mapping
Bidirectional mapping from OptiX variable UUIDs to their corresponding OR-Tools variable objects for efficient lookup during solving.
- Type:
Dict[str, IntVar|BoolVar]
- _constraint_mapping
Mapping from OptiX constraint UUIDs to OR-Tools constraint objects for tracking and solution analysis purposes.
- Type:
Dict[str, Constraint]
- _constraint_expr_mapping
Mapping from constraint UUIDs to their mathematical expressions for solution value calculation.
- Type:
Dict[str, LinearExpr]
- Type Support:
Boolean Variables: Automatically detected from 0-1 bounds, mapped to BoolVar
Integer Variables: Bounded integer variables with custom ranges, mapped to IntVar
Linear Expressions: Sum of variables with integer or float coefficients
Special Constraints: Non-linear relationships handled through CP-SAT primitives
Example
Comprehensive solver setup and configuration:
# Create solver with advanced configuration solver = OXORToolsSolverInterface( equalizeDenominators=True, # Handle fractional coefficients solutionCount=10, # Find up to 10 solutions maxTime=1800 # 30-minute time limit ) # Setup problem solver.create_variables(problem) solver.create_constraints(problem) solver.create_special_constraints(problem) if isinstance(problem, OXLPProblem): solver.create_objective(problem) # Solve and analyze status = solver.solve(problem) if status == OXSolutionStatus.OPTIMAL: for i, solution in enumerate(solver): print(f"Solution {i+1}: {solution.decision_variable_values}") print(f"Objective: {solution.objective_function_value}") # Access solver statistics logs = solver.get_solver_logs()
Warning
OR-Tools CP-SAT requires integer coefficients for all constraints and objectives. When using float coefficients, the equalizeDenominators parameter must be enabled to perform automatic conversion, or an OXception will be raised during constraint creation.
Note
This implementation is optimized for discrete optimization problems. For continuous optimization or large-scale linear programming, consider using the Gurobi solver interface which may provide better performance for those problem types.
- __init__(**kwargs)[source]
Initialize the OR-Tools solver interface.
- Parameters:
**kwargs – Solver parameters. Supported parameters: - equalizeDenominators (bool): Use denominator equalization for float handling. - solutionCount (int): Maximum number of solutions to find. - maxTime (int): Maximum solving time in seconds.
- create_special_constraints(prb: OXCSPProblem)[source]
Create all special constraints from the problem.
- Parameters:
prb (OXCSPProblem) – The problem containing special constraints.
- Raises:
OXception – If an unsupported special constraint type is encountered.
- create_objective(prb: OXLPProblem)[source]
Create the objective function in the OR-Tools model.
- Parameters:
prb (OXLPProblem) – The linear programming problem containing the objective function.
- Raises:
OXception – If no objective function is specified or if float weights are used without denominator equalization enabled.
- class SolutionLimiter(max_solution_count: int, solver: OXORToolsSolverInterface, prb: OXCSPProblem)[source]
Bases:
CpSolverSolutionCallbackCallback class to limit the number of solutions found.
This class extends CpSolverSolutionCallback to control the number of solutions collected during the solving process.
- _solver
Reference to the solver interface.
- Type:
- _problem
The problem being solved.
- Type:
- __init__(max_solution_count: int, solver: OXORToolsSolverInterface, prb: OXCSPProblem)[source]
Initialize the solution limiter callback.
- Parameters:
max_solution_count (int) – Maximum number of solutions to collect.
solver (OXORToolsSolverInterface) – Reference to the solver interface.
prb (OXCSPProblem) – The problem being solved.
- solve(prb: OXCSPProblem) OXSolutionStatus[source]
Solve the optimization problem using OR-Tools CP-SAT solver.
- Parameters:
prb (OXCSPProblem) – The problem to solve.
- Returns:
The status of the solution process.
- Return type:
OXSolutionStatus
- Raises:
OXception – If the solver returns an unexpected status.
Gurobi Solver
Gurobi Solver Integration Module
This module provides Gurobi commercial solver integration for the OptiX mathematical optimization framework. It implements the Gurobi-specific solver interface that enables high-performance optimization for linear programming, goal programming, and constraint satisfaction problems using Gurobi’s advanced optimization engine.
The module is organized around the following key components:
- Architecture:
Solver Interface: Gurobi-specific implementation of OptiX solver interface
Variable Translation: Automatic conversion of OptiX variables to Gurobi format
Constraint Handling: Support for all OptiX constraint types and operators
Solution Extraction: Comprehensive solution status and value retrieval
- Key Features:
High-performance commercial optimization engine integration
Support for binary, integer, and continuous variable types
Advanced constraint handling including goal programming
Configurable solver parameters and optimization settings
Robust solution status detection and error handling
- Solver Capabilities:
Linear Programming (LP): Standard optimization with linear constraints
Goal Programming (GP): Multi-objective optimization with deviation variables
Constraint Satisfaction (CSP): Feasibility problems without optimization
Mixed-Integer Programming: Support for both continuous and integer variables
- Usage:
The Gurobi solver is typically accessed through OptiX’s unified solver factory:
from solvers.OXSolverFactory import solve from problem import OXLPProblem # Create your optimization problem problem = OXLPProblem() # ... configure variables, constraints, objective ... # Solve using Gurobi status = solve(problem, 'Gurobi', use_continuous=True)
- Requirements:
Gurobi optimization software and valid license
gurobipy Python package
OptiX framework core components
Notes
Gurobi requires a valid license for operation
Performance characteristics may vary based on problem size and type
Advanced Gurobi parameters can be configured through solver settings
- class solvers.gurobi.OXGurobiSolverInterface(**kwargs)[source]
Bases:
OXSolverInterfaceGurobi-specific implementation of the OptiX solver interface.
This class provides a concrete implementation of the OXSolverInterface for the Gurobi optimization solver. It handles the translation between OptiX’s abstract problem representation and Gurobi’s specific API calls, variable types, and constraint formats.
The interface supports both continuous and integer optimization modes, with automatic handling of variable bounds, constraint operators, and objective function setup. Special support is provided for goal programming with positive and negative deviation variables.
- _model
The underlying Gurobi model instance
- Type:
gp.Model
- Parameters:
Example
Direct usage of the Gurobi interface:
solver = OXGurobiSolverInterface(use_continuous=True) # The solver is typically used through the factory pattern # but can be used directly for advanced Gurobi-specific features solver.create_variables(problem.variables) solver.create_constraints(problem.constraints) solver.create_objective(problem) status = solver.solve(problem) if status == OXSolutionStatus.OPTIMAL: solution = solver.get_solutions()[0]
- __init__(**kwargs)[source]
Initialize the Gurobi solver interface with configuration parameters.
Creates a new Gurobi model instance and initializes internal mappings for variables, constraints, and constraint expressions. Configuration parameters are passed to the parent OXSolverInterface class.
- Parameters:
**kwargs – Configuration parameters including: use_continuous (bool): Use continuous variables instead of integers equalizeDenominators (bool): Normalize fractional coefficients
Note
The Gurobi model is created with the name “OptiX Model” and uses default Gurobi settings unless modified through solver parameters.
- create_objective(prb: OXLPProblem)[source]
Create and configure the objective function in the Gurobi model.
Translates the OptiX objective function to Gurobi format, handling both minimization and maximization objectives. Supports continuous and integer coefficient modes with automatic goal programming objective creation.
- Parameters:
prb (OXLPProblem) – Problem instance with objective function definition
- Raises:
OXception – If no objective function is specified
OXException – If float weights are used in integer mode without proper configuration
Note
For goal programming problems, the objective is automatically created
Fractional coefficients require equalizeDenominators parameter in integer mode
Objective type (minimize/maximize) is preserved from the problem definition
- create_special_constraints(prb: OXCSPProblem)[source]
Create special non-linear constraints for constraint satisfaction problems.
This method is intended for handling special constraints that cannot be expressed as standard linear constraints (e.g., multiplication, division, modulo, conditional constraints). Currently not implemented for Gurobi.
- Parameters:
prb (OXCSPProblem) – Constraint satisfaction problem with special constraints
Note
Implementation is pending for advanced constraint types that require special handling in the Gurobi solver.
- get_solver_logs() List[str | List[str]] | None[source]
Retrieve solver execution logs and diagnostic information.
Returns detailed logs from the Gurobi solver execution including performance metrics, iteration details, and diagnostic messages. Currently not implemented.
- Returns:
Solver logs if available, None otherwise
- Return type:
Optional[LogsType]
Note
Implementation is pending for comprehensive log extraction from the Gurobi solver instance.
- solve(prb: OXCSPProblem) OXSolutionStatus[source]
Solve the optimization problem using Gurobi solver.
Executes the Gurobi optimization process and extracts solution information including variable values, constraint evaluations, and objective function value. Creates a comprehensive solution object for optimal solutions.
- Parameters:
prb (OXCSPProblem) – Problem instance to solve
- Returns:
- Status of the optimization process:
OPTIMAL: Solution found successfully
INFEASIBLE: No feasible solution exists
UNBOUNDED: Problem is unbounded
ERROR: Solver encountered an error or indeterminate status
- Return type:
OXSolutionStatus
Note
Solution details are stored in the _solutions list for optimal solutions
Constraint values include left-hand side, operator, and right-hand side
Objective function value is included for linear programming problems
- class solvers.gurobi.OXGurobiSolverInterface(**kwargs)[source]
Bases:
OXSolverInterfaceGurobi-specific implementation of the OptiX solver interface.
This class provides a concrete implementation of the OXSolverInterface for the Gurobi optimization solver. It handles the translation between OptiX’s abstract problem representation and Gurobi’s specific API calls, variable types, and constraint formats.
The interface supports both continuous and integer optimization modes, with automatic handling of variable bounds, constraint operators, and objective function setup. Special support is provided for goal programming with positive and negative deviation variables.
- _model
The underlying Gurobi model instance
- Type:
gp.Model
- Parameters:
Example
Direct usage of the Gurobi interface:
solver = OXGurobiSolverInterface(use_continuous=True) # The solver is typically used through the factory pattern # but can be used directly for advanced Gurobi-specific features solver.create_variables(problem.variables) solver.create_constraints(problem.constraints) solver.create_objective(problem) status = solver.solve(problem) if status == OXSolutionStatus.OPTIMAL: solution = solver.get_solutions()[0]
- __init__(**kwargs)[source]
Initialize the Gurobi solver interface with configuration parameters.
Creates a new Gurobi model instance and initializes internal mappings for variables, constraints, and constraint expressions. Configuration parameters are passed to the parent OXSolverInterface class.
- Parameters:
**kwargs – Configuration parameters including: use_continuous (bool): Use continuous variables instead of integers equalizeDenominators (bool): Normalize fractional coefficients
Note
The Gurobi model is created with the name “OptiX Model” and uses default Gurobi settings unless modified through solver parameters.
- create_special_constraints(prb: OXCSPProblem)[source]
Create special non-linear constraints for constraint satisfaction problems.
This method is intended for handling special constraints that cannot be expressed as standard linear constraints (e.g., multiplication, division, modulo, conditional constraints). Currently not implemented for Gurobi.
- Parameters:
prb (OXCSPProblem) – Constraint satisfaction problem with special constraints
Note
Implementation is pending for advanced constraint types that require special handling in the Gurobi solver.
- create_objective(prb: OXLPProblem)[source]
Create and configure the objective function in the Gurobi model.
Translates the OptiX objective function to Gurobi format, handling both minimization and maximization objectives. Supports continuous and integer coefficient modes with automatic goal programming objective creation.
- Parameters:
prb (OXLPProblem) – Problem instance with objective function definition
- Raises:
OXception – If no objective function is specified
OXException – If float weights are used in integer mode without proper configuration
Note
For goal programming problems, the objective is automatically created
Fractional coefficients require equalizeDenominators parameter in integer mode
Objective type (minimize/maximize) is preserved from the problem definition
- solve(prb: OXCSPProblem) OXSolutionStatus[source]
Solve the optimization problem using Gurobi solver.
Executes the Gurobi optimization process and extracts solution information including variable values, constraint evaluations, and objective function value. Creates a comprehensive solution object for optimal solutions.
- Parameters:
prb (OXCSPProblem) – Problem instance to solve
- Returns:
- Status of the optimization process:
OPTIMAL: Solution found successfully
INFEASIBLE: No feasible solution exists
UNBOUNDED: Problem is unbounded
ERROR: Solver encountered an error or indeterminate status
- Return type:
OXSolutionStatus
Note
Solution details are stored in the _solutions list for optimal solutions
Constraint values include left-hand side, operator, and right-hand side
Objective function value is included for linear programming problems
- get_solver_logs() List[str | List[str]] | None[source]
Retrieve solver execution logs and diagnostic information.
Returns detailed logs from the Gurobi solver execution including performance metrics, iteration details, and diagnostic messages. Currently not implemented.
- Returns:
Solver logs if available, None otherwise
- Return type:
Optional[LogsType]
Note
Implementation is pending for comprehensive log extraction from the Gurobi solver instance.
Solution Management
Examples
Basic Solving
from problem import OXLPProblem, ObjectiveType
from constraints import RelationalOperators
from solvers import solve
# Create problem
problem = OXLPProblem()
problem.create_decision_variable("x", "Variable X", 0, 10)
problem.create_decision_variable("y", "Variable Y", 0, 10)
# Add constraint
problem.create_constraint(
variables=[var.id for var in problem.variables],
weights=[1, 1],
operator=RelationalOperators.LESS_THAN_EQUAL,
value=15
)
# Set objective
problem.create_objective_function(
variables=[var.id for var in problem.variables],
weights=[3, 2],
objective_type=ObjectiveType.MAXIMIZE
)
# Solve with OR-Tools
status, solution = solve(problem, 'ORTools')
print(f"Status: {status}")
if solution:
for sol in solution:
print(f"Objective value: {sol.objective_value}")
sol.print_solution_for(problem)
# Solve with Gurobi
try:
status, solution = solve(problem, 'Gurobi')
print(f"Gurobi Status: {status}")
except Exception as e:
print(f"Gurobi not available: {e}")
Solver Comparison
import time
from solvers import solve
def compare_solvers(problem, solvers=['ORTools', 'Gurobi']):
"""Compare performance of different solvers on the same problem."""
results = {}
for solver_name in solvers:
try:
start_time = time.time()
status, solution = solve(problem, solver_name)
solve_time = time.time() - start_time
results[solver_name] = {
'status': status,
'solve_time': solve_time,
'objective_value': solution[0].objective_value if solution else None
}
print(f"{solver_name}:")
print(f" Status: {status}")
print(f" Time: {solve_time:.4f} seconds")
if solution:
print(f" Objective: {solution[0].objective_value}")
print()
except Exception as e:
print(f"{solver_name} failed: {e}")
results[solver_name] = {'error': str(e)}
return results
# Usage
results = compare_solvers(problem)
Custom Solver Implementation
from solvers import OXSolverInterface, OXSolverSolution, OXSolutionStatus
import random
class RandomSolverInterface(OXSolverInterface):
"""A simple random solver for demonstration purposes."""
def __init__(self):
super().__init__()
self.solutions = []
def solve(self, problem):
"""Solve the problem using random sampling."""
self.solutions = []
# Simple random search (not optimal, just for demo)
best_objective = float('-inf') if problem.objective_function.objective_type == ObjectiveType.MAXIMIZE else float('inf')
best_values = {}
for _ in range(1000): # 1000 random samples
values = {}
objective_value = 0
# Generate random values for each variable
for variable in problem.variables:
random_value = random.uniform(variable.lower_bound, variable.upper_bound)
values[variable.id] = random_value
# Check constraints (simplified)
feasible = True
for constraint in problem.constraints:
constraint_value = sum(
constraint.weights[i] * values[constraint.variables[i]]
for i in range(len(constraint.variables))
)
if constraint.operator == RelationalOperators.LESS_THAN_EQUAL:
if constraint_value > constraint.value:
feasible = False
break
elif constraint.operator == RelationalOperators.GREATER_THAN_EQUAL:
if constraint_value < constraint.value:
feasible = False
break
elif constraint.operator == RelationalOperators.EQUAL:
if abs(constraint_value - constraint.value) > 1e-6:
feasible = False
break
if feasible:
# Calculate objective value
objective_value = sum(
problem.objective_function.weights[i] * values[problem.objective_function.variables[i]]
for i in range(len(problem.objective_function.variables))
)
# Check if this is the best solution so far
is_better = False
if problem.objective_function.objective_type == ObjectiveType.MAXIMIZE:
is_better = objective_value > best_objective
else:
is_better = objective_value < best_objective
if is_better:
best_objective = objective_value
best_values = values.copy()
# Create solution
if best_values:
solution = OXSolverSolution(
objective_value=best_objective,
variable_values=best_values,
status=OXSolutionStatus.OPTIMAL
)
self.solutions = [solution]
return OXSolutionStatus.OPTIMAL
else:
return OXSolutionStatus.INFEASIBLE
def get_solution(self):
"""Return the best solution found."""
return self.solutions
# Custom solvers would need to be registered through the solver factory
# This is an example of implementing a custom solver interface
Advanced Solver Configuration
from solvers.ortools import OXORToolsSolverInterface
from solvers.gurobi import OXGurobiSolverInterface
# Configure OR-Tools solver
ortools_solver = OXORToolsSolverInterface()
ortools_solver.set_time_limit(300) # 5 minutes
ortools_solver.set_num_threads(4)
# Configure Gurobi solver (if available)
try:
gurobi_solver = OXGurobiSolverInterface()
gurobi_solver.set_parameter('TimeLimit', 300)
gurobi_solver.set_parameter('Threads', 4)
gurobi_solver.set_parameter('MIPGap', 0.01) # 1% optimality gap
except ImportError:
print("Gurobi not available")
# Solve with configured solvers
status = ortools_solver.solve(problem)
ortools_solutions = ortools_solver.get_solution()
Parallel Solving
import concurrent.futures
import time
def solve_parallel(problem, solvers=['ORTools', 'Gurobi'], timeout=300):
"""Solve the same problem with multiple solvers in parallel."""
def solve_with_solver(solver_name):
try:
start_time = time.time()
status, solution = solve(problem, solver_name)
solve_time = time.time() - start_time
return {
'solver': solver_name,
'status': status,
'solution': solution,
'time': solve_time
}
except Exception as e:
return {
'solver': solver_name,
'error': str(e),
'time': None
}
# Use ThreadPoolExecutor for parallel execution
with concurrent.futures.ThreadPoolExecutor(max_workers=len(solvers)) as executor:
# Submit all solver tasks
future_to_solver = {
executor.submit(solve_with_solver, solver): solver
for solver in solvers
}
results = []
# Collect results as they complete
for future in concurrent.futures.as_completed(future_to_solver, timeout=timeout):
try:
result = future.result()
results.append(result)
print(f"Completed: {result['solver']} in {result.get('time', 'N/A')} seconds")
except Exception as e:
solver = future_to_solver[future]
results.append({
'solver': solver,
'error': str(e),
'time': None
})
return results
# Usage
parallel_results = solve_parallel(problem)
# Find the best result
best_result = None
for result in parallel_results:
if 'error' not in result and result['solution']:
if best_result is None or result['time'] < best_result['time']:
best_result = result
if best_result:
print(f"Best solver: {best_result['solver']} ({best_result['time']:.4f}s)")
Solution Analysis
def analyze_solution(solution, problem):
"""Analyze and validate a solution."""
if not solution:
print("No solution available")
return
sol = solution[0] # Get first solution
print("=== Solution Analysis ===")
print(f"Objective Value: {sol.objective_value}")
print(f"Status: {sol.status}")
print()
print("Variable Values:")
for var_id, value in sol.variable_values.items():
variable = next((v for v in problem.variables if v.id == var_id), None)
if variable:
print(f" {variable.name}: {value:.6f}")
print()
# Validate constraints
print("Constraint Validation:")
all_satisfied = True
for i, constraint in enumerate(problem.constraints):
constraint_value = sum(
constraint.weights[j] * sol.variable_values[constraint.variables[j]]
for j in range(len(constraint.variables))
)
satisfied = False
if constraint.operator == RelationalOperators.LESS_THAN_EQUAL:
satisfied = constraint_value <= constraint.value + 1e-6
op_str = "<="
elif constraint.operator == RelationalOperators.GREATER_THAN_EQUAL:
satisfied = constraint_value >= constraint.value - 1e-6
op_str = ">="
elif constraint.operator == RelationalOperators.EQUAL:
satisfied = abs(constraint_value - constraint.value) <= 1e-6
op_str = "=="
status_icon = "✅" if satisfied else "❌"
print(f" Constraint {i+1}: {constraint_value:.6f} {op_str} {constraint.value} {status_icon}")
if not satisfied:
all_satisfied = False
print(f"\nAll constraints satisfied: {'✅' if all_satisfied else '❌'}")
# Calculate objective value manually to verify
if hasattr(problem, 'objective_function') and problem.objective_function:
manual_objective = sum(
problem.objective_function.weights[i] * sol.variable_values[problem.objective_function.variables[i]]
for i in range(len(problem.objective_function.variables))
)
print(f"Manual objective calculation: {manual_objective:.6f}")
print(f"Solver objective value: {sol.objective_value:.6f}")
print(f"Difference: {abs(manual_objective - sol.objective_value):.8f}")
# Usage
status, solution = solve(problem, 'ORTools')
analyze_solution(solution, problem)
Multi-Scenario Solving
The solve_all_scenarios function enables comprehensive scenario-based optimization analysis:
from problem import OXLPProblem, ObjectiveType
from constraints import RelationalOperators
from data import OXData
from solvers import solve_all_scenarios
# Create problem with scenario-based data
problem = OXLPProblem()
# Create decision variables
x = problem.create_decision_variable("production_x", "Production of X", 0, 100)
y = problem.create_decision_variable("production_y", "Production of Y", 0, 100)
# Create data object with scenarios
demand_data = OXData()
demand_data.demand = 100 # Default scenario
demand_data.price = 5.0
# Create scenarios for different market conditions
demand_data.create_scenario("High_Demand", demand=150, price=6.0)
demand_data.create_scenario("Low_Demand", demand=75, price=4.5)
demand_data.create_scenario("Peak_Season", demand=200, price=7.0)
# Add data to problem database
problem.db.add_object(demand_data)
# Create constraints using scenario data
problem.create_constraint(
variables=[x.id, y.id],
weights=[1, 1],
operator=RelationalOperators.LESS_THAN_EQUAL,
value=demand_data.demand,
description="Total production must not exceed demand"
)
# Create objective function using scenario data
problem.create_objective_function(
variables=[x.id, y.id],
weights=[demand_data.price, 3.0],
objective_type=ObjectiveType.MAXIMIZE,
description="Maximize revenue"
)
# Solve across all scenarios
scenario_results = solve_all_scenarios(problem, 'ORTools', maxTime=300)
print(f"Solved {len(scenario_results)} scenarios")
print(f"Scenarios: {list(scenario_results.keys())}")
# Analyze results across scenarios
best_scenario = None
best_value = float('-inf')
for scenario_name, result in scenario_results.items():
print(f"\n=== Scenario: {scenario_name} ===")
if result['status'] == OXSolutionStatus.OPTIMAL:
solution = result['solution']
print(f"Status: Optimal")
print(f"Objective Value: {solution.objective_value:.2f}")
print(f"Production X: {solution.variable_values[x.id]:.2f}")
print(f"Production Y: {solution.variable_values[y.id]:.2f}")
# Track best scenario
if solution.objective_value > best_value:
best_value = solution.objective_value
best_scenario = scenario_name
else:
print(f"Status: {result['status']}")
if best_scenario:
print(f"\nBest performing scenario: {best_scenario} (${best_value:.2f})")
Advanced Multi-Scenario Analysis
from problem import OXLPProblem
from data import OXData
from solvers import solve_all_scenarios
import statistics
def comprehensive_scenario_analysis(problem, solver='ORTools'):
"""Perform comprehensive multi-scenario optimization analysis."""
# Solve all scenarios
results = solve_all_scenarios(problem, solver, maxTime=600)
# Collect statistics
optimal_scenarios = []
objective_values = []
for scenario_name, result in results.items():
if result['status'] == OXSolutionStatus.OPTIMAL:
optimal_scenarios.append(scenario_name)
objective_values.append(result['solution'].objective_value)
if not objective_values:
print("No optimal solutions found across scenarios")
return
# Statistical analysis
print("=== Multi-Scenario Analysis ===")
print(f"Total scenarios: {len(results)}")
print(f"Optimal scenarios: {len(optimal_scenarios)}")
print(f"Success rate: {len(optimal_scenarios)/len(results)*100:.1f}%")
print()
print("=== Objective Value Statistics ===")
print(f"Best value: {max(objective_values):.2f}")
print(f"Worst value: {min(objective_values):.2f}")
print(f"Average value: {statistics.mean(objective_values):.2f}")
print(f"Median value: {statistics.median(objective_values):.2f}")
print(f"Standard deviation: {statistics.stdev(objective_values):.2f}")
print()
# Scenario ranking
scenario_ranking = []
for scenario_name, result in results.items():
if result['status'] == OXSolutionStatus.OPTIMAL:
scenario_ranking.append((scenario_name, result['solution'].objective_value))
scenario_ranking.sort(key=lambda x: x[1], reverse=True)
print("=== Scenario Ranking ===")
for i, (scenario, value) in enumerate(scenario_ranking, 1):
print(f"{i:2d}. {scenario:<20}: ${value:8.2f}")
# Sensitivity analysis
if len(objective_values) > 1:
value_range = max(objective_values) - min(objective_values)
cv = statistics.stdev(objective_values) / statistics.mean(objective_values)
print(f"\n=== Sensitivity Analysis ===")
print(f"Value range: ${value_range:.2f}")
print(f"Coefficient of variation: {cv:.3f}")
if cv > 0.2:
print("⚠️ High sensitivity to scenario parameters")
elif cv > 0.1:
print("⚡ Moderate sensitivity to scenario parameters")
else:
print("✅ Low sensitivity to scenario parameters")
return results
# Usage with complex multi-object scenarios
problem = OXLPProblem()
# Create variables
x = problem.create_decision_variable("x", "Variable X", 0, 50)
y = problem.create_decision_variable("y", "Variable Y", 0, 50)
# Create multiple data objects with coordinated scenarios
capacity_data = OXData()
capacity_data.max_capacity = 100
capacity_data.create_scenario("Expansion", max_capacity=150)
capacity_data.create_scenario("Recession", max_capacity=80)
capacity_data.create_scenario("Growth", max_capacity=120)
cost_data = OXData()
cost_data.unit_cost = 2.0
cost_data.create_scenario("Expansion", unit_cost=1.8) # Lower costs during expansion
cost_data.create_scenario("Recession", unit_cost=2.5) # Higher costs during recession
cost_data.create_scenario("Growth", unit_cost=2.2) # Moderate cost increase
# Add to database
problem.db.add_object(capacity_data)
problem.db.add_object(cost_data)
# Create constraints and objectives using scenario data
problem.create_constraint([x.id, y.id], [1, 1], "<=", capacity_data.max_capacity)
problem.create_objective_function([x.id, y.id], [cost_data.unit_cost, 3.0], "maximize")
# Perform comprehensive analysis
analysis_results = comprehensive_scenario_analysis(problem, 'Gurobi')
Constraint-Based Scenarios
from constraints import RelationalOperators
from solvers import solve_all_scenarios
# Create problem with constraint scenarios
problem = OXLPProblem()
x = problem.create_decision_variable("x", "Production X", 0, 100)
y = problem.create_decision_variable("y", "Production Y", 0, 100)
# Create base constraint
resource_constraint = problem.create_constraint(
variables=[x.id, y.id],
weights=[2, 1],
operator=RelationalOperators.LESS_THAN_EQUAL,
value=200,
description="Resource availability constraint"
)
# Add constraint scenarios for different resource conditions
resource_constraint.create_scenario(
"Limited_Resources",
rhs=150,
description="Resource shortage scenario"
)
resource_constraint.create_scenario(
"Abundant_Resources",
rhs=300,
description="Resource abundance scenario"
)
resource_constraint.create_scenario(
"Emergency_Resources",
rhs=100,
description="Emergency resource rationing"
)
# Create objective
problem.create_objective_function(
variables=[x.id, y.id],
weights=[5, 4],
objective_type=ObjectiveType.MAXIMIZE
)
# Solve across constraint scenarios
constraint_results = solve_all_scenarios(problem, 'ORTools')
# Analyze impact of resource availability
print("=== Resource Scenario Analysis ===")
for scenario_name, result in constraint_results.items():
if result['status'] == OXSolutionStatus.OPTIMAL:
solution = result['solution']
total_production = solution.variable_values[x.id] + solution.variable_values[y.id]
print(f"{scenario_name}:")
print(f" Objective: ${solution.objective_value:.2f}")
print(f" Total Production: {total_production:.2f} units")
print(f" X Production: {solution.variable_values[x.id]:.2f}")
print(f" Y Production: {solution.variable_values[y.id]:.2f}")
print()
Mixed Data and Constraint Scenarios
from data import OXData
from solvers import solve_all_scenarios
# Complex scenario setup with both data and constraint scenarios
problem = OXLPProblem()
# Variables
x = problem.create_decision_variable("x", "Product X", 0, 100)
y = problem.create_decision_variable("y", "Product Y", 0, 100)
# Data object scenarios for market conditions
market_data = OXData()
market_data.price_x = 10.0
market_data.price_y = 8.0
market_data.create_scenario("Bull_Market", price_x=12.0, price_y=10.0)
market_data.create_scenario("Bear_Market", price_x=8.0, price_y=6.0)
problem.db.add_object(market_data)
# Constraint scenarios for operational conditions
capacity_constraint = problem.create_constraint(
variables=[x.id, y.id],
weights=[1, 1],
operator=RelationalOperators.LESS_THAN_EQUAL,
value=150,
description="Production capacity"
)
capacity_constraint.create_scenario("Maintenance", rhs=100)
capacity_constraint.create_scenario("Overtime", rhs=200)
# Objective using data scenarios
problem.create_objective_function(
variables=[x.id, y.id],
weights=[market_data.price_x, market_data.price_y],
objective_type=ObjectiveType.MAXIMIZE
)
# This will solve all combinations:
# - Default + Default, Bull_Market + Default, Bear_Market + Default
# - Default + Maintenance, Bull_Market + Maintenance, Bear_Market + Maintenance
# - Default + Overtime, Bull_Market + Overtime, Bear_Market + Overtime
mixed_results = solve_all_scenarios(problem, 'Gurobi', use_continuous=True)
print(f"Total scenario combinations solved: {len(mixed_results)}")
# Group results by data vs constraint scenarios
market_scenarios = {}
capacity_scenarios = {}
for scenario_name, result in mixed_results.items():
if result['status'] == OXSolutionStatus.OPTIMAL:
solution = result['solution']
# Categorize scenarios
if 'Market' in scenario_name:
market_scenarios[scenario_name] = solution.objective_value
elif scenario_name in ['Maintenance', 'Overtime']:
capacity_scenarios[scenario_name] = solution.objective_value
else:
print(f"Default scenario value: ${solution.objective_value:.2f}")
print("\n=== Market Impact Analysis ===")
for scenario, value in market_scenarios.items():
print(f"{scenario}: ${value:.2f}")
print("\n=== Capacity Impact Analysis ===")
for scenario, value in capacity_scenarios.items():
print(f"{scenario}: ${value:.2f}")
See Also
Problem Module - Problem type definitions
../tutorials/custom_solvers - Creating custom solver implementations
../user_guide/solvers - Detailed solver configuration guide