PhyNetPy Documentation

Library for the Development and Use of Phylogenetic Network Methods

ModelMove Module v1.0.0

Network topology move operations for MCMC search (add/remove/flip reticulation, SPR).

Author:
Mark Kessler
Last Edit:
4/24/26
Source:
ModelMove.py

Exceptions

exception MoveError(Exception)

This exception is raised whenever there is a fatal error in executing a network move.

Move

class Move(ABC)

Abstract base class for every network proposal move. A :class:`Move` is a stateful object whose ``execute`` method mutates the ``Network`` held by a :class:`Model` and whose ``undo`` method restores the pre-execute state. The contract is: * ``execute(model)`` performs the proposal and records just enough information (either compact deltas in ``same_move_info`` or a full ``copy.deepcopy`` snapshot in ``undo_info``) to later roll back or replay the move. * ``undo(model)`` returns the network to its pre-execute state. Must be cheap and side-effect-free on anything else. * ``same_move(model)`` re-plays the proposal on a clone of the model, used by the parallel MCMC driver. * ``hastings_ratio()`` returns ``q(x | x') / q(x' | x)`` for proper MCMC; see the method-level docstring below for why eve...

Constructor

__init__() -> None

Initialise per-instance bookkeeping fields.

Methods

execute(model: Model) -> Model abstract

Apply the proposal to ``model.network`` in place.

Parameter Type Description
model Model whose ``network`` will be mutated. The move uses ``model.rng`` (a seeded ``numpy.random.Generator``) for every random decision.
Returns: The same ``model`` for chaining convenience. The underlying network is mutated in place; no copy is returned.
undo(model: Model) -> None abstract

Undo the most recent call to :meth:`execute`.

Parameter Type Description
model The same model that was passed to :meth:`execute`. After this call, the network is restored to its pre-execute state.
same_move(model: Model) -> None abstract

Replay the last proposal on an identical (cloned) model. Used by the parallel MCMC driver, which runs multiple chains with shared move sequences. Concrete moves that use deep-copy ``undo_info`` typically implement this as a no-op, since replay is not meaningful without the exact random draws.

Parameter Type Description
model A topologically-equivalent clone of the model passed to :meth:`execute`.
touched_nodes(net: Network) -> 'set[Node] | None'

Network nodes whose incident branches or gammas this move modified. Consumed by scorers that maintain per-network-edge likelihood caches (see :class:`phynetpy._mcmc_gt._GTLikelihoodEngine`): any node returned here -- plus its ancestor path to the root -- will have its cached partials invalidated, everything else is reused across iterations. The default implementation returns ``None`` (fully dirty), which is the safe fallback for moves whose impact on the network structure is hard to localise. Topology-preserving moves (``ChangeNodeHeight``, ``ChangeInheritanceProb``) should override and return the single node whose incident edges changed. Topology-changing moves (``SPR``, ``AddReticulation``, ``RemoveReticulation``, ``RelocateReticulation``, ``ChangeReticSource``, ``ChangeReticDest``, ``FlipReticulation``) generally keep the default ``None``: they replace edges entirely, which the engine's per-edge cache has to rebuild anyway.

Parameter Type Description
net Current network (i.e. the network after :meth:`execute`). Overrides may use it to resolve stored labels to live :class:`Node` references.
Returns: ``None`` for "fully dirty" (the engine rebuilds every cached partial), or a possibly-empty ``set[Node]`` of touched network nodes.
hastings_ratio -> float abstract

Proposal asymmetry correction ``q(x|x') / q(x'|x)``. A proper MCMC sampler uses the Hastings ratio to correct for asymmetric proposal distributions so that the chain converges to the target distribution. **Every concrete move in this module currently returns 1.0**, which would be exact only for strictly symmetric proposals. Three are not: * :class:`SPR` weights candidate regraft edges by ``1 / d ** distance_decay`` of their topological distance from the prune point. The reverse regraft probability depends on the hop-distance distribution *after* the move, which is not cheap to compute on a network. * :class:`ChangeNodeHeight` draws a Gaussian delta whose sigma is a fraction of the per-node *feasible* half-range; the half-range changes after the move, so proposals are mildly asymmetric even though the draw itself is symmetric. * :class:`ChangeInheritanceProb` clamps its Gaussian draw to the open interval ``(eps, 1 - eps)`` (rather than renormalising a truncated Gaussian). This piles proposal mass on the boundaries. For the MPL search this module is driving, the Metropolis-Hastings criterion is applied to a *log-likelihood surface* being maximised under simulated annealing -- not to a stationary distribution we care about matching. The un-corrected acceptance rule is still monotone in the score, so the search is still guaranteed to climb in expectation; the bias affects dwell-time distribution, which matters for Bayesian posteriors but not for finding the optimum. Returning 1.0 is therefore a deliberate SA-scoped shortcut, not an oversight. Bayesian callers should override these ratios before using the moves in an MCMC sampler.

