CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-pyomo

The Pyomo optimization modeling framework for formulating, analyzing, and solving mathematical optimization problems

Pending
Overview
Eval results
Files

gdp.mddocs/

Generalized Disjunctive Programming

Components for modeling logical relationships and disjunctive constraints in optimization problems. GDP enables the formulation of problems with logical decision-making and alternative process paths through disjuncts and disjunctions.

Capabilities

Disjunct Components

Components for defining disjunctive blocks containing variables, constraints, and logical relationships.

class Disjunct:
    """
    Disjunct component for GDP modeling.
    
    A disjunct represents a logical block that is either active or inactive.
    When active, all constraints within the disjunct must be satisfied.
    """
    def __init__(self, *args, **kwargs): ...
    
    def activate(self):
        """Activate this disjunct."""
    
    def deactivate(self):
        """Deactivate this disjunct."""
    
    def is_active(self):
        """Check if disjunct is active."""
    
    @property
    def indicator_var(self):
        """Get the binary indicator variable for this disjunct."""

class DisjunctData:
    """
    Data container for disjunct components.
    
    Stores the actual constraint and variable data associated with
    a specific disjunct instance.
    """
    def __init__(self, component=None): ...
    
    def activate(self): ...
    def deactivate(self): ...
    def is_active(self): ...

Disjunction Components

Components for defining logical disjunctions (OR relationships) between disjuncts.

class Disjunction:
    """
    Disjunction component representing logical OR relationships.
    
    A disjunction requires that exactly one of its constituent
    disjuncts must be active (true).
    """
    def __init__(self, *args, **kwargs): ...
    
    def activate(self):
        """Activate this disjunction."""
    
    def deactivate(self):
        """Deactivate this disjunction."""
    
    def is_active(self):
        """Check if disjunction is active."""

class DisjunctionData:
    """
    Data container for disjunction components.
    
    Stores the disjunct references and logical relationships
    for a specific disjunction instance.
    """
    def __init__(self, component=None): ...
    
    def activate(self): ...
    def deactivate(self): ...
    def is_active(self): ...

GDP Error Handling

Exception class for GDP-specific errors and validation issues.

class GDP_Error(Exception):
    """GDP-specific error class for logical modeling issues."""
    def __init__(self, message): ...

Usage Examples

Basic Disjunctive Model

from pyomo.environ import *
from pyomo.gdp import Disjunct, Disjunction

model = ConcreteModel()

# Decision variables
model.x = Var(bounds=(0, 10))
model.y = Var(bounds=(0, 10))

# Create disjuncts for alternative process paths
model.path1 = Disjunct()
model.path2 = Disjunct()

# Path 1: High-temperature, low-pressure process
model.path1.temp_constraint = Constraint(expr=model.x >= 8)
model.path1.pressure_constraint = Constraint(expr=model.y <= 3)
model.path1.cost = Var(bounds=(0, 100))
model.path1.cost_def = Constraint(expr=model.path1.cost == 2*model.x + model.y)

# Path 2: Low-temperature, high-pressure process  
model.path2.temp_constraint = Constraint(expr=model.x <= 5)
model.path2.pressure_constraint = Constraint(expr=model.y >= 6)
model.path2.cost = Var(bounds=(0, 100))
model.path2.cost_def = Constraint(expr=model.path2.cost == model.x + 3*model.y)

# Disjunction: exactly one path must be chosen
model.path_choice = Disjunction(expr=[model.path1, model.path2])

# Objective: minimize total cost
model.obj = Objective(
    expr=model.path1.cost + model.path2.cost,
    sense=minimize
)

Indexed Disjuncts and Disjunctions

from pyomo.environ import *
from pyomo.gdp import Disjunct, Disjunction

model = ConcreteModel()

# Sets for indexing
model.UNITS = Set(initialize=[1, 2, 3])
model.MODES = Set(initialize=['low', 'med', 'high'])

# Variables
model.flow = Var(model.UNITS, bounds=(0, 100))
model.operating = Var(model.UNITS, domain=Binary)

# Indexed disjuncts for operating modes
model.mode_disjunct = Disjunct(model.UNITS, model.MODES)

# Define constraints for each mode
for unit in model.UNITS:
    # Low mode: 0-30% capacity
    model.mode_disjunct[unit, 'low'].flow_lower = Constraint(
        expr=model.flow[unit] >= 0
    )
    model.mode_disjunct[unit, 'low'].flow_upper = Constraint(
        expr=model.flow[unit] <= 30
    )
    
    # Medium mode: 30-70% capacity
    model.mode_disjunct[unit, 'med'].flow_lower = Constraint(
        expr=model.flow[unit] >= 30
    )
    model.mode_disjunct[unit, 'med'].flow_upper = Constraint(
        expr=model.flow[unit] <= 70
    )
    
    # High mode: 70-100% capacity
    model.mode_disjunct[unit, 'high'].flow_lower = Constraint(
        expr=model.flow[unit] >= 70
    )
    model.mode_disjunct[unit, 'high'].flow_upper = Constraint(
        expr=model.flow[unit] <= 100
    )

