Skip to content

chore: add docs, fail fast on execute for structural flawed solutions, make ScoreAnalysis not fail fact on structural flawed input - #2609

Open
Christopher-Chianelli wants to merge 5 commits into
TimefoldAI:no-loopsfrom
Christopher-Chianelli:chore/structural-score-fail-fasts
Open

chore: add docs, fail fast on execute for structural flawed solutions, make ScoreAnalysis not fail fact on structural flawed input#2609
Christopher-Chianelli wants to merge 5 commits into
TimefoldAI:no-loopsfrom
Christopher-Chianelli:chore/structural-score-fail-fasts

Conversation

@Christopher-Chianelli

Copy link
Copy Markdown
Contributor

For the record, I strongly dislike this change to ScoreAnalysis (returning with structural flaws instead of throwing an exception), and it will force the old implementation of the graph to live forever in some ways.

@triceo triceo left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leaving some comments.

Your comment about keeping the graph forever is noted. It is an implementation downside of a requirement which makes sense for users; we take the pain. Going forward, maybe we refactor the graph algorithm; it is fast becoming marginal, maybe we do not need all the incrementality anymore. Anyway, not for now.

@NullMarked
public interface StructuralFlawAnalysis {
/**
* Return a collection of {@link ai.timefold.solver.core.api.domain.entity.PlanningEntity}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No FQNs please.

* Return a collection of {@link ai.timefold.solver.core.api.domain.entity.PlanningEntity}
* that have inconsistent shadow variables.
*/
Collection<Object> getInconsistentEntities();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we separate this into individual loops?
IMO that would make some sense - it would allow for better debugging of what is looping.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, I would prefer if this returned a SequencedSet - clearer semantics, without having to explain.

* without recalculating the score for performance reasons.
*
* @param move the move to execute
* @throws IllegalArgumentException if the move causes the solution to have a negative {@link Score#structuralScore()}.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO it should by explained somewhere why this exception is there and what the user can do to avoid it.

<Score_ extends Score<Score_>> ScoreAnalysis<Score_> analyze(InnerScore<Score_> state,
Map<ConstraintRef, ConstraintMatchTotal<Score_>> constraintMatchTotalMap, ScoreAnalysisFetchPolicy fetchPolicy);
Map<ConstraintRef, ConstraintMatchTotal<Score_>> constraintMatchTotalMap,
Collection<Object> inconsistentEntities,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since I'd like this to be separated into individual loops, arguably an internal wrapping type would work better.

* this solution must not be modified any further.
* @return the result of the consumer
*/
<Result_> @Nullable Result_ executeTemporarilyHandlingStructurallyFlawedSolutions(Move<Solution_> move,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Javadoc here is missing param structurallyFlawedSolutionConsumer - what should the consumer do? How does it interact with the other consumer? IMO this method is significantly under-documented.

}

/**
* @return true if the last {@link #updateShadowVariables()} was successful, false otherwise.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Define "successful".

Map<ConstraintRef, ConstraintMatchTotal<Score_>> constraintMatchTotalMap, ScoreAnalysisFetchPolicy fetchPolicy);
Map<ConstraintRef, ConstraintMatchTotal<Score_>> constraintMatchTotalMap,
Collection<Object> inconsistentEntities,
@Nullable ScoreDefinition<Score_> scoreDefinition,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why nullable? When? IMO should be documented.

if (innerScore.isStructurallyFlawed()) {
// If there were a fixed dependency loop, the shadow variable session would fail fast before here
throw new IllegalStateException(
"Impossible state: The initial solution passed to the solver is inconsistent even after unassigning involved entities.");

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this actually "impossible state"? If this could be user error, then the "impossible state" prefix should not be there - that is reserved for unforeseen flaws in our own logic.

Comment on lines +94 to +99
[NOTE]
====
The score analysis of structurally flawed solutions has a structural flaw analysis,
which can be used to get inconsistent entities.
Constraint analyses are still available for structurally flawed solutions, but will exclude any matches sourced from an inconsistent entity.
====

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
[NOTE]
====
The score analysis of structurally flawed solutions has a structural flaw analysis,
which can be used to get inconsistent entities.
Constraint analyses are still available for structurally flawed solutions, but will exclude any matches sourced from an inconsistent entity.
====
NOTE: The score analysis of structurally flawed solutions has a structural flaw analysis,
which can be used to get inconsistent entities.
Constraint analyses are still available for structurally flawed solutions, but will exclude any matches sourced from an inconsistent entity.

A more concise alternative where you do not need to remember the closing statement, therefore avoiding bugs from not closing the block.

For these simple blocks, it works well. For the latter blocks of multiple paragraphs, the verbose syntax is much better.

For example, if `c` depends on `a`, and `a` is in a loop with `b`, then `c` is also considered part of the loop.

When a declarative shadow variable is inconsistent, it will be set to `null`.
When a declarative shadow variable is inconsistent, the solver's reaction depends on whether any entity in the model declares a `@ShadowVariablesInconsistent` field.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO there should be a place (where exactly?) in which we discuss:

  • The recommendations around this. (In other words: drop this, get a significantly faster solve.)
  • The consequences. (If you have the annotation, you need to have a constraint based on the field. If you do not have the annotation, you do not need any constraint, because you get a structural score.)

Methinks we should slightly restructure the section; guide people from the start towards the annotation-less approach, and only mention the annotation once in a corner somewhere, with the explicit mention of it being deprecated. (In other words: the structural approach is the default, anything else is a legacy behavior which should only be mentioned, not promoted.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants