-
Notifications
You must be signed in to change notification settings - Fork 219
chore: more specific problem scale #2549
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,8 @@ | ||
| package ai.timefold.solver.core.impl.score.director; | ||
|
|
||
| import java.util.LinkedHashMap; | ||
| import java.util.Objects; | ||
| import java.util.SequencedMap; | ||
| import java.util.function.Consumer; | ||
|
|
||
| import ai.timefold.solver.core.api.solver.ProblemSizeStatistics; | ||
|
|
@@ -28,6 +30,8 @@ | |
|
|
||
| // Negative if not calculated, non-negative if cached | ||
| private long cachedApproximateValueCount = -1L; | ||
| private long @Nullable [][] cachedValueCountByEntityAndVariableOrdinal; | ||
| private long @Nullable [] cachedEntityCountByEntityOrdinal; | ||
| private double cachedProblemScale = -1.0; | ||
|
|
||
| ValueRangeStatistics(ValueRangeManager<Solution_> valueRangeManager, SolutionDescriptor<Solution_> solutionDescriptor, | ||
|
|
@@ -61,6 +65,18 @@ | |
| var approximateValueCount = new MutableLong(); | ||
| var maxValueRangeSize = new MutableLong(0L); | ||
|
|
||
| cachedEntityCountByEntityOrdinal = new long[solutionDescriptor.getEntityDescriptors().size()]; | ||
| cachedValueCountByEntityAndVariableOrdinal = new long[cachedEntityCountByEntityOrdinal.length][]; | ||
| for (var entityDescriptor : solutionDescriptor.getEntityDescriptors()) { | ||
| // TimefoldTestResourceTest in Quarkus has an entity class with a basic variable | ||
| // that was not considered a genuine entity. | ||
| // (i.e. solutionDescriptor.getGenuineEntityDescriptors() did not have it). | ||
| // Are subclass entities that do not add new basic/list variable not considered | ||
| // genuine entities? | ||
| cachedValueCountByEntityAndVariableOrdinal[entityDescriptor.getOrdinal()] = | ||
| new long[entityDescriptor.getMaxVariableOrdinal()]; | ||
| } | ||
|
|
||
| var listVariableDescriptor = solutionDescriptor.getListVariableDescriptor(); | ||
| if (listVariableDescriptor != null) { | ||
| var countOnSolution = | ||
|
|
@@ -69,6 +85,8 @@ | |
| maxValueRangeSize.setValue(countOnSolution); | ||
| if (listVariableDescriptor.canExtractValueRangeFromSolution()) { | ||
| approximateValueCount.add(countOnSolution); | ||
| cachedValueCountByEntityAndVariableOrdinal[listVariableDescriptor.getEntityDescriptor() | ||
| .getOrdinal()][listVariableDescriptor.getOrdinal()] += countOnSolution; | ||
| } | ||
| if (!listVariableDescriptor.allowsUnassignedValues()) { | ||
| // We count every possibly unassigned element in every list variable. | ||
|
|
@@ -81,6 +99,8 @@ | |
| if (basicVariable.canExtractValueRangeFromSolution()) { | ||
| var countOnSolution = valueRangeManager.countOnSolution(basicVariable.getValueRangeDescriptor(), solution); | ||
| approximateValueCount.add(countOnSolution); | ||
| cachedValueCountByEntityAndVariableOrdinal[basicVariable.getEntityDescriptor().getOrdinal()][basicVariable | ||
| .getOrdinal()] += countOnSolution; | ||
| if (maxValueRangeSize.longValue() < countOnSolution) { | ||
| maxValueRangeSize.setValue(countOnSolution); | ||
| } | ||
|
|
@@ -94,6 +114,9 @@ | |
| var entityDescriptor = solutionDescriptor.findEntityDescriptorOrFail(entity.getClass()); | ||
| if (entityDescriptor.isGenuine()) { | ||
| genuineEntityCount.increment(); | ||
| // Should we include immovable entities for the count? | ||
| // Problem scale ignores them. | ||
|
Comment on lines
+117
to
+118
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's a good question. Maybe we log that the entity count includes immovable as well? Not sure. We should definitely log not just "entity count" but "genuine entity count". |
||
| cachedEntityCountByEntityOrdinal[entityDescriptor.getOrdinal()]++; | ||
| var uninitializedVariableCountForEntity = entityDescriptor.countUninitializedVariables(entity); | ||
| if (uninitializedVariableCountForEntity > 0) { | ||
| uninitializedEntityCount.increment(); | ||
|
|
@@ -110,8 +133,11 @@ | |
| for (var genuineVariable : entityDescriptor.getGenuineVariableDescriptorList()) { | ||
| if (genuineVariable instanceof BasicVariableDescriptor<Solution_> basicVariableDescriptor | ||
| && !basicVariableDescriptor.canExtractValueRangeFromSolution()) { | ||
| approximateValueCount | ||
| .add(valueRangeManager.countOnEntity(basicVariableDescriptor.getValueRangeDescriptor(), entity)); | ||
| var rangeValueCount = | ||
| valueRangeManager.countOnEntity(basicVariableDescriptor.getValueRangeDescriptor(), entity); | ||
| approximateValueCount.add(rangeValueCount); | ||
| cachedValueCountByEntityAndVariableOrdinal[entityDescriptor.getOrdinal()][genuineVariable.getOrdinal()] += | ||
| rangeValueCount; | ||
| } | ||
| } | ||
| if (!entityDescriptor.hasAnyListVariables()) { | ||
|
|
@@ -124,8 +150,10 @@ | |
| unassignedValueCount.subtract(countOnEntity); | ||
| } | ||
| if (!listVariableDescriptor.canExtractValueRangeFromSolution()) { | ||
| approximateValueCount | ||
| .add(valueRangeManager.countOnEntity(listVariableDescriptor.getValueRangeDescriptor(), entity)); | ||
| var listValueCount = valueRangeManager.countOnEntity(listVariableDescriptor.getValueRangeDescriptor(), entity); | ||
| approximateValueCount.add(listValueCount); | ||
| cachedValueCountByEntityAndVariableOrdinal[entityDescriptor.getOrdinal()][listVariableDescriptor | ||
| .getOrdinal()] += listValueCount; | ||
| } | ||
| // TODO maybe detect duplicates and elements that are outside the value range | ||
| }); | ||
|
|
@@ -152,10 +180,30 @@ | |
| computeInitializationStatistics(null, false); | ||
| } | ||
| if (cachedProblemSizeStatistics == null) { | ||
| var entityClassToEntityCount = new LinkedHashMap<Class<?>, Long>(); | ||
| var entityClassToVariableToValueCount = new LinkedHashMap<Class<?>, SequencedMap<String, Long>>(); | ||
| for (var entityDescriptor : solutionDescriptor.getGenuineEntityDescriptors()) { | ||
|
Check failure on line 185 in core/src/main/java/ai/timefold/solver/core/impl/score/director/ValueRangeStatistics.java
|
||
| entityClassToEntityCount.put(entityDescriptor.getEntityClass(), | ||
| cachedEntityCountByEntityOrdinal[entityDescriptor.getOrdinal()]); | ||
| var variableToValueCount = new LinkedHashMap<String, Long>(); | ||
| for (var variableDescriptor : entityDescriptor.getBasicVariableDescriptorList()) { | ||
| variableToValueCount.put(variableDescriptor.getVariableName(), | ||
| cachedValueCountByEntityAndVariableOrdinal[entityDescriptor.getOrdinal()][variableDescriptor | ||
| .getOrdinal()]); | ||
| } | ||
| if (entityDescriptor.hasAnyListVariables()) { | ||
| variableToValueCount.put(entityDescriptor.getListVariableDescriptor().getVariableName(), | ||
| cachedValueCountByEntityAndVariableOrdinal[entityDescriptor.getOrdinal()][entityDescriptor | ||
| .getListVariableDescriptor().getOrdinal()]); | ||
| } | ||
| entityClassToVariableToValueCount.put(entityDescriptor.getEntityClass(), variableToValueCount); | ||
| } | ||
| cachedProblemSizeStatistics = new ProblemSizeStatistics( | ||
| solutionDescriptor.getGenuineEntityCount(solution), | ||
| entityClassToEntityCount, | ||
| solutionDescriptor.getGenuineVariableCount(solution), | ||
| cachedApproximateValueCount, | ||
| entityClassToVariableToValueCount, | ||
| cachedProblemScale); | ||
| } | ||
| return cachedProblemSizeStatistics; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -220,6 +220,22 @@ public void solvingStarted(SolverScope<Solution_> solverScope) { | |
| problemSizeStatistics.entityCount(), problemSizeStatistics.variableCount(), | ||
| problemSizeStatistics.approximateValueCount(), | ||
| problemSizeStatistics.approximateProblemScaleAsFormattedString()); | ||
| if (logger.isDebugEnabled()) { | ||
| var genuineEntityClassCountEntries = problemSizeStatistics.genuineEntityClassToEntityCount().entrySet(); | ||
| for (var genuineEntityCountEntry : genuineEntityClassCountEntries) { | ||
| var geninueEntityClass = genuineEntityCountEntry.getKey(); | ||
| logger.debug(" Entity ({}) count: {}", | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| geninueEntityClass.getCanonicalName(), | ||
| genuineEntityCountEntry.getValue()); | ||
| for (var geninueVariableEntry : problemSizeStatistics | ||
| .genuineEntityClassToVariableToValueCount() | ||
| .get(geninueEntityClass).entrySet()) { | ||
| var genuineVariable = geninueVariableEntry.getKey(); | ||
| logger.debug(" Variable ({}) estimated value count: {}", | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe we also want to show the variable's value type? Not sure. |
||
| genuineVariable, geninueVariableEntry.getValue()); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seeing as this is a public API, we should add a backwards-compatible constructor, and deprecate it.
Maybe even a migration script, this one will be easy.