SeqOpt.run
- SeqOpt.run(df_seq, df_feat, objectives, algorithm='nsga2', pop_size=50, n_gen=20, crossover='uniform', mutation='substitution', cx_prob=0.5, mut_prob=0.2, survival='mu_plus_lambda', variation='and', constraints=None, penalty='delta', hof_size=10, n_mut_max=5, region=None, to_aa=None, init='random', seed=None, jmd_n_len=10, jmd_c_len=10)[source]
Run multi-objective directed evolution and return the Pareto front of variants.
A population of multi-mutation variants of the single wild-type
df_seqis evolved by NSGA-II (algorithm='nsga2') or an importance-ordered greedy walk (algorithm='greedy'), scored on every objective, and reduced to the non-dominated trade-off front.- Parameters:
df_seq (pd.DataFrame, shape (1, n_seq_info)) – DataFrame containing an
entrycolumn with unique protein identifiers, in the position-based format (sequence,tmd_start,tmd_stop). SeeSequenceFeature.get_df_parts()for the fulldf_seqformat specification. Must hold exactly one wild-type sequence (SeqOpt optimizes one sequence per run).df_feat (pd.DataFrame) – CPP feature set (output of
CPP.run()) defining the features and the residue attribution (feat_importance/feat_impact,positions) the search reads.objectives (list of (str, str, object)) –
(name, goal, source)per objective;goalin{'max','min'}andsourcein{'delta_pred','delta_cpp','shift_score','n_mut'}or acallable(sequence) -> float. The callable receives the variant sequence and returns a scalar, so any external predictor (scikit / torch model, or a sequence-level tool / web API such as a topology or signal-peptide predictor) can be optimized as an objective; its result is cached per distinct variant. At least two objectives.algorithm (str, default='nsga2') –
'nsga2'(population) or'greedy'(importance-ordered single path).pop_size (int, default=50) – Population size (NSGA-II only).
n_gen (int, default=20) – Number of generations (NSGA-II only).
crossover (str, default='uniform') – Crossover operator:
'uniform'/'one_point'/'two_point'.mutation (str, default='substitution') – Mutation operator:
'substitution'or'shift'.cx_prob (float, default=0.5) – Per-pair crossover probability.
mut_prob (float, default=0.2) – Per-individual mutation probability.
survival (str, default='mu_plus_lambda') – Survival scheme:
'mu_plus_lambda'(elitist),'mu_comma_lambda'or'ea_simple'(generational replacement).variation (str, default='and') – Variation scheme:
'and'(varAnd — crossover and mutation) or'or'(varOr — each offspring is crossover or mutation or reproduction; needs cx_prob+mut_prob<=1).constraints (list of callable, optional) – Feasibility predicates
genome -> bool(True= feasible). Infeasible variants are penalized so the search avoids them.penalty (str, default='delta') – Penalty applied to infeasible variants:
'delta'(fixed worst objective) or'closest_valid'(penalty scaled by the number of violated constraints).hof_size (int, default=10) – Size of the single-objective Hall of Fame (
SeqOpt.hall_of_fame_) accumulated across generations.n_mut_max (int, default=5) – Maximum number of point mutations per variant.
region (str or list of int, optional) – Restrict the mutable span (see
SeqMut.scan()).to_aa (list of str, optional) – Substitution alphabet (see
SeqMut.scan()).init (str, default='random') – Population initialization:
'random'or'suggest'(warm start from the top single mutations).seed (int, optional) – Per-call seed; overrides the constructor
random_state.jmd_n_len (int, default=10) – Length of JMD-N in number of amino acids.
jmd_c_len (int, default=10) – Length of JMD-C in number of amino acids.
- Returns:
df_pareto – One row per final-population variant with
entry,variant,n_mut,sequence_mut, one column per objective (named byobjectives), the non-dominatedrank(0 = best front) and thecrowdingdistance, sorted byrankthen descendingcrowding.- Return type:
pd.DataFrame
Examples
import numpy as np, pandas as pd import matplotlib.pyplot as plt from sklearn.ensemble import RandomForestClassifier import aaanalysis as aa aa.options["verbose"] = False # Gamma-secretase (GSEC) substrate data + the bundled interpretable CPP feature set. df_feat = aa.load_features(name="DOM_GSEC") # 150 CPP features (with positions, feat_importance) df_seq = aa.load_dataset(name="DOM_GSEC", n=50) # 100 TMD sequences, label 1 = GSEC substrate labels = df_seq["label"].to_list() # A simple RandomForest substrate classifier on the CPP feature matrix. sf = aa.SequenceFeature() X = np.asarray(sf.feature_matrix(features=df_feat["feature"], df_parts=sf.get_df_parts(df_seq=df_seq), df_scales=aa.load_scales()), dtype=float) model = RandomForestClassifier(n_estimators=100, random_state=0).fit(X, labels) # Pick a NON-substrate as the wild-type and design a "super substrate": mutate its TMD to # maximize the predicted substrate probability with as few mutations as possible. wt = df_seq[df_seq["label"] == 0].iloc[[0]].reset_index(drop=True) objectives = [("substrate", "max", "delta_pred"), # raise P(GSEC substrate) (RF prediction shift) ("parsimony", "min", "n_mut")] # with as few mutations as possible
Run NSGA-II and read off the Pareto front (
rank=0= the non-dominated trade-offs):seqopt = aa.SeqOpt(mode="importance", model=model, target_class=1, random_state=42) df_pareto = seqopt.run(df_seq=wt, df_feat=df_feat, objectives=objectives, algorithm="nsga2", pop_size=40, n_gen=20, n_mut_max=5, region="tmd") aa.display_df(df_pareto, n_rows=10, show_shape=True)
DataFrame shape: (7, 8)
entry variant n_mut sequence_mut substrate parsimony rank crowding 1 Q14802 0 MQKVTLGLLVFLAGF...PGETPPLITPGSAQS 0.000000 0.000000 0 inf 2 Q14802 L37T+I55T+V56L+S58Q+A59R 5 MQKVTLGLLVFLAGF...PGETPPLITPGSAQS 37.000000 5.000000 0 inf 3 Q14802 L37G+A59R 2 MQKVTLGLLVFLAGF...PGETPPLITPGSAQS 29.000000 2.000000 0 0.429730 4 Q14802 A59R 1 MQKVTLGLLVFLAGF...PGETPPLITPGSAQS 16.000000 1.000000 0 0.316216 5 Q14802 L37G+V56L+A59R 3 MQKVTLGLLVFLAGF...PGETPPLITPGSAQS 33.000000 3.000000 0 0.281081 6 Q14802 A59K 1 MQKVTLGLLVFLAGF...PGETPPLITPGSAQS 16.000000 1.000000 0 0.275676 7 Q14802 L37G+V56L+S58Q+A59R 4 MQKVTLGLLVFLAGF...PGETPPLITPGSAQS 35.000000 4.000000 0 0.254054 Guidance mode. Above used
mode=\"importance\"(staticfeat_importance). The headlinemode=\"impact\"refits aShapModelevery generation (fuzzy labeling) to mutate the residues SHAP deems most important — pass the labeled reference set:df_shap = aa.SeqOpt(mode="impact", model=model, target_class=1, df_seq_ref=df_seq, labels=labels, random_state=0).run( df_seq=wt, df_feat=df_feat, objectives=objectives, pop_size=12, n_gen=4, n_mut_max=4, region="tmd") aa.display_df(df_shap, n_rows=10, show_shape=True)
DataFrame shape: (9, 8)
entry variant n_mut sequence_mut substrate parsimony rank crowding 1 Q14802 0 MQKVTLGLLVFLAGF...PGETPPLITPGSAQS 0.000000 0.000000 0 inf 2 Q14802 C49E+S58R 2 MQKVTLGLLVFLAGF...PGETPPLITPGSAQS 21.000000 2.000000 0 inf 3 Q14802 S58F 1 MQKVTLGLLVFLAGF...PGETPPLITPGSAQS 8.000000 1.000000 0 0.559524 4 Q14802 V56M 1 MQKVTLGLLVFLAGF...PGETPPLITPGSAQS 8.000000 1.000000 0 0.440476 5 Q14802 C49E 1 MQKVTLGLLVFLAGF...PGETPPLITPGSAQS 8.000000 1.000000 0 0.000000 6 Q14802 C49E+S58R+A59W 3 MQKVTLGLLVFLAGF...PGETPPLITPGSAQS 20.000000 3.000000 1 inf 7 Q14802 A50G 1 MQKVTLGLLVFLAGF...PGETPPLITPGSAQS 0.000000 1.000000 1 inf 8 Q14802 A50G+S58R 2 MQKVTLGLLVFLAGF...PGETPPLITPGSAQS 14.000000 2.000000 1 1.000000 9 Q14802 C49Y+A50G+S58R+A59W 4 MQKVTLGLLVFLAGF...PGETPPLITPGSAQS 20.000000 4.000000 2 inf Algorithm + operators. Swap the search and the evolutionary operators: a deterministic importance-ordered
greedybaseline, or NSGA-II with varOr variation,ea_simplesurvival, two-point crossover, shift mutation.df_greedy = seqopt.run(df_seq=wt, df_feat=df_feat, objectives=objectives, algorithm="greedy", n_mut_max=5, region="tmd") df_or = seqopt.run(df_seq=wt, df_feat=df_feat, objectives=objectives, pop_size=40, n_gen=20, n_mut_max=5, region="tmd", variation="or", survival="ea_simple", crossover="two_point", mutation="shift", cx_prob=0.5, mut_prob=0.3) aa.display_df(df_greedy, n_rows=10, show_shape=True)
DataFrame shape: (5, 8)
entry variant n_mut sequence_mut substrate parsimony rank crowding 1 Q14802 A59K 1 MQKVTLGLLVFLAGF...PGETPPLITPGSAQS 16.000000 1.000000 0 inf 2 Q14802 I55A+V56L+M57A+S58K+A59K 5 MQKVTLGLLVFLAGF...PGETPPLITPGSAQS 29.000000 5.000000 0 inf 3 Q14802 S58K+A59K 2 MQKVTLGLLVFLAGF...PGETPPLITPGSAQS 23.000000 2.000000 0 0.596154 4 Q14802 M57A+S58K+A59K 3 MQKVTLGLLVFLAGF...PGETPPLITPGSAQS 25.000000 3.000000 0 0.442308 5 Q14802 I55A+M57A+S58K+A59K 4 MQKVTLGLLVFLAGF...PGETPPLITPGSAQS 28.000000 4.000000 0 0.403846 Constraints + Hall of Fame. Forbid mutating the first TMD residue (a feasibility callable, penalized), and read the single-objective best-k variants:
tmd_start = int(wt["tmd_start"].iloc[0]) df_con = seqopt.run(df_seq=wt, df_feat=df_feat, objectives=objectives, pop_size=40, n_gen=20, n_mut_max=5, region="tmd", constraints=[lambda g: tmd_start not in g], penalty="delta", hof_size=5) print("Hall of Fame (best by substrate gain):", seqopt.hall_of_fame_) aa.display_df(df_con, n_rows=10, show_shape=True)
Hall of Fame (best by substrate gain): ['C49L+S58R+A59R', 'C49L+I55C+S58R+A59R', 'I43V+C49L+I55W+A59R', 'I43V+C49L+A50G+I55W+A59R', 'I43V+C49L+A50S+I55W+A59R'] DataFrame shape: (6, 8)
entry variant n_mut sequence_mut substrate parsimony rank crowding 1 Q14802 0 MQKVTLGLLVFLAGF...PGETPPLITPGSAQS 0.000000 0.000000 0 inf 2 Q14802 C49L+S58R+A59R 3 MQKVTLGLLVFLAGF...PGETPPLITPGSAQS 36.000000 3.000000 0 inf 3 Q14802 A59R 1 MQKVTLGLLVFLAGF...PGETPPLITPGSAQS 16.000000 1.000000 0 0.388889 4 Q14802 C49M+A59R 2 MQKVTLGLLVFLAGF...PGETPPLITPGSAQS 27.000000 2.000000 0 0.319444 5 Q14802 A59K 1 MQKVTLGLLVFLAGF...PGETPPLITPGSAQS 16.000000 1.000000 0 0.319444 6 Q14802 S58R+A59R 2 MQKVTLGLLVFLAGF...PGETPPLITPGSAQS 27.000000 2.000000 0 0.291667 Any predictor as an objective. Besides the features-model (
delta_pred), an objective can be anycallable(sequence) -> float— a scikit/torch model or a sequence-level tool / web API (e.g. a topology or signal-peptide predictor such as Phobius / TMHMM). Results are cached per variant. Here a simple aromatic-stretch proxy:import re def aromatic_stretch(sequence): return float(max((len(r) for r in re.findall(r"[FWY]+", sequence)), default=0)) obj_ext = [("substrate", "max", "delta_pred"), ("aromatic", "min", aromatic_stretch), # external predictor / API goes here ("parsimony", "min", "n_mut")] df_ext = seqopt.run(df_seq=wt, df_feat=df_feat, objectives=obj_ext, pop_size=30, n_gen=15, n_mut_max=4, region="tmd") aa.display_df(df_ext, n_rows=10, show_shape=True)
DataFrame shape: (6, 9)
entry variant n_mut sequence_mut substrate aromatic parsimony rank crowding 1 Q14802 0 MQKVTLGLLVFLAGF...PGETPPLITPGSAQS 0.000000 3.000000 0.000000 0 inf 2 Q14802 C49F+G52L+S58R+A59K 4 MQKVTLGLLVFLAGF...PGETPPLITPGSAQS 36.000000 3.000000 4.000000 0 inf 3 Q14802 A59R 1 MQKVTLGLLVFLAGF...PGETPPLITPGSAQS 16.000000 3.000000 1.000000 0 inf 4 Q14802 C49F+A59K 2 MQKVTLGLLVFLAGF...PGETPPLITPGSAQS 26.000000 3.000000 2.000000 0 0.305556 5 Q14802 C49F+S58R+A59K 3 MQKVTLGLLVFLAGF...PGETPPLITPGSAQS 31.000000 3.000000 3.000000 0 0.259259 6 Q14802 A59K 1 MQKVTLGLLVFLAGF...PGETPPLITPGSAQS 16.000000 3.000000 1.000000 0 0.231481 Further parameters.
initchooses the population start ('random'or'suggest', a warm start from the top single mutations),to_aarestricts the substitution alphabet,seedmakes a single run reproducible (overriding the constructorrandom_state), andjmd_n_len/jmd_c_lenset the split geometry used to place mutations and build the CPP features. Kept small (few generations) so it finishes fast:df_further = seqopt.run(df_seq=wt, df_feat=df_feat, objectives=objectives, algorithm="nsga2", pop_size=12, n_gen=4, n_mut_max=4, region="tmd", init="suggest", # warm start from top single mutations to_aa=["A", "L", "V", "K", "R"], # restrict substitution alphabet seed=1, # reproducible run (overrides random_state) jmd_n_len=10, jmd_c_len=10) # split geometry aa.display_df(df_further, n_rows=10, show_shape=True)
DataFrame shape: (4, 8)
entry variant n_mut sequence_mut substrate parsimony rank crowding 1 Q14802 S58R+A59K 2 MQKVTLGLLVFLAGF...PGETPPLITPGSAQS 23.000000 2.000000 0 inf 2 Q14802 0 MQKVTLGLLVFLAGF...PGETPPLITPGSAQS 0.000000 0.000000 0 inf 3 Q14802 A59R 1 MQKVTLGLLVFLAGF...PGETPPLITPGSAQS 16.000000 1.000000 0 0.597826 4 Q14802 A59K 1 MQKVTLGLLVFLAGF...PGETPPLITPGSAQS 16.000000 1.000000 0 0.402174