mixle.stats.graphs.hyperedge_replacement_grammar module¶
Hyperedge-replacement graph grammar (HRG) – a distribution over networks you can score, fit, and sample.
The second main kind of graph grammar (the other is vertex replacement; see
vertex_replacement_grammar). A production A -> R rewrites a nonterminal HYPEREDGE labelled A
with a ranked tuple of attachment nodes (its tentacles) by a right-hand-side hypergraph R carrying
an ordered tuple of rank(A) external nodes; the rewrite fuses R’s external nodes with the
hyperedge’s tentacles (so the gluing is intrinsic – no embedding relation, unlike NLC). HRGs are
context-free and confluent, with cleaner parsing theory.
Observations are GRAPHS (networkx graphs, all-terminal); the start symbol has rank 0 by default, so a
derivation generates a graph with no boundary. The distribution mirrors vertex_replacement_grammar:
log_density(graph)is the MARGINAL likelihood – the graph is parsed (reduced back to the start symbol by un-applying productions) and scored as the log-sum over all derivations (the inside / sum-product recursion). Exact when the parse forest is fully explored, a lower bound if the budget truncates it,-infif the grammar cannot derive the graph.best_derivationgives the Viterbi parse.sample()runs a real hyperedge-replacement derivation.the estimator learns rule FREQUENCIES by Viterbi parse-counting (structure given; induction is out of scope).
- class Hypergraph(graph=None, hyperedges=())[source]
Bases:
objectA hypergraph: a networkx graph of terminal (rank-2) edges plus a list of nonterminal hyperedges.
graphholds the nodes and terminal edges (withlabel/node_color/weight/edge_colorattributes, as for vertex replacement).hyperedgesis a list of(label, tuple_of_attachment_nodes)– the nonterminal hyperedges still to be rewritten.- copy()[source]
- class HyperedgeReplacementRule(lhs, rhs, external, frequency=1.0)[source]
Bases:
objectA production
lhs -> rhs: replace a rank-k nonterminal hyperedge byrhs, fusing externals.externalis the ordered tuple ofrhsnodes (length = rank oflhs) fused, in order, with the rewritten hyperedge’s tentacles.frequencyweights the production within its left-hand side.- property rank: int
- class HyperedgeReplacementGrammar(name='')[source]
Bases:
objectA container of HyperedgeReplacementRule objects keyed by left-hand-side symbol.
- add_rule(rule)[source]
- Parameters:
rule (HyperedgeReplacementRule)
- Return type:
None
- refresh_rules()[source]
- Return type:
None
- generate_graph(grammar, start_symbol, target_n=100, rng=None, start_rank=0)[source]
Generate a graph by a hyperedge-replacement derivation.
Begins with a single nonterminal hyperedge
start_symbolonstart_rankfresh boundary nodes (default 0 -> no boundary). Repeatedly rewrites a nonterminal hyperedge by one of its symbol’s rules (probability proportional to frequency), fusing the rule’s external nodes onto the hyperedge’s tentacles.target_nis a soft node budget: once reached the derivation prefers terminal-only rules, and any hyperedges left after the step cap are dropped. Returns a networkx graph.
- best_derivation(graph, grammar, start_symbol, budget=_PARSE_BUDGET)[source]
Best (Viterbi) hyperedge-replacement derivation of a graph: (log_prob, [rules]) or (-inf, None).
- marginal_log_prob(graph, grammar, start_symbol, budget=_PARSE_BUDGET, with_status=False)[source]
Marginal log-likelihood: log-sum over ALL hyperedge-replacement derivations that yield the graph.
Exact when the parse forest is fully explored; a variational lower bound (ELBO) if the budget/depth cap truncates it.
with_statusreturns(value, exact)withexactFalse iff a cap was hit.
- class HyperedgeReplacementGrammarDistribution(grammar, start_symbol=None, orig_n=100, name=None)[source]
Bases:
SequenceEncodableProbabilityDistributionA distribution over GRAPHS parameterised by a hyperedge-replacement grammar.
log_density(graph)is the marginal likelihood (sum over derivations, by parsing);sample()emits graphs by derivation; the estimator learns rule frequencies by Viterbi parse-counting.- density_semantics()[source]
What
log_densityreturns relative to the true log-density (default: exact).Override to declare that this distribution’s
log_densityis a variational lower bound (ELBO), an upper bound, or an approximation rather than the exactlog p(x). This is surfaced as theExactDensitycapability and noted inmixle.describe(), so code that needs an exact likelihood canrequire(x, ExactDensity)instead of silently trusting a bound.
- density(x)[source]
Return the probability density or mass at a single observation.
Concrete default: exponentiate
log_density(the abstract method subclasses must provide). Leaves with a cheaper closed form may override this.
- log_density(x, with_status=False)[source]
Marginal log-likelihood of graph
x(seemarginal_log_prob).with_status-> (value, exact).
- seq_encode(x)[source]
- seq_log_density(x, with_status=False)[source]
Return vectorized log-density values for sequence-encoded observations.
- sampler(seed=None)[source]
Return a sampler for drawing observations from this distribution.
- estimator(pseudo_count=None)[source]
Return an estimator for fitting this distribution from data.
- dist_to_encoder()[source]
Return the data encoder used by this distribution for vectorized methods.
- class HyperedgeReplacementGrammarSampler(grammar, start_symbol=None, orig_n=100, seed=None)[source]
Bases:
DistributionSamplerSample graphs from a hyperedge-replacement grammar by derivation.
- sample(size=None, *, batched=True)[source]
Draw observations.
Combinator samplers (mixture/sequence/…) accept
batched. Withbatched=True(the default) each child stream is drawn in one vectorized call instead of a per-draw Python loop – far faster. Because every child sampler owns an independentRandomState, batching consumes each stream in the same order as the loop, so the draws are identical to the legacy path.batched=Falseforces that legacy per-draw loop as a guaranteed- stable reference. Leaf samplers are already vectorized and ignore the flag.
- class HyperedgeReplacementGrammarAccumulator(grammar=None, start_symbol=None, keys=None)[source]
Bases:
SequenceEncodableStatisticAccumulatorAccumulate Viterbi rule-firing counts: parse each graph and tally how often each rule fires.
- update(x, weight, estimate)[source]
- initialize(x, weight, rng)[source]
- seq_initialize(x, weights, rng)[source]
- seq_update(x, weights, estimate)[source]
- combine(suff_stat)[source]
- value()[source]
- from_value(x)[source]
- key_merge(stats_dict)[source]
Pool this accumulator’s statistics into
stats_dictunder its merge key.The structural default implements the common single-key pattern: store the accumulator under
self.keysthe first time the key is seen, elsecombineinto the one already there. Accumulators with several named keys (e.g. an HMM’s init/trans/state keys) or a non-accumulator stats payload override this. AkeysofNone(the default) is a no-op.
- key_replace(stats_dict)[source]
Replace this accumulator’s statistics from the pooled
stats_dictentry (see key_merge).
- acc_to_encoder()[source]
- class HyperedgeReplacementGrammarAccumulatorFactory(grammar=None, start_symbol=None, keys=None)[source]
Bases:
StatisticAccumulatorFactoryCreates accumulators carrying the rule structure whose frequencies are estimated.
- make()[source]
- class HyperedgeReplacementGrammarEstimator(grammar=None, start_symbol=None, pseudo_count=None, name=None, keys=None)[source]
Bases:
ParameterEstimatorEstimate rule FREQUENCIES from graphs by Viterbi parse-counting (the structure is given).
- accumulator_factory()[source]
- estimate(nobs, suff_stat)[source]