Skip to content

Conversation

@tias
Copy link
Collaborator

@tias tias commented Sep 10, 2025

this needs more extensive testing...

(but its small test-case works)

also, there was a tricky aspect of using direct encoding (with weight 0 literals) in the objective... henk could you have a look?

@tias tias requested a review from hbierlee September 10, 2025 16:34
Copy link
Contributor

@hbierlee hbierlee left a comment

Choose a reason for hiding this comment

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

Ok, looks good to me!

The transform_objective should become part of the int2bool encode_expr (as it just encodes a linear sum, which is implemented that as part of encoding a linear constraint). This can wait until we need it for other optimization-compatible Boolean solvers.

@tias tias added this to the v.0.9.28 milestone Oct 6, 2025
@hbierlee
Copy link
Contributor

hbierlee commented Nov 3, 2025

@tias

as discussed in the meeting, more important than the above review questions, is that we should consider whether we can add RC2 as a subsolver? Just from a user perspective, should "Pysat" and "RC2" seen side by side, or RC2 as part/subsolver of Pysat?

If subsolver, I can try to refactor that. If not, probably I can still remove some duplication by perhaps subclassing.

@hbierlee
Copy link
Contributor

hbierlee commented Dec 12, 2025

Currently working on this, my idea is:

  • try to make RC2 a sub-solver of PySAT, since this duplicates quite a bit of code
  • see if we can also make it work for decision problems
  • process my above comments

@hbierlee
Copy link
Contributor

@tias since you made the original draft, could you review? Two executive decisions are needed:

  • should we make RC2 a sub-solver of pysat, which would be easy to do, it's just about the user perspective. Since the solver properties (supports objectives, but only once, is incremental but without assumptions,..) are fairly different.. I'm leaning towards "no" at the moment?
  • time limit is not supported, but we should not (maybe cannot) do this in a subprocess since we'd have to parse the text. Instead, I can open a PR in PySAT. In the meantime, I'd say we can leave the time limit as unsupported for this release, and fix it in the next release when that PR is merged.

@hbierlee
Copy link
Contributor

I've added a PR for the missing interrupt feature (required for timeouts) in RC2 pysathq/pysat#211. As above, the question is whether we wait for that to merge this PR, or if we merge RC2 w/o time limits

@hbierlee hbierlee marked this pull request as draft December 18, 2025 14:00
@hbierlee
Copy link
Contributor

Seems good to wait for the upstream PR, since the release is not imminent.

On the other hand, one outstanding TODO is supporting atmost constraints. Working on that now.

@hbierlee
Copy link
Contributor

Ready for review!

I've added native support for AtMostK constraints for RC2 (in case the solver supports it - otherwise these AtMostK are encoded in solve).
This keeps all configuration in solve, like most CPMpy solvers (except e.g. Exact).

One other (arguable) change is that I replaced the RC2 solver arguments which are added explicitly in RC2solve(..) by just **kwargs, which is in line with the other solvers; additional kwargs are for the solver.
I kept the competition defaults if no kwargs are given.
Happy to revert it.

One side note is that RC2 does not allow the user currently to pass arguments for its oracle sub-solver. That might be worth an issue. For instance, currently, even though Cadical has native AtMostK support, we cannot actually enable it when using RC2. This might be worth a PySAT issue.

@hbierlee hbierlee marked this pull request as ready for review December 18, 2025 16:44
@hbierlee
Copy link
Contributor

hbierlee commented Dec 22, 2025

Currently, two small issues preventing merge when implementing timeouts:

Copy link
Collaborator Author

@tias tias left a comment

Choose a reason for hiding this comment

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

the native handling seems to create technical debt, not a fan... to discuss

atmosts = []
if isinstance(cnf, list):
clauses = cnf
elif isinstance(cnf, tuple):
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

euh... what?
so somebody will use 'append_formula' somewhere, and it will give it a tuple, and they will know what this tuple should be, and elsewhere we should know that any tuples is an atmost?

