Search code examples
optaplannertimefold

Replacing default solution cloner


For my planning problem with one real planning entity class I need to declare various PriorityQueues in one of the problem classes.

However, I'm getting following error message:

Exception in thread "main" java.lang.IllegalStateException: The cloneCollectionClass (class java.util.ArrayList) created for originalCollectionClass (class java.util.PriorityQueue) is not assignable to the field's type (class java.util.PriorityQueue).
Maybe consider replacing the default SolutionCloner.

So it seems that PriorityQueue is not cloned as a PriorityQueue by default, but replaced by an ArrayList by the default cloner.

The manual says

When the FieldAccessingSolutionCloner clones one of your collections or maps, it may not recognize the implementation and replace it with ArrayList, LinkedHashSet, TreeSet, LinkedHashMap or TreeMap (whichever is more applicable) . It recognizes most of the common JDK collection and map implementations.

So PriorityQueue is not in the 'most common JDK collection ... implementations.' Fine, it's not even a Collection.

There are three possible solutions to my problem:

  1. Place the PriorityQueues in a custom VariableListener (one PriorityQueue per planning fact, e.g. in a Map), then make sure the VariableListener is triggered properly
  2. Don't bother with PriorityQueues, convert all to ArrayList and just sort the Array (e.g., Arrays.sort(pq.toArray())) as required in my custom VariableListener
  3. Create a custom SolutionCloner as suggested by the error message. I don't really want to write one from scratch, I'd prefer just to extend the custom cloner.

And here are my two questions:

  • Any hints on a preference of the three solutions points above?
  • Where is the custom cloner located and how would I start extending it?

Thank you in advance!


Solution

  • PriorityQueue is a collection - I'd argue that cloning queues is something we should fix in the default cloner directly. Looking at the source code, we even support cloning Deques. That said, before we fix it:

    • Using an ArrayList may be simple enough, assuming the overhead of sorting isn't too much.
    • I wouldn't try implementing cloning yourself, cloning is a hard thing to do properly - but if you have to, ai.timefold.solver.core.api.domain.solution.cloner.SolutionCloner is what you have to implement.

    The variable listener approach seems like the most correct thing to do - but I wonder what it is that you're doing. Having to "declare various PriorityQueues in one of the problem classes" sounds suspicious - problem facts must be immutable, otherwise trouble will ensue.