jijzepttools.modeling.algorithm.benders_decomposition.generate#
Problem generation functions for Benders decomposition.
This module provides functions to generate master problems, subproblems, and Benders cuts for the decomposition algorithm.
Functions#
|
Generate master problem containing only binary variables and α variable. |
|
Generate subproblem with continuous variables and slack variables using generate_always_feasible_problem_forall. |
|
Generate Phase 1 subproblem with slack-only objective for two-phase Benders. |
|
Generate Phase 1 Pareto-optimal subproblem with relaxed binary variables. |
|
Generate Pareto-optimal subproblem with binary variables relaxed to continuous. |
|
Generate Benders feasibility cut. |
|
Create α variable for master problem objective bound. |
|
Create placeholder for slack variable penalty coefficient. |
Module Contents#
- generate_master_problem(original_problem: jijmodeling.Problem, binary_vars: List[jijmodeling.BinaryVar], alpha_var: jijmodeling.IntegerVar | None = None, cuts: List[jijmodeling.Constraint] = None, phase1_mode: bool = False) jijmodeling.Problem#
Generate master problem containing only binary variables and α variable.
The master problem includes: - Binary variables from original problem - α variable (represents lower bound on continuous part) - Constraints involving only binary variables - Benders cuts (feasibility and optimality cuts)
- Parameters:
original_problem (jm.Problem) – Original mixed-integer problem
binary_vars (tp.List[jm.BinaryVar]) – Binary variables for master problem
alpha_var (jm.DecisionVariable, optional) – α variable for objective bound
cuts (tp.List[jm.Constraint], optional) – Benders cuts to add
phase1_mode (bool) – If True, generate Phase 1 master problem with objective = α only (no binary cost term). Used for two-phase Benders where Phase 1 minimizes slack penalty only.
- Returns:
Master problem
- Return type:
jm.Problem
Example
>>> x = jm.BinaryVar("x", shape=(2,)) >>> y = jm.ContinuousVar("y", shape=(2,), lower_bound=0) >>> problem = jm.Problem("orig") >>> problem += 3*x[0] + 2*x[1] + 4*y[0] + 5*y[1] # objective >>> problem += jm.Constraint("c1", x[0] + x[1] <= 1) # master constraint >>> master = generate_master_problem(problem, [x]) >>> # master problem contains: min α + 3*x[0] + 2*x[1] s.t. x[0] + x[1] <= 1 >>> master_phase1 = generate_master_problem(problem, [x], phase1_mode=True) >>> # Phase 1 master contains: min α s.t. x[0] + x[1] <= 1
- generate_subproblem(original_problem: jijmodeling.Problem, binary_vars: List[jijmodeling.BinaryVar], use_always_feasible: bool = True, slack_M: float = 1000.0, exclude_constraints: List[str] | None = None) jijmodeling.Problem#
Generate subproblem with continuous variables and slack variables using generate_always_feasible_problem_forall.
The subproblem includes: - Continuous variables from original problem - Slack variables to ensure feasibility (added by generate_always_feasible_problem_forall) - Binary variables replaced with placeholders (to be fixed)
- Parameters:
original_problem (jm.Problem) – Original mixed-integer problem
binary_vars (tp.List[jm.BinaryVar]) – Binary variables (to be replaced)
use_always_feasible (bool) – Whether to use always feasible transformation
slack_M (float) – Penalty coefficient for slack variables
exclude_constraints (tp.Optional[tp.List[str]], optional) – List of constraint names to exclude from slack variables. Defaults to None.
- Returns:
Subproblem with continuous variables and slack variables
- Return type:
jm.Problem
Example
>>> x = jm.BinaryVar("x", shape=(2,)) >>> y = jm.ContinuousVar("y", shape=(2,), lower_bound=0, upper_bound=10) >>> problem = jm.Problem("orig") >>> problem += 3*x[0] + 2*x[1] + 4*y[0] + 5*y[1] # objective >>> problem += jm.Constraint("c2", x[0] + y[0] <= 2) # subproblem constraint >>> subproblem = generate_subproblem(problem, [x])
- generate_phase1_subproblem(original_problem: jijmodeling.Problem, binary_vars: List[jijmodeling.BinaryVar], exclude_constraints: List[str] | None = None, slack_bound: int = 100000000) jijmodeling.Problem#
Generate Phase 1 subproblem with slack-only objective for two-phase Benders.
- Phase 1 focuses on feasibility only. The objective is:
minimize bigM * sum(slack_vars)
The original continuous objective (c^T y) is NOT included. This allows generating cuts that are independent of the objective function, making them reusable across different objective functions with the same feasible region.
Note: The bigM penalty coefficient is provided via instance_data[“bigM”] at solve time, not as a parameter to this function.
- Parameters:
original_problem (jm.Problem) – Original mixed-integer problem
binary_vars (tp.List[jm.BinaryVar]) – Binary variables (to be replaced)
exclude_constraints (tp.Optional[tp.List[str]], optional) – List of constraint names to exclude from slack variables. Defaults to None.
slack_bound (int) – Upper bound for slack variables. Defaults to 100000000. This should be large enough to ensure feasibility.
- Returns:
Phase 1 subproblem with slack-only objective
- Return type:
jm.Problem
Example
>>> x = jm.BinaryVar("x", shape=(2,)) >>> y = jm.ContinuousVar("y", shape=(2,), lower_bound=0, upper_bound=10) >>> problem = jm.Problem("orig") >>> problem += 3*x[0] + 2*x[1] + 4*y[0] + 5*y[1] # objective >>> problem += jm.Constraint("c2", x[0] + y[0] <= 2) # subproblem constraint >>> phase1_subproblem = generate_phase1_subproblem(problem, [x]) >>> # Phase 1 objective: minimize bigM * (v + w), NOT including 4*y[0] + 5*y[1]
- generate_phase1_pareto_subproblem(original_problem: jijmodeling.Problem, binary_vars: List[jijmodeling.BinaryVar], exclude_constraints: List[str] | None = None, slack_bound: int = 100000000) jijmodeling.Problem#
Generate Phase 1 Pareto-optimal subproblem with relaxed binary variables.
This is the Phase 1 version of generate_pareto_subproblem(). Binary variables are relaxed to continuous (0 <= x <= 1) to allow fractional core point values for Magnanti-Wong method.
The objective is slack-only: minimize bigM * sum(slack_vars)
Note: The bigM penalty coefficient is provided via instance_data[“bigM”] at solve time.
- Parameters:
original_problem (jm.Problem) – Original mixed-integer problem
binary_vars (tp.List[jm.BinaryVar]) – Binary variables (to be relaxed to continuous)
exclude_constraints (tp.Optional[tp.List[str]], optional) – Constraint names to exclude.
slack_bound (int) – Upper bound for slack variables. Defaults to 100000000.
- Returns:
Phase 1 Pareto subproblem with relaxed binary variables
- Return type:
jm.Problem
- generate_pareto_subproblem(original_problem: jijmodeling.Problem, binary_vars: List[jijmodeling.BinaryVar], use_always_feasible: bool = True, slack_M: float = 1000.0, exclude_constraints: List[str] | None = None) jijmodeling.Problem#
Generate Pareto-optimal subproblem with binary variables relaxed to continuous.
This subproblem is used for generating Pareto-optimal cuts (Magnanti-Wong method). The key difference from generate_subproblem is that binary variables are replaced with continuous variables (0 <= x <= 1) to allow fractional core point values.
- Parameters:
original_problem (jm.Problem) – Original mixed-integer problem
binary_vars (tp.List[jm.BinaryVar]) – Binary variables (to be relaxed to continuous)
use_always_feasible (bool) – Whether to use always feasible transformation
slack_M (float) – Penalty coefficient for slack variables
exclude_constraints (tp.Optional[tp.List[str]], optional) – Constraint names to exclude from slack.
- Returns:
Pareto subproblem with relaxed binary variables (as continuous 0-1)
- Return type:
jm.Problem
- generate_cut_term(constraint: jijmodeling.Constraint, dual_variable: jijmodeling.Placeholder, binary_vars: List[jijmodeling.BinaryVar]) Any#
Generate Benders feasibility cut.
Feasibility cut: Σ λᵢ * gᵢ(x) ≤ 0 where gᵢ(x) is the binary part of constraint i
- Parameters:
constraint (jm.Constraint) – Original coupling constraint
dual_variable (jm.Placeholder) – Dual variable from subproblem
binary_vars (tp.List[jm.BinaryVar]) – Binary variables
- Returns:
expression for feasibility cut
- Return type:
tp.Any
Example
>>> # For constraint: 2*x[0] + 3*y[0] <= 5 >>> # With dual variable λ = 2.0 >>> # Creates feasibility cut: 2.0 * 2*x[0] <= 0 (simplified: 4*x[0] <= 0)
- create_alpha_variable(name: str = 'alpha', lower_bound: float = -100000, upper_bound: float = 100000) jijmodeling.IntegerVar#
Create α variable for master problem objective bound.
- Parameters:
name (str) – Variable name
lower_bound (float) – Lower bound
upper_bound (float) – Upper bound
- Returns:
Alpha variable
- Return type:
jm.IntegerVar
Example
>>> alpha = create_alpha_variable() >>> # Creates integer variable α ∈ [-100000, 100000]
- create_slack_penalty_placeholder(name: str = 'slack_M') jijmodeling.Placeholder#
Create placeholder for slack variable penalty coefficient.
- Parameters:
name (str) – Placeholder name
- Returns:
Slack penalty placeholder
- Return type:
jm.Placeholder
Example
>>> slack_M = create_slack_penalty_placeholder() >>> # Creates placeholder for slack penalty coefficient