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: OXObject

Fundamental 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:

str

description

Detailed description of the variable’s purpose and meaning within the optimization context. Used for documentation and model interpretation purposes.

Type:

str

value

The current assigned value of the variable. Can be None for unassigned variables. Should respect the defined bounds when set by optimization solvers.

Type:

float | int | bool

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.

Type:

float | int

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.

Type:

float | int

related_data

Dictionary mapping relationship type names to UUID identifiers of related objects. Enables complex data modeling and constraint relationships.

Type:

dict[str, UUID]

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.

name: str = ''
description: str = ''
value: float | int | bool = None
upper_bound: float | int = inf
lower_bound: float | int = 0
related_data: dict[str, UUID]
__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:

str

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>"
__init__(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>) None

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: OXVariable

Specialized 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:

bool

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.

desired: bool = False
__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:

str

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)"
__init__(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) None

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: OXObjectPot

Type-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.

__init__(id: ~uuid.UUID = <factory>, class_name: str = '', objects: list[~base.OXObject.OXObject] = <factory>) None

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