Returns: float: Hastings Ratio. ``1.0`` for symmetric moves and for the SA-approximate path used by ``MPL.search``.

AddReticulation

class AddReticulation(Move)

A move that adds a reticulation to a network. Host edges are split at a random point so their original branch length is preserved across the two halves. The new reticulation edge is given a short length drawn from Exp(mean=0.1*min_host_len) and both parent edges of the new reticulation node receive gamma = 0.5. Uses deep-copy for undo/same_move to guarantee correctness.

Constructor

__init__(max_reticulations: int | None = None) -> None

Create a new ``AddReticulation`` proposal.

Parameter Type Description
max_reticulations Optional hard cap on the number of reticulations. If the current network already has ``max_reticulations`` reticulation nodes, the proposal silently returns without modifying the network. ``None`` disables the cap.

Methods

execute(model: Model) -> Model

Insert a new reticulation into ``model.network``.

Parameter Type Description
model Model whose ``network`` will be mutated in place.
Returns: The same ``model`` (mutated) for chaining convenience.
undo(model: Model) -> None

Undo the most recent call to :meth:`execute`.

Parameter Type Description
model The same model that was passed to :meth:`execute`. After this call, the network is restored to its pre-execute state.
same_move(model: Model) -> None

Replay the last proposal on an identical (cloned) model. Used by the parallel MCMC driver, which runs multiple chains with shared move sequences. Concrete moves that use deep-copy ``undo_info`` typically implement this as a no-op, since replay is not meaningful without the exact random draws.

Parameter Type Description
model A topologically-equivalent clone of the model passed to :meth:`execute`.
hastings_ratio -> float

Proposal asymmetry correction ``q(x|x') / q(x'|x)``. A proper MCMC sampler uses the Hastings ratio to correct for asymmetric proposal distributions so that the chain converges to the target distribution. **Every concrete move in this module currently returns 1.0**, which would be exact only for strictly symmetric proposals. Three are not: * :class:`SPR` weights candidate regraft edges by ``1 / d ** distance_decay`` of their topological distance from the prune point. The reverse regraft probability depends on the hop-distance distribution *after* the move, which is not cheap to compute on a network. * :class:`ChangeNodeHeight` draws a Gaussian delta whose sigma is a fraction of the per-node *feasible* half-range; the half-range changes after the move, so proposals are mildly asymmetric even though the draw itself is symmetric. * :class:`ChangeInheritanceProb` clamps its Gaussian draw to the open interval ``(eps, 1 - eps)`` (rather than renormalising a truncated Gaussian). This piles proposal mass on the boundaries. For the MPL search this module is driving, the Metropolis-Hastings criterion is applied to a *log-likelihood surface* being maximised under simulated annealing -- not to a stationary distribution we care about matching. The un-corrected acceptance rule is still monotone in the score, so the search is still guaranteed to climb in expectation; the bias affects dwell-time distribution, which matters for Bayesian posteriors but not for finding the optimum. Returning 1.0 is therefore a deliberate SA-scoped shortcut, not an oversight. Bayesian callers should override these ratios before using the moves in an MCMC sampler.

Returns: float: Hastings Ratio. ``1.0`` for symmetric moves and for the SA-approximate path used by ``MPL.search``.

RemoveReticulation

class RemoveReticulation(Move)

Remove a reticulation node from the network. Algorithm: 1. Pick a random reticulation node c (in=2, out=1). 2. Randomly choose one of its two parent edges to delete. 3. Remove that parent edge. c now has (in=1, out=1) -- suppress c. 4. The source of the deleted edge may now be degree-2 -- suppress it. Uses deep-copy for undo/same_move to guarantee correctness.

Constructor

__init__() -> None

Initialise per-instance bookkeeping fields.

Methods

execute(model: Model) -> Model

Apply the proposal to ``model.network`` in place.

Parameter Type Description
model Model whose ``network`` will be mutated. The move uses ``model.rng`` (a seeded ``numpy.random.Generator``) for every random decision.
Returns: The same ``model`` for chaining convenience. The underlying network is mutated in place; no copy is returned.
undo(model: Model) -> None

Undo the most recent call to :meth:`execute`.

Parameter Type Description
model The same model that was passed to :meth:`execute`. After this call, the network is restored to its pre-execute state.
same_move(model: Model) -> None

Replay the last proposal on an identical (cloned) model. Used by the parallel MCMC driver, which runs multiple chains with shared move sequences. Concrete moves that use deep-copy ``undo_info`` typically implement this as a no-op, since replay is not meaningful without the exact random draws.

