Spaces:
Sleeping
Sleeping
File size: 4,639 Bytes
177c40c |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 |
"""
Domain model for your optimization problem.
This file defines:
1. Problem facts (@dataclass) - immutable input data
2. Planning entities (@planning_entity) - what the solver assigns
3. Planning solution (@planning_solution) - container for the problem
TODO: Replace this example with your own domain model.
"""
from dataclasses import dataclass, field
from typing import Annotated, Optional, List
from datetime import datetime
from solverforge_legacy.solver import SolverStatus
from solverforge_legacy.solver.domain import (
planning_entity,
planning_solution,
PlanningId,
PlanningVariable,
PlanningEntityCollectionProperty,
ProblemFactCollectionProperty,
ValueRangeProvider,
PlanningScore,
)
from solverforge_legacy.solver.score import HardSoftScore
from .json_serialization import JsonDomainBase
from pydantic import Field
# =============================================================================
# PROBLEM FACTS (immutable input data)
# =============================================================================
@dataclass
class Resource:
"""
A resource that can be assigned to tasks.
TODO: Replace with your own problem fact (e.g., Employee, Room, Vehicle).
"""
name: Annotated[str, PlanningId]
capacity: int = 100
skills: set[str] = field(default_factory=set)
# =============================================================================
# PLANNING ENTITIES (what the solver optimizes)
# =============================================================================
@planning_entity
@dataclass
class Task:
"""
A task to be assigned to a resource.
The `resource` field is the planning variable - the solver will
try different assignments to find the best solution.
TODO: Replace with your own planning entity (e.g., Shift, Lesson, Delivery).
"""
id: Annotated[str, PlanningId]
name: str
duration: int # in minutes
required_skill: str = ""
# This is the planning variable - solver assigns this
resource: Annotated[Resource | None, PlanningVariable] = None
def has_required_skill(self) -> bool:
"""Check if assigned resource has the required skill.
NOTE: We use len(str(...)) instead of boolean check because
required_skill may be a Java String during constraint evaluation.
"""
if self.resource is None:
return False
if len(str(self.required_skill)) == 0:
return True
return str(self.required_skill) in self.resource.skills
# =============================================================================
# PLANNING SOLUTION (container)
# =============================================================================
@planning_solution
@dataclass
class Schedule:
"""
The planning solution containing all problem facts and planning entities.
TODO: Rename to match your domain (e.g., Timetable, RoutePlan, Roster).
"""
resources: Annotated[
list[Resource],
ProblemFactCollectionProperty,
ValueRangeProvider
]
tasks: Annotated[list[Task], PlanningEntityCollectionProperty]
score: Annotated[HardSoftScore | None, PlanningScore] = None
solver_status: SolverStatus = SolverStatus.NOT_SOLVING
# =============================================================================
# PYDANTIC MODELS (for REST API serialization)
# =============================================================================
class ResourceModel(JsonDomainBase):
"""Pydantic model for Resource serialization."""
name: str
capacity: int = 100
skills: List[str] = Field(default_factory=list)
class TaskModel(JsonDomainBase):
"""Pydantic model for Task serialization."""
id: str
name: str
duration: int
required_skill: str = Field(default="", alias="requiredSkill")
resource: Optional[str] = None # Resource name or None
class ConstraintWeightsModel(JsonDomainBase):
"""Pydantic model for constraint weight configuration."""
required_skill: int = Field(default=100, ge=0, le=100)
resource_capacity: int = Field(default=100, ge=0, le=100)
minimize_duration: int = Field(default=50, ge=0, le=100)
balance_load: int = Field(default=50, ge=0, le=100)
class ScheduleModel(JsonDomainBase):
"""Pydantic model for Schedule serialization."""
resources: List[ResourceModel]
tasks: List[TaskModel]
score: Optional[str] = None
solver_status: Optional[str] = Field(default=None, alias="solverStatus")
constraint_weights: Optional[ConstraintWeightsModel] = Field(default=None, alias="constraintWeights")
|