Scoring

Descriptors are computed once, centrally, by rbfenetmap.core.descriptors.compute_descriptors(). A scorer receives a plain Mapping[str, float] and nothing else – no molecules, no mapping object, no RDKit.

That narrow interface buys three things: re-scoring under new weights costs nothing because no mapping is recomputed; a scorer is testable against hand-written dictionaries with no chemistry involved; and a third-party scorer cannot reach past its inputs and grow a dependency on how the mapping was produced.

Reject versus bad score

The boundary is hard and deliberate.

Rejection is structural and originates only in the mapper, the repair, or validation – never from a weighted sum crossing a threshold. An infeasible edge gets EdgeScore(total=inf, feasible=False) and is excluded from the graph the planner sees.

A bad score is a large finite cost and stays in the candidate pool, where the planner can still use it if the alternative is a disconnected network.

A scorer must never invent a rejection. rejections is passed in so it can be propagated, not added to.

The linear scorer

Each descriptor is normalised so 1.0 means roughly “one typical unit of badness”, then multiplied by a weight and clipped. The normalisation is what makes the weights interpretable: a weight of 4.0 on charge_delta against 1.0 on softcore_atoms says a unit charge change costs about as much as four soft-core-sized problems – a statement a chemist can argue with.

Term

Descriptor

Weight

softcore_atoms

n_softcore_max_heavy / 8

1.00

charge_delta

charge_delta (cap 2)

4.00

mcs_deficit

1 - mcs_fraction

2.00

ring_delta

ring_delta (cap 3)

1.00

core_rmsd

core_rmsd in angstroms (cap 3)

1.00

repair_cost

n_demoted_atoms / 6

0.75

ring_atoms_in_softcore

n_ring_atoms_in_softcore / 6

0.50

softcore_asymmetry, heavy_atom_delta

... / 8

0.25

rotatable_delta

rotatable_delta / 3

0.20

logp_delta

|logp_delta| / 2 (cap 3)

0.10

Override with --weights key=value (repeatable) or --weights-file. An unknown key raises: a silently ignored typo would make a tuning run look effective while it quietly used the defaults.

Other scorers

lomaplike

Multiplicative similarity in (0, 1], converted to a cost by -log. Composition differs meaningfully from a weighted sum: any single factor near zero sinks the whole edge regardless of the rest. That is the right shape when the penalties are independent reasons the edge will not converge, rather than competing preferences to balance.

softcore-size

Cost equals the larger soft-core heavy-atom count. The honest baseline any richer scorer should be shown to beat, and – because its costs are whole numbers – what makes planner tests verifiable by hand.