this is is too implicit...

in pysat itself we do not support native atmostk? then I find it weird that we hack it in here, and especially in this way...

Copy link
Contributor

Choose a reason for hiding this comment

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

  • the reason that it's more complicated here with the overriding of append_formula is that we collect the encoding in WCNFPlus and then instantiate the solver in solve from the WCNFPlus. This is so that we can give the solver args there as in most solvers (Exact being the only exception). The implementation may be simplified if we do require the RC2 solve args to be given to init so we don't have this WCNFPlus in-between.
  • in pysat, we do support native atmost k's (https://github.com/CPMpy/cpmpy/blob/master/cpmpy/solvers/pysat.py#L547)
  • Side note: nobody is supposed to use append_formula, it's a method of a "private" field (although I suppose they could get it through the native_model).

if time_limit is not None:
raise NotImplementedError("CPM_rc2: time limit not yet supported")

# hack to support decision problems
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I don't think we should? we can just raise that the solver is not for that?

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm also fine with that, it just seemed a very easy extension + allows us to more easily test RC2


# determine subsolver
default_kwargs = {"solver": "glucose3", "adapt": True, "exhaust": True, "minz": True}
stratified = kwargs.get("stratified", True)
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

why handle stratified differently? Can just be in default_kwargs as true too?

Copy link
Contributor

Choose a reason for hiding this comment

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

Because stratified is not an RC2 argument, you use it by using the different class. I can add it to default_kwargs, but then I need to remove it again when I pass the args to compute

if sub_solver and not Solver(name=sub_solver).supports_atmost():
# encode AtMostK constraints since they are unsupported by sub-solver oracle
atmosts = self.pysat_solver.atms
self.pysat_solver.__class__ = WCNF
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

a line like this is black magic and to be avoided

Copy link
Contributor

Choose a reason for hiding this comment

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

yeah.. there's no official way to convert WCNFPlus to WCNF, and this avoids copying. I've made a change which avoids the downclass though.

@hbierlee
Copy link
Contributor

hbierlee commented Dec 22, 2025

Small comments are resolved, I think there are two knots to cut:

  • Simple one: should we keep in the slight hack to enable solving decision problems with RC2? To me it seems useful, and the hack is theoretically sound
  • See comment near WCNFPlus/AtMostK stuff about the implementation, but I believe the main reason it's made complicated is because of the in-between WCNFPlus object required to keep solve arguments in solve rather than in __init__. I see 3 options:
    1. Keep AtMostK stuff as implemented, so that we support all features and sub-solvers for RC2 (note that pysat does support atmostk).
    2. Pass encoded constraints directly to RC2 without the WCNF[Plus] in-between, this may simplify the implementation, and keeps the AtMostK native feature, however, we have to move (some) solver args to __init__ (like in Exact), and other features like process will be lost as they only work if RC2 is bootstrapped from a formula.
    3. Omit AtMostK support, just always encode everything into a WCNF object, passed to RC2 at solve time (which keeps solve arguments there)

@hbierlee
Copy link
Contributor

hbierlee commented Dec 22, 2025

Fixed the new failing test, and have a pretty good workaround for the time limit issue. [edit; the workaround is the intended method]

I think all that remains are the two points above, if @tias can decide between the options then this PR should still be made mergeable in pretty short order.

The CI is taking a bit long recently, not sure why (although adding RC2 as another solver probably doesn't help of course). [edit: seems that's just 8 more minutes from adding RC2]

@hbierlee hbierlee marked this pull request as ready for review December 23, 2025 12:20
@hbierlee
Copy link
Contributor

hbierlee commented Dec 23, 2025

@tias I still can't add you as reviewer since you're the author of the PR, but if you could make some executive decisions on #729 (comment), and have a look at whatever comments are unresolved, then that'd be great

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.

3 participants