discrete_optimization.generic_tools.transformation package
Submodules
discrete_optimization.generic_tools.transformation.composite module
Composite transformations for chaining multiple transformations.
- class discrete_optimization.generic_tools.transformation.composite.CompositeTransformation(transformations: list[ProblemTransformation])[source]
Bases:
ProblemTransformationCompose multiple transformations into a single transformation.
Example
Chain transformations T1: RCPSP → Multiskill and T2: Multiskill → Preemptive
# >>> t1 = RcpspToMultiskillTransformation() # >>> t2 = MultiskillToPreemptiveTransformation() # >>> composite = CompositeTransformation([t1, t2]) # >>> # Now composite: RCPSP → Preemptive
- Back-transformation automatically chains in reverse:
S_preemptive → T2⁻¹ → S_multiskill → T1⁻¹ → S_rcpsp
- back_transform_solution(solution: Solution, source_problem: Problem) Solution[source]
Back-transform through the chain in reverse order.
If we have transformations [T1, T2, T3]: - T1: P1 → P2 - T2: P2 → P3 - T3: P3 → P4
Then back-transform: S4 → T3⁻¹ → S3 → T2⁻¹ → S2 → T1⁻¹ → S1
- Parameters:
solution – Solution in final target problem space
source_problem – Original source problem
- Returns:
Solution in original source problem space
- forward_transform_solution(solution: Solution, target_problem: Problem) Solution | None[source]
Forward-transform through the chain.
Only works if ALL transformations support forward transformation.
- Parameters:
solution – Solution in source problem space
target_problem – Final target problem
- Returns:
Solution in final target problem space, or None if any transformation doesn’t support forward transformation
- is_bidirectional(source_problem: Problem) bool[source]
Check if all transformations are bidirectional.
- Parameters:
source_problem – Source problem to check
- Returns:
True if all transformations support forward transformation
- transform_problem(source_problem: Problem) Problem[source]
Apply all transformations in sequence.
- Parameters:
source_problem – Original problem
- Returns:
Final transformed problem
- transformations: list[ProblemTransformation]
- discrete_optimization.generic_tools.transformation.composite.chain_transformations(*transformations: ProblemTransformation) CompositeTransformation[source]
Chain multiple transformations into a composite.
Example
# >>> t1 = RcpspToMultiskillTransformation() # >>> t2 = MultiskillToPreemptiveTransformation() # >>> t3 = PreemptiveToMultiskillTransformation() # >>> # >>> composite = chain_transformations(t1, t2, t3) # >>> # Equivalent to: RCPSP → Multiskill → Preemptive → Multiskill
- Parameters:
*transformations – Transformations to chain
- Returns:
CompositeTransformation instance
discrete_optimization.generic_tools.transformation.problem_transformation module
Base class for problem transformations.
- class discrete_optimization.generic_tools.transformation.problem_transformation.ProblemTransformation[source]
Bases:
ABC,Generic[P1,S1,P2,S2]Base class for transforming between two problem types.
A transformation defines: 1. How to convert problem P1 → P2 (required) 2. How to convert solutions S2 → S1 (required, for back-transformation) 3. How to convert solutions S1 → S2 (optional, for warmstart support) 4. Metadata documenting what’s lost/gained in EACH direction (recommended)
The transformation is stateless and can be reused across multiple problem instances.
Important: Transformations can be asymmetric! - Forward problem transformation (P1 → P2) may be lossy - Backward solution transformation (S2 → S1) may be exact (or vice versa)
Example
BinPack → SALBP transformation: - Forward (problem): Lossy (incompatibility constraints lost) - Backward (solution): Exact (SALBP solutions map perfectly to BinPack)
# >>> class BinpackToSalbpTransformation(ProblemTransformation): # … def get_forward_metadata(self): # … # Problem transformation: BinPack → SALBP (lossy) # … return lossy_transformation( # … losses=[InformationLoss( # … name=”incompatibility_constraints”, # … loss_type=LossType.CONSTRAINT, # … description=”Item incompatibility constraints”, # … reason=”SALBP has no incompatibility concept”, # … impact=LossImpact.MAJOR # … )] # … ) # … # … def get_backward_metadata(self): # … # Solution transformation: SALBP solution → BinPack solution (exact) # … return exact_transformation( # … use_cases=[“Direct mapping: stations → bins”] # … )
- abstractmethod back_transform_solution(solution: S2, source_problem: P1) S1[source]
Convert solution from target problem back to source problem.
This is REQUIRED for all transformations.
- Parameters:
solution – Solution in target problem space
source_problem – Original problem (to associate with back-transformed solution)
- Returns:
Corresponding solution in source problem space
- forward_transform_solution(solution: S1, target_problem: P2) S2 | None[source]
Convert solution from source problem to target problem.
This is OPTIONAL - only needed for warmstart support. Return None if transformation not supported/meaningful.
- Parameters:
solution – Solution in source problem space
target_problem – Transformed problem (to associate with forward-transformed solution)
- Returns:
Corresponding solution in target problem space, or None if not supported
- get_forward_metadata() TransformationMetadata[source]
Get metadata for problem transformation (P1 → P2).
Documents what information is lost when transforming the PROBLEM.
Override this method to provide detailed information about: - Constraints that cannot be represented in target problem - Objectives that are ignored or approximated - Assumptions made during transformation
- Returns:
TransformationMetadata documenting losses in problem transformation
- Default:
Returns exact_transformation() (no losses documented)
Note
Solutions from the target problem always map back MECHANICALLY via back_transform_solution(), but may not satisfy all constraints from the original source problem if this transformation is lossy.
- Example: BinPack → SALBP loses incompatibility constraints.
Solutions from SALBP solvers map back to BinPack allocations, but may violate incompatibility if that constraint was present.
Always verify solutions in the original problem after solving via transformation!
- has_constraint_loss() bool[source]
Check if transformation loses any constraints
- Returns:
True if some constraints cannot be represented
- has_objective_loss() bool[source]
Check if transformation loses any objectives.
- Returns:
True if some objectives cannot be represented
- is_bidirectional(source_problem: P1) bool[source]
Check if transformation supports both directions.
- Parameters:
source_problem – Problem to check bidirectionality for
- Returns:
True if forward_transform_solution is implemented
discrete_optimization.generic_tools.transformation.solver_discovery module
Solver discovery via transformation graph.
This module uses the transformation graph to discover all solvers accessible for a given problem type, including those available through problem transformations.
- class discrete_optimization.generic_tools.transformation.solver_discovery.SolverAccessibilityReport(problem_type: str, direct_solvers: list[SolverOption] = <factory>, transformation_solvers: list[SolverOption] = <factory>)[source]
Bases:
objectReport of all solvers accessible for a problem type.
- direct_solvers: list[SolverOption]
- problem_type: str
- property total_solvers: int
Total number of solvers.
- transformation_solvers: list[SolverOption]
- class discrete_optimization.generic_tools.transformation.solver_discovery.SolverOption(problem_type: str, solver_class: type[SolverDO], solver_name: str, path: TransformationPath | None = None, is_direct: bool = True)[source]
Bases:
objectRepresents a solver option for a problem.
- property access_method: str
Get access method description.
- is_direct: bool = True
- path: TransformationPath | None = None
- problem_type: str
- solver_name: str
- discrete_optimization.generic_tools.transformation.solver_discovery.build_solver_accessibility_report(problem_type: str, transformation_graph: TransformationGraph | None = None, max_path_length: int = 3) SolverAccessibilityReport[source]
Build comprehensive solver accessibility report.
- Parameters:
problem_type – Problem class name
transformation_graph – Optional pre-built graph (will build if None)
max_path_length – Maximum transformation path length
- Returns:
SolverAccessibilityReport
- discrete_optimization.generic_tools.transformation.solver_discovery.compare_solver_options(problem_type: str, max_path_length: int = 3) None[source]
Compare solver options for a problem (with transformation graph).
- Parameters:
problem_type – Problem class name
max_path_length – Maximum transformation path length
- discrete_optimization.generic_tools.transformation.solver_discovery.discover_solvers_for_problem(problem_type: str) dict[str, list[type[SolverDO]]][source]
Discover all solver classes for a problem type.
- Parameters:
problem_type – Problem class name (e.g., “BinPackProblem”)
- Returns:
Dict mapping solver names to solver classes
- discrete_optimization.generic_tools.transformation.solver_discovery.find_best_transformation_path(source: str, target: str, transformation_graph: TransformationGraph | None = None) TransformationPath | None[source]
Find best transformation path between problems.
“Best” = prefer exact, then lowest weight, then shortest.
- Parameters:
source – Source problem type
target – Target problem type
transformation_graph – Optional pre-built graph
- Returns:
Best TransformationPath or None
discrete_optimization.generic_tools.transformation.transformation_graph module
Transformation graph for discovering solver accessibility.
This module builds a graph of problem transformations and uses it to: - Discover all transformations in the codebase - Find paths between problem types - Calculate transformation quality (based on losses) - List all solvers accessible for a given problem
- class discrete_optimization.generic_tools.transformation.transformation_graph.TransformationEdge(source_problem: str, target_problem: str, transformation_class: type[ProblemTransformation], transformation_instance: ProblemTransformation, forward_exact: bool, backward_exact: bool, max_impact: LossImpact, num_losses: int)[source]
Bases:
objectEdge in transformation graph representing a transformation.
- backward_exact: bool
- forward_exact: bool
- get_weight(strategy: WeightingStrategy = WeightingStrategy.UNIFORM) float[source]
Get edge weight based on strategy.
- Parameters:
strategy – Weighting strategy
- Returns:
Edge weight (lower = better)
- max_impact: LossImpact
- num_losses: int
- source_problem: str
- target_problem: str
- transformation_class: type[ProblemTransformation]
- transformation_instance: ProblemTransformation
- class discrete_optimization.generic_tools.transformation.transformation_graph.TransformationGraph[source]
Bases:
objectGraph of problem transformations.
Builds and analyzes a graph where: - Nodes = problem types (e.g., “BinPackProblem”, “SalbpProblem”) - Edges = transformations with metadata
- add_transformation(transformation_class: type[ProblemTransformation]) None[source]
Add a transformation to the graph.
- Parameters:
transformation_class – Transformation class to add
- find_all_paths(source: str, target: str, max_length: int = 5) list[TransformationPath][source]
Find all paths between two problem types.
- Parameters:
source – Source problem type name
target – Target problem type name
max_length – Maximum path length
- Returns:
List of TransformationPath objects
- find_path(source: str, target: str, strategy: WeightingStrategy = WeightingStrategy.UNIFORM) TransformationPath | None[source]
Find shortest path between two problem types.
- Parameters:
source – Source problem type name
target – Target problem type name
strategy – Weighting strategy for path finding
- Returns:
TransformationPath if path exists, None otherwise
- get_connected_components() list[set[str]][source]
Get weakly connected components.
- Returns:
List of sets, each containing connected problem types
- get_reachable_problems(source: str) set[str][source]
Get all problem types reachable from source.
- Parameters:
source – Source problem type
- Returns:
Set of reachable problem type names
- graph: DiGraph
- transformations: dict[tuple[str, str], TransformationEdge]
- class discrete_optimization.generic_tools.transformation.transformation_graph.TransformationPath(problem_sequence: list[str], transformations: list[TransformationEdge], total_weight: float, is_exact: bool)[source]
Bases:
objectPath through transformation graph.
- is_exact: bool
- problem_sequence: list[str]
- total_weight: float
- transformations: list[TransformationEdge]
- class discrete_optimization.generic_tools.transformation.transformation_graph.WeightingStrategy(*values)[source]
Bases:
EnumStrategy for weighting transformation edges.
- BY_IMPACT = 'by_impact'
- BY_LOSS_COUNT = 'by_loss_count'
- PREFER_EXACT = 'prefer_exact'
- UNIFORM = 'uniform'
- discrete_optimization.generic_tools.transformation.transformation_graph.build_transformation_graph() TransformationGraph[source]
Build transformation graph from codebase.
- Returns:
TransformationGraph with all discovered transformations
- discrete_optimization.generic_tools.transformation.transformation_graph.discover_transformations(base_module: str = 'discrete_optimization') TransformationGraph[source]
Discover all transformations in codebase.
- Parameters:
base_module – Base module to search (default: discrete_optimization)
- Returns:
TransformationGraph with all discovered transformations
discrete_optimization.generic_tools.transformation.transformation_metadata module
Metadata for documenting transformation characteristics and losses.
This module provides classes to explicitly document: - Whether transformations are exact or lossy - What information is lost (constraints, objectives) - Why the loss occurs - Impact on solution quality
- class discrete_optimization.generic_tools.transformation.transformation_metadata.InformationLoss(name: str, loss_type: LossType, description: str, reason: str, impact: LossImpact, workaround: str | None = None)[source]
Bases:
objectDocuments a specific piece of information lost in transformation.
- name
Name of the lost element (e.g., “incompatibility_constraints”)
- Type:
str
- loss_type
Type of loss (constraint, objective, parameter)
- description
Human-readable description of what’s lost
- Type:
str
- reason
Why this information cannot be represented in target
- Type:
str
- workaround
Optional suggestion for handling the loss
- Type:
str | None
- impact
Impact on solution quality
Example
# >>> loss = InformationLoss( # … name=”incompatibility_constraints”, # … loss_type=LossType.CONSTRAINT, # … description=”Item incompatibility constraints (items that cannot be in same bin)”, # … reason=”SALBP has no concept of task incompatibility”, # … impact=LossImpact.MAJOR, # … workaround=”Pre-filter incompatible items or use BinPack→RCPSP with virtual resources” # … )
- description: str
- impact: LossImpact
- name: str
- reason: str
- workaround: str | None = None
- class discrete_optimization.generic_tools.transformation.transformation_metadata.LossImpact(*values)[source]
Bases:
EnumImpact of information loss on solution quality.
NONE: No loss, exact transformation MINOR: Loss unlikely to affect practical solutions MODERATE: May affect solution quality, case-dependent MAJOR: Significant impact, solutions may be infeasible in original problem CRITICAL: Transformation not recommended without manual verification
- CRITICAL = 'critical'
- MAJOR = 'major'
- MINOR = 'minor'
- MODERATE = 'moderate'
- NONE = 'none'
- class discrete_optimization.generic_tools.transformation.transformation_metadata.LossType(*values)[source]
Bases:
EnumType of information that can be lost in transformation.
- CONSTRAINT = 'constraint'
- OBJECTIVE = 'objective'
- PARAMETER = 'parameter'
- STRUCTURE = 'structure'
- class discrete_optimization.generic_tools.transformation.transformation_metadata.TransformationCompleteness(*values)[source]
Bases:
EnumClassification of transformation completeness.
EXACT: Perfect bidirectional mapping, no information loss LOSSY_CONSTRAINTS: Some constraints cannot be represented in target LOSSY_OBJECTIVES: Some objectives cannot be represented in target LOSSY_BOTH: Both constraints and objectives are lost SUBSET: Source is a strict subset of target (generalization)
- EXACT = 'exact'
- LOSSY_BOTH = 'lossy_both'
- LOSSY_CONSTRAINTS = 'lossy_constraints'
- LOSSY_OBJECTIVES = 'lossy_objectives'
- SUBSET = 'subset'
- class discrete_optimization.generic_tools.transformation.transformation_metadata.TransformationMetadata(completeness: TransformationCompleteness, losses: list[InformationLoss] = <factory>, gains: list[str] = <factory>, assumptions: list[str] = <factory>, use_cases: list[str] = <factory>, warnings: list[str] = <factory>)[source]
Bases:
objectComplete metadata for a problem transformation.
- completeness
Overall classification of transformation completeness
- losses
List of specific information losses
- gains
Information added by target (may be ignored)
- Type:
list[str]
- assumptions
Assumptions made during transformation
- Type:
list[str]
- use_cases
Recommended use cases for this transformation
- Type:
list[str]
- warnings
Important warnings for users
- Type:
list[str]
Example
# >>> metadata = TransformationMetadata( # … completeness=TransformationCompleteness.LOSSY_CONSTRAINTS, # … losses=[ # … InformationLoss( # … name=”incompatibility_constraints”, # … loss_type=LossType.CONSTRAINT, # … description=”Item incompatibility constraints”, # … reason=”SALBP has no incompatibility concept”, # … impact=LossImpact.MAJOR # … ) # … ], # … assumptions=[“No item incompatibility constraints”], # … use_cases=[“Pure bin packing without incompatibility”], # … warnings=[“Solutions may violate incompatibility if present”] # … )
- assumptions: list[str]
- completeness: TransformationCompleteness
- gains: list[str]
- get_losses_by_type(loss_type: LossType) list[InformationLoss][source]
Get all losses of a specific type.
- get_max_impact() LossImpact[source]
Get the maximum impact level among all losses.
- losses: list[InformationLoss]
- use_cases: list[str]
- warnings: list[str]
- discrete_optimization.generic_tools.transformation.transformation_metadata.exact_transformation(use_cases: list[str] | None = None) TransformationMetadata[source]
Create metadata for an exact transformation.
- discrete_optimization.generic_tools.transformation.transformation_metadata.lossy_transformation(losses: list[InformationLoss], assumptions: list[str] | None = None, use_cases: list[str] | None = None, warnings: list[str] | None = None) TransformationMetadata[source]
Create metadata for a lossy transformation.
- discrete_optimization.generic_tools.transformation.transformation_metadata.subset_transformation(use_cases: list[str] | None = None, assumptions: list[str] | None = None) TransformationMetadata[source]
Create metadata for a subset transformation (source ⊂ target).
discrete_optimization.generic_tools.transformation.transformation_solver module
Solver wrapper that applies problem transformation before solving.
- class discrete_optimization.generic_tools.transformation.transformation_solver.TransformationSolver(transformation: ProblemTransformation, solver_brick: SubBrick, source_problem: Problem | None = None, target_problem: Problem | None = None, problem: Problem | None = None, params_objective_function: ParamsObjectiveFunction | None = None, **kwargs: Any)[source]
Bases:
SolverDO,WarmstartMixinSolver that applies problem transformation before solving.
This wrapper: 1. Transforms P1 → P2 using transformation (or uses pre-computed target_problem) 2. Instantiates and solves P2 using solver_brick 3. Back-transforms solutions S2 → S1 4. Stores solutions as S1 in ResultStorage
- Example (simple):
# >>> transformation = RcpspToMultiskillTransformation() # >>> solver = TransformationSolver( # … transformation=transformation, # … source_problem=rcpsp_problem, # … solver_brick=SubBrick( # … cls=CPSatMultiskillSolver, # … kwargs={“time_limit”: 60} # … ) # … ) # >>> result = solver.solve() # Returns RcpspSolution!
- Example (with pre-computed target problem):
# >>> transformation = RcpspToMultiskillTransformation() # >>> target_problem = transformation.transform_problem(rcpsp_problem) # >>> solver = TransformationSolver( # … transformation=transformation, # … source_problem=rcpsp_problem, # … target_problem=target_problem, # Already transformed! # … solver_brick=SubBrick(…) # … )
- Example (for use in SequentialMetasolver):
# >>> # TransformationSolver also accepts ‘problem’ arg for SolverDO compatibility # >>> solver = TransformationSolver( # … problem=rcpsp_problem, # Used as source_problem # … transformation=RcpspToMultiskillTransformation(), # … solver_brick=SubBrick(…) # … )
Note: Callbacks currently receive transformed (target) solutions. Future enhancement will back-transform solutions for callbacks.
- init_model(**kwargs: Any) None[source]
Initialize the wrapped solver’s model.
- Parameters:
**kwargs – Arguments passed to wrapped solver’s init_model
- set_warm_start(solution: Solution) None[source]
Set warmstart solution from SOURCE problem.
- Parameters:
solution – Solution in source problem space (S1)
- Raises:
ValueError – If transformation doesn’t support forward transformation or wrapped solver doesn’t support warmstart
- solve(callbacks: list[Callback] | None = None, **kwargs: Any) ResultStorage[source]
Solve by transforming, solving, and back-transforming.
- Parameters:
callbacks – Callbacks (currently receive target problem solutions)
**kwargs – Arguments passed to wrapped solver
- Returns:
ResultStorage containing SOURCE problem solutions (S1)
- transformation: ProblemTransformation
Module contents
Problem transformation framework for discrete optimization.
This module provides tools for transforming problems between different formulations and solving via transformed representations.
- class discrete_optimization.generic_tools.transformation.CompositeTransformation(transformations: list[ProblemTransformation])[source]
Bases:
ProblemTransformationCompose multiple transformations into a single transformation.
Example
Chain transformations T1: RCPSP → Multiskill and T2: Multiskill → Preemptive
# >>> t1 = RcpspToMultiskillTransformation() # >>> t2 = MultiskillToPreemptiveTransformation() # >>> composite = CompositeTransformation([t1, t2]) # >>> # Now composite: RCPSP → Preemptive
- Back-transformation automatically chains in reverse:
S_preemptive → T2⁻¹ → S_multiskill → T1⁻¹ → S_rcpsp
- back_transform_solution(solution: Solution, source_problem: Problem) Solution[source]
Back-transform through the chain in reverse order.
If we have transformations [T1, T2, T3]: - T1: P1 → P2 - T2: P2 → P3 - T3: P3 → P4
Then back-transform: S4 → T3⁻¹ → S3 → T2⁻¹ → S2 → T1⁻¹ → S1
- Parameters:
solution – Solution in final target problem space
source_problem – Original source problem
- Returns:
Solution in original source problem space
- forward_transform_solution(solution: Solution, target_problem: Problem) Solution | None[source]
Forward-transform through the chain.
Only works if ALL transformations support forward transformation.
- Parameters:
solution – Solution in source problem space
target_problem – Final target problem
- Returns:
Solution in final target problem space, or None if any transformation doesn’t support forward transformation
- is_bidirectional(source_problem: Problem) bool[source]
Check if all transformations are bidirectional.
- Parameters:
source_problem – Source problem to check
- Returns:
True if all transformations support forward transformation
- transform_problem(source_problem: Problem) Problem[source]
Apply all transformations in sequence.
- Parameters:
source_problem – Original problem
- Returns:
Final transformed problem
- transformations: list[ProblemTransformation]
- class discrete_optimization.generic_tools.transformation.InformationLoss(name: str, loss_type: LossType, description: str, reason: str, impact: LossImpact, workaround: str | None = None)[source]
Bases:
objectDocuments a specific piece of information lost in transformation.
- name
Name of the lost element (e.g., “incompatibility_constraints”)
- Type:
str
- loss_type
Type of loss (constraint, objective, parameter)
- description
Human-readable description of what’s lost
- Type:
str
- reason
Why this information cannot be represented in target
- Type:
str
- workaround
Optional suggestion for handling the loss
- Type:
str | None
- impact
Impact on solution quality
Example
# >>> loss = InformationLoss( # … name=”incompatibility_constraints”, # … loss_type=LossType.CONSTRAINT, # … description=”Item incompatibility constraints (items that cannot be in same bin)”, # … reason=”SALBP has no concept of task incompatibility”, # … impact=LossImpact.MAJOR, # … workaround=”Pre-filter incompatible items or use BinPack→RCPSP with virtual resources” # … )
- description: str
- impact: LossImpact
- name: str
- reason: str
- workaround: str | None = None
- class discrete_optimization.generic_tools.transformation.LossImpact(*values)[source]
Bases:
EnumImpact of information loss on solution quality.
NONE: No loss, exact transformation MINOR: Loss unlikely to affect practical solutions MODERATE: May affect solution quality, case-dependent MAJOR: Significant impact, solutions may be infeasible in original problem CRITICAL: Transformation not recommended without manual verification
- CRITICAL = 'critical'
- MAJOR = 'major'
- MINOR = 'minor'
- MODERATE = 'moderate'
- NONE = 'none'
- class discrete_optimization.generic_tools.transformation.LossType(*values)[source]
Bases:
EnumType of information that can be lost in transformation.
- CONSTRAINT = 'constraint'
- OBJECTIVE = 'objective'
- PARAMETER = 'parameter'
- STRUCTURE = 'structure'
- class discrete_optimization.generic_tools.transformation.ProblemTransformation[source]
Bases:
ABC,Generic[P1,S1,P2,S2]Base class for transforming between two problem types.
A transformation defines: 1. How to convert problem P1 → P2 (required) 2. How to convert solutions S2 → S1 (required, for back-transformation) 3. How to convert solutions S1 → S2 (optional, for warmstart support) 4. Metadata documenting what’s lost/gained in EACH direction (recommended)
The transformation is stateless and can be reused across multiple problem instances.
Important: Transformations can be asymmetric! - Forward problem transformation (P1 → P2) may be lossy - Backward solution transformation (S2 → S1) may be exact (or vice versa)
Example
BinPack → SALBP transformation: - Forward (problem): Lossy (incompatibility constraints lost) - Backward (solution): Exact (SALBP solutions map perfectly to BinPack)
# >>> class BinpackToSalbpTransformation(ProblemTransformation): # … def get_forward_metadata(self): # … # Problem transformation: BinPack → SALBP (lossy) # … return lossy_transformation( # … losses=[InformationLoss( # … name=”incompatibility_constraints”, # … loss_type=LossType.CONSTRAINT, # … description=”Item incompatibility constraints”, # … reason=”SALBP has no incompatibility concept”, # … impact=LossImpact.MAJOR # … )] # … ) # … # … def get_backward_metadata(self): # … # Solution transformation: SALBP solution → BinPack solution (exact) # … return exact_transformation( # … use_cases=[“Direct mapping: stations → bins”] # … )
- abstractmethod back_transform_solution(solution: S2, source_problem: P1) S1[source]
Convert solution from target problem back to source problem.
This is REQUIRED for all transformations.
- Parameters:
solution – Solution in target problem space
source_problem – Original problem (to associate with back-transformed solution)
- Returns:
Corresponding solution in source problem space
- forward_transform_solution(solution: S1, target_problem: P2) S2 | None[source]
Convert solution from source problem to target problem.
This is OPTIONAL - only needed for warmstart support. Return None if transformation not supported/meaningful.
- Parameters:
solution – Solution in source problem space
target_problem – Transformed problem (to associate with forward-transformed solution)
- Returns:
Corresponding solution in target problem space, or None if not supported
- get_forward_metadata() TransformationMetadata[source]
Get metadata for problem transformation (P1 → P2).
Documents what information is lost when transforming the PROBLEM.
Override this method to provide detailed information about: - Constraints that cannot be represented in target problem - Objectives that are ignored or approximated - Assumptions made during transformation
- Returns:
TransformationMetadata documenting losses in problem transformation
- Default:
Returns exact_transformation() (no losses documented)
Note
Solutions from the target problem always map back MECHANICALLY via back_transform_solution(), but may not satisfy all constraints from the original source problem if this transformation is lossy.
- Example: BinPack → SALBP loses incompatibility constraints.
Solutions from SALBP solvers map back to BinPack allocations, but may violate incompatibility if that constraint was present.
Always verify solutions in the original problem after solving via transformation!
- has_constraint_loss() bool[source]
Check if transformation loses any constraints
- Returns:
True if some constraints cannot be represented
- has_objective_loss() bool[source]
Check if transformation loses any objectives.
- Returns:
True if some objectives cannot be represented
- is_bidirectional(source_problem: P1) bool[source]
Check if transformation supports both directions.
- Parameters:
source_problem – Problem to check bidirectionality for
- Returns:
True if forward_transform_solution is implemented
- class discrete_optimization.generic_tools.transformation.TransformationCompleteness(*values)[source]
Bases:
EnumClassification of transformation completeness.
EXACT: Perfect bidirectional mapping, no information loss LOSSY_CONSTRAINTS: Some constraints cannot be represented in target LOSSY_OBJECTIVES: Some objectives cannot be represented in target LOSSY_BOTH: Both constraints and objectives are lost SUBSET: Source is a strict subset of target (generalization)
- EXACT = 'exact'
- LOSSY_BOTH = 'lossy_both'
- LOSSY_CONSTRAINTS = 'lossy_constraints'
- LOSSY_OBJECTIVES = 'lossy_objectives'
- SUBSET = 'subset'
- class discrete_optimization.generic_tools.transformation.TransformationMetadata(completeness: TransformationCompleteness, losses: list[InformationLoss] = <factory>, gains: list[str] = <factory>, assumptions: list[str] = <factory>, use_cases: list[str] = <factory>, warnings: list[str] = <factory>)[source]
Bases:
objectComplete metadata for a problem transformation.
- completeness
Overall classification of transformation completeness
- losses
List of specific information losses
- gains
Information added by target (may be ignored)
- Type:
list[str]
- assumptions
Assumptions made during transformation
- Type:
list[str]
- use_cases
Recommended use cases for this transformation
- Type:
list[str]
- warnings
Important warnings for users
- Type:
list[str]
Example
# >>> metadata = TransformationMetadata( # … completeness=TransformationCompleteness.LOSSY_CONSTRAINTS, # … losses=[ # … InformationLoss( # … name=”incompatibility_constraints”, # … loss_type=LossType.CONSTRAINT, # … description=”Item incompatibility constraints”, # … reason=”SALBP has no incompatibility concept”, # … impact=LossImpact.MAJOR # … ) # … ], # … assumptions=[“No item incompatibility constraints”], # … use_cases=[“Pure bin packing without incompatibility”], # … warnings=[“Solutions may violate incompatibility if present”] # … )
- assumptions: list[str]
- completeness: TransformationCompleteness
- gains: list[str]
- get_losses_by_type(loss_type: LossType) list[InformationLoss][source]
Get all losses of a specific type.
- get_max_impact() LossImpact[source]
Get the maximum impact level among all losses.
- losses: list[InformationLoss]
- use_cases: list[str]
- warnings: list[str]
- class discrete_optimization.generic_tools.transformation.TransformationSolver(transformation: ProblemTransformation, solver_brick: SubBrick, source_problem: Problem | None = None, target_problem: Problem | None = None, problem: Problem | None = None, params_objective_function: ParamsObjectiveFunction | None = None, **kwargs: Any)[source]
Bases:
SolverDO,WarmstartMixinSolver that applies problem transformation before solving.
This wrapper: 1. Transforms P1 → P2 using transformation (or uses pre-computed target_problem) 2. Instantiates and solves P2 using solver_brick 3. Back-transforms solutions S2 → S1 4. Stores solutions as S1 in ResultStorage
- Example (simple):
# >>> transformation = RcpspToMultiskillTransformation() # >>> solver = TransformationSolver( # … transformation=transformation, # … source_problem=rcpsp_problem, # … solver_brick=SubBrick( # … cls=CPSatMultiskillSolver, # … kwargs={“time_limit”: 60} # … ) # … ) # >>> result = solver.solve() # Returns RcpspSolution!
- Example (with pre-computed target problem):
# >>> transformation = RcpspToMultiskillTransformation() # >>> target_problem = transformation.transform_problem(rcpsp_problem) # >>> solver = TransformationSolver( # … transformation=transformation, # … source_problem=rcpsp_problem, # … target_problem=target_problem, # Already transformed! # … solver_brick=SubBrick(…) # … )
- Example (for use in SequentialMetasolver):
# >>> # TransformationSolver also accepts ‘problem’ arg for SolverDO compatibility # >>> solver = TransformationSolver( # … problem=rcpsp_problem, # Used as source_problem # … transformation=RcpspToMultiskillTransformation(), # … solver_brick=SubBrick(…) # … )
Note: Callbacks currently receive transformed (target) solutions. Future enhancement will back-transform solutions for callbacks.
- init_model(**kwargs: Any) None[source]
Initialize the wrapped solver’s model.
- Parameters:
**kwargs – Arguments passed to wrapped solver’s init_model
- set_warm_start(solution: Solution) None[source]
Set warmstart solution from SOURCE problem.
- Parameters:
solution – Solution in source problem space (S1)
- Raises:
ValueError – If transformation doesn’t support forward transformation or wrapped solver doesn’t support warmstart
- solve(callbacks: list[Callback] | None = None, **kwargs: Any) ResultStorage[source]
Solve by transforming, solving, and back-transforming.
- Parameters:
callbacks – Callbacks (currently receive target problem solutions)
**kwargs – Arguments passed to wrapped solver
- Returns:
ResultStorage containing SOURCE problem solutions (S1)
- transformation: ProblemTransformation
- discrete_optimization.generic_tools.transformation.chain_transformations(*transformations: ProblemTransformation) CompositeTransformation[source]
Chain multiple transformations into a composite.
Example
# >>> t1 = RcpspToMultiskillTransformation() # >>> t2 = MultiskillToPreemptiveTransformation() # >>> t3 = PreemptiveToMultiskillTransformation() # >>> # >>> composite = chain_transformations(t1, t2, t3) # >>> # Equivalent to: RCPSP → Multiskill → Preemptive → Multiskill
- Parameters:
*transformations – Transformations to chain
- Returns:
CompositeTransformation instance
- discrete_optimization.generic_tools.transformation.exact_transformation(use_cases: list[str] | None = None) TransformationMetadata[source]
Create metadata for an exact transformation.
- discrete_optimization.generic_tools.transformation.lossy_transformation(losses: list[InformationLoss], assumptions: list[str] | None = None, use_cases: list[str] | None = None, warnings: list[str] | None = None) TransformationMetadata[source]
Create metadata for a lossy transformation.
- discrete_optimization.generic_tools.transformation.subset_transformation(use_cases: list[str] | None = None, assumptions: list[str] | None = None) TransformationMetadata[source]
Create metadata for a subset transformation (source ⊂ target).