Replies: 4 comments 4 replies
-
Hi @zzk0803, thank you for your message and detail. I had a look at the repository you mentioned. What confuses me a little bit is your statement on not being able to use the PlanningListVariable
I am not entirely sure I grasp your problem here. Do I understand it correctly that you want a sequence, but the increments of that sequence must match the pre-set timeslots? As per your questions:
|
Beta Was this translation helpful? Give feedback.
-
Hi TomCools, Thank you again for your thoughtful response — I really appreciate you taking the time to look into this! Before diving into technical solutions, I’d like to clarify why I’m insisting on keeping datetimeGrain (previously called datetimeSlot) as a core planning variable. This design stems from observing player behavior in simulation games like Township: A more realistic interaction pattern is: Players open the game every hour (e.g., every 60 minutes) and batch-schedule upcoming actions, such as: 10:00: Plant wheat, corn, and potatoes (within field capacity limits); Here’s the key nuance: Globally, the execution order is fixed by the natural chronological order of datetimeGrain—the solver must not reorder these, as the player has already committed to acting at specific times. The actual producingDateTime must not be earlier than the start of its assigned datetimeGrain. In short: The overall plan isn’t a single chain, but each factory internally forms a chain—and that chain’s order must strictly follow the chronological order of the datetimeGrains assigned to its arrangements. I previously tried sorting the factory’s arrangement list by datetimeGrain inside a VariableListener before computing completion times, but this caused score corruption—because if the list is a @PlanningListVariable, its order is itself a decision variable, and forcibly reordering it in a listener breaks consistency with the solver’s state. Therefore, datetimeGrain isn’t an optional helper—it’s the foundation of both player intent and resource sequencing, and must remain a planning variable. This brings me to your mention of @CascadingUpdateShadowVariable, which sparked a key question: I understand that @CascadingUpdateShadowVariable predates the newer declarative shadow variables (@ShadowVariable + @ShadowSource). producingDateTime = max(datetimeGrain.start, previous.completedDateTime) Yet the official documentation doesn’t clearly address this pattern. Instead, your reply highlighted @CascadingUpdateShadowVariable, which made me wonder: Does @CascadingUpdateShadowVariable offer capabilities beyond what @ShadowVariable + @PreviousElementShadowVariable can achieve (e.g., automatic cascading propagation, safer incremental updates)? “Each factory must maintain a chain ordered by external datetimeGrain, with completion times cascading forward.” If the declarative approach (@PlanningListVariable + @ShadowVariable + @PreviousElementShadowVariable) is viable, then the critical challenge becomes: Thank you so much for your guidance. These distinctions are crucial for me to model correctly with Timefold! Best regards, |
Beta Was this translation helpful? Give feedback.
-
Can yall stop tagging me
Leeban Ahmed
…On Mon, Sep 29, 2025 at 8:01 PM zzk0803 ***@***.***> wrote:
Thank you again for the detailed suggestion!
However, I realize there’s a subtle but important difference in our
assumptions:
In my use case, the datetimeGrain is not a fixed commitment, but a
planning variable to be optimized.
The player hasn’t decided “I will act at 11:00”; instead, the solver must
choose the best time grain for each action, considering factory queues
、arrangement dependencies(product bom)、and other constrains.
Therefore, pinning the list order (via @PlanningPinToIndex) would prevent
the solver from exploring better time assignments, which is the core of the
optimization.
—
Reply to this email directly, view it on GitHub
<#1837 (reply in thread)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AALMIOZJW3S55J7BYN2N3KD3VHW6ZAVCNFSM6AAAAACHUZ3OQ6VHI2DSMVQWIX3LMV43URDJONRXK43TNFXW4Q3PNVWWK3TUHMYTINJUG44TQNY>
.
You are receiving this because you were mentioned.Message ID:
***@***.***
.com>
|
Beta Was this translation helpful? Give feedback.
-
I’m starting to realize that my problem might be far more difficult and complex than I originally thought — or perhaps it’s actually simple, and I just haven’t managed to break through that final layer of understanding. Until now, I’ve been quietly proud of my little “clever hack”: using I don’t deny the authority of Score corruption! Timefold is magic — but even magic has its fundamental laws. Thankfully, there’s still that “unconventional” approach — the hard-won little trick I finally figured out. It works. It runs. I understand it. It solves my problem. It doesn’t throw errors. So, thank you — truly — to all the maintainers and experts on the front lines of development for taking the time to answer what might seem like a naive or trivial question. I’ve learned something valuable from this exchange. No matter what, I’ll keep following this gem of an open-source project. I haven’t studied anything — not even Spring or Tomcat — with this level of persistence. I think I’ll end the discussion here. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
Hi Timefold team and community,
I’m reaching out for guidance on migrating a non-trivial scheduling model to the newer declarative shadow variable approach. My current implementation relies on a custom
VariableListener
, which works well—but I understand thatVariableListener
may be deprecated in future versions, and the new declarative style doesn’t allow access toScoreDirector
. I’ve tried several approaches without success and would greatly appreciate your advice.Background & Motivation
In the legacy version, I successfully used a
VariableListener
on myProducingArrangement
entity to compute two shadow variables:producingDateTime
(actual start time)completedDateTime
(actual completion time)These depend on two planning variables:
factoryInstance
(which production unit to assign to)datetimeSlot
(which time granule to assign to)The listener maintained an ordered sequence per factory, inserted the arrangement into a
TreeSet
, and propagated updated times across affected arrangements viaScoreDirector.getWorkingSolution()
.However, the new declarative shadow variable mechanism prohibits access to
ScoreDirector
, making this pattern impossible to replicate directly. Given the potential deprecation ofVariableListener
, I’m eager to find a sustainable, forward-compatible solution.Domain & Model Overview
My problem involves joint resource and time scheduling with sequence constraints:
FactoryInstance
: A production unit. Most have a finite queue (e.g., 6 slots) and process jobs in FIFO order; a few support parallel execution.DatetimeSlot
: A time granule derived from a work calendar and slot size (e.g., 30min, 60min).Product
: A problem fact containing BOM and processing duration.ProducingArrangement
: The core planning entity with:factoryInstance
,datetimeSlot
producingDateTime
,completedDateTime
The key challenge: the actual start time of an arrangement depends on both the factory’s queue state and the completion time of prior arrangements in the same factory, which in turn may span multiple
datetimeSlot
s.Why Chain/Sequence Variables Didn’t Work
I initially explored chained variables or
@PlanningListVariable
, but abandoned them because the logical sequence (determined by factory queue order) often diverges from the assignment order ofdatetimeSlot
. For example, an arrangement assigned to an earlier time slot might end up later in the chain due to factory capacity constraints—breaking time consistency.Attempts with Declarative Shadow Variables (and Why They Failed)
✅ Attempt 1: Intermediate variables + basic shadow computation
I tried computing intermediate data from planning variables and deriving shadow times from them.
Problem: When the logical order of arrangements changes (e.g., due to a different
factory
assignment), the underlying planning variables may not change—but the sequence does. This leads to score corruption, as shadow times aren’t updated for arrangements whose relative position changed but whose direct variables didn’t.✅ Attempt 2:
Factory
holds@PlanningListVariable List<Arrangement>
Modeled like a chained list per factory.
Problem: Same as before—the list order doesn’t align with chronological
datetimeSlot
assignments, causing incorrect time propagation.✅ Attempt 3: Introduce
FactoryDatetimeSlot
(Cartesian product)Created a composite entity for each
(factory, datetimeSlot)
pair, holding a@PlanningListVariable List<Arrangement>
.lastCompletedTime
onFactoryDatetimeSlot
(even though the docs discourage deriving shadow vars from mutable lists, it seemed computable).Arrangement
’s shadow variableSupplier
, I recursively looked up the previousFactoryDatetimeSlot
’slastCompletedTime
and set:Problem: Still results in score corruption. I suspect the dependency chain across multiple
FactoryDatetimeSlot
entities isn’t fully propagated during incremental score calculation.Additional Context
Questions for the Team
VariableListener
is indeed being phased out, are there plans for alternative APIs that allow controlled access to working solution state during shadow variable updates?Thank you very much for your time and continued work on this excellent framework. Any insights or suggestions would be deeply appreciated!
Beta Was this translation helpful? Give feedback.
All reactions