Parameter Type Description
model A topologically-equivalent clone of the model passed to :meth:`execute`.
hastings_ratio -> float

Proposal asymmetry correction ``q(x|x') / q(x'|x)``. A proper MCMC sampler uses the Hastings ratio to correct for asymmetric proposal distributions so that the chain converges to the target distribution. **Every concrete move in this module currently returns 1.0**, which would be exact only for strictly symmetric proposals. Three are not: * :class:`SPR` weights candidate regraft edges by ``1 / d ** distance_decay`` of their topological distance from the prune point. The reverse regraft probability depends on the hop-distance distribution *after* the move, which is not cheap to compute on a network. * :class:`ChangeNodeHeight` draws a Gaussian delta whose sigma is a fraction of the per-node *feasible* half-range; the half-range changes after the move, so proposals are mildly asymmetric even though the draw itself is symmetric. * :class:`ChangeInheritanceProb` clamps its Gaussian draw to the open interval ``(eps, 1 - eps)`` (rather than renormalising a truncated Gaussian). This piles proposal mass on the boundaries. For the MPL search this module is driving, the Metropolis-Hastings criterion is applied to a *log-likelihood surface* being maximised under simulated annealing -- not to a stationary distribution we care about matching. The un-corrected acceptance rule is still monotone in the score, so the search is still guaranteed to climb in expectation; the bias affects dwell-time distribution, which matters for Bayesian posteriors but not for finding the optimum. Returning 1.0 is therefore a deliberate SA-scoped shortcut, not an oversight. Bayesian callers should override these ratios before using the moves in an MCMC sampler.

Returns: float: Hastings Ratio. ``1.0`` for symmetric moves and for the SA-approximate path used by ``MPL.search``.

FlipReticulation

class FlipReticulation(Move)

