Skip to content
This repository was archived by the owner on Jul 17, 2024. It is now read-only.

feat: Improve ScoreAnalysis debug information #102

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions tests/test_solution_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,21 @@ def assert_score_analysis(problem: Solution, score_analysis: ScoreAnalysis):
assert_constraint_analysis(problem, constraint_analysis)


def assert_score_analysis_summary(score_analysis: ScoreAnalysis):
summary = score_analysis.summary
assert "Explanation of score (3):" in summary
assert "Constraint matches:" in summary
assert "3: constraint (Maximize Value) has 3 matches:" in summary
assert "1: justified with" in summary

match = score_analysis.constraint_analyses[0]
match_summary = match.summary
assert "Explanation of score (3):" in match_summary
assert "Constraint matches:" in match_summary
assert "3: constraint (Maximize Value) has 3 matches:" in match_summary
assert "1: justified with" in match_summary


def assert_solution_manager(solution_manager: SolutionManager[Solution]):
problem: Solution = Solution([Entity('A', 1), Entity('B', 1), Entity('C', 1)], [1, 2, 3])
assert problem.score is None
Expand All @@ -140,6 +155,9 @@ def assert_solution_manager(solution_manager: SolutionManager[Solution]):
score_analysis = solution_manager.analyze(problem)
assert_score_analysis(problem, score_analysis)

score_analysis = solution_manager.analyze(problem)
assert_score_analysis_summary(score_analysis)


def test_solver_manager_score_manager():
with SolverManager.create(SolverFactory.create(solver_config)) as solver_manager:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -445,14 +445,19 @@ class ConstraintAnalysis(Generic[Score_]):
but still non-zero constraint weight; non-empty if constraint has matches.
This is a list to simplify access to individual elements,
but it contains no duplicates just like `set` wouldn't.

summary : str
Returns a diagnostic text
that explains part of the score quality through the ConstraintAnalysis API.
"""
_delegate: '_JavaConstraintAnalysis[Score_]'

def __init__(self, delegate: '_JavaConstraintAnalysis[Score_]'):
self._delegate = delegate
delegate.constraintRef()

def __str__(self):
return self.summary

@property
def constraint_ref(self) -> ConstraintRef:
return ConstraintRef(package_name=self._delegate.constraintRef().packageName(),
Expand All @@ -479,6 +484,9 @@ def matches(self) -> list[MatchAnalysis[Score_]]:
def score(self) -> Score_:
return to_python_score(self._delegate.score())

@property
def summary(self) -> str:
return self._delegate.summarize()

class ScoreAnalysis:
"""
Expand Down Expand Up @@ -510,6 +518,16 @@ class ScoreAnalysis:
constraint_analyses : list[ConstraintAnalysis]
Individual ConstraintAnalysis instances that make up this ScoreAnalysis.

summary : str
Returns a diagnostic text
that explains the solution through the `ConstraintMatch` API
to identify which constraints cause that score quality.

In case of an infeasible solution, this can help diagnose the cause of that.
Do not parse the return value, its format may change without warning.
Instead, to provide this information in a UI or a service,
use `constraint_analyses` and convert those into a domain-specific API.

Notes
-----
the constructors of this record are off-limits.
Expand All @@ -520,6 +538,9 @@ class ScoreAnalysis:
def __init__(self, delegate: '_JavaScoreAnalysis'):
self._delegate = delegate

def __str__(self):
return self.summary

@property
def score(self) -> 'Score':
return to_python_score(self._delegate.score())
Expand All @@ -541,6 +562,10 @@ def constraint_analyses(self) -> list[ConstraintAnalysis]:
list['_JavaConstraintAnalysis[Score]'], self._delegate.constraintAnalyses())
]

@property
def summary(self) -> str:
return self._delegate.summarize()


__all__ = ['ScoreExplanation',
'ConstraintRef', 'ConstraintMatch', 'ConstraintMatchTotal',
Expand Down
Loading