Wrap MCSAT as a Nelson-Oppen theory solver in CDCL(T) architecture#611
Wrap MCSAT as a Nelson-Oppen theory solver in CDCL(T) architecture#611disteph wants to merge 15 commits into
Conversation
Conflicts resolved with a hybrid of both sides: - tests/regress/run_test.sh: keep this branch's explicit --dpllt for the non-mcsat side of /both/ tests (symmetric with --mcsat), because yices' default solver path is heuristically chosen and is not guaranteed to be DPLL(T) for every logic. Also adopt master's new per-mode .mcsat.gold / .dpllt.gold override mechanism so tests that intentionally differ between the two solvers can supply separate gold files. - tests/regress/both/README.md: document the symmetric --mcsat / --dpllt pair and the per-mode gold-override convention in one place.
Supported by Codex/GPT5.5 and Windsurf/Opus4.7
Supported by Codex/GPT5.5 and Windsurf/Opus4.7
Supported by Codex/GPT5.5 and Windsurf/Opus4.7
Supported by Codex/GPT5.5 and Windsurf/Opus4.7
Supported by Codex/GPT5.5 and Windsurf/Opus4.7
b7cefeb to
8c84b94
Compare
Supported by Codex/GPT5.5 and Windsurf/Opus4.7
Review of PR #611 by Windsurf/Opus4.7: MCSAT supplement for CDCL(T)Baseline: SummaryThis PR adds a cube-only MCSAT theory checker as an E-graph satellite inside CDCL(T). The high-level architecture is sound and the implementation is coherent:
I found no soundness blockers. The points below are quality, documentation, and follow-up coverage. What The PR AddsThe main additions are:
Configuration And User-Facing SurfaceThe configuration surface is well-structured:
The new Remaining Notes
Atom Routing And ObservationThe routing design is solid:
The ownership-vs-observation split is the right design choice. It lets simplex keep doing cheap exact linear reasoning while MCSAT checks the harder exact cube. Follow-Up Performance NotesThese are not merge blockers:
Simplex RelaxationThe simplex relaxation layer is a useful optimization:
The implementation correctly falls back to MCSAT-only ownership when no sound relaxation is available. Follow-Up Precision NoteArithmetic ITEs and some opaque arithmetic constructs are relaxed coarsely by abstracting the whole term. This is sound but may lose precision. A future improvement could recursively relax supported substructure inside those terms. Finite-field atoms have no useful simplex relaxation and remain MCSAT-only, which is the correct policy. MCSAT SatelliteThe satellite implementation has the right shape:
The active-cube conflict fallback can produce large clauses on difficult instances. That is a performance/quality concern, not a soundness issue. E-Graph IntegrationThe E-graph integration is clean:
Compatibility Notes
Thread SafetyThe thread-safety policy is coherent:
This relies on the existing Yices invariant that internalized term-table reads during CDCL(T) search are safe while other threads may append under the global lock. The PR does not make that invariant worse; it serializes the new MCSAT mutations that the supplement introduces. A multi-threaded stress test with two independent supplement-enabled contexts would be a useful follow-up, but I do not consider it required for this PR. Error HandlingThe error-handling behavior is sound:
One possible follow-up is to introduce a distinct external error code for the new internalization case that maps to TestsThe API test coverage is strong. It covers:
The SMT2 regression coverage is minimal but meaningful: it exercises a nonlinear arithmetic case through Useful follow-up tests:
Open Questions / Follow-UpsThese do not block merge, but are worth tracking:
VerdictThe PR is mergeable. The main architectural choices are sound:
I found no soundness blockers. The remaining items are performance, documentation, and additional regression coverage that can be handled as follow-ups. |
Summary
This branch extends the CDCL(T) solver so it can use MCSAT as an E-graph satellite for arithmetic and finite-field constraints that are outside the simplex solver’s supported fragment.
The main goal is to let CDCL(T) contexts handle nonlinear arithmetic and finite-field arithmetic by delegating the relevant theory checks to an internal MCSAT satellite, while preserving the existing simplex-based CDCL(T) behavior when the supplement is not enabled.
With this PR, the top-level CDCL(T) and MCSAT solvers are essentially covering the same theories / logics: MCSAT can still be used as the top-level solver, while CDCL(T) can now call on MCSAT internally for the fragments it could not previously cover directly. This motivates the symmetric CLI controls:
--mcsatforces top-level MCSAT, and--dplltforces top-level CDCL(T).This branch adds:
ctx->mcsat.Objectives And Constraints
The immediate objective of this PR is to extend CDCL(T) with supplemental support for theory fragments currently handled by MCSAT, without changing pure MCSAT mode or the default simplex-only CDCL(T) behavior.
Design constraints:
ctx->mcsatkeeps its existing meaning: it is used only for pureCTX_ARCH_MCSAT.A secondary design consideration is compatibility with possible future work where the E-graph could host multiple arithmetic-capable satellites with different cost/expressivity tradeoffs. For example, a future difference-logic satellite could detect cheap IDL/RDL conflicts before simplex, while MCSAT handles nonlinear or finite-field constraints after cheaper solvers have had a chance to run. This PR does not require or implement that broader architecture; it just avoids making that direction harder.
Configuration And User-Facing Behavior
This PR adds CDCL(T)-side support for using MCSAT internally as a supplement.
The SMT2 frontend now supports symmetric top-level solver selection:
--mcsatforces the top-level MCSAT solver.--dplltforces the top-level CDCL(T) solver.This symmetry matters more after this PR because both top-level solvers now cover essentially the same logics: MCSAT covers them directly, while CDCL(T) covers them by combining its existing E-graph/simplex machinery with the supplemental MCSAT satellite.
When CDCL(T) is selected for a logic whose default architecture would normally be MCSAT, the CDCL(T) context can be built with the MCSAT supplement enabled. This lets CDCL(T) delegate nonlinear and finite-field consistency checks to the satellite.
The API configuration also supports explicit control of the supplement. Explicitly disabling the supplement while forcing CDCL(T) for a logic that requires MCSAT support is rejected as an invalid configuration.
Architecture
Separate MCSAT State
The supplemental solver is not stored in
ctx->mcsat.Instead, the CDCL(T) context attaches an MCSAT satellite to the E-graph. The satellite owns its own internal MCSAT context/state. This avoids changing the meaning of existing
ctx->mcsat != NULLchecks elsewhere in the codebase.Pure MCSAT mode still uses the existing
CTX_ARCH_MCSATpath.Cube-Based MCSAT Checking
The satellite registers the atoms and arrangement facts that CDCL(T)/E-graph need it to check. At check time, it builds a cube from the current CDCL(T) assignment:
A, the satellite creates labels for the positive and negative polarity.Ais true, the positive label is assumed.MCSAT is then called on this labeled cube.
This means CDCL(T) remains responsible for Boolean search, while MCSAT checks consistency of the active theory cube.
Atom Ownership And Observation
The branch distinguishes between atom ownership and observation.
For atoms that simplex can handle exactly:
For atoms outside simplex’s supported fragment:
This avoids asking simplex to internalize power products or finite-field constructs that would normally raise unsupported-theory or nonlinearity errors.
Arithmetic Observer Interface
This branch adds an arithmetic observer path in the E-graph.
The observer path lets supplemental arithmetic-capable satellites receive:
The MCSAT satellite uses this path to receive arrangement facts and mappings from E-graph theory variables back to terms. The interface is deliberately narrow: observers receive notifications, and conflicts are reported through their normal
propagate/final_checkcallbacks.Observers are notification-only. They do not report immediate conflicts through the equality/disequality notification return values.
Check Order
Simplex still runs before MCSAT. If simplex finds a conflict, the search can learn that conflict without invoking MCSAT. If simplex does not find a conflict, MCSAT is run to validate the nonlinear/finite-field portion of the active cube.
This keeps the cheaper existing arithmetic solver in front of the more expressive supplemental solver.
Conflict Learning
MCSAT conflicts are converted into CDCL(T) clauses.
The satellite uses labels to relate MCSAT assumptions back to CDCL(T) literals and E-graph explanations. When MCSAT reports unsat, the satellite extracts the relevant labels from the MCSAT conflict/interpolant and expands them to CDCL(T)-level antecedents.
For E-graph arrangement facts, the satellite stores enough information to ask the E-graph for the explanation of an equality/disequality when needed.
If the MCSAT interpolant contains internal Boolean structure that is not one of the public labels, the implementation falls back to a sound active-cube clause. This is less precise than the ideal label-only explanation, but still sound. Debug assertions are present to catch genuinely unexpandable labels.
Model Construction
CDCL(T) model construction still happens first.
After the standard CDCL(T)/simplex model is built, the MCSAT satellite can overlay missing values needed for nonlinear arithmetic or finite-field terms. The overlay does not change the Boolean assignment chosen by CDCL(T), and it does not overwrite values already assigned by the CDCL(T)+simplex model.
The model path is tested with nonlinear arithmetic examples where the value of a nonlinear term must come from the MCSAT side rather than from the simplex relaxation.
Simplex Relaxation Of Nonlinear Arithmetic
This branch also adds a simplex relaxation layer for MCSAT-owned nonlinear arithmetic atoms.
The idea is:
(* x y), the exact atom is still owned by MCSAT.For example, an atom like this:
can be relaxed for simplex as:
where
p_xyis an internal abstraction for(* x y).The relaxation is an over-approximation:
This lets simplex cheaply detect some conflicts involving nonlinear atoms without making simplex responsible for nonlinear arithmetic semantics.
The abstraction cache is canonicalized by normalized term, so equivalent products such as
(* x y)and(* y x)share the same abstraction.Relaxation abstraction terms are internal implementation details and are not exported as user-visible model values.
Thread Safety
Full top-level MCSAT check-sat calls are protected by the global Yices mutex.
This branch adds equivalent protection around the internal MCSAT operations performed by the supplemental satellite, without locking the whole CDCL(T) check-sat path.
The satellite obtains/releases the global Yices mutex around calls into the internal MCSAT engine, including:
To support this safely when the caller already owns the global lock in some paths, the global lock is now initialized as a recursive mutex.
On POSIX this uses
PTHREAD_MUTEX_RECURSIVE. On Windows this delegates toCRITICAL_SECTION, which is already recursive for the owning thread.The CDCL(T) check-sat path itself is not globally locked.
Error Handling
The branch preserves the existing error behavior when the supplement is not configured.
In particular:
The branch also fixes/maintains configuration validation around incompatible top-level solver selections.
Tests
The branch adds and updates tests covering:
--dplltbehavior on nonlinear arithmetic.Validation run locally included:
with
make checkreporting:Notes And Follow-Up Work
Potential follow-ups include:
CTX_ARCH_EGFUNSPLXBVfor some supplement-enabled logics.