Skip to content

Build a MarkovChain incrementally instead of all at once#458

Open
peterrrock2 wants to merge 3 commits into
1.0.0from
feat/builder-pattern-mc
Open

Build a MarkovChain incrementally instead of all at once#458
peterrrock2 wants to merge 3 commits into
1.0.0from
feat/builder-pattern-mc

Conversation

@peterrrock2

Copy link
Copy Markdown
Collaborator

Summary

This PR lets users create a MarkovChain first and configure it piece by piece, rather than
assembling every argument before the chain can exist. Every constructor parameter is now optional,
and the chain can be completed by assigning attributes and calling small helper methods afterwards.

The old all-at-once construction still works unchanged; this only adds the incremental path.

Incremental configuration

MarkovChain.__init__ has been updated to make it so that most argruments are optional. This
allows legacy workflows to still work, but also enables a "Chain-first" paradigm:

chain = MarkovChain(total_steps=1000, rng=2024)

chain.initial_partition = Partition(graph, assignment="2011_PLA_1")
chain.add_updaters({
    "population": Tally("TOT_POP", alias="population"),
    "cut_edges": cut_edges,
})
chain.proposal_fn = ReCom.district_pairs_mst(...)
chain.add_constraint(contiguous)
chain.acceptance_fn = always_accept

Assignment order does not matter. The full configuration is validated by check_valid when
iteration begins, so an incomplete or inconsistent chain is reported at the point it is run rather
than silently misbehaving. Attributes are also locked during iteration to prevent mid-run mutation.

New helper methods

  • add_updater(name, updater) / add_updaters(mapping): register updaters on the chain. They are
    applied to the initial partition immediately if one is set, otherwise as soon as one is assigned,
    and every partition the chain produces inherits them.
  • add_constraint(constraint) / add_constraints(iterable): append to the chain's constraints.
    Each new constraint is checked against the initial partition as soon as it is added, so a starting
    plan that violates one is reported immediately.
  • check_valid(): validate the complete configuration on demand.

While a run is in progress the chain is locked, and these mutators raise a clear error rather than
corrupting an in-flight iteration.

Supporting changes

  • A ConstraintFn type alias (Callable[[Partition], bool]) is introduced and used across the
    constraint and optimization APIs to slim down repeated signatures.
  • The optimizers (gingleator, optimization) and metagraph are updated to the new chain
    surface.
  • The tutorial notebooks are nudged toward the incremental style.

Testing

  • Tests pass locally
  • Added/updated tests
  • Manually tested relevant behavior

tests/test_chain.py gains coverage for building a chain incrementally, adding updaters and
constraints before and after the initial partition is set, deferred validation at iteration time,
and the locking behavior that prevents mutation mid-run.

Verified with make test.

@peterrrock2
peterrrock2 requested a review from chief-dweeb July 19, 2026 20:57
Base automatically changed from feat/recom-namespacing to 1.0.0 July 19, 2026 21:31
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.

1 participant