Source code for discrete_optimization.facility.transformations.to_generic
# Copyright (c) 2026 AIRBUS and its affiliates.
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
"""
Transformation from Facility to GenSchedulingImpl
"""
from discrete_optimization.facility.problem import FacilityProblem, FacilitySolution
from discrete_optimization.generic_tasks_tools.generic_scheduling_impl import (
GenericSchedulingImplProblem,
GenericSchedulingImplSolution,
)
from discrete_optimization.generic_tools.transformation.problem_transformation import (
ProblemTransformation,
)
from discrete_optimization.generic_tools.transformation.transformation_metadata import (
TransformationMetadata,
exact_transformation,
)
[docs]
class FacilityToGenericSchedulingTransformation(
ProblemTransformation[
FacilityProblem,
FacilitySolution,
GenericSchedulingImplProblem,
GenericSchedulingImplSolution,
]
):
"""
Transform Facility to GenericSchedulingImpl.
"""
[docs]
def get_forward_metadata(self) -> TransformationMetadata:
"""Metadata for forward problem transformation (Facility → GenSchedulingImpl).
This direction is EXACT: all constraints can be represented in GenSchedulingImpl.
"""
return exact_transformation(
use_cases=[
"Exact encoding of Facility as scheduling problem",
]
)
[docs]
def transform_problem(
self, source_problem: FacilityProblem
) -> GenericSchedulingImplProblem:
"""Transform FacilityProblem to GenericSchedulingImpl problem.
Args:
source_problem: FacilityProblem problem instance
Returns:
Equivalent GenericSchedulingImpl problem
"""
tasks = source_problem.tasks_list
unary_resource = {f for f in source_problem.unary_resources_list}
non_renewable_resource = {
f"N{f.index}": int(f.capacity) for f in source_problem.facilities
}
resource_consumptions = {t: {0: {"is_a_facility": 1}} for t in tasks}
unary_resource_consumptions_dependent = {}
for t in tasks:
unary_resource_consumptions_dependent[t] = {
0: {n: {} for n in non_renewable_resource}
}
for f in source_problem.facilities:
corresp_capa = f"N{f.index}"
unary_resource_consumptions_dependent[t][0][corresp_capa][f] = int(
t.demand
)
from discrete_optimization.generic_tasks_tools.objectives.allocation_cost import (
AllocationCostComputer,
)
from discrete_optimization.generic_tasks_tools.objectives.unary_resource_used import (
UnaryResourcesUsedComputer,
)
computer_used_facilities = UnaryResourcesUsedComputer(
weight_objective=1,
weight_per_unary_resource={
f: int(f.setup_cost) for f in source_problem.facilities
},
)
allocation_computer = AllocationCostComputer(
weight_objective=1,
cost_allocation_resource_to_task={
t: {
f: int(
source_problem.evaluate_customer_facility(
customer=t, facility=f
)
)
for f in unary_resource
}
for t in tasks
},
)
return GenericSchedulingImplProblem(
horizon=1,
durations_per_mode={n: {0: 0} for n in source_problem.customers},
resource_consumptions=resource_consumptions,
unary_resources=unary_resource,
skills={"is_a_facility"},
non_renewable_resources=non_renewable_resource,
unary_resource_consumptions_dependent=unary_resource_consumptions_dependent,
unary_resources_skills={f: {"is_a_facility": 1} for f in unary_resource},
list_objective_computer=[allocation_computer, computer_used_facilities],
)
[docs]
def back_transform_solution(
self, solution: GenericSchedulingImplSolution, source_problem: FacilityProblem
) -> FacilitySolution:
"""Transform GenericSchedulingImpl solution back to FacilityLocation solution.
Returns:
Equivalent FacilityLocation solution
"""
# Extract bin assignment from start times
# Tasks scheduled at time t are assigned to bin t
facility_for_customer = [-1] * len(source_problem.tasks_list)
for i, item in enumerate(source_problem.tasks_list):
if solution.is_present(item):
allocated = solution.get_task_allocation(item)
if len(allocated) >= 1:
one = list(allocated)[0]
facility_for_customer[i] = (
source_problem.get_index_from_unary_resource(one)
)
return FacilitySolution(
problem=source_problem, facility_for_customers=facility_for_customer
)