Skip to content

Add counter-target selectivity - #10

Open
ssiddhantsharma wants to merge 1 commit into
polizzilab:mainfrom
ssiddhantsharma:add-selectivity-counter-target
Open

Add counter-target selectivity#10
ssiddhantsharma wants to merge 1 commit into
polizzilab:mainfrom
ssiddhantsharma:add-selectivity-counter-target

Conversation

@ssiddhantsharma

@ssiddhantsharma ssiddhantsharma commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Adds counter-target selectivity to the loop: alongside the on-target ligand, each
round's designs are also folded against one or more off-target ligands, and two
objectives reward on-target binding minus the worst-case off-target binding:

  • iptm_selectivityon_iptm - max(offtarget_iptm)
  • pbind_selectivityon_affinity_probability - max(offtarget_affinity_probability)

This is the paired on-minus-off difference: a design that binds the target but not
the counter-target scores highest, so you can steer designs away from a similar
molecule you don't want bound.

Off by default; enabled with --counter-target-smiles <SMILES> [...] (or
counter_target_smiles=[...]).

How it fits

Same seams as the existing objectives, no restructuring:

  • fold_counter_targets() folds each design against each counter-target using the
    existing predict_complex_structures() / get_boltz_yaml_boilerplate(), writes to
    its own offtarget_<i>_boltz_inputs dir, and keeps the worst-case (strongest)
    off-target binding per design keyed by Boltz stem. Off-target structures are only
    scored, never promoted to backbones.
  • parse_boltz_binding_metrics() reads iptm (+ affinity_probability_binary when
    predicting affinity) from the Boltz outputs.
  • identify_backbone_candidates() injects offtarget_* into the existing
    confidence_data dict, so compute_objective_function() just sees new keys.

Wired through run_nise_boltz2x_cli.py too (--counter-target-smiles plus the new
objectives in the choices list).

Notes

  • Multiple counter-targets take the worst case via nanmax, so a design that binds
    any of them is penalised, and a missing off-target affinity is skipped rather than
    propagated as nan.
  • The on-minus-off difference doesn't put a floor on on-target binding on its own —
    combine it with an on-target objective/threshold if you want that.
  • Cost: one extra fold per counter-target per round.

Testing

  • tests/test_selectivity.py covers the objectives (including a case where a
    selective design beats a promiscuous one) and the Boltz metric parser.
  • Checked against real Boltz output that the off-target fold lands where the code
    expects and parses correctly; the fold reuses the same predict path NISE already
    runs for the on-target ligand.

Fold each round's designs against one or more off-target ligands in addition to the
on-target ligand, and add two objectives that reward on-target binding minus the
worst-case off-target binding (iptm_selectivity, pbind_selectivity) so designs can be
steered away from a similar molecule.

The off-target fold reuses predict_complex_structures / get_boltz_yaml_boilerplate,
writes to its own offtarget_<i>_boltz_inputs dir, and the worst-case metrics are keyed
by Boltz stem and injected into confidence_data. Off by default (--counter-target-smiles).
Wires the flag through run_nise_boltz2x_cli.py and adds tests/test_selectivity.py.
@ssiddhantsharma ssiddhantsharma changed the title Add counter-target selectivity objectives Add counter-target selectivity Aug 11, 2026
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