Skip to content

Fix #1252: symmetric split point in dichotomic ObjectiveStrategy - #1253

Merged
cprudhom merged 2 commits into
developfrom
fix/1252-objective-strategy-split
Oct 14, 2026
Merged

cprudhom merged 2 commits into
developfrom
fix/1252-objective-strategy-split

Conversation

@cprudhom

Copy link
Copy Markdown
Member

Fixes #1252.

Problem

In ObjectiveStrategy.getDecision(), the DICHOTOMIC target was rounded down whatever the resolution policy. Rounding down is right for MINIMIZE (obj <= target), but it is not the mirror image for MAXIMIZE (obj >= target):

  • on an interval with an even number of values, the maximization branch kept more than half of them;
  • on a two-value interval [a, a+1], the decision obj >= a did not reduce the domain at all.

As a result, minimizing f and maximizing -f did not explore the same tree.

Changes

  • Symmetric rounding: the split point is now rounded towards the bound to improve (down when minimizing, up when maximizing), so mirrored objectives try mirrored cuts.
  • largerHalfFirst parameter: selects the opposite rounding (up when minimizing, down when maximizing), which tries the larger half of the domain first: a more cautious cut with a stronger refutation. This is option B from the issue discussion. Also available through Search.objectiveStrategy(objective, largerHalfFirst).
  • No-op guard: a decision that would not reduce the domain of the objective is no longer returned (getDecision() returns null).
  • Single constructor: ObjectiveStrategy(objective, policy, fraction, largerHalfFirst).
  • fraction replaces int[] coefs: with DICHOTOMIC, the split point is LB + fraction * (UB - LB), with fraction in [0, 1]. It is ignored for BOTTOM_UP and TOP_DOWN.

Behaviour and API changes

  • Search.objectiveStrategy(obj, DICHOTOMIC) tries a different cut when maximizing (rounded up instead of down). Minimization is unchanged by default.
  • The two- and three-argument constructors of ObjectiveStrategy are removed. Former coefficients [a, b] become fraction = b / (a + b), e.g. [1, 1] → 0.5, [1, 2] → 2. / 3.
  • An out-of-range fraction throws IllegalArgumentException.

Notes

  • Math.ceilDiv, suggested in the issue, is only available from Java 18; the rounding is done with Math.floor / Math.ceil on the double split point.
  • With a fraction that has no exact binary representation (0.1, 1. / 3), a split point that is theoretically an integer may be shifted by one. 0.5 is exact over the whole int range. This only affects which cut is tried, not the correctness of the search.
  • The default (largerHalfFirst = false) is the option proposed in the issue. On the instance reported there, the other option explored fewer nodes; which default is best deserves a wider benchmark.

Tests

  • ObjectiveTest#testDichotomicIsSymmetric: minimizing f, maximizing 35 - f and maximizing -f try mirrored cuts, for both values of largerHalfFirst and with negative bounds.
  • ObjectiveTest#testDichotomicFraction: mirrored cuts with fractions 0.75 / 0.25, and rejection of an out-of-range fraction.
  • DCMST now builds the strategy through Search.objectiveStrategy.

ObjectiveTest and DCMST pass (21 tests), as does mvn checkstyle:check on the solver module. The full test suite was not run locally.

@cprudhom cprudhom added this to the 6.0.3 milestone Oct 12, 2026
@cprudhom cprudhom self-assigned this Oct 12, 2026
@mergify

mergify Bot commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

Tick the box to add this pull request to the merge queue (same as @mergifyio queue).

  • Queue this pull request

Comment thread solver/src/main/java/org/chocosolver/solver/objective/ObjectiveStrategy.java Outdated
The dichotomic target was rounded down whatever the resolution policy,
so minimizing f and maximizing -f did not try mirrored cuts, and a
two-value interval gave a no-op decision when maximizing.

The target is now rounded towards the bound to improve (down when
minimizing, up when maximizing). A new parameter, largerHalfFirst,
selects the opposite rounding, which tries the larger half first.
A decision that would not reduce the domain is no longer returned.

ObjectiveStrategy now has a single constructor,
ObjectiveStrategy(objective, policy, fraction, largerHalfFirst).
The int[] coefs parameter is replaced by a double fraction in [0, 1]:
[a, b] becomes b / (a + b).
@cprudhom
cprudhom force-pushed the fix/1252-objective-strategy-split branch from 4c66547 to ba34204 Compare October 13, 2026 16:16
@cprudhom
cprudhom merged commit cb2e210 into develop Oct 14, 2026
30 of 31 checks passed
@cprudhom
cprudhom deleted the fix/1252-objective-strategy-split branch October 14, 2026 15:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants