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

Compose 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

is_forward_exact() bool[source]

Check if forward problem transformation is exact.

Returns:

True if problem transformation preserves all information

print_metadata() None[source]

Print human-readable transformation metadata (both directions).

abstractmethod transform_problem(source_problem: P1) P2[source]

Transform source problem to target problem.

This method should be deterministic: same source → same target.

Parameters:

source_problem – The original problem to transform

Returns:

Transformed problem instance

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

Report of all solvers accessible for a problem type.

direct_solvers: list[SolverOption]
print_report() None[source]

Print human-readable report.

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

Represents 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_class: type[SolverDO]
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: object

Edge 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: object

Graph 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
print_summary() None[source]

Print summary of transformation graph.

transformations: dict[tuple[str, str], TransformationEdge]
visualize(output_file: str | None = None) None[source]

Visualize transformation graph.

Parameters:

output_file – Optional output file path (requires matplotlib/graphviz)

class discrete_optimization.generic_tools.transformation.transformation_graph.TransformationPath(problem_sequence: list[str], transformations: list[TransformationEdge], total_weight: float, is_exact: bool)[source]

Bases: object

Path 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: Enum

Strategy 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: object

Documents 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)

Type:

discrete_optimization.generic_tools.transformation.transformation_metadata.LossType

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

Type:

discrete_optimization.generic_tools.transformation.transformation_metadata.LossImpact

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
loss_type: LossType
name: str
reason: str
workaround: str | None = None
class discrete_optimization.generic_tools.transformation.transformation_metadata.LossImpact(*values)[source]

Bases: Enum

Impact 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'
severity() int[source]

Get numeric severity level (0=NONE, 4=CRITICAL).

class discrete_optimization.generic_tools.transformation.transformation_metadata.LossType(*values)[source]

Bases: Enum

Type 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: Enum

Classification 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: object

Complete metadata for a problem transformation.

completeness

Overall classification of transformation completeness

Type:

discrete_optimization.generic_tools.transformation.transformation_metadata.TransformationCompleteness

losses

List of specific information losses

Type:

list[discrete_optimization.generic_tools.transformation.transformation_metadata.InformationLoss]

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.

has_constraint_loss() bool[source]

Check if any constraints are lost.

has_objective_loss() bool[source]

Check if any objectives are lost.

is_exact() bool[source]

Check if transformation is exact (no 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, WarmstartMixin

Solver 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)

source_problem: Problem
target_problem: Problem
transformation: ProblemTransformation
wrapped_solver: SolverDO

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

Compose 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: object

Documents 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)

Type:

discrete_optimization.generic_tools.transformation.transformation_metadata.LossType

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

Type:

discrete_optimization.generic_tools.transformation.transformation_metadata.LossImpact

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
loss_type: LossType
name: str
reason: str
workaround: str | None = None
class discrete_optimization.generic_tools.transformation.LossImpact(*values)[source]

Bases: Enum

Impact 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'
severity() int[source]

Get numeric severity level (0=NONE, 4=CRITICAL).

class discrete_optimization.generic_tools.transformation.LossType(*values)[source]

Bases: Enum

Type 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

is_forward_exact() bool[source]

Check if forward problem transformation is exact.

Returns:

True if problem transformation preserves all information

print_metadata() None[source]

Print human-readable transformation metadata (both directions).

abstractmethod transform_problem(source_problem: P1) P2[source]

Transform source problem to target problem.

This method should be deterministic: same source → same target.

Parameters:

source_problem – The original problem to transform

Returns:

Transformed problem instance

class discrete_optimization.generic_tools.transformation.TransformationCompleteness(*values)[source]

Bases: Enum

Classification 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: object

Complete metadata for a problem transformation.

completeness

Overall classification of transformation completeness

Type:

discrete_optimization.generic_tools.transformation.transformation_metadata.TransformationCompleteness

losses

List of specific information losses

Type:

list[discrete_optimization.generic_tools.transformation.transformation_metadata.InformationLoss]

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.

has_constraint_loss() bool[source]

Check if any constraints are lost.

has_objective_loss() bool[source]

Check if any objectives are lost.

is_exact() bool[source]

Check if transformation is exact (no 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, WarmstartMixin

Solver 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)

source_problem: Problem
target_problem: Problem
transformation: ProblemTransformation
wrapped_solver: SolverDO
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).