Flip the direction of one reticulation edge. Picks a random reticulation edge ``z -> c`` and reverses it to ``c -> z``, provided the result is still a valid DAG. After the flip, ``z`` becomes a reticulation (in-degree 2) and ``c`` may lose its reticulation status. Invariants enforced on the candidate edge before the flip: * ``c`` must be a reticulation (so it currently has in-degree 2 -- otherwise the flip wouldn't produce a well-defined reticulation swap). * ``z`` must not be the root (the root cannot become a reticulation). * ``z`` must not already be a reticulation and must have in-degree <= 1 (otherwise the flip would push ``z``'s in-degree to 3, an invalid structure that the acyclicity check does not catch). If the post-flip network is cyclic (rare but possible through other ...

Constructor

__init__() -> None

Initialise with no per-instance parameters (see :class:`Move`).

Methods

execute(model: Model) -> Model

Propose a single reticulation-edge flip on ``model.network``.

Parameter Type Description
model Model whose ``network`` will be mutated in place.
Returns: The same ``model`` (mutated) for chaining convenience.
undo(model: Model) -> None

Undo the most recent call to :meth:`execute`.

Parameter Type Description
model The same model that was passed to :meth:`execute`. After this call, the network is restored to its pre-execute state.
same_move(model: Model) -> None

Replay the last proposal on an identical (cloned) model. Used by the parallel MCMC driver, which runs multiple chains with shared move sequences. Concrete moves that use deep-copy ``undo_info`` typically implement this as a no-op, since replay is not meaningful without the exact random draws.

Parameter Type Description
model A topologically-equivalent clone of the model passed to :meth:`execute`.
hastings_ratio -> float

Proposal asymmetry correction ``q(x|x') / q(x'|x)``. A proper MCMC sampler uses the Hastings ratio to correct for asymmetric proposal distributions so that the chain converges to the target distribution. **Every concrete move in this module currently returns 1.0**, which would be exact only for strictly symmetric proposals. Three are not: * :class:`SPR` weights candidate regraft edges by ``1 / d ** distance_decay`` of their topological distance from the prune point. The reverse regraft probability depends on the hop-distance distribution *after* the move, which is not cheap to compute on a network. * :class:`ChangeNodeHeight` draws a Gaussian delta whose sigma is a fraction of the per-node *feasible* half-range; the half-range changes after the move, so proposals are mildly asymmetric even though the draw itself is symmetric. * :class:`ChangeInheritanceProb` clamps its Gaussian draw to the open interval ``(eps, 1 - eps)`` (rather than renormalising a truncated Gaussian). This piles proposal mass on the boundaries. For the MPL search this module is driving, the Metropolis-Hastings criterion is applied to a *log-likelihood surface* being maximised under simulated annealing -- not to a stationary distribution we care about matching. The un-corrected acceptance rule is still monotone in the score, so the search is still guaranteed to climb in expectation; the bias affects dwell-time distribution, which matters for Bayesian posteriors but not for finding the optimum. Returning 1.0 is therefore a deliberate SA-scoped shortcut, not an oversight. Bayesian callers should override these ratios before using the moves in an MCMC sampler.

Returns: float: Hastings Ratio. ``1.0`` for symmetric moves and for the SA-approximate path used by ``MPL.search``.

SwitchParentage

class SwitchParentage(Move)

PSPP (Parentage-Switching for Polyploid Phylogenetics) move for Infer_MP_Allop. Alters the genetic parentage of a subnetwork while maintaining the same ploidy values for each leaf. Uses deep-copy for undo/same_move to guarantee correctness.

Constructor

__init__(debug_id: int = 0) -> None

Initialise per-instance bookkeeping fields.

Methods

execute(model: Model) -> Model

Executes the PSPP (Switch-Parentage) move.

Parameter Type Description
model Model A model object with a populated network field.
Returns: Model: The modified model with a newly proposed network topology.
undo(model: Model) -> None

Restores the network to its pre-move state.

same_move(model: Model) -> None

Replays the same topology change on a different model instance.

hastings_ratio -> float

Proposal asymmetry correction ``q(x|x') / q(x'|x)``. A proper MCMC sampler uses the Hastings ratio to correct for asymmetric proposal distributions so that the chain converges to the target distribution. **Every concrete move in this module currently returns 1.0**, which would be exact only for strictly symmetric proposals. Three are not: * :class:`SPR` weights candidate regraft edges by ``1 / d ** distance_decay`` of their topological distance from the prune point. The reverse regraft probability depends on the hop-distance distribution *after* the move, which is not cheap to compute on a network. * :class:`ChangeNodeHeight` draws a Gaussian delta whose sigma is a fraction of the per-node *feasible* half-range; the half-range changes after the move, so proposals are mildly asymmetric even though the draw itself is symmetric. * :class:`ChangeInheritanceProb` clamps its Gaussian draw to the open interval ``(eps, 1 - eps)`` (rather than renormalising a truncated Gaussian). This piles proposal mass on the boundaries. For the MPL search this module is driving, the Metropolis-Hastings criterion is applied to a *log-likelihood surface* being maximised under simulated annealing -- not to a stationary distribution we care about matching. The un-corrected acceptance rule is still monotone in the score, so the search is still guaranteed to climb in expectation; the bias affects dwell-time distribution, which matters for Bayesian posteriors but not for finding the optimum. Returning 1.0 is therefore a deliberate SA-scoped shortcut, not an oversight. Bayesian callers should override these ratios before using the moves in an MCMC sampler.

Returns: float: Hastings Ratio. ``1.0`` for symmetric moves and for the SA-approximate path used by ``MPL.search``.

SPR

class SPR(Move)

Subtree Prune and Regraft on a phylogenetic network. Network-safe algorithm: 1. Select a random edge (u -> v) to prune, subject to: - v is not a leaf - v is not a reticulation (we don't detach reticulation children) - u is not a reticulation with out-degree 1 (would leave it (2,0)) - u is not root with out-degree 2 (would leave root with 1 child) 2. Remove the edge (u -> v). 3. Repair u: if u becomes (1,1), suppress u (merge incident edges). 4. Collect subtree nodes rooted at v (used to prevent cycles). 5. Select a target edge (a -> b) where neither endpoint is in the subtree (prevents cycles). Edges are weighted by inverse topological distance from the prune point so that nearby regraft locations are strongly preferred. 6. Subdivide (a -> b) by inserting a new node w: a -> ...

Constructor

__init__(distance_decay: float | None = None, debug_id: int = 0) -> None

Initialise per-instance bookkeeping fields.

Methods

execute(model: Model) -> Model

Apply the proposal to ``model.network`` in place.

Parameter Type Description
model Model whose ``network`` will be mutated. The move uses ``model.rng`` (a seeded ``numpy.random.Generator``) for every random decision.
Returns: The same ``model`` for chaining convenience. The underlying network is mutated in place; no copy is returned.
undo(model: Model) -> None

Undo the most recent call to :meth:`execute`.

Parameter Type Description
model The same model that was passed to :meth:`execute`. After this call, the network is restored to its pre-execute state.
same_move(model: Model) -> None

Replay the last proposal on an identical (cloned) model. Used by the parallel MCMC driver, which runs multiple chains with shared move sequences. Concrete moves that use deep-copy ``undo_info`` typically implement this as a no-op, since replay is not meaningful without the exact random draws.

Parameter Type Description
model A topologically-equivalent clone of the model passed to :meth:`execute`.
hastings_ratio -> float

Proposal asymmetry correction ``q(x|x') / q(x'|x)``. A proper MCMC sampler uses the Hastings ratio to correct for asymmetric proposal distributions so that the chain converges to the target distribution. **Every concrete move in this module currently returns 1.0**, which would be exact only for strictly symmetric proposals. Three are not: * :class:`SPR` weights candidate regraft edges by ``1 / d ** distance_decay`` of their topological distance from the prune point. The reverse regraft probability depends on the hop-distance distribution *after* the move, which is not cheap to compute on a network. * :class:`ChangeNodeHeight` draws a Gaussian delta whose sigma is a fraction of the per-node *feasible* half-range; the half-range changes after the move, so proposals are mildly asymmetric even though the draw itself is symmetric. * :class:`ChangeInheritanceProb` clamps its Gaussian draw to the open interval ``(eps, 1 - eps)`` (rather than renormalising a truncated Gaussian). This piles proposal mass on the boundaries. For the MPL search this module is driving, the Metropolis-Hastings criterion is applied to a *log-likelihood surface* being maximised under simulated annealing -- not to a stationary distribution we care about matching. The un-corrected acceptance rule is still monotone in the score, so the search is still guaranteed to climb in expectation; the bias affects dwell-time distribution, which matters for Bayesian posteriors but not for finding the optimum. Returning 1.0 is therefore a deliberate SA-scoped shortcut, not an oversight. Bayesian callers should override these ratios before using the moves in an MCMC sampler.

Returns: float: Hastings Ratio. ``1.0`` for symmetric moves and for the SA-approximate path used by ``MPL.search``.

ChangeNodeHeight

class ChangeNodeHeight(Move)

Slide an internal node up or down by adjusting incident branch lengths. A random internal (non-root, non-leaf) node is chosen and shifted by a Gaussian delta. The step standard deviation is ``sigma_frac`` times the feasible half-range (the maximum shift that doesn't push any incident branch below ``_EPSILON``), so proposals stay in bounds naturally while still concentrating mass around the current height. ``sigma_frac`` is meant to be tuned by ``MPLKernel``'s adaptive scheduler so the acceptance rate tracks a target. The uniform proposal it replaced was equivalent to ``sigma_frac ≈ 0.58`` (sd of a uniform is ``half_range/sqrt(3)``) and always ignored acceptance feedback. Starting at ``0.4`` gives a tighter, higher-acceptance default that the adaptive layer can still crank back up when usef...

Constructor

__init__(sigma_frac: float | None = None) -> None

Initialise per-instance bookkeeping fields.

Methods

execute(model: Model) -> Model

Apply the proposal to ``model.network`` in place.

Parameter Type Description
model Model whose ``network`` will be mutated. The move uses ``model.rng`` (a seeded ``numpy.random.Generator``) for every random decision.
Returns: The same ``model`` for chaining convenience. The underlying network is mutated in place; no copy is returned.
undo(model: Model) -> None

Undo the most recent call to :meth:`execute`.

Parameter Type Description
model The same model that was passed to :meth:`execute`. After this call, the network is restored to its pre-execute state.
same_move(model: Model) -> None

Replay the last proposal on an identical (cloned) model. Used by the parallel MCMC driver, which runs multiple chains with shared move sequences. Concrete moves that use deep-copy ``undo_info`` typically implement this as a no-op, since replay is not meaningful without the exact random draws.

Parameter Type Description
model A topologically-equivalent clone of the model passed to :meth:`execute`.
touched_nodes(net: Network) -> 'set[Node] | None'

Return the single node whose height changed. Sliding one internal node by a Gaussian delta only modifies its incident parent / child branch lengths. For the MCMC_GT engine this means only the DP partials on the touched node's ancestor-to-root path need recomputation.

hastings_ratio -> float

Proposal asymmetry correction ``q(x|x') / q(x'|x)``. A proper MCMC sampler uses the Hastings ratio to correct for asymmetric proposal distributions so that the chain converges to the target distribution. **Every concrete move in this module currently returns 1.0**, which would be exact only for strictly symmetric proposals. Three are not: * :class:`SPR` weights candidate regraft edges by ``1 / d ** distance_decay`` of their topological distance from the prune point. The reverse regraft probability depends on the hop-distance distribution *after* the move, which is not cheap to compute on a network. * :class:`ChangeNodeHeight` draws a Gaussian delta whose sigma is a fraction of the per-node *feasible* half-range; the half-range changes after the move, so proposals are mildly asymmetric even though the draw itself is symmetric. * :class:`ChangeInheritanceProb` clamps its Gaussian draw to the open interval ``(eps, 1 - eps)`` (rather than renormalising a truncated Gaussian). This piles proposal mass on the boundaries. For the MPL search this module is driving, the Metropolis-Hastings criterion is applied to a *log-likelihood surface* being maximised under simulated annealing -- not to a stationary distribution we care about matching. The un-corrected acceptance rule is still monotone in the score, so the search is still guaranteed to climb in expectation; the bias affects dwell-time distribution, which matters for Bayesian posteriors but not for finding the optimum. Returning 1.0 is therefore a deliberate SA-scoped shortcut, not an oversight. Bayesian callers should override these ratios before using the moves in an MCMC sampler.

Returns: float: Hastings Ratio. ``1.0`` for symmetric moves and for the SA-approximate path used by ``MPL.search``.

ChangeInheritanceProb

class ChangeInheritanceProb(Move)

Propose a new inheritance probability for a random reticulation node. The two parent edges of a reticulation always carry complementary gammas (gamma and 1-gamma). The new value is drawn from a Gaussian centered on the current gamma, truncated to (epsilon, 1-epsilon), so proposals stay close to the current value and accept more often than a flat Uniform(0, 1) draw.

Constructor

__init__(sigma: float | None = None) -> None

Initialise per-instance bookkeeping fields.

Methods

execute(model: Model) -> Model

Apply the proposal to ``model.network`` in place.

Parameter Type Description
model Model whose ``network`` will be mutated. The move uses ``model.rng`` (a seeded ``numpy.random.Generator``) for every random decision.
Returns: The same ``model`` for chaining convenience. The underlying network is mutated in place; no copy is returned.
undo(model: Model) -> None

Undo the most recent call to :meth:`execute`.

Parameter Type Description
model The same model that was passed to :meth:`execute`. After this call, the network is restored to its pre-execute state.
same_move(model: Model) -> None

Replay the last proposal on an identical (cloned) model. Used by the parallel MCMC driver, which runs multiple chains with shared move sequences. Concrete moves that use deep-copy ``undo_info`` typically implement this as a no-op, since replay is not meaningful without the exact random draws.

Parameter Type Description
model A topologically-equivalent clone of the model passed to :meth:`execute`.
touched_nodes(net: Network) -> 'set[Node] | None'

Return the single reticulation whose inheritance prob changed. Updating the gamma on one reticulation's two parent edges only affects that node's incident branch weights -- the rest of the per-network-edge partial cache can be reused.

hastings_ratio -> float

Proposal asymmetry correction ``q(x|x') / q(x'|x)``. A proper MCMC sampler uses the Hastings ratio to correct for asymmetric proposal distributions so that the chain converges to the target distribution. **Every concrete move in this module currently returns 1.0**, which would be exact only for strictly symmetric proposals. Three are not: * :class:`SPR` weights candidate regraft edges by ``1 / d ** distance_decay`` of their topological distance from the prune point. The reverse regraft probability depends on the hop-distance distribution *after* the move, which is not cheap to compute on a network. * :class:`ChangeNodeHeight` draws a Gaussian delta whose sigma is a fraction of the per-node *feasible* half-range; the half-range changes after the move, so proposals are mildly asymmetric even though the draw itself is symmetric. * :class:`ChangeInheritanceProb` clamps its Gaussian draw to the open interval ``(eps, 1 - eps)`` (rather than renormalising a truncated Gaussian). This piles proposal mass on the boundaries. For the MPL search this module is driving, the Metropolis-Hastings criterion is applied to a *log-likelihood surface* being maximised under simulated annealing -- not to a stationary distribution we care about matching. The un-corrected acceptance rule is still monotone in the score, so the search is still guaranteed to climb in expectation; the bias affects dwell-time distribution, which matters for Bayesian posteriors but not for finding the optimum. Returning 1.0 is therefore a deliberate SA-scoped shortcut, not an oversight. Bayesian callers should override these ratios before using the moves in an MCMC sampler.

Returns: float: Hastings Ratio. ``1.0`` for symmetric moves and for the SA-approximate path used by ``MPL.search``.

ChangeReticSource

class ChangeReticSource(Move)

Move the source (tail / parent) end of a reticulation edge. Picks a random reticulation edge ``z -> c``, detaches the source end, cleans up anything ``z`` leaves behind (deg-2 collapse or orphan-chain pruning), then reattaches ``c``'s parent from a freshly-inserted node split into a randomly chosen host edge. Cycle-safe: host edges downstream of ``c`` are filtered out. Uses deep-copy for undo/same_move to guarantee correctness.

Constructor

__init__() -> None

Initialise with no per-instance parameters (see :class:`Move`).

Methods

execute(model: Model) -> Model

Propose a source-relocation for one reticulation edge.

Parameter Type Description
model Model whose ``network`` will be mutated in place.
Returns: The same ``model`` (mutated) for chaining convenience.
undo(model: Model) -> None

Undo the most recent call to :meth:`execute`.

Parameter Type Description
model The same model that was passed to :meth:`execute`. After this call, the network is restored to its pre-execute state.
same_move(model: Model) -> None

Replay the last proposal on an identical (cloned) model. Used by the parallel MCMC driver, which runs multiple chains with shared move sequences. Concrete moves that use deep-copy ``undo_info`` typically implement this as a no-op, since replay is not meaningful without the exact random draws.

Parameter Type Description
model A topologically-equivalent clone of the model passed to :meth:`execute`.
hastings_ratio -> float

Proposal asymmetry correction ``q(x|x') / q(x'|x)``. A proper MCMC sampler uses the Hastings ratio to correct for asymmetric proposal distributions so that the chain converges to the target distribution. **Every concrete move in this module currently returns 1.0**, which would be exact only for strictly symmetric proposals. Three are not: * :class:`SPR` weights candidate regraft edges by ``1 / d ** distance_decay`` of their topological distance from the prune point. The reverse regraft probability depends on the hop-distance distribution *after* the move, which is not cheap to compute on a network. * :class:`ChangeNodeHeight` draws a Gaussian delta whose sigma is a fraction of the per-node *feasible* half-range; the half-range changes after the move, so proposals are mildly asymmetric even though the draw itself is symmetric. * :class:`ChangeInheritanceProb` clamps its Gaussian draw to the open interval ``(eps, 1 - eps)`` (rather than renormalising a truncated Gaussian). This piles proposal mass on the boundaries. For the MPL search this module is driving, the Metropolis-Hastings criterion is applied to a *log-likelihood surface* being maximised under simulated annealing -- not to a stationary distribution we care about matching. The un-corrected acceptance rule is still monotone in the score, so the search is still guaranteed to climb in expectation; the bias affects dwell-time distribution, which matters for Bayesian posteriors but not for finding the optimum. Returning 1.0 is therefore a deliberate SA-scoped shortcut, not an oversight. Bayesian callers should override these ratios before using the moves in an MCMC sampler.

Returns: float: Hastings Ratio. ``1.0`` for symmetric moves and for the SA-approximate path used by ``MPL.search``.

RelocateReticulation

class RelocateReticulation(Move)

Atomically relocate a reticulation node end-to-end. Picks a random reticulation node, detaches both incoming edges and the single outgoing edge, cleans up any passthroughs or orphan chains left behind, and then reattaches a fresh reticulation on two newly split host edges. This is equivalent to ``ChangeReticSource + ChangeReticDest`` in a single atomic step, avoiding the deep likelihood valley that would arise from the intermediate partially-detached state. Cycle-safe via a ``descendants_of_child`` filter on host edges. Uses deep-copy for undo to guarantee correctness.

Constructor

__init__() -> None

Initialise with no per-instance parameters (see :class:`Move`).

Methods

execute(model: Model) -> Model

Propose a whole-reticulation relocation.

Parameter Type Description
model Model whose ``network`` will be mutated in place.
Returns: The same ``model`` (mutated) for chaining convenience.
undo(model: Model) -> None

Undo the most recent call to :meth:`execute`.

Parameter Type Description
model The same model that was passed to :meth:`execute`. After this call, the network is restored to its pre-execute state.
same_move(model: Model) -> None

Replay the last proposal on an identical (cloned) model. Used by the parallel MCMC driver, which runs multiple chains with shared move sequences. Concrete moves that use deep-copy ``undo_info`` typically implement this as a no-op, since replay is not meaningful without the exact random draws.

Parameter Type Description
model A topologically-equivalent clone of the model passed to :meth:`execute`.
hastings_ratio -> float

Proposal asymmetry correction ``q(x|x') / q(x'|x)``. A proper MCMC sampler uses the Hastings ratio to correct for asymmetric proposal distributions so that the chain converges to the target distribution. **Every concrete move in this module currently returns 1.0**, which would be exact only for strictly symmetric proposals. Three are not: * :class:`SPR` weights candidate regraft edges by ``1 / d ** distance_decay`` of their topological distance from the prune point. The reverse regraft probability depends on the hop-distance distribution *after* the move, which is not cheap to compute on a network. * :class:`ChangeNodeHeight` draws a Gaussian delta whose sigma is a fraction of the per-node *feasible* half-range; the half-range changes after the move, so proposals are mildly asymmetric even though the draw itself is symmetric. * :class:`ChangeInheritanceProb` clamps its Gaussian draw to the open interval ``(eps, 1 - eps)`` (rather than renormalising a truncated Gaussian). This piles proposal mass on the boundaries. For the MPL search this module is driving, the Metropolis-Hastings criterion is applied to a *log-likelihood surface* being maximised under simulated annealing -- not to a stationary distribution we care about matching. The un-corrected acceptance rule is still monotone in the score, so the search is still guaranteed to climb in expectation; the bias affects dwell-time distribution, which matters for Bayesian posteriors but not for finding the optimum. Returning 1.0 is therefore a deliberate SA-scoped shortcut, not an oversight. Bayesian callers should override these ratios before using the moves in an MCMC sampler.

Returns: float: Hastings Ratio. ``1.0`` for symmetric moves and for the SA-approximate path used by ``MPL.search``.

ChangeReticDest

class ChangeReticDest(Move)

Move the destination (head) end of a reticulation edge. Picks a random reticulation edge ``z -> c``, detaches the destination end, clears ``c``'s reticulation flag and collapses it if it became a deg-2 passthrough, then reattaches into a freshly-inserted reticulation node split onto a randomly chosen host edge. Cycle-safe: edges upstream of ``z`` are filtered out. Uses deep-copy for undo/same_move to guarantee correctness.

Constructor

__init__() -> None

Initialise with no per-instance parameters (see :class:`Move`).

Methods

execute(model: Model) -> Model

Propose a destination-relocation for one reticulation edge.

Parameter Type Description
model Model whose ``network`` will be mutated in place.
Returns: The same ``model`` (mutated) for chaining convenience.
undo(model: Model) -> None

Undo the most recent call to :meth:`execute`.

Parameter Type Description
model The same model that was passed to :meth:`execute`. After this call, the network is restored to its pre-execute state.
same_move(model: Model) -> None

Replay the last proposal on an identical (cloned) model. Used by the parallel MCMC driver, which runs multiple chains with shared move sequences. Concrete moves that use deep-copy ``undo_info`` typically implement this as a no-op, since replay is not meaningful without the exact random draws.

Parameter Type Description
model A topologically-equivalent clone of the model passed to :meth:`execute`.
hastings_ratio -> float

Proposal asymmetry correction ``q(x|x') / q(x'|x)``. A proper MCMC sampler uses the Hastings ratio to correct for asymmetric proposal distributions so that the chain converges to the target distribution. **Every concrete move in this module currently returns 1.0**, which would be exact only for strictly symmetric proposals. Three are not: * :class:`SPR` weights candidate regraft edges by ``1 / d ** distance_decay`` of their topological distance from the prune point. The reverse regraft probability depends on the hop-distance distribution *after* the move, which is not cheap to compute on a network. * :class:`ChangeNodeHeight` draws a Gaussian delta whose sigma is a fraction of the per-node *feasible* half-range; the half-range changes after the move, so proposals are mildly asymmetric even though the draw itself is symmetric. * :class:`ChangeInheritanceProb` clamps its Gaussian draw to the open interval ``(eps, 1 - eps)`` (rather than renormalising a truncated Gaussian). This piles proposal mass on the boundaries. For the MPL search this module is driving, the Metropolis-Hastings criterion is applied to a *log-likelihood surface* being maximised under simulated annealing -- not to a stationary distribution we care about matching. The un-corrected acceptance rule is still monotone in the score, so the search is still guaranteed to climb in expectation; the bias affects dwell-time distribution, which matters for Bayesian posteriors but not for finding the optimum. Returning 1.0 is therefore a deliberate SA-scoped shortcut, not an oversight. Bayesian callers should override these ratios before using the moves in an MCMC sampler.

Returns: float: Hastings Ratio. ``1.0`` for symmetric moves and for the SA-approximate path used by ``MPL.search``.

Module Functions

insert_node_in_edge(edge: Edge, node: Node, net: Network) -> None

Given an edge, a -> b, place a node c, such that a -> c -> b. This requires the deletion of edge a -> b, then the addition of edges a -> c and c -> b. Gamma preservation. When ``b`` is a reticulation, the original edge ``a -> b`` carries an inheritance probability ``gamma`` that represents one component of ``b``'s parental-mass split. After the insertion the new in-edge to ``b`` is ``c -> b`` (``a -> c`` is just a fresh tree edge feeding the new internal node), so we propagate the original gamma to ``c -> b``. Without this, every ``insert_node_in_edge`` call on a retic in-edge silently breaks the retic gamma sum invariant -- which is the root cause of the historical 14% bad-gamma rate in ``AddReticulation``. The new ``a -> c`` edge has no gamma (its retic-ness, if any, is the caller's responsibility to set explicitly).

Parameter Type Description
edge Edge An edge, a -> b
node Node A node, c.
net Network The network that contains nodes a, b, and c
connect_nodes(src: Node, dest: Node, net: Network) -> None

Given two nodes in a network, connect them and check whether or not a reticulation is created.

Parameter Type Description
src Node The parent of the new edge
dest Node The child of the new edge
net Network Network for which to add the edge

Navigation

Modules

This Page