11package ai .timefold .solver .core .impl .constructionheuristic ;
22
3- import java .util .ArrayList ;
43import java .util .Objects ;
54import java .util .Optional ;
65
2928import ai .timefold .solver .core .impl .constructionheuristic .placer .PooledEntityPlacerFactory ;
3029import ai .timefold .solver .core .impl .constructionheuristic .placer .QueuedEntityPlacerFactory ;
3130import ai .timefold .solver .core .impl .constructionheuristic .placer .QueuedValuePlacerFactory ;
32- import ai .timefold .solver .core .impl .constructionheuristic .placer .internal .QueuedMultiplePlacerConfig ;
3331import ai .timefold .solver .core .impl .domain .solution .descriptor .SolutionDescriptor ;
3432import ai .timefold .solver .core .impl .domain .variable .descriptor .ListVariableDescriptor ;
3533import ai .timefold .solver .core .impl .heuristic .HeuristicConfigPolicy ;
@@ -87,38 +85,10 @@ public ConstructionHeuristicPhase<Solution_> buildPhase(int phaseIndex, boolean
8785 }
8886
8987 private Optional <EntityPlacerConfig <?>> getValidEntityPlacerConfig () {
90- if (phaseConfig .getEntityPlacerConfigList () == null || phaseConfig .getEntityPlacerConfigList ().isEmpty ()) {
88+ var entityPlacerConfig = phaseConfig .getEntityPlacerConfig ();
89+ if (entityPlacerConfig == null ) {
9190 return Optional .empty ();
9291 }
93-
94- if (phaseConfig .getEntityPlacerConfigList ().size () > 2 ) {
95- throw new IllegalArgumentException (
96- "The Construction Heuristic configuration (%s) only support a maximum of two entity placers."
97- .formatted (phaseConfig ));
98- }
99- if (phaseConfig .getEntityPlacerConfigList ().stream ().anyMatch (PooledEntityPlacerConfig .class ::isInstance )
100- && phaseConfig .getEntityPlacerConfigList ().size () == 2 ) {
101- throw new IllegalArgumentException (
102- "The Construction Heuristic configuration (%s) does not support multiple configurations when using the pooled placer configuration %s."
103- .formatted (phaseConfig , PooledEntityPlacerConfig .class .getSimpleName ()));
104- }
105- if (phaseConfig .getEntityPlacerConfigList ().stream ().map (EntityPlacerConfig ::getClass ).distinct ().count () == 1
106- && phaseConfig .getEntityPlacerConfigList ().size () == 2 ) {
107- var message = "The Construction Heuristic configuration (%s) cannot contain duplicate placer configurations."
108- .formatted (phaseConfig );
109- if (phaseConfig .getEntityPlacerConfigList ().get (0 ) instanceof QueuedEntityPlacerConfig ) {
110- throw new IllegalArgumentException ("""
111- %s
112- Maybe define multiple move selectors if there are more than one basic variables.""" .formatted (message ));
113- }
114- throw new IllegalArgumentException (message );
115- }
116-
117- var entityPlacerConfig = phaseConfig .getEntityPlacerConfigList ().get (0 );
118- if (phaseConfig .getEntityPlacerConfigList ().size () == 2 ) {
119- entityPlacerConfig = new QueuedMultiplePlacerConfig ()
120- .withPlacerConfigList (phaseConfig .getEntityPlacerConfigList ());
121- }
12292 if (phaseConfig .getConstructionHeuristicType () != null ) {
12393 throw new IllegalArgumentException (
12494 "The constructionHeuristicType (%s) must not be configured if the entityPlacerConfig (%s) is explicitly configured."
@@ -130,32 +100,14 @@ private Optional<EntityPlacerConfig<?>> getValidEntityPlacerConfig() {
130100 "The moveSelectorConfigList (%s) cannot be configured if the entityPlacerConfig (%s) is explicitly configured."
131101 .formatted (moveSelectorConfigList , entityPlacerConfig ));
132102 }
133-
134103 return Optional .of (entityPlacerConfig );
135104 }
136105
137- @ SuppressWarnings ("rawtypes" )
138106 private EntityPlacerConfig <?> buildDefaultEntityPlacerConfig (HeuristicConfigPolicy <Solution_ > configPolicy ,
139107 ConstructionHeuristicType constructionHeuristicType ) {
140- var listVariableDescriptor = findValidListVariableDescriptor (configPolicy .getSolutionDescriptor ()).orElse (null );
141- if (configPolicy .getSolutionDescriptor ().hasBothBasicAndListVariables ()) {
142- if (listVariableDescriptor == null ) {
143- throw new IllegalStateException ("Impossible state: the list variable descriptor is null." );
144- }
145- var placerConfigList = new ArrayList <EntityPlacerConfig >();
146- // Generate the default configuration for the list variable
147- placerConfigList .add (buildListVariableQueuedValuePlacerConfig (configPolicy , listVariableDescriptor ));
148- // Generate a single config for the basic variable(s)
149- // When multiple basic variables are defined, a Cartesian product is created
150- placerConfigList .add (buildUnfoldedEntityPlacerConfig (configPolicy , constructionHeuristicType ));
151- return new QueuedMultiplePlacerConfig ().withPlacerConfigList (placerConfigList );
152- } else {
153- if (listVariableDescriptor != null ) {
154- return buildListVariableQueuedValuePlacerConfig (configPolicy , listVariableDescriptor );
155- } else {
156- return buildUnfoldedEntityPlacerConfig (configPolicy , constructionHeuristicType );
157- }
158- }
108+ return findValidListVariableDescriptor (configPolicy .getSolutionDescriptor ())
109+ .map (listVariableDescriptor -> buildListVariableQueuedValuePlacerConfig (configPolicy , listVariableDescriptor ))
110+ .orElseGet (() -> buildUnfoldedEntityPlacerConfig (configPolicy , constructionHeuristicType ));
159111 }
160112
161113 private Optional <ListVariableDescriptor <?>>
@@ -166,6 +118,15 @@ private EntityPlacerConfig<?> buildDefaultEntityPlacerConfig(HeuristicConfigPoli
166118 }
167119 failIfConfigured (phaseConfig .getConstructionHeuristicType (), "constructionHeuristicType" );
168120 failIfConfigured (phaseConfig .getMoveSelectorConfigList (), "moveSelectorConfigList" );
121+ // When an entity has both list and basic variables,
122+ // the CH configuration will require two separate placers to initialize each variable,
123+ // which cannot be deduced automatically by default, since a single placer would be returned
124+ if (listVariableDescriptor .getEntityDescriptor ().hasAnyGenuineBasicVariables ()) {
125+ throw new IllegalArgumentException ("""
126+ The entity (%s) has both basic and list variables and cannot be deduced automatically.
127+ Maybe customize the phase configuration and add separate construction heuristic phases for each variable."""
128+ .formatted (listVariableDescriptor .getEntityDescriptor ().getEntityClass ()));
129+ }
169130 return Optional .of (listVariableDescriptor );
170131 }
171132
0 commit comments