orthonym.assembly.candidate_pool#
Note
Internal API. Names and behaviour may change between releases.
CandidatePool: the candidate names of one assemble_name call, the gates that void a candidate, and the choice of the winner.
WHAT IT DOES#
A handler that names the molecule calls pool.add(name, handler_id, features,...). add scores the name with
coverage_scoring.compute_confidence, applies the two gates that void a candidate, and stores the survivor:
RATIO_REJECT_FLOOR(every handler): a candidate whoseratiofactor is below the floor is voided, unless the producer measured a complete atom partition (coverage_measured=True).the Tier-B gate (the
ring_bhandlers ofHANDLER_POLICIES): a candidate whose confidence is belowCONFIDENCE_GATE_THRESHOLDis voided.
A voided candidate returns None and the caller falls through to the next handler.
best returns the candidate of the first handler whose policy is direct_return; when none fired it returns the
first candidate added. The inner handlers run in dispatch order (assembly/inner_dispatch.py) and the first one that
succeeds returns its own result, so the winner of a pool is the first name that a handler produced. The three Tier-A ring
handlers (complex_ring, heterocycle, benzene) are the exception: handlers/tier_a_ring.py adds up to three
candidates and ranks them with coverage_scoring.select_best_candidate (highest confidence, then handler priority,
then alphanumerical order of the name).
The pool does not rank parent skeletons by the criteria. That ranking is rules/p44_scorer.py
(select_parent_unified), and the ranking of finished names is assembly/offer_pool.py (rank_offers,
select_offer, select_rt_passing), which namer.py consumes.
FIELDS SET AFTER SCORING#
parent_atom_indices, ring_info, tree, node_scores and tree_rewritten are set on the candidate after
compute_confidence has returned and never enter it: the confidence is a function of the name, the handler and the
features alone, so passing an atom set or a tree cannot change it. factors['parent_correctness'] is recorded after
scoring in the same way; ParentCorrectnessScorer is bound once at module load (not imported per add call).
POOL STACK#
assemble_name is recursive (nested substituent and fragment namings, N-oxide, decomposition). Each call pushes a
fresh pool with push_pool and pops it in a finally clause with pop_pool; get_current_pool returns
the top. A recursive call therefore never adds to its caller’s pool. The stack is thread-local, so concurrent runners
keep independent stacks.
- class orthonym.assembly.candidate_pool.HandlerPolicy(handler_id, tier, priority, direct_return=False)#
Bases:
objectDeclarative policy for one handler’s pool participation.
- Fields:
handler_id: Handler name (matches HANDLER_PRIORITY keys). tier: ‘ring_a’ | ‘ring_b’ | ‘chain’ | ‘direct_return’. priority: Tiebreak priority (pulled from HANDLER_PRIORITY). direct_return: True for Tier B and direct-return handlers
(best returns the first such candidate); False for the Tier A ring handlers (ranked by select_best_candidate) and chain.
addapplies the module constant CONFIDENCE_GATE_THRESHOLD to thering_bhandlers; a policy carries no threshold of its own.- handler_id: str#
- tier: str#
- priority: int#
- direct_return: bool = False#
- class orthonym.assembly.candidate_pool.CandidatePool#
Bases:
objectHolds scored candidate names from one assemble_name invocation.
bestreturns the candidate of the firstdirect_returnhandler, else the first candidate added (module docstring).parent_correctnessis recorded inaddthrough the module-levelParentCorrectnessScorerbinding; it is computed after the confidence, so it does not entercand.confidence.Tier B gate semantics: for a
ring_bhandler,addreturns None (gate-rejected) whencand.confidenceis belowCONFIDENCE_GATE_THRESHOLD(0.40), and the caller falls through to the next handler.Direct-return handlers:
addalso setsself._direct_return_winner(the first direct-return candidate), whichbestreturns.- add(name, handler_id, features, parent_atom_indices=None, ring_info=None, tree=None, coverage_measured=False)#
Score and add a candidate. Returns the candidate, or None if gate-rejected (Tier B handlers only).
- BYTE-IDENTICAL CONTRACT (RESEARCH Risk 1):
parent_atom_indices is set POST-HOC on the returned CandidateName (NOT passed into compute_confidence). This guarantees confidence is byte-identical to current composer.py code.
- Parameters:
name (str) – The IUPAC name produced by the handler.
handler_id (str) – Key into HANDLER_POLICIES. Unknown handler_id is accepted with no policy (no gate, no direct-return flag).
features (Any) – MolecularFeatures (passed through to compute_confidence and to ParentCorrectnessScorer).
parent_atom_indices (Set[int] | None) – Optional set of atom indices comprising the parent structure. Used by ParentCorrectnessScorer; does NOT affect compute_confidence (Risk 1).
coverage_measured (bool) – True when the producer MEASURED a complete atom partition for
name(every heavy atom bound to one of its fragments;_w2_atom_coverage_verdict). The RATIO_REJECT_FLOOR is a character-count stand-in for coverage, so it is skipped then: numerical-term chain names are short by design (‘octacontane’, ratio 0.092). Does not affect the confidence.
- Returns:
The added CandidateName on success. None if Tier B gate rejected (cand.confidence < CONFIDENCE_GATE_THRESHOLD).
- Return type:
CandidateName | None
- best()#
Return the winning candidate, or None when the pool is empty.
The candidate of the first
direct_returnhandler wins (the same result as the sequential “if X applies: return X” dispatch ofassemble_name); when no such handler added a candidate, the first candidate added wins. For example, the Tier-Acomplex_ringhandler (direct_returnFalse) may add ‘1-methoxypyrene’ and thepolycyclichandler (direct_returnTrue) then add ‘2-methoxypyrene’ for the same molecule:bestreturns thepolycycliccandidate, not_candidates[0].
- all_candidates()#
Return all collected candidates (for logging/diagnostics).
- orthonym.assembly.candidate_pool.push_pool(features=None)#
Push a fresh pool onto the per-thread stack and return it.
Called by assemble_name prologue (composer.py). MUST be paired with pop_pool in a finally clause so the pool is popped on every exit path (normal return, exception, early return statements).
(code review 2026-05-30): when
featuresis provided, the a phasecontroller flag/oracle are bound NOW (construction) off that authoritative per-call features object, instead of being lifted lazily off the first add call.
features=None(get_current_pool auto-push, tests) leaves the pool’s flag at its safe default and the add fallback binds it later.Returns the new pool that is now the active cascade scope for the currently-executing assemble_name call.
- orthonym.assembly.candidate_pool.pop_pool()#
Pop the top pool from the per-thread stack.
Called by assemble_name epilogue (composer.py, in finally clause). Safe to call when the stack is empty (no-op) — defensive against any code path that pops without a matching push.
- orthonym.assembly.candidate_pool.get_current_pool()#
Return the top of the per-thread pool stack — the active cascade for the currently-executing assemble_name call.
Auto-pushes a fresh pool if the stack is empty. This covers two cases: (1) a handler called get_current_pool outside an assemble_name scope (e.g., from a unit test that invokes pool.add directly), and (2) backward compatibility with any legacy caller that expected the pre-fix lazy-init behavior.
- orthonym.assembly.candidate_pool.clear_pool(features=None)#
BACKWARD-COMPAT: replace the top of the stack with a fresh pool.
Pre-fix code (Plan 03) called clear_pool in assemble_name’s prologue to reset state. Post-fix, push_pool is the right primitive (the wrapping try/finally handles pop). This alias preserves the call-site signature so any handler still calling clear_pool directly does not break.
Behavior: if the stack is empty, push a fresh pool (same as pre-fix lazy init); if non-empty, replace the top so the current cascade restarts cleanly without affecting outer cascades on the stack.
(code review 2026-05-30): binds the a phase controller flag/oracle
off
featuresat construction (see push_pool) when provided.