Data Module
The data module provides scenario-based data management capabilities for optimization problems. It includes data objects with multi-scenario support and type-safe database collections for organizing data.
Data Classes
Data Objects
- class data.OXData(id: ~uuid.UUID = <factory>, class_name: str = '', active_scenario: str = 'Default', scenarios: dict[str, dict[str, ~typing.Any]] = <factory>)[source]
Bases:
OXObjectA base class for data objects with scenario support.
This class provides a mechanism for storing different attribute values for different scenarios. When an attribute is accessed, the system first checks if it exists in the active scenario, and if not, falls back to the object’s own attribute.
- scenarios
A dictionary mapping scenario names to dictionaries of attribute values.
Examples
>>> data = OXData() >>> data.value = 10 >>> data.create_scenario("Optimistic", value=20) >>> data.create_scenario("Pessimistic", value=5) >>> print(data.value) # Default scenario 10 >>> data.active_scenario = "Optimistic" >>> print(data.value) # Optimistic scenario 20 >>> data.active_scenario = "Pessimistic" >>> print(data.value) # Pessimistic scenario 5
- __getattribute__(item)[source]
Custom attribute access that checks the active scenario first.
When an attribute is accessed, this method first checks if it exists in the active scenario, and if not, falls back to the object’s own attribute.
- Parameters:
item (str) – The name of the attribute to access.
- Returns:
- The value of the attribute in the active scenario, or the
object’s own attribute if not found in the active scenario.
- Return type:
Any
- create_scenario(scenario_name: str, **kwargs)[source]
Create a new scenario with the specified attribute values.
If the “Default” scenario doesn’t exist yet, it is created first, capturing the object’s current attribute values.
- Parameters:
scenario_name (str) – The name of the new scenario.
**kwargs – Attribute-value pairs for the new scenario.
- Raises:
OXception – If an attribute in kwargs doesn’t exist in the object.
Examples
>>> data = OXData() >>> data.value = 10 >>> data.create_scenario("Optimistic", value=20) >>> data.active_scenario = "Optimistic" >>> print(data.value) 20
Database Collections
- class data.OXDatabase(id: ~uuid.UUID = <factory>, class_name: str = '', objects: list[~base.OXObject.OXObject] = <factory>)[source]
Bases:
OXObjectPotA container for OXData objects.
This class extends OXObjectPot to provide a container specifically for OXData objects. It enforces type safety by ensuring that only OXData objects can be added to or removed from the database.
Examples
>>> db = OXDatabase() >>> data1 = OXData() >>> data2 = OXData() >>> db.add_object(data1) >>> db.add_object(data2) >>> len(db) 2 >>> for data in db: ... print(data.id) 12345678-1234-5678-1234-567812345678 87654321-4321-8765-4321-876543210987
See also
base.OXObjectPot.OXObjectPotdata.OXData.OXData- add_object(obj: OXObject)[source]
Add an OXData object to the database.
- Parameters:
obj (OXObject) – The object to add. Must be an instance of OXData.
- Raises:
OXception – If the object is not an instance of OXData.
- remove_object(obj: OXObject)[source]
Remove an OXData object from the database.
- Parameters:
obj (OXObject) – The object to remove. Must be an instance of OXData.
- Raises:
OXception – If the object is not an instance of OXData.
ValueError – If the object is not in the database.
Constants
- data.NON_SCENARIO_FIELDS = ['active_scenario', 'scenarios', 'id', 'class_name']
Built-in mutable sequence.
If no argument is given, the constructor creates a new empty list. The argument must be an iterable if specified.
List of field names that are excluded from scenario management to prevent infinite loops and maintain object integrity. These fields are always accessed from the base object.
Examples
Basic Data Objects with Scenarios
from data import OXData
# Create a data object with base values
demand_data = OXData()
demand_data.quantity = 100
demand_data.cost = 50.0
# Create scenarios for sensitivity analysis
demand_data.create_scenario("High_Demand", quantity=150, cost=55.0)
demand_data.create_scenario("Low_Demand", quantity=75, cost=45.0)
# Access values in different scenarios
print(demand_data.quantity) # 100 (Default scenario)
demand_data.active_scenario = "High_Demand"
print(demand_data.quantity) # 150
demand_data.active_scenario = "Low_Demand"
print(demand_data.quantity) # 75
Database Collections
from data import OXDatabase, OXData
# Create a database for organizing data objects
db = OXDatabase()
# Create multiple data objects
factory_a = OXData()
factory_a.location = "Factory_A"
factory_a.capacity = 500
factory_a.create_scenario("Expansion", capacity=750)
factory_b = OXData()
factory_b.location = "Factory_B"
factory_b.capacity = 300
factory_b.create_scenario("Expansion", capacity=450)
# Add objects to database
db.add_object(factory_a)
db.add_object(factory_b)
print(f"Total factories: {len(db)}")
# Iterate through all objects
for factory in db:
print(f"Factory at {factory.location}: capacity {factory.capacity}")
# Switch to expansion scenario
factory_a.active_scenario = "Expansion"
factory_b.active_scenario = "Expansion"
# Check expanded capacities
for factory in db:
print(f"Expanded {factory.location}: capacity {factory.capacity}")
Scenario Management for Optimization
from data import OXData
# Create demand data with multiple scenarios
demand = OXData()
demand.product = "Widget_A"
demand.base_demand = 1000
demand.seasonal_factor = 1.0
# Create scenarios for different market conditions
demand.create_scenario(
"Optimistic",
base_demand=1200,
seasonal_factor=1.2
)
demand.create_scenario(
"Pessimistic",
base_demand=800,
seasonal_factor=0.8
)
demand.create_scenario(
"Realistic",
base_demand=1000,
seasonal_factor=1.0
)
# Function to calculate total demand
def calculate_total_demand(data, season_multiplier=1.0):
return data.base_demand * data.seasonal_factor * season_multiplier
# Compare scenarios
scenarios = ["Default", "Optimistic", "Pessimistic", "Realistic"]
for scenario in scenarios:
demand.active_scenario = scenario
total = calculate_total_demand(demand)
print(f"{scenario} scenario: {total} units")
Scenario-Based Sensitivity Analysis
def run_sensitivity_analysis(data_objects, scenarios, optimization_function):
"""Run optimization across multiple scenarios."""
results = {}
for scenario_name in scenarios:
print(f"Running scenario: {scenario_name}")
# Switch all data objects to the same scenario
for data_obj in data_objects:
if scenario_name in data_obj.scenarios:
data_obj.active_scenario = scenario_name
else:
data_obj.active_scenario = "Default"
# Run optimization with current scenario data
result = optimization_function(data_objects)
results[scenario_name] = result
return results
# Usage example
def simple_profit_calculation(data_objects):
total_profit = 0
for obj in data_objects:
if hasattr(obj, 'revenue') and hasattr(obj, 'cost'):
total_profit += obj.revenue - obj.cost
return total_profit
# Create test data
product1 = OXData()
product1.revenue = 100
product1.cost = 60
product1.create_scenario("HighPrice", revenue=120, cost=60)
product1.create_scenario("LowCost", revenue=100, cost=45)
product2 = OXData()
product2.revenue = 80
product2.cost = 50
product2.create_scenario("HighPrice", revenue=95, cost=50)
product2.create_scenario("LowCost", revenue=80, cost=40)
products = [product1, product2]
scenarios = ["Default", "HighPrice", "LowCost"]
results = run_sensitivity_analysis(products, scenarios, simple_profit_calculation)
for scenario, profit in results.items():
print(f"{scenario}: ${profit} profit")
Working with Multiple Data Types
from data import OXData, OXDatabase
def create_supply_chain_data():
"""Create a multi-type supply chain dataset."""
# Create suppliers with different scenarios
supplier_db = OXDatabase()
supplier_a = OXData()
supplier_a.name = "Supplier_A"
supplier_a.lead_time = 14
supplier_a.reliability = 0.95
supplier_a.cost_factor = 1.0
supplier_a.create_scenario("Crisis", lead_time=21, reliability=0.85, cost_factor=1.3)
supplier_b = OXData()
supplier_b.name = "Supplier_B"
supplier_b.lead_time = 7
supplier_b.reliability = 0.98
supplier_b.cost_factor = 1.1
supplier_b.create_scenario("Crisis", lead_time=10, reliability=0.90, cost_factor=1.4)
supplier_db.add_object(supplier_a)
supplier_db.add_object(supplier_b)
# Create demand points with scenarios
demand_db = OXDatabase()
region_1 = OXData()
region_1.name = "Region_1"
region_1.demand = 1000
region_1.max_price = 50
region_1.create_scenario("Growth", demand=1300, max_price=55)
region_1.create_scenario("Recession", demand=700, max_price=45)
region_2 = OXData()
region_2.name = "Region_2"
region_2.demand = 800
region_2.max_price = 48
region_2.create_scenario("Growth", demand=1100, max_price=52)
region_2.create_scenario("Recession", demand=600, max_price=42)
demand_db.add_object(region_1)
demand_db.add_object(region_2)
return supplier_db, demand_db
# Create the data
suppliers, demand_points = create_supply_chain_data()
# Analyze different scenarios
scenarios = ["Default", "Growth", "Crisis", "Recession"]
for scenario in scenarios:
print(f"\n=== {scenario} Scenario ===")
# Switch all objects to the scenario
for supplier in suppliers:
if scenario in supplier.scenarios:
supplier.active_scenario = scenario
else:
supplier.active_scenario = "Default"
for demand in demand_points:
if scenario in demand.scenarios:
demand.active_scenario = scenario
else:
demand.active_scenario = "Default"
# Calculate scenario metrics
total_demand = sum(d.demand for d in demand_points)
avg_lead_time = sum(s.lead_time for s in suppliers) / len(suppliers)
avg_reliability = sum(s.reliability for s in suppliers) / len(suppliers)
print(f"Total demand: {total_demand}")
print(f"Avg lead time: {avg_lead_time:.1f} days")
print(f"Avg reliability: {avg_reliability:.2%}")
UUID-Based Object Access
from data import OXDatabase, OXData
# Create database with data objects
db = OXDatabase()
# Add several objects
for i in range(5):
obj = OXData()
obj.value = i * 10
obj.category = f"Type_{i % 3}"
db.add_object(obj)
print(f"Database contains {len(db)} objects")
# Access objects by UUID (inherited from OXObjectPot)
first_obj = list(db)[0]
found_obj = db.get_object_by_id(first_obj.id)
print(f"Found object with value: {found_obj.value}")
# Manual filtering using iteration
type_0_objects = [obj for obj in db if obj.category == "Type_0"]
print(f"Found {len(type_0_objects)} objects of Type_0")
# Remove objects
db.remove_object(first_obj)
print(f"After removal: {len(db)} objects")
Advanced Scenario Patterns
def create_monte_carlo_scenarios(base_data, num_scenarios=100):
"""Create multiple scenarios for Monte Carlo analysis."""
import random
for i in range(num_scenarios):
# Create variations around base values
demand_variation = random.uniform(0.8, 1.2)
cost_variation = random.uniform(0.9, 1.1)
scenario_name = f"MonteCarlo_{i+1:03d}"
base_data.create_scenario(
scenario_name,
demand=int(base_data.demand * demand_variation),
cost=base_data.cost * cost_variation
)
# Create base data
product = OXData()
product.demand = 1000
product.cost = 50.0
product.revenue = 75.0
# Generate Monte Carlo scenarios
create_monte_carlo_scenarios(product, 10) # Create 10 scenarios
# Run analysis across all scenarios
profits = []
for scenario_name in product.scenarios:
product.active_scenario = scenario_name
profit = product.revenue - product.cost
profits.append((scenario_name, profit))
# Show results
for scenario, profit in sorted(profits, key=lambda x: x[1], reverse=True)[:5]:
print(f"{scenario}: ${profit:.2f} profit")
Type Safety and Error Handling
from data import OXDatabase, OXData
from base import OXObject, OXception
# Demonstrate type safety
db = OXDatabase()
data_obj = OXData()
# This works - OXData is allowed
db.add_object(data_obj)
print("OXData object added successfully")
# This will fail - only OXData objects allowed
try:
non_data_obj = OXObject() # Base object, not OXData
db.add_object(non_data_obj)
except OXception as e:
print(f"Type safety error: {e}")
# Scenario error handling
try:
data_obj.create_scenario("TestScenario", nonexistent_attr="value")
except OXception as e:
print(f"Scenario creation error: {e}")
See Also
base - Base classes that data objects inherit from
Problem Module - Problem classes that use data objects
Variables Module - Variable creation that can be linked to data objects
../user_guide/scenarios - Advanced scenario modeling guide