Skip to content

Commit e13b83c

Browse files
committed
feat: Support deep_planning_clone class decorator
1 parent e474d86 commit e13b83c

File tree

2 files changed

+63
-4
lines changed

2 files changed

+63
-4
lines changed

docs/src/modules/ROOT/pages/using-timefold-solver/modeling-planning-problems.adoc

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2659,6 +2659,10 @@ If any of your problem facts needs to be deep cloned for a planning clone,
26592659
for example if the problem fact references a planning entity or the planning solution,
26602660
mark its class with a `@DeepPlanningClone` annotation:
26612661
2662+
[tabs]
2663+
===
2664+
Java::
2665+
+
26622666
[source,java,options="nowrap"]
26632667
----
26642668
@DeepPlanningClone
@@ -2669,13 +2673,42 @@ public class SeatDesignationDependency {
26692673
}
26702674
----
26712675
2676+
Python::
2677+
+
2678+
[source,python,options="nowrap"]
2679+
----
2680+
@deep_planning_clone
2681+
class SeatDesignationDependency:
2682+
leftSeatDesignation: SeatDesignation # planning entity
2683+
rightSeatDesignation: SeatDesignation # planning entity
2684+
----
2685+
====
2686+
26722687
In the example above, because `SeatDesignationDependency` references the planning entity `SeatDesignation`
26732688
(which is deep planning cloned automatically), it should also be deep planning cloned.
26742689
26752690
Alternatively, the `@DeepPlanningClone` annotation also works on a getter method or a field to planning clone it.
26762691
If that property is a `Collection` or a `Map`, it will shallow clone it and deep planning clone
26772692
any element thereof that is an instance of a class that has a `@DeepPlanningClone` annotation.
26782693
2694+
[tabs]
2695+
====
2696+
Java::
2697+
+
2698+
[source,java,options="nowrap"]
2699+
----
2700+
@DeepPlanningClone
2701+
private SeatDesignationDependency seatDesignationDependency;
2702+
----
2703+
2704+
Python::
2705+
+
2706+
[source,python,options="nowrap"]
2707+
----
2708+
seat_designation_dependency: Annotated[SeatDesignationDependency, DeepPlanningClone]
2709+
----
2710+
====
2711+
26792712
[NOTE]
26802713
====
26812714
Values of Java's `enum` and `record` types are never deep-cloned.

python/python-core/src/main/python/domain/_annotations.py

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -653,13 +653,13 @@ def __init__(self, *,
653653

654654
class DeepPlanningClone(JavaAnnotation):
655655
"""
656-
Marks a problem fact class as being required to be deep planning cloned.
657-
Not needed for a `planning_solution` or `planning_entity` because those are automatically deep cloned.
658-
It can also mark an attribute as being required to be deep planning cloned.
656+
Marks an attribute as being required to be deep planning cloned.
659657
This is especially useful for `list` (or `dict`) properties.
660658
Not needed for a `list` (or `dist`) attribute with a generic type of `planning_entity`,
661659
because those are automatically deep cloned.
662660
661+
To annotate a class, use @deep_planning_clone
662+
663663
Notes
664664
-----
665665
If it annotates an attribute returning `list` (or `dict`),
@@ -873,6 +873,32 @@ def constraint_configuration(constraint_configuration_class: Type[Solution_]) ->
873873
out = add_class_annotation(JavaConstraintConfiguration)(constraint_configuration_class)
874874
return out
875875

876+
def deep_planning_clone(entity_class: Type[A] = None) -> Type[A]:
877+
"""
878+
Marks a problem fact class as being required to be deep planning cloned.
879+
Not needed for a `planning_solution` or `planning_entity` because those are automatically deep cloned.
880+
881+
To annotate an attribute, use DeepPlanningClone
882+
883+
Examples
884+
--------
885+
>>> from timefold.solver.domain import deep_planning_clone
886+
>>>
887+
>>> @deep_planning_clone
888+
... @dataclass
889+
... class Timeslot:
890+
... day_of_week: str
891+
... start_time: time
892+
... end_time: time
893+
"""
894+
ensure_init()
895+
from _jpyinterpreter import add_class_annotation
896+
from .._timefold_java_interop import _add_to_compilation_queue
897+
from ai.timefold.solver.core.api.domain.solution.cloner import (
898+
DeepPlanningClone as JavaDeepPlanningClone)
899+
out = add_class_annotation(JavaDeepPlanningClone)(entity_class)
900+
_add_to_compilation_queue(entity_class)
901+
return out
876902

877903
__all__ = ['PlanningId', 'PlanningScore', 'PlanningPin', 'PlanningPinToIndex',
878904
'PlanningVariable', 'PlanningVariableGraphType', 'PlanningListVariable',
@@ -883,4 +909,4 @@ def constraint_configuration(constraint_configuration_class: Type[Solution_]) ->
883909
'PlanningEntityProperty', 'PlanningEntityCollectionProperty',
884910
'ValueRangeProvider', 'DeepPlanningClone', 'ConstraintConfigurationProvider',
885911
'ConstraintWeight',
886-
'planning_entity', 'planning_solution', 'constraint_configuration']
912+
'planning_entity', 'planning_solution', 'constraint_configuration', 'deep_planning_clone']

0 commit comments

Comments
 (0)