Repository navigation
Duplicable models: record a model to build independent copies (first step towards multi-threading) - #1251
Draft
cprudhom wants to merge 6 commits into
Draft
Duplicable models: record a model to build independent copies (first step towards multi-threading)#1251cprudhom wants to merge 6 commits into
cprudhom wants to merge 6 commits into
Conversation
Contributor
|
This pull request does not currently match the merge queue conditions, so it cannot be queued from here. The box comes back if it matches again. |
- Tuples, HybridTuples and MultivaluedDecisionDiagram can be frozen: once frozen, they cannot be modified anymore and can be safely shared among models solved concurrently. - Tuples.toMatrix() no longer returns null when its soft cache has been cleared by the GC, and supports concurrent calls. - FiniteAutomaton.clone() no longer shares its working buffer with the original and keeps the determinism flag. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
RecordingModelProcessor generates, at compile time, a subclass of a model which journalizes every factory method of IModel. The solver module declares it as an annotation processor (build-time only dependency). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A model created with Model.record(...) journalizes its construction into a ModelSpec (package org.chocosolver.solver.spec): calls to the factory methods, posts, reifications, tasks, hooks, seed, objective and groups. Model.duplicate() replays it into an independent copy, which can be solved in another thread. Model.create(...) is equivalent to new Model(...). - RecordingModel is generated by choco-codegen from AbstractRecordingModel; Constraint, Task and OptionalTask journalize their own operations. - Only top-level operations are journalized; what cannot be journalized is reported by snapshot(). - Sharing policy (Values): immutable values and frozen tuples are shared, arrays and automata are copied, capturing lambdas are rejected. - Custom constraints are built with the new factory IConstraintFactory.custom(name, vars[, data], propagator). - A spec can be rewritten (Variant: seed, settings, search, consistency of allDifferent, table algorithm, extra or removed constraints), given a search (SearchDecl), and its solutions exchanged (SpecSolution). - SharedObjects detects mutable objects shared by two models. - Assertions check that constraints and search strategies only involve variables of their own model. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- ParallelPortfolio.of(model, n): the recorded model is the first worker, plus n-1 copies built concurrently; of(model, variants) adds one copy per variant. - ParallelPortfolio.of(spec, n) and of(spec, variants) build all the workers from a ModelSpec. - getBestSolution() expresses the solution with the variables of the recorded model; getBestSpecSolution() with the identifiers of the spec. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- RegParser.newModel(name, settings) can be overridden to build recorded models; RegParser.getModels() returns all the models built. - The FlatZinc parser builds BoolSumEq0Reif/BoolSumLeq0Reif with Model.custom, so that these models can be duplicated. - Spec tests and benches (group "spec", added to the CI): equivalence of plain, recorded and replayed models on the instances of the repository, portfolio and recording overhead benches (etc/spec-bench.sh, one JVM per instance). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
cprudhom
force-pushed
the
feat/model-duplicate
branch
from
October 1, 2026 15:58
a3c5708 to
39a25ef
Compare
cprudhom
marked this pull request as draft
October 1, 2026 16:26
The equivalence test ran on the 331 instances of the test resources with 1000 nodes per resolution: about 36 min on the CI, and 3 instances exceeded the time-out. Many instances are similar (e.g., variants which only differ by search annotations, ignored by the test). - spec-instances.txt lists one instance per family of problems (the fastest one): 175 instances; -Dspec.dir=<dir> still runs a whole directory. - The node limit of each resolution defaults to 100 (-Dspec.nodes). - Time-out per instance: 120 s. Locally: 49 s instead of 614 s.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR makes a
Modelduplicable: its construction can be recorded and replayed into independent copies, each of which can be built and solved in its own thread.It is a first step towards making Choco exploit multi-threaded architectures more easily. Today, a
ParallelPortfolioneeds the user (or the parsers) to build the same model n times. With this PR, a model is described once and the copies are built concurrently, from a journal shared by all of them. This is also the groundwork for future work: portfolio-level limits, parsers that parse once instead of n times, or other ways of running models in parallel.The guiding principle: we duplicate a model, not its resolution settings. The search, limits and monitors set on
model.getSolver()are not copied.What changes
Recording and replay (new package
org.chocosolver.solver.spec)Model.record(...)creates aRecordingModelthat journalizes its construction into aModelSpec: calls to the factory methods ofIModel, posts and unposts, reifications (reify,reifyWith,implies,impliedBy), tasks, hooks, seed, name, objective and groups.RecordingModelis generated at compile time by a new annotation processor (new modulechoco-codegen, build-time only). New factory methods are therefore covered automatically, and a test checks that every factory over integer and boolean variables is replayed identically.Tuples/HybridTuples/ MDDs, which are frozen first (newfreeze()methods);snapshot()/duplicate()instead of producing a wrong copy.Custom constraints
IConstraintFactory.custom(name, vars, propagator), plus a variant with data:custom(name, vars, data, propagator), e.g.model.custom("atMostK", x, 3, PropAtMostK::new). Custom constraints built this way can be duplicated.BoolSumEq0Reif/BoolSumLeq0Reif.Portfolio
ParallelPortfolio.of(model, n): the recorded model is the first worker, plus n-1 copies built concurrently.of(model, variants)adds one copy per variant.of(spec, n)andof(spec, variants)build all the workers from aModelSpec.getBestSolution()returns the solution expressed with the variables of the recorded model.Advanced API
Variantrewrites a spec: seed, settings, search, consistency ofallDifferent, table algorithm, added or removed constraints.SearchDecldeclares a search for each copy, andSpecSolutionexchanges solutions between copies.SharedObjectsis a diagnostic tool that finds mutable objects shared by two models.Other changes
Tuples.toMatrix()no longer returnsnullwhen the GC has cleared its soft cache.FiniteAutomaton.clone()no longer shares its working buffer with the original.RegParser.newModel(...)can be overridden, andRegParser.getModels()returns all the models built by the parser.Validation
Equivalence: the model built directly, the recorded model and two replayed copies must have the same structure (variables, domains, constraints, propagators, in the same order) and the same search (solutions, nodes, fails). The copies must not share mutable objects.
331 instances of the repository: all pass. This is the new test group
spec, added to the CI.597 real instances (XCSP 2024 CSP/COP and MiniZinc), one JVM per instance:
SETCARD), out of scopeAircraftAssemblyLineUnit tests: 1,229 tests in
org.chocosolver.solver.spec, which replay every integer and boolean factory method, then the whole1sgroup (9,715 tests), with no failure.Recording overhead: measured on the same 597 real instances (median of 3 runs after warm-up, 6 JVMs in parallel). The figures below are for the 251 instances whose construction takes at least 100 ms.
Takuzu)duplicate()(replay) / parsingsnapshot()/ parsingWordSquare, +100%). Models made of a very large number of small calls (SAT encodings) also pay for the journal itself.Limitations and next steps
getIbexHandler()andremoveMinisat(), are reported as not journalizable.Settingsmixes modelling and resolution concerns: it should be rationalized in a separate change.🤖 Generated with Claude Code