# Each unit must operate in exactly one mode
def mode_disjunction_rule(model, unit):
    return [model.mode_disjunct[unit, mode] for mode in model.MODES]

model.mode_choice = Disjunction(model.UNITS, rule=mode_disjunction_rule)

Hierarchical Disjunctions

from pyomo.environ import *
from pyomo.gdp import Disjunct, Disjunction

model = ConcreteModel()

# Variables
model.x = Var(bounds=(0, 20))
model.y = Var(bounds=(0, 20))

# Top-level choice: Process A or Process B
model.process_A = Disjunct()
model.process_B = Disjunct()

# Within Process A: two sub-options
model.process_A.option_A1 = Disjunct()
model.process_A.option_A2 = Disjunct()

# Process A, Option 1
model.process_A.option_A1.con1 = Constraint(expr=model.x <= 8)
model.process_A.option_A1.con2 = Constraint(expr=model.y >= 5)

# Process A, Option 2
model.process_A.option_A2.con1 = Constraint(expr=model.x >= 12)
model.process_A.option_A2.con2 = Constraint(expr=model.y <= 10)

# Process A must choose one sub-option
model.process_A.sub_choice = Disjunction(
    expr=[model.process_A.option_A1, model.process_A.option_A2]
)

# Process B (single option)
model.process_B.con1 = Constraint(expr=model.x + model.y <= 15)
model.process_B.con2 = Constraint(expr=model.x - model.y >= 2)

# Top-level disjunction
model.main_choice = Disjunction(expr=[model.process_A, model.process_B])

# Objective
model.obj = Objective(expr=model.x + model.y, sense=maximize)

Solving GDP Models

from pyomo.environ import *
from pyomo.gdp import Disjunct, Disjunction
import pyomo.contrib.gdpopt as gdpopt

# Create GDP model (using previous example)
model = create_gdp_model()  # Your GDP model creation function

# Method 1: Use GDP-specific solver
solver = SolverFactory('gdpopt')
results = solver.solve(model, tee=True)

# Method 2: Transform to MILP and solve
from pyomo.gdp import TransformationFactory

# Big-M transformation
bigm = TransformationFactory('gdp.bigm')
bigm.apply_to(model)

# Solve transformed model
milp_solver = SolverFactory('gurobi')  # or 'cplex', 'glpk', etc.
results = milp_solver.solve(model, tee=True)

# Method 3: Hull reformulation
model_hull = model.clone()
hull = TransformationFactory('gdp.hull')
hull.apply_to(model_hull)
results = milp_solver.solve(model_hull, tee=True)

Accessing Solution Information

from pyomo.environ import *
from pyomo.gdp import Disjunct, Disjunction

# After solving GDP model
if results.solver.termination_condition == TerminationCondition.optimal:
    print("Solution found!")
    
    # Check which disjuncts are active
    for disjunct in [model.path1, model.path2]:
        if disjunct.indicator_var.value >= 0.5:  # Binary variable ≈ 1
            print(f"{disjunct.name} is active")
            
            # Access variables within active disjunct
            if hasattr(disjunct, 'cost'):
                print(f"  Cost: {value(disjunct.cost)}")
    
    # Access main variables
    print(f"x = {value(model.x)}")
    print(f"y = {value(model.y)}")
    print(f"Objective = {value(model.obj)}")

Conditional Constraints with GDP

from pyomo.environ import *
from pyomo.gdp import Disjunct, Disjunction

model = ConcreteModel()

# Variables
model.x = Var(bounds=(0, 100))
model.setup_cost = Var(bounds=(0, 1000))
model.operating_cost = Var(bounds=(0, 500))

# Disjunct for "facility not built"
model.not_built = Disjunct()
model.not_built.no_production = Constraint(expr=model.x == 0)
model.not_built.no_setup = Constraint(expr=model.setup_cost == 0)
model.not_built.no_operating = Constraint(expr=model.operating_cost == 0)

# Disjunct for "facility built"
model.built = Disjunct()
model.built.min_production = Constraint(expr=model.x >= 20)  # Minimum viable scale
model.built.setup_cost_def = Constraint(expr=model.setup_cost == 200)
model.built.operating_cost_def = Constraint(expr=model.operating_cost == 5 * model.x)

# Either build or don't build
model.build_decision = Disjunction(expr=[model.not_built, model.built])

# Objective: maximize profit (revenue - costs)
model.revenue = Expression(expr=10 * model.x)
model.total_cost = Expression(expr=model.setup_cost + model.operating_cost)
model.obj = Objective(expr=model.revenue - model.total_cost, sense=maximize)

Install with Tessl CLI

npx tessl i tessl/pypi-pyomo

docs

advanced-extensions.md

core-modeling.md

dae.md

data-management.md

domain-sets.md

gdp.md

index.md

mathematical-functions.md

mpec.md

optimization-interface.md

tile.json