Variables Module
The variables module provides comprehensive decision variable management for the OptiX optimization framework. It implements a complete variable system supporting linear programming (LP), goal programming (GP), and constraint satisfaction problems (CSP) with advanced features for bounds management, relationship tracking, and specialized variable types.
Core Variable Classes
Base Decision Variable
- class variables.OXVariable(id: ~uuid.UUID = <factory>, class_name: str = '', name: str = '', description: str = '', value: float | int | bool = None, upper_bound: float | int = inf, lower_bound: float | int = 0, related_data: dict[str, ~uuid.UUID] = <factory>)[source]
Bases:
OXObjectFundamental decision variable class for mathematical optimization problems.
This class provides a comprehensive representation of decision variables used in optimization modeling within the OptiX framework. It extends the base OXObject class to include domain-specific features such as bounds management, value tracking, and relationship linking essential for complex optimization scenarios.
The class implements automatic validation, intelligent naming, and flexible data relationships to support various optimization paradigms including linear programming, integer programming, and goal programming applications.
- Key Capabilities:
Automatic bounds validation with infinity support for unbounded variables
UUID-based relationship tracking for linking variables to data entities
Intelligent automatic naming using UUID when names are not provided
Type-safe value assignment with comprehensive validation
Integration with solver interfaces through standardized attributes
- name
The human-readable identifier for the variable. If empty or whitespace, automatically generated as “var_<uuid>” to ensure uniqueness and traceability throughout the optimization process.
- Type:
- description
Detailed description of the variable’s purpose and meaning within the optimization context. Used for documentation and model interpretation purposes.
- Type:
- value
The current assigned value of the variable. Can be None for unassigned variables. Should respect the defined bounds when set by optimization solvers.
- upper_bound
The maximum allowable value for the variable. Defaults to positive infinity for unbounded variables. Must be greater than or equal to lower_bound.
- lower_bound
The minimum allowable value for the variable. Defaults to 0 for non-negative variables. Must be less than or equal to upper_bound.
Dictionary mapping relationship type names to UUID identifiers of related objects. Enables complex data modeling and constraint relationships.
- Raises:
OXception – If lower_bound is greater than upper_bound during initialization. This validation ensures mathematical consistency of the variable domain.
- Performance:
Variable creation is optimized for large-scale problems with minimal overhead
Bounds checking is performed only during initialization and explicit validation
String representation is cached for efficient display in large variable sets
- Thread Safety:
Individual variable instances are thread-safe for read operations
Modification operations should be synchronized in multi-threaded environments
The related_data dictionary requires external synchronization for concurrent access
Examples
Create variables for different optimization scenarios:
# Production planning variable with finite bounds production = OXVariable( name="daily_production", description="Daily production quantity in units", lower_bound=0, upper_bound=1000, value=500 ) # Binary decision variable for facility location facility_open = OXVariable( name="facility_open", description="Whether to open the facility (0=closed, 1=open)", lower_bound=0, upper_bound=1, value=0 ) # Unbounded variable for inventory surplus/deficit inventory_delta = OXVariable( name="inventory_change", description="Change in inventory level (positive=surplus, negative=deficit)", lower_bound=float('-inf'), upper_bound=float('inf') ) # Link variable to related business entities from uuid import uuid4 customer_id = uuid4() production.related_data["customer"] = customer_id production.related_data["facility"] = facility_open.id
Note
Variables are immutable after solver assignment to maintain solution integrity
The bounds validation is strict and prevents invalid domain specifications
Automatic naming ensures no variable is left without a unique identifier
Related data relationships support complex constraint modeling patterns
See also
variables.OXDeviationVar.OXDeviationVar: Specialized deviation variables for goal programming.variables.OXVariableSet.OXVariableSet: Container for managing variable collections.base.OXObject: Base class providing UUID and serialization capabilities.- __post_init__()[source]
Initialize and validate the variable after dataclass construction.
This method is automatically invoked by the dataclass mechanism after all field assignments are complete. It performs critical validation and setup operations to ensure the variable is in a consistent and valid state.
The initialization process includes: 1. Calling the parent OXObject initialization for UUID and class name setup 2. Validating that bounds are mathematically consistent (lower ≤ upper) 3. Generating an automatic name if none was provided or if empty/whitespace 4. Ensuring all internal state is properly configured for optimization use
- Validation Rules:
Lower bound must be less than or equal to upper bound
Infinite bounds are permitted and properly handled
Empty or whitespace-only names trigger automatic UUID-based naming
- Raises:
OXception – If lower_bound is greater than upper_bound. This ensures mathematical validity of the variable’s domain and prevents optimization solver errors that would occur with invalid bounds.
Note
This method is called automatically and should not be invoked manually
The automatic naming scheme uses format “var_<uuid>” for traceability
All validation occurs during object creation, not during value assignment
Parent initialization must complete successfully for proper inheritance
Examples
The validation prevents invalid variable creation:
# This will raise OXception due to invalid bounds try: invalid_var = OXVariable(lower_bound=10, upper_bound=5) except OXception as e: print("Invalid bounds detected:", e) # This creates a variable with automatic naming auto_named = OXVariable(name=" ") # Whitespace triggers auto-naming print(auto_named.name) # Output: "var_<some-uuid>"
- __str__()[source]
Return a string representation of the variable.
Provides a concise, human-readable identifier for the variable that is suitable for display in optimization model summaries, debug output, and solver interfaces.
- Returns:
- The variable’s name, which serves as its primary identifier
in the optimization context. This will be either the user-provided name or the automatically generated “var_<uuid>” format.
- Return type:
Note
The string representation is optimized for readability and brevity
Used extensively by solvers and constraint display mechanisms
Guaranteed to be unique due to UUID-based automatic naming fallback
Examples
named_var = OXVariable(name="production_rate") print(str(named_var)) # Output: "production_rate" auto_var = OXVariable() # No name provided print(str(auto_var)) # Output: "var_<uuid>"
Goal Programming Variables
- class variables.OXDeviationVar(id: ~uuid.UUID = <factory>, class_name: str = '', name: str = '', description: str = '', value: float | int | bool = None, upper_bound: float | int = inf, lower_bound: float | int = 0, related_data: dict[str, ~uuid.UUID] = <factory>, desired: bool = False)[source]
Bases:
OXVariableSpecialized decision variable for goal programming deviation measurement.
This class extends the base OXVariable to provide goal programming-specific functionality for measuring and tracking deviations from target goals. Deviation variables are essential components of goal programming models where multiple objectives are balanced through the minimization of unwanted deviations.
In goal programming, deviation variables come in pairs (positive and negative) to measure over-achievement and under-achievement relative to goal targets. The desirability flag helps optimization algorithms prioritize which deviations to minimize, supporting complex multi-objective decision making scenarios.
- Key Capabilities:
Goal programming deviation measurement with directional semantics
Desirability tracking for optimization objective formulation
Full integration with standard optimization variable operations
Enhanced string representation showing goal programming characteristics
Seamless compatibility with OXVariable-based constraint systems
- Mathematical Context:
In goal programming, a typical goal constraint has the form: achievement_level + negative_deviation - positive_deviation = target_value
Where: - achievement_level: actual performance or resource usage - negative_deviation: shortfall below target (under-achievement) - positive_deviation: excess above target (over-achievement) - target_value: desired goal level
- desired
Flag indicating whether this deviation is desirable in the optimization context. False (default) means the deviation should be minimized, while True means it may be acceptable or even beneficial. This flag influences objective function formulation in goal programming models.
- Type:
- Performance:
Inherits all performance characteristics from OXVariable
Minimal overhead for the additional boolean flag
String representation includes desirability information with minimal cost
- Thread Safety:
Same thread safety characteristics as OXVariable
The desired flag is immutable after initialization for consistency
Examples
Create deviation variables for different goal programming scenarios:
from variables.OXDeviationVar import OXDeviationVar # Budget constraint - over-spending is undesirable budget_overrun = OXDeviationVar( name="budget_positive_deviation", description="Amount by which spending exceeds budget", lower_bound=0, upper_bound=float('inf'), desired=False # Minimize over-spending ) # Production target - under-production is undesirable production_shortfall = OXDeviationVar( name="production_negative_deviation", description="Amount by which production falls short of target", lower_bound=0, upper_bound=float('inf'), desired=False # Minimize under-production ) # Quality improvement - exceeding quality targets may be desired quality_improvement = OXDeviationVar( name="quality_positive_deviation", description="Amount by which quality exceeds minimum standards", lower_bound=0, upper_bound=float('inf'), desired=True # Exceeding quality standards is good ) # Capacity utilization - some under-utilization might be acceptable capacity_slack = OXDeviationVar( name="capacity_negative_deviation", description="Unused capacity below target utilization", lower_bound=0, upper_bound=100, # Maximum 100% under-utilization desired=False # Generally want to minimize unused capacity ) # Display deviation characteristics print(budget_overrun) # Shows desired status in string representation # Goal programming objective formulation example undesired_deviations = [budget_overrun, production_shortfall, capacity_slack] objective_terms = [dev for dev in undesired_deviations if not dev.desired]
Note
Deviation variables are typically non-negative in goal programming models
Pairs of positive/negative deviation variables are common for each goal
The desired flag influences objective function coefficient assignment
String representation clearly shows both variable name and desirability status
See also
variables.OXVariable.OXVariable: Base variable class with bounds and relationships.constraints.OXConstraint.OXGoalConstraint: Goal constraints that use deviation variables.variables.OXVariableSet.OXVariableSet: Container for managing deviation variable collections.- __str__()[source]
Return enhanced string representation including desirability status.
Provides a comprehensive string representation that includes both the variable name from the parent class and the goal programming-specific desirability flag. This enhanced representation is useful for debugging, model visualization, and optimization solver interfaces.
- Returns:
- String in format “variable_name (desired: boolean_value)” that
clearly indicates both the variable identifier and its role in the goal programming objective function.
- Return type:
Examples
desired_dev = OXDeviationVar(name="quality_surplus", desired=True) undesired_dev = OXDeviationVar(name="cost_overrun", desired=False) print(desired_dev) # Output: "quality_surplus (desired: True)" print(undesired_dev) # Output: "cost_overrun (desired: False)"
Goal Programming Examples
from variables import OXDeviationVar, OXVariableSet
# Create deviation variables for goal programming
goal_vars = OXVariableSet()
# Positive deviation (over-achievement) - undesirable
budget_overrun = OXDeviationVar(
name="budget_deviation_positive",
description="Amount exceeding budget target",
lower_bound=0,
upper_bound=float('inf'),
desired=False # We want to minimize this
)
goal_vars.add_object(budget_overrun)
# Negative deviation (under-achievement) - sometimes desirable
cost_savings = OXDeviationVar(
name="cost_deviation_negative",
description="Amount below cost target",
lower_bound=0,
upper_bound=float('inf'),
desired=True # We want to maximize savings
)
goal_vars.add_object(cost_savings)
# Quality deviation - minimize any deviation from target
quality_deviation = OXDeviationVar(
name="quality_deviation",
description="Deviation from quality target",
lower_bound=0,
upper_bound=float('inf'),
desired=False # Any deviation is undesirable
)
goal_vars.add_object(quality_deviation)
# Print deviation variable details
for var in goal_vars:
print(f"{var.name}: desired={var.desired}")
print(f" String representation: {str(var)}")
Variable Collections
- class variables.OXVariableSet(id: ~uuid.UUID = <factory>, class_name: str = '', objects: list[~base.OXObject.OXObject] = <factory>)[source]
Bases:
OXObjectPotType-safe container for managing collections of optimization variables.
This specialized container class extends OXObjectPot to provide comprehensive management of OXVariable instances with strict type enforcement and advanced querying capabilities. It serves as the primary collection mechanism for organizing decision variables in complex optimization models.
The class implements robust validation, efficient storage, and relationship-based querying to support large-scale optimization scenarios where variables need to be organized, searched, and managed based on their business relationships and mathematical properties.
- Key Capabilities:
Strict type enforcement ensuring only OXVariable instances are stored
Relationship-based querying using variable related_data attributes
Full iteration support with Python’s standard collection protocols
Memory-efficient storage optimized for large variable collections
Thread-safe read operations for concurrent optimization environments
- Architecture:
The container inherits from OXObjectPot to leverage proven collection management patterns while adding variable-specific functionality such as relationship querying and type validation. All operations maintain the mathematical integrity required for optimization model consistency.
- Performance Characteristics:
Variable addition: O(1) average case with type validation overhead
Variable removal: O(n) linear search with type validation
Relationship queries: O(n) linear scan with predicate evaluation
Iteration: O(n) with minimal memory overhead for large collections
- Thread Safety:
Read operations (iteration, querying, length) are thread-safe
Write operations (add, remove) require external synchronization
Related_data modifications on contained variables need coordination
Examples
Build and query variable collections for optimization models:
from variables.OXVariableSet import OXVariableSet from variables.OXVariable import OXVariable from uuid import uuid4 # Create variable set for production planning production_vars = OXVariableSet() # Create variables for different products and facilities facility1_id = uuid4() facility2_id = uuid4() product_a_id = uuid4() product_b_id = uuid4() # Production variable for Product A at Facility 1 var_a1 = OXVariable( name="prod_A_facility1", description="Production of Product A at Facility 1", lower_bound=0, upper_bound=1000 ) var_a1.related_data["facility"] = facility1_id var_a1.related_data["product"] = product_a_id # Production variable for Product B at Facility 2 var_b2 = OXVariable( name="prod_B_facility2", description="Production of Product B at Facility 2", lower_bound=0, upper_bound=800 ) var_b2.related_data["facility"] = facility2_id var_b2.related_data["product"] = product_b_id # Add variables to the set production_vars.add_object(var_a1) production_vars.add_object(var_b2) # Query variables by facility facility1_vars = production_vars.query(facility=facility1_id) print(f"Facility 1 variables: {[v.name for v in facility1_vars]}") # Query variables by product type product_a_vars = production_vars.query(product=product_a_id) print(f"Product A variables: {[v.name for v in product_a_vars]}") # Iterate through all variables total_capacity = sum(var.upper_bound for var in production_vars) print(f"Total production capacity: {total_capacity}")
Note
Type validation occurs at runtime during add/remove operations
Query operations scan all variables for matching relationships
Container operations maintain the same semantics as OXObjectPot
Variables can be queried by any combination of related_data attributes
See also
base.OXObjectPot.OXObjectPot: Base container class with collection operations.variables.OXVariable.OXVariable: Variable type managed by this container.base.OXObject: Base object type for UUID and serialization support.- add_object(obj: OXObject)[source]
Add an OXVariable instance to the variable collection.
This method performs type validation to ensure only OXVariable instances are added to the set, maintaining type safety and collection integrity. The validation occurs before delegation to the parent container’s add operation, preventing invalid state and ensuring optimization model consistency.
The method enforces the container’s type invariant that all contained objects must be optimization variables, which is essential for the specialized querying and management operations provided by this class.
- Parameters:
obj (OXObject) – The variable object to add to the collection. Must be an instance of OXVariable or its subclasses. The object will be stored and can be retrieved through iteration or relationship-based queries.
- Raises:
OXception – If the provided object is not an instance of OXVariable. This strict type checking prevents runtime errors and maintains the mathematical integrity of variable collections.
- Performance:
Time complexity: O(1) average case for the type check and container addition
Space complexity: O(1) additional memory for the new variable reference
Type validation adds minimal overhead compared to container operations
Note
Duplicate variables (same UUID) will be handled by the parent container
The variable’s related_data can be modified after addition for querying
Type validation is strict and does not allow duck-typing or coercion
Examples
Add variables with proper type validation:
var_set = OXVariableSet() # Valid addition - OXVariable instance production_var = OXVariable(name="production", lower_bound=0) var_set.add_object(production_var) # Success # Valid addition - OXVariable subclass deviation_var = OXDeviationVar(name="deviation") var_set.add_object(deviation_var) # Success # Invalid addition - wrong type from base.OXObject import OXObject generic_obj = OXObject() try: var_set.add_object(generic_obj) # Raises OXception except OXception as e: print("Type validation prevented invalid addition")
See also
remove_object(): Type-safe variable removal from the collection.query(): Relationship-based variable querying capabilities.
- remove_object(obj: OXObject)[source]
Remove an OXVariable instance from the variable collection.
This method performs type validation to ensure only OXVariable instances are removed from the set, maintaining type safety and preventing invalid removal operations. The validation occurs before delegation to the parent container’s removal operation.
- Parameters:
obj (OXObject) – The variable object to remove from the collection. Must be an instance of OXVariable that is currently stored in the set. The object will be completely removed from the collection.
- Raises:
OXception – If the provided object is not an instance of OXVariable. This maintains the type safety invariant of the container.
ValueError – If the object is not currently in the set. This is raised by the parent container when attempting to remove a non-existent object.
- Performance:
Time complexity: O(n) where n is the number of variables (linear search)
Space complexity: O(1) as removal only deallocates the reference
Type validation overhead is minimal compared to the search operation
Note
Removal is based on object identity (UUID), not value equality
After removal, the variable can no longer be queried or iterated
Related data relationships are not automatically cleaned up
Examples
Remove variables with proper validation:
var_set = OXVariableSet() production_var = OXVariable(name="production") var_set.add_object(production_var) # Valid removal - variable exists in set var_set.remove_object(production_var) # Success # Invalid removal - wrong type from base.OXObject import OXObject generic_obj = OXObject() try: var_set.remove_object(generic_obj) # Raises OXception except OXception as e: print("Type validation prevented invalid removal") # Invalid removal - variable not in set try: var_set.remove_object(production_var) # Raises ValueError except ValueError as e: print("Variable not found in set")
See also
add_object(): Type-safe variable addition to the collection.query(): Find variables before removal operations.
- query(**kwargs) list[OXObject][source]
Search for variables based on their relationship data attributes.
This method provides powerful relationship-based querying capabilities by searching through all variables in the collection and returning those that match the specified related_data criteria. Variables are included in the result only if they contain ALL specified relationship key-value pairs.
The query system enables complex filtering scenarios essential for large-scale optimization models where variables need to be organized and accessed based on their business relationships, such as customers, facilities, products, time periods, or other domain-specific entities.
- Query Logic:
Variables must have ALL specified keys in their related_data dictionary
Values must match exactly (no partial matching or type coercion)
Variables without any matching keys are excluded from results
Empty queries (no kwargs) return no results for safety
- Parameters:
**kwargs – Key-value pairs to match against variables’ related_data dictionaries. Keys represent relationship types (e.g., ‘customer’, ‘facility’) and values are the corresponding UUID identifiers. A variable is included in results only if its related_data contains ALL specified key-value pairs with exact matches.
- Returns:
- A list of OXVariable instances that match ALL query criteria.
The list is empty if no variables match or if no query parameters are provided. Variables are returned in the order they are stored in the container.
- Return type:
list[OXObject]
- Raises:
OXception – If a non-OXVariable object is encountered during the search. This should never occur due to type validation but provides a safety check against container corruption.
- Performance:
Time complexity: O(n × k) where n is variables count and k is query criteria count
Space complexity: O(m) where m is the number of matching variables
Linear scan through all variables makes this suitable for moderate-sized collections
Note
Query parameters are case-sensitive and require exact key matches
UUID values are compared for exact equality (no fuzzy matching)
Variables can be queried by any combination of related_data attributes
Results maintain references to original variables (not copies)
Examples
Query variables using relationship-based filtering:
from variables.OXVariableSet import OXVariableSet from variables.OXVariable import OXVariable from uuid import uuid4 # Set up variables with relationships var_set = OXVariableSet() customer1_id = uuid4() customer2_id = uuid4() facility1_id = uuid4() facility2_id = uuid4() # Create variables for different customer-facility combinations var1 = OXVariable(name="prod_c1_f1") var1.related_data["customer"] = customer1_id var1.related_data["facility"] = facility1_id var2 = OXVariable(name="prod_c1_f2") var2.related_data["customer"] = customer1_id var2.related_data["facility"] = facility2_id var3 = OXVariable(name="prod_c2_f1") var3.related_data["customer"] = customer2_id var3.related_data["facility"] = facility1_id # Add to set for var in [var1, var2, var3]: var_set.add_object(var) # Query by single criterion customer1_vars = var_set.query(customer=customer1_id) print(f"Customer 1 variables: {len(customer1_vars)}") # Output: 2 # Query by multiple criteria (AND operation) specific_vars = var_set.query(customer=customer1_id, facility=facility1_id) print(f"Customer 1 at Facility 1: {len(specific_vars)}") # Output: 1 # Empty query returns no results empty_result = var_set.query() print(f"Empty query result: {len(empty_result)}") # Output: 0
See also
add_object(): Add variables with relationship data for querying.search_by_function(): Lower-level search functionality from parent class.
Variable Architecture
OptiX variables are designed for optimization problems with the following key features:
Bounds Management: All variables support lower and upper bounds with automatic validation
Relationship Tracking: UUID-based linking to business entities through related_data
Value Storage: Optional value assignment for initial solutions or fixed variables
Goal Programming: Specialized deviation variables with desirability indicators
Examples
Creating Variables
from variables import OXVariable
from uuid import uuid4
# Create a basic decision variable
production_rate = OXVariable(
name="production_rate",
description="Daily production rate (units/day)",
lower_bound=0.0,
upper_bound=1000.0,
value=500.0 # Optional initial value
)
# Create a variable with entity relationships
facility_id = uuid4()
machine_hours = OXVariable(
name="machine_hours",
description="Machine operating hours per day",
lower_bound=0,
upper_bound=24,
related_data={"facility": facility_id}
)
# Variables auto-generate names if not provided
auto_var = OXVariable(
description="Automatically named variable",
lower_bound=0,
upper_bound=100
)
print(f"Auto-generated name: {auto_var.name}") # Will be "var_<uuid>"
print(f"Production rate bounds: [{production_rate.lower_bound}, {production_rate.upper_bound}]")
print(f"Machine hours facility: {machine_hours.related_data.get('facility')}")
Variable Sets and Collections
from variables import OXVariableSet, OXVariable
# Create variable set
variables = OXVariableSet()
# Add variables for production planning
products = ["A", "B", "C"]
factories = ["Factory_1", "Factory_2"]
for product in products:
for factory in factories:
var = OXVariable(
name=f"production_{product}_{factory}",
description=f"Production of {product} at {factory}",
lower_bound=0,
upper_bound=500,
variable_type="continuous"
)
variables.add_variable(var)
print(f"Total variables: {len(variables)}")
# Search for specific variables
product_a_vars = variables.search_by_function(
lambda v: "product_A" in v.name
)
print(f"Product A variables: {len(product_a_vars)}")
# Search by name pattern
factory_1_vars = variables.search_by_name("Factory_1")
print(f"Factory 1 variables: {len(factory_1_vars)}")
# Search by type
continuous_vars = variables.search_by_type("continuous")
print(f"Continuous variables: {len(continuous_vars)}")
Advanced Variable Management
def create_transportation_variables(origins, destinations, products):
"""Create variables for a transportation problem."""
variables = OXVariableSet()
for origin in origins:
for destination in destinations:
for product in products:
# Flow variable
flow_var = OXVariable(
name=f"flow_{origin.id}_{destination.id}_{product.id}",
description=f"Flow of {product.name} from {origin.name} to {destination.name}",
lower_bound=0,
upper_bound=min(origin.capacity, destination.demand[product.id]),
related_data={
"origin": origin.id,
"destination": destination.id,
"product": product.id
}
)
variables.add_object(flow_var)
# Binary variable for route activation
route_var = OXVariable(
name=f"route_{origin.id}_{destination.id}_{product.id}",
description=f"Route activation for {product.name} from {origin.name} to {destination.name}",
lower_bound=0,
upper_bound=1,
related_data={
"origin": origin.id,
"destination": destination.id,
"product": product.id,
"is_binary": True
}
)
variables.add_object(route_var)
return variables
# Usage example
# variables = create_transportation_variables(warehouses, customers, products)
Working with Variable Values
from variables import OXVariable
# Create variable with initial value
machine_capacity = OXVariable(
name="machine_capacity",
description="Machine processing capacity",
lower_bound=0,
upper_bound=1000,
value=500 # Initial value
)
# Update value
machine_capacity.value = 800
print(f"Current value: {machine_capacity.value}")
# Update bounds directly
machine_capacity.lower_bound = 100
machine_capacity.upper_bound = 1200
print(f"New bounds: [{machine_capacity.lower_bound}, {machine_capacity.upper_bound}]")
# Manual validation of values
test_values = [50, 150, 1100, 1300]
for test_value in test_values:
is_valid = machine_capacity.lower_bound <= test_value <= machine_capacity.upper_bound
print(f"Value {test_value} is within bounds: {is_valid}")
Variable Search and Filtering
def demonstrate_variable_search(variable_set):
"""Demonstrate various variable search capabilities."""
from uuid import uuid4
print("Variable Search Examples")
print("=" * 40)
# Search by name pattern using list comprehension
production_vars = [v for v in variable_set if "production" in v.name.lower()]
print(f"Production variables: {len(production_vars)}")
# Search by bounds
bounded_vars = [v for v in variable_set if 0 <= v.lower_bound and v.upper_bound <= 100]
print(f"Variables bounded between 0 and 100: {len(bounded_vars)}")
# Search by description keywords
cost_vars = [v for v in variable_set if "cost" in v.description.lower()]
print(f"Cost-related variables: {len(cost_vars)}")
# Query by relationships (if you've set up related_data)
# Example: Find all variables related to a specific facility
facility_id = uuid4() # Example facility ID
facility_vars = variable_set.query(facility=facility_id)
print(f"Variables for facility {facility_id}: {len(facility_vars)}")
# Find variables by checking related_data for binary indicators
binary_vars = [v for v in variable_set if v.related_data.get("is_binary", False)]
print(f"Binary variables: {len(binary_vars)}")
# Get specific variables by name
target_names = ["production_A_Factory_1", "production_B_Factory_1"]
specific_vars = [v for v in variable_set if v.name in target_names]
print(f"Specific named variables found: {len(specific_vars)}")
Dynamic Variable Creation
def create_scheduling_variables(tasks, time_periods, resources):
"""Create variables for a scheduling problem with dynamic structure."""
variables = OXVariableSet()
# Task assignment variables (binary)
for task in tasks:
for period in time_periods:
if task.can_start_in_period(period):
var = OXVariable(
name=f"start_{task.id}_period_{period}",
description=f"Task {task.name} starts in period {period}",
lower_bound=0,
upper_bound=1,
related_data={"task": task.id, "period": period, "is_binary": True}
)
variables.add_object(var)
# Resource allocation variables (continuous)
for task in tasks:
for resource in resources:
if task.can_use_resource(resource):
var = OXVariable(
name=f"allocation_{task.id}_{resource.id}",
description=f"Allocation of {resource.name} to {task.name}",
lower_bound=0,
upper_bound=resource.capacity,
related_data={"task": task.id, "resource": resource.id}
)
variables.add_object(var)
# Completion time variables (continuous)
for task in tasks:
var = OXVariable(
name=f"completion_{task.id}",
description=f"Completion time of {task.name}",
lower_bound=task.earliest_start,
upper_bound=task.latest_finish,
related_data={"task": task.id}
)
variables.add_object(var)
return variables
Variable Validation and Analysis
def validate_variable_set(variable_set):
"""Validate a variable set for common issues."""
issues = []
# Check for duplicate names
names = [var.name for var in variable_set]
duplicate_names = set([name for name in names if names.count(name) > 1])
if duplicate_names:
issues.append(f"Duplicate variable names: {duplicate_names}")
# Check for invalid bounds
invalid_bounds_vars = []
for var in variable_set:
if var.lower_bound > var.upper_bound:
invalid_bounds_vars.append(var.name)
if invalid_bounds_vars:
issues.append(f"Variables with invalid bounds: {invalid_bounds_vars}")
# Check for extremely large bounds (potential numerical issues)
large_bound_vars = []
for var in variable_set:
if abs(var.lower_bound) > 1e6 or abs(var.upper_bound) > 1e6:
large_bound_vars.append(var.name)
if large_bound_vars:
issues.append(f"Variables with very large bounds: {large_bound_vars}")
# Check for missing descriptions
no_description_vars = [var.name for var in variable_set if not var.description]
if no_description_vars:
issues.append(f"Variables without descriptions: {no_description_vars}")
return issues
def analyze_variable_structure(variable_set):
"""Analyze the structure and properties of a variable set."""
analysis = {
'total_variables': len(variable_set),
'variable_types': {},
'bound_statistics': {
'min_lower_bound': float('inf'),
'max_lower_bound': float('-inf'),
'min_upper_bound': float('inf'),
'max_upper_bound': float('-inf'),
'unbounded_variables': 0
},
'fixed_variables': 0,
'name_patterns': {}
}
# Since OXVariable doesn't have variable_type attribute, we can infer from bounds
for var in variable_set:
# Infer type based on bounds and related_data
if var.related_data.get("is_binary", False) or (var.lower_bound == 0 and var.upper_bound == 1):
var_type = "binary"
elif var.lower_bound == int(var.lower_bound) and var.upper_bound == int(var.upper_bound):
var_type = "integer"
else:
var_type = "continuous"
analysis['variable_types'][var_type] = analysis['variable_types'].get(var_type, 0) + 1
# Bound statistics
if var.lower_bound != float('-inf'):
analysis['bound_statistics']['min_lower_bound'] = min(
analysis['bound_statistics']['min_lower_bound'], var.lower_bound
)
analysis['bound_statistics']['max_lower_bound'] = max(
analysis['bound_statistics']['max_lower_bound'], var.lower_bound
)
if var.upper_bound != float('inf'):
analysis['bound_statistics']['min_upper_bound'] = min(
analysis['bound_statistics']['min_upper_bound'], var.upper_bound
)
analysis['bound_statistics']['max_upper_bound'] = max(
analysis['bound_statistics']['max_upper_bound'], var.upper_bound
)
if (var.lower_bound == float('-inf') or var.upper_bound == float('inf')):
analysis['bound_statistics']['unbounded_variables'] += 1
# Check if variable has a fixed value
if var.value is not None:
analysis['fixed_variables'] += 1
# Name patterns
name_parts = var.name.split('_')
if len(name_parts) > 1:
pattern = name_parts[0]
analysis['name_patterns'][pattern] = analysis['name_patterns'].get(pattern, 0) + 1
return analysis
def print_variable_analysis(analysis):
"""Print formatted variable analysis."""
print("Variable Set Analysis")
print("=" * 50)
print(f"Total Variables: {analysis['total_variables']}")
print(f"Fixed Variables: {analysis['fixed_variables']}")
print("\nVariable Types:")
for var_type, count in analysis['variable_types'].items():
percentage = (count / analysis['total_variables']) * 100
print(f" {var_type}: {count} ({percentage:.1f}%)")
print("\nBound Statistics:")
bounds = analysis['bound_statistics']
if bounds['min_lower_bound'] != float('inf'):
print(f" Lower bounds range: [{bounds['min_lower_bound']}, {bounds['max_lower_bound']}]")
if bounds['min_upper_bound'] != float('inf'):
print(f" Upper bounds range: [{bounds['min_upper_bound']}, {bounds['max_upper_bound']}]")
print(f" Unbounded variables: {bounds['unbounded_variables']}")
print("\nName Patterns:")
sorted_patterns = sorted(analysis['name_patterns'].items(), key=lambda x: x[1], reverse=True)
for pattern, count in sorted_patterns[:10]: # Top 10 patterns
print(f" {pattern}_*: {count} variables")
# Usage examples
issues = validate_variable_set(variable_set)
if issues:
print("Variable Set Issues:")
for issue in issues:
print(f" - {issue}")
else:
print("Variable set validation passed!")
analysis = analyze_variable_structure(variable_set)
print_variable_analysis(analysis)
Variable Transformation and Scaling
def scale_variables(variable_set, scaling_factor=1.0):
"""Scale variable bounds by a given factor."""
scaled_variables = OXVariableSet()
for var in variable_set:
scaled_var = OXVariable(
name=var.name,
description=var.description,
lower_bound=var.lower_bound * scaling_factor,
upper_bound=var.upper_bound * scaling_factor,
value=var.value * scaling_factor if var.value is not None else None,
related_data=var.related_data.copy()
)
scaled_variables.add_object(scaled_var)
return scaled_variables
def normalize_variables(variable_set):
"""Normalize variables to [0, 1] range."""
normalized_variables = OXVariableSet()
for var in variable_set:
# Check if variable appears to be binary
is_binary = var.related_data.get("is_binary", False) or (var.lower_bound == 0 and var.upper_bound == 1)
if not is_binary:
# Normalize to [0, 1] range
normalized_var = OXVariable(
name=f"norm_{var.name}",
description=f"Normalized {var.description}",
lower_bound=0.0,
upper_bound=1.0
)
normalized_variables.add_object(normalized_var)
else:
# Keep binary variables as is
normalized_variables.add_object(var)
return normalized_variables
def create_auxiliary_variables(base_variables, auxiliary_type="slack"):
"""Create auxiliary variables (slack, surplus, artificial) for constraints."""
auxiliary_variables = OXVariableSet()
for i, base_var in enumerate(base_variables):
if auxiliary_type == "slack":
aux_var = OXVariable(
name=f"slack_{i}",
description=f"Slack variable for constraint {i}",
lower_bound=0,
upper_bound=float('inf')
)
elif auxiliary_type == "surplus":
aux_var = OXVariable(
name=f"surplus_{i}",
description=f"Surplus variable for constraint {i}",
lower_bound=0,
upper_bound=float('inf')
)
elif auxiliary_type == "artificial":
aux_var = OXVariable(
name=f"artificial_{i}",
description=f"Artificial variable for constraint {i}",
lower_bound=0,
upper_bound=float('inf')
)
auxiliary_variables.add_object(aux_var)
return auxiliary_variables
Performance Optimization
def optimize_variable_access(variable_set):
"""Optimize variable set for faster access patterns."""
# Create lookup dictionaries for O(1) access
name_lookup = {var.name: var for var in variable_set}
id_lookup = {var.id: var for var in variable_set}
type_lookup = {}
# Group variables by inferred type
for var in variable_set:
# Infer type from bounds and related_data
if var.related_data.get("is_binary", False) or (var.lower_bound == 0 and var.upper_bound == 1):
var_type = "binary"
elif var.lower_bound == int(var.lower_bound) and var.upper_bound == int(var.upper_bound):
var_type = "integer"
else:
var_type = "continuous"
if var_type not in type_lookup:
type_lookup[var_type] = []
type_lookup[var_type].append(var)
# Create optimized variable set with fast lookups
class OptimizedVariableSet(OXVariableSet):
def __init__(self, variables):
super().__init__()
self.data = list(variables)
self._name_lookup = name_lookup
self._id_lookup = id_lookup
self._type_lookup = type_lookup
def get_variable_by_name_fast(self, name):
return self._name_lookup.get(name)
def get_variable_by_id_fast(self, var_id):
return self._id_lookup.get(var_id)
def get_variables_by_type_fast(self, var_type):
return self._type_lookup.get(var_type, [])
return OptimizedVariableSet(variable_set)
# Usage
optimized_vars = optimize_variable_access(variable_set)
var = optimized_vars.get_variable_by_name_fast("production_A_Factory_1")
Variable Export and Import
import json
from datetime import datetime
def export_variables_to_json(variable_set, filename=None):
"""Export variable set to JSON format."""
if filename is None:
filename = f"variables_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json"
export_data = {
'metadata': {
'export_date': datetime.now().isoformat(),
'total_variables': len(variable_set),
'optix_version': '1.0.0'
},
'variables': []
}
for var in variable_set:
var_data = {
'id': str(var.id),
'name': var.name,
'description': var.description,
'lower_bound': var.lower_bound if var.lower_bound != float('-inf') else None,
'upper_bound': var.upper_bound if var.upper_bound != float('inf') else None,
'value': var.value,
'related_data': {k: str(v) for k, v in var.related_data.items()}
}
export_data['variables'].append(var_data)
with open(filename, 'w') as f:
json.dump(export_data, f, indent=2)
return filename
def import_variables_from_json(filename):
"""Import variable set from JSON format."""
with open(filename, 'r') as f:
import_data = json.load(f)
variable_set = OXVariableSet()
for var_data in import_data['variables']:
# Reconstruct related_data with UUIDs
from uuid import UUID
related_data = {}
for k, v in var_data.get('related_data', {}).items():
try:
related_data[k] = UUID(v)
except:
related_data[k] = v
var = OXVariable(
name=var_data['name'],
description=var_data['description'],
lower_bound=var_data.get('lower_bound', 0),
upper_bound=var_data.get('upper_bound', float('inf')),
value=var_data.get('value'),
related_data=related_data
)
variable_set.add_object(var)
return variable_set
# Usage
filename = export_variables_to_json(variable_set)
print(f"Variables exported to {filename}")
imported_variables = import_variables_from_json(filename)
print(f"Imported {len(imported_variables)} variables")
See Also
Problem Module - Problem classes that use variables
Constraints Module - Constraint definitions that reference variables
Data Module - Database integration for variable creation
../user_guide/variables - Advanced variable management guide