Skip to content

Commit 384611b

Browse files
chore: review comments
1 parent 75967f5 commit 384611b

File tree

14 files changed

+56
-393
lines changed

14 files changed

+56
-393
lines changed

core/src/main/java/ai/timefold/solver/core/impl/domain/variable/declarative/AffectedEntitiesUpdater.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -120,13 +120,13 @@ private void initializeChangeQueue(BitSet changed) {
120120
changed.clear();
121121
}
122122

123-
private boolean updateEntityShadowVariables(GraphNode<Solution_> entityVariable, boolean isVariableLooped) {
123+
private boolean updateEntityShadowVariables(GraphNode<Solution_> entityVariable, boolean isVariableInconsistent) {
124124
var entity = entityVariable.entity();
125125
var shadowVariableReferences = entityVariable.variableReferences();
126-
var loopDescriptor = shadowVariableReferences.get(0).shadowVariablesInconsistentDescriptor();
126+
var inconsistentDescriptor = shadowVariableReferences.get(0).shadowVariablesInconsistentDescriptor();
127127
var anyChanged = false;
128128

129-
if (loopDescriptor != null) {
129+
if (inconsistentDescriptor != null) {
130130
// Do not need to update anyChanged here; the graph already marked
131131
// all nodes whose looped status changed for us
132132
var groupEntities = shadowVariableReferences.get(0).groupEntities();
@@ -136,15 +136,15 @@ private boolean updateEntityShadowVariables(GraphNode<Solution_> entityVariable,
136136
for (var i = 0; i < groupEntityIds.length; i++) {
137137
var groupEntity = groupEntities[i];
138138
var groupEntityId = groupEntityIds[i];
139-
anyChanged |= updateLoopedStatusOfEntity(groupEntity, groupEntityId, loopDescriptor);
139+
anyChanged |= updateLoopedStatusOfEntity(groupEntity, groupEntityId, inconsistentDescriptor);
140140
}
141141
} else {
142-
anyChanged |= updateLoopedStatusOfEntity(entity, entityVariable.entityId(), loopDescriptor);
142+
anyChanged |= updateLoopedStatusOfEntity(entity, entityVariable.entityId(), inconsistentDescriptor);
143143
}
144144
}
145145

146146
for (var shadowVariableReference : shadowVariableReferences) {
147-
anyChanged |= updateShadowVariable(isVariableLooped, shadowVariableReference, entity);
147+
anyChanged |= updateShadowVariable(isVariableInconsistent, shadowVariableReference, entity);
148148
}
149149

150150
return anyChanged;

core/src/main/java/ai/timefold/solver/core/impl/domain/variable/declarative/RootVariableSource.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -399,7 +399,7 @@ private static ParentVariableType determineParentVariableType(Class<?> rootClass
399399
if (getAnnotation(declaringClass, memberName, ShadowVariablesInconsistent.class) != null) {
400400
throw new IllegalArgumentException("""
401401
The source path (%s) starting from root class (%s) accesses a @%s property (%s).
402-
Supplier methods are only called when none of their dependencies are inconsistent,
402+
Supplier methods are only called when all of their dependencies are consistent,
403403
so reading @%s properties are not needed since they are guaranteed to be false
404404
when the supplier is called.
405405
Maybe remove the source path (%s) from the @%s?

core/src/main/java/ai/timefold/solver/core/impl/score/director/AbstractScoreDirector.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ protected void setWorkingSolution(Solution_ workingSolution, Consumer<Object> en
251251
if (entityAndFactVisitor != null) {
252252
solutionDescriptor.visitAllProblemFacts(workingSolution, entityAndFactVisitor);
253253
}
254-
Consumer<Object> entityValidator = scoreDirectorFactory.getEntityValidator(this);
254+
Consumer<Object> entityValidator = entity -> scoreDirectorFactory.validateEntity(this, entity);
255255
entityAndFactVisitor = entityAndFactVisitor == null ? entityValidator : entityAndFactVisitor.andThen(entityValidator);
256256
setWorkingEntityListDirty(workingSolution);
257257
// This visits all the entities.

core/src/main/java/ai/timefold/solver/core/impl/score/director/AbstractScoreDirectorFactory.java

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
package ai.timefold.solver.core.impl.score.director;
22

3-
import java.util.function.Consumer;
4-
53
import ai.timefold.solver.core.api.domain.solution.PlanningSolution;
64
import ai.timefold.solver.core.api.score.Score;
5+
import ai.timefold.solver.core.api.score.director.ScoreDirector;
76
import ai.timefold.solver.core.config.solver.EnvironmentMode;
87
import ai.timefold.solver.core.impl.domain.solution.descriptor.SolutionDescriptor;
8+
import ai.timefold.solver.core.impl.domain.variable.descriptor.BasicVariableDescriptor;
99
import ai.timefold.solver.core.impl.domain.variable.descriptor.ListVariableDescriptor;
1010
import ai.timefold.solver.core.impl.score.constraint.ConstraintMatchPolicy;
1111
import ai.timefold.solver.core.impl.score.definition.ScoreDefinition;
@@ -109,8 +109,25 @@ public void assertScoreFromScratch(Solution_ solution) {
109109
}
110110
}
111111

112-
public Consumer<Object> getEntityValidator(InnerScoreDirector<Solution_, Score_> scoreDirector) {
113-
return new EntityValidator<>(scoreDirector);
112+
public void validateEntity(ScoreDirector<Solution_> scoreDirector, Object entity) {
113+
if (listVariableDescriptor == null) { // Only basic variables.
114+
var entityDescriptor = solutionDescriptor.findEntityDescriptorOrFail(entity.getClass());
115+
if (entityDescriptor.isMovable(scoreDirector.getWorkingSolution(), entity)) {
116+
return;
117+
}
118+
for (var variableDescriptor : entityDescriptor.getGenuineVariableDescriptorList()) {
119+
var basicVariableDescriptor = (BasicVariableDescriptor<Solution_>) variableDescriptor;
120+
if (basicVariableDescriptor.allowsUnassigned()) {
121+
continue;
122+
}
123+
var value = basicVariableDescriptor.getValue(entity);
124+
if (value == null) {
125+
throw new IllegalStateException(
126+
"The entity (%s) has a variable (%s) pinned to null, even though unassigned values are not allowed."
127+
.formatted(entity, basicVariableDescriptor.getVariableName()));
128+
}
129+
}
130+
}
114131
}
115132

116133
}

core/src/main/java/ai/timefold/solver/core/impl/score/director/EntityValidator.java

Lines changed: 0 additions & 183 deletions
This file was deleted.

core/src/main/java/ai/timefold/solver/core/impl/solver/DefaultSolver.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ public void setMonitorTagMap(Map<String, String> monitorTagMap) {
183183
solverScope.setInitialSolution(Objects.requireNonNull(problem, "The problem must not be null."));
184184
solverScope.setSolver(this);
185185
outerSolvingStarted(solverScope);
186+
186187
var restartSolver = true;
187188
while (restartSolver) {
188189
var sample = solveLengthTimer.start();
@@ -223,6 +224,10 @@ public void solvingStarted(SolverScope<Solution_> solverScope) {
223224
var startingSolverCount = solverScope.getStartingSolverCount() + 1;
224225
solverScope.setStartingSolverCount(startingSolverCount);
225226
registerSolverSpecificMetrics();
227+
228+
// Update the best solution, since problem's shadows and score were updated
229+
bestSolutionRecaller.updateBestSolutionAndFireIfInitialized(solverScope);
230+
226231
logger.info("Solving {}: time spent ({}), best score ({}), "
227232
+ "environment mode ({}), move thread count ({}), random ({}).",
228233
(startingSolverCount == 1 ? "started" : "restarted"),

core/src/main/java/ai/timefold/solver/core/preview/api/domain/variable/declarative/ShadowVariablesInconsistent.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
* `a` depends on `b` and `b` depends on `a`).
2525
* </li>
2626
* <li>
27-
* One of its source variables is looped (for example,
27+
* One of its source variables is inconsistent (for example,
2828
* `c` depends on `a`, which depends on `b`, and `b` depends on `a`).
2929
* </li>
3030
* </ul>
@@ -39,7 +39,7 @@
3939
* be updated after the {@link ShadowSources} marked method is called, causing
4040
* score corruption. {@link ShadowSources} marked methods do not need to check
4141
* {@link ShadowVariablesInconsistent} properties, since they are only called if all
42-
* their dependencies are not looped.
42+
* their dependencies are consistent.
4343
*/
4444
@Target({ METHOD, FIELD })
4545
@Retention(RUNTIME)

0 commit comments

Comments
 (0)