kcat sensitivity tuning#
MATLAB functions in GECKO/src/kcat_tuning of the GECKO toolbox.
Functions#
Function |
Summary |
|---|---|
The fewest-changes point whose RMSE is within |
|
Tune kcats against experimental data with CMA-ES. |
|
Score one ecModel's current kcats against evotune data. |
|
Add a carbon number field to the model. |
|
Index of the parameter each isozyme copy follows. |
|
Load experimental data for evotune (CMA-ES) kcat tuning. |
|
How many kcats differ from their prior by more than relTol. |
|
Score kcat with progressively more of it reverted to |
|
The tunable kcat subset plus everything derived |
|
Report which kcats the data can actually speak to. |
|
Build a tunableMask from a screenKcatLeverage report. |
|
Relax the most limiting kcats to reach a growth rate. |
|
Fit the average enzyme saturation factor of an ecModel. |
|
Round values in selected columns to six significant digits. |
|
Sweep priorPenaltyWeight and report the |
Reference#
bestParsimonious#
function point = bestParsimonious(points,varargin)
The fewest-changes point whose RMSE is within
tolerance of the best on a parsimonyFrontier.
Defaults to 2%, which is the scale of alternate-optimum noise typically seen from re-solving an LP with a slightly different kcat vector -- below that, two vectors are not meaningfully different in fit, so the sparser one wins.
Parameters
points: struct arrayas returned by parsimonyFrontier.
Name-Value Arguments
tolerance: doubledefault 0.02.
Returns
point: structthe chosen entry of points.
See also
parsimonyFrontier
cmaesKcatTuning#
function [ecModel,result] = cmaesKcatTuning(ecModel,varargin)
Tune kcats against experimental data with CMA-ES.
Configuration comes entirely from modelAdapter.params.evotune: trust tiers (kcatSources/sigma0logSource/sigma0logDefault), maxGrowthWeight, priorPenaltyWeight and tieIsozymes control the search, and maxGenerations/rmseThreshold are its stopping conditions.
Vendors a separable (diagonal-covariance) CMA-ES -- MATLAB has no built-in implementation and Global Optimization Toolbox doesn't ship one either. Separable rather than full-covariance because that is what geckopy's own port actually runs (cma's CMA_diagonal=True) and what its defaults were validated against; it is also lower risk to implement correctly. See the "CMA-ES core" local functions below.
Parameters
ecModel: structecModel with a populated ec.kcat.
Name-Value Arguments
modelAdapter: ModelAdaptera loaded model adapter (default: the current default model adapter).
evoData: structas returned by loadEvotuneData (default: loaded from modelAdapter).
tunableMask, screen, targetImpactSharecontrol which kcats are searched over, in order of precedence. Pass tunableMask to fix the set yourself. Otherwise, a mask is built by selectTunableMask at targetImpactShare (default 0.9), from screen if given, or from a fresh screenKcatLeverage call otherwise.
popsize: doubleCMA-ES population size. Defaults to 4 + floor(3*log(n)), the standard dimension-scaled default, so it adapts to however many free parameters the mask/tying produced.
nProc: doublenumber of parallel workers used to score each generation's candidates (default 1, i.e. serial). Requires Parallel Computing Toolbox for > 1.
seed: doubleif given, seeds the random draws for a reproducible run.
verbose: logicalwhether per-generation progress is printed (default true).
Returns
ecModel: structmutated: tunable rows carry the best kcat vector CMA-ES found, with kcat constraints already applied. MATLAB structs are copied on assignment, so -- unlike geckopy's in-place mutation -- this output must be captured by the caller.
result: structrxns/oldKcat/newKcat/groups cover the selected tunable set, tied groups included (their members share one value in newKcat). rmseTrace/objectiveTrace record the best-so-far plain RMSE and optimised objective per generation. nGenerations is how many generations actually ran. converged is true if rmseTrace(end) <= params.rmseThreshold.
Raises
errorif there are no tunable kcats, or fewer than two free parameters remain after masking and tying -- too little for CMA-ES to search over.
See also
screenKcatLeverage, selectTunableMask, tunePriorPenaltyWeight
evotuneScore#
function [objective,rmse,rmseDetail,ecModel] = evotuneScore(ecModel,evoData,varargin)
Score one ecModel's current kcats against evotune data.
Internal helper used by cmaesKcatTuning, screenKcatLeverage and tunePriorPenaltyWeight to score one ecModel (with a given set of kcats already applied via applyKcatConstraints) against the experimental growth and flux data. Fuses what geckopy splits into simulate.py (the FBA half) and distance.py (the RMSE half) into one function, matching this module's own predecessor abc_max.m -- there is no MATLAB test suite here to benefit from Python's testability-driven split.
Parameters
ecModel: structecModel with the current candidate kcats already applied (via applyKcatConstraints). Not mutated.
evoData: structstructure with experimental data to be used, as returned by loadEvotuneData.
Name-Value Arguments
modelAdapter: ModelAdaptera loaded model adapter (default: the current default model adapter).
maxGrowthWeight: doublerelative weight of the max-growth dataset against the flux dataset: (rmseFlux + w*rmseMaxGrate) / (w+1) (default 1.0).
priorPenaltyWeight: doubleweight on a prior term added to the objective (default 0, i.e. the objective equals the plain RMSE). See kcatVec/kcat0/sigma0log below.
kcatVec, kcat0, sigma0log: doubleonly used when priorPenaltyWeight > 0: the tunable kcat subset's current values, prior values, and per-kcat prior log-space std dev, all the same length and in the same order. The penalty added to the objective is priorPenaltyWeight * mean((log(kcatVec./kcat0)./sigma0log).^2).
Returns
objective: doublermse, plus the prior penalty term when priorPenaltyWeight > 0. This is what a search should minimise.
rmse: doublethe plain (unpenalised) average RMSE, always comparable across runs regardless of priorPenaltyWeight.
rmseDetail: cellper-condition RMSE values, labelled by data type and condition index.
ecModel: structthe input ecModel, with ec.excarbon cached on it if it wasn't already present. MATLAB structs are copied on assignment, not shared by reference like a Python object, so callers scoring many candidates in a loop should carry this output back in as the base copy for the next candidate, rather than recomputing excarbon (an expensive metabolite-formula scan) on every single call.
See also
cmaesKcatTuning, screenKcatLeverage, loadEvotuneData
fillCarbonNum#
function model = fillCarbonNum(model)
Add a carbon number field to the model.
Adds a model.excarbon field with the carbon number of each exchange reaction, used to weight the RMSE during evotune kcat tuning.
Parameters
model: structecModel that was generated by makeEcModel, or loaded from an earlier run. Not compatible with ecModels generated by earlier GECKO versions (pre 3.0).
Returns
model: structecModel with an added model.excarbon field.
isozymeTieMap#
function tieMap = isozymeTieMap(rxnIds,kcat0,varargin)
Index of the parameter each isozyme copy follows.
A reaction catalysed by several isozymes gets one ec.rxns row per isozyme, each with its own kcat. Where the kcat assignment could tell them apart, those are genuinely different parameters; where it could not, every copy shares the same value from the same source, and an untied search is then free to invent a difference between them that means nothing biologically. Copies group together (and share one parameter) when they share a base reaction (the part of the rxn id before its "EXP" isozyme suffix), a prior value, and -- when sources is given -- a source.
Parameters
rxnIds: cellec.rxns entries.
kcat0: doubleprior kcat per row, same length as rxnIds.
Name-Value Arguments
sources: cellec.source entries, same length as rxnIds (default: not used, so grouping ignores source).
relTol: doublerelative tolerance for treating two prior values as equal (default 1e-9).
Returns
tieMap: doubleone entry per position. tieMap(i) == i for a free parameter, otherwise the (lower) index of the representative it follows.
See also
cmaesKcatTuning, screenKcatLeverage
loadEvotuneData#
function evoData = loadEvotuneData(varargin)
Load experimental data for evotune (CMA-ES) kcat tuning.
Reads the three experimental data files used by cmaesKcatTuning from the data subfolder of the model directory defined by the model adapter. Each file is optional: a missing file yields [] (fluxData/maxGrate) or an empty cell array (zeroFlux) rather than an error.
Name-Value Arguments
modelAdapter: ModelAdaptera loaded model adapter (default: the current default model adapter).
Returns
evoData: structstructure with the loaded experimental data.
Notes
The evoData structure has the following fields:
fluxData : flux data loaded from evotuneFluxData.tsv, with the biomass reaction id stored in fluxData.biomass. [] if that file isn't present.
maxGrate : maximum growth-rate data loaded from evotuneMaxGrowth.tsv, with the biomass reaction id stored in maxGrate.biomass. [] if that file isn't present.
zeroFlux : reaction IDs assumed to carry zero flux, loaded from evotuneZeroExch.tsv. Empty cell array if that file isn't present.
See also
cmaesKcatTuning, evotuneScore
nChanged#
function n = nChanged(kcat,kcat0,varargin)
How many kcats differ from their prior by more than relTol.
The 2% default matches the threshold MATLAB's own per-source "unchanged" reporting has historically used.
Parameters
kcat, kcat0: doubletuned and prior kcat vectors, same length.
Name-Value Arguments
relTol: doubledefault 0.02.
Returns
n: doublecount of entries where abs(log(kcat./kcat0)) > log1p(relTol).
See also
tunePriorPenaltyWeight, parsimonyFrontier
parsimonyFrontier#
function points = parsimonyFrontier(scoreFcn,kcat,kcat0,sigma0log,varargin)
Score kcat with progressively more of it reverted to
prior.
A tuned kcat vector that fits well by rewriting every parameter is not a useful answer: changes should be few, and concentrated in the parameters least confident about. This sweeps a revert-threshold (in units of each kcat's own prior sigma) and reports how RMSE trades off against how many kcats stayed at their tuned value, so a sparser result within noise of the best fit can be preferred over the raw tuner output. See bestParsimonious to pick a point off this frontier.
Parameters
scoreFcn: function_handlescores one kcat vector; called once per threshold. Build it against a specific ecModel/evoData, e.g. scoreFcn = @(vec) evotuneScoreVector(ecModel,tunableIdx, ... ecRxnIdsTunable,evoData,modelAdapter,params,vec); where evotuneScoreVector applies vec to ecModel.ec.kcat(tunableIdx), calls applyKcatConstraints, and returns evotuneScore's plain rmse.
kcat, kcat0, sigma0log: doubletuned vector, prior vector, and per-kcat prior sigma, all the same length.
Name-Value Arguments
thresholds: doublesigma cutoffs to sweep, ascending (default [0 1 2 3 4 6 8 10 15]). 0 reverts nothing, reproducing kcat's own score.
Returns
points: struct arrayone entry per threshold, in the order given: threshold, kcat (the partially-reverted vector), nChanged, meanDev (mean movement in sigma units), rmse.
See also
bestParsimonious, nChanged
resolveTunableContext#
function [tunableIdx,ecRxnIdsTunable,kcat0,groups,sigma0log,tieMap] = resolveTunableContext(ecModel,params,tunableMask)
The tunable kcat subset plus everything derived
from it: source-group trust tiers and isozyme tie groups.
Shared by screenKcatLeverage and cmaesKcatTuning so a screen and the tuning run it feeds are always looking at exactly the same parameters.
Parameters
ecModel: structecModel with a populated ec.kcat.
params: structmodelAdapter.params.evotune (kcatSources, sigma0logSource, sigma0logDefault, tieIsozymes).
tunableMask: logicalsame length as ecModel.ec.rxns; [] to consider every kcat > 0.
Returns
tunableIdx: doubleindices into ecModel.ec.rxns/ec.kcat of the tunable subset.
ecRxnIdsTunable : cell
:
kcat0 : double
:
groups: cellsource-group name per tunable row ('unlabelled' if unmatched).
sigma0log: doubleper-row prior std dev in log-space.
tieMap: doubleisozymeTieMap(ecRxnIdsTunable,kcat0,'sources',groups) if params.tieIsozymes, otherwise every row is its own free parameter.
See also
screenKcatLeverage, cmaesKcatTuning, isozymeTieMap
screenKcatLeverage#
function screen = screenKcatLeverage(ecModel,varargin)
Report which kcats the data can actually speak to.
No optimisation happens here -- this only says which parameters are worth curating or tuning, and can be read on its own well before any tuning run. Each tie-group (isozyme copies sharing a prior and a source move together when params.tieIsozymes) is perturbed up and down by fold, and its leverage is the largest resulting change in RMSE. Groups are ranked by that leverage weighted by sigma0log: a trusted source needs proportionally more measured effect to rank alongside an untrusted one.
Costs one simulation per condition per tie-group probed (two probes each, up and down) plus one baseline -- comparable in scale to a full tuning run's evaluation budget, not a cheap pre-check.
Parameters
ecModel: structecModel with a populated ec.kcat.
Name-Value Arguments
modelAdapter: ModelAdaptera loaded model adapter (default: the current default model adapter).
evoData: structas returned by loadEvotuneData (default: loaded from modelAdapter).
tunableMask: logicalrestrict which kcats are screened (default: every ec.kcat > 0).
fold: doublefold-change used to perturb each tie-group up and down (default 2.0).
nProc: doublenumber of parallel workers used to score candidate vectors (default 1, i.e. serial). Requires Parallel Computing Toolbox for > 1.
Returns
screen: tableone row per tie-group, sorted by rank descending: rxnId (the group's representative), nIsozymes, sourceGroup, kcat0, leverage, sigma0log, rank (leverage*sigma0log), cumLeverageShare (running total of rank as a fraction of its sum), and positions (a cell array of indices into ecModel.ec.rxns) -- what selectTunableMask needs to expand a row back into a mask.
See also
selectTunableMask, cmaesKcatTuning
selectTunableMask#
function mask = selectTunableMask(ecModel,screen,varargin)
Build a tunableMask from a screenKcatLeverage report.
Keeps the fewest highest-ranked groups whose combined leverage reaches targetImpactShare of the total -- a relative cutoff, so the same targetImpactShare selects a comparable quality of parameter set on any model, rather than an absolute leverage value or a fixed count calibrated on a different model's scale.
Pure and cheap -- no simulation, only screen is read.
Parameters
ecModel : struct
:
screen: tableas returned by screenKcatLeverage.
Name-Value Arguments
targetImpactShare: doubledefault 0.9. Not a validated universal constant -- check the resulting mask's size against screenKcatLeverage's own curve before trusting it on a new model.
Returns
mask: logicallength numel(ecModel.ec.rxns).
See also
screenKcatLeverage, cmaesKcatTuning
sensitivityTuning#
function [model, tunedKcats] = sensitivityTuning(model, varargin)
Relax the most limiting kcats to reach a growth rate.
Iteratively increases the kcat of whichever enzyme currently limits growth the most, until the model reaches the desired growth rate, updating the tuned values in model.ec.kcat.
Parameters
model: structan ecModel in GECKO 3 format (with ecModel.ec structure).
Name-Value Arguments
desiredGrowthRate: doublekcats will be relaxed until this growth rate is reached (default: the experimental growth rate params.gR_exp from the model adapter).
modelAdapter: ModelAdaptera loaded model adapter (default: the current default model adapter).
foldChange: doublekcat values will be increased by this fold-change (default 10).
protToIgnore: cellvector of protein ids to be ignored in tuned kcats, e.g. {'P38122', 'Q99271'} (default []).
verbose: logicalwhether progress should be reported (default true).
Returns
model: structecModel with updated model.ec.kcat.
tunedKcats: structstructure with information on tuned kcat values.
Notes
The tunedKcats structure has the following fields:
rxns : identifiers of reactions with tuned kcat values.
rxnNames : names of the reactions in tunedKcats.rxns.
enzymes : enzymes that catalyze the reactions in tunedKcats.rxns, whose kcat value has been tuned.
oldKcat : kcat values in the input model.
newKcat : kcat values in the output model, after tuning.
source : source label of each kcat, taken from the input model before tuning.
The model.ec.notes field will contain the original kcat value and source, unless the kcat has previously been set by sensitivityTuning, in which case the notes field remains unchanged.
Examples
% optional arguments may be given positionally or as name-value pairs:
[model, tunedKcats] = sensitivityTuning(model);
[model, tunedKcats] = sensitivityTuning(model, 'foldChange', 10);
[model, tunedKcats] = sensitivityTuning(model, desiredGrowthRate, modelAdapter, foldChange, protToIgnore, verbose);
sigmaFitter#
function [model, sigma] = sigmaFitter(model, varargin)
Fit the average enzyme saturation factor of an ecModel.
Scans candidate sigma-factors from 0.01 to 1 in steps of 0.01 and picks the one whose simulated growth rate best matches the provided experimental growth rate, then applies it to the model's protein pool constraint.
Parameters
model: structan ecModel in GECKO 3 format (with ecModel.ec structure).
Name-Value Arguments
growthRate: doublegrowth rate that should be reached (default: read from the model adapter).
Ptot: doubletotal cellular protein content in g/gDCW (default: read from the model adapter; if not specified there, 0.5 g/gDCW is assumed).
f: doubleestimated fraction of enzymes in the model (default: read from the model adapter; if not specified there, 0.5 is assumed).
makePlot: logicalwhether a plot should be made (default true).
modelAdapter: ModelAdaptera loaded model adapter (default: the current default model adapter).
Returns
model: structecModel with protein pool exchange upper bound adapted to the optimal sigma-factor.
sigma: doubleoptimal sigma-factor.
Examples
% optional arguments may be given positionally or as name-value pairs:
[model, sigma] = sigmaFitter(model);
[model, sigma] = sigmaFitter(model, 'makePlot', false);
[model, sigma] = sigmaFitter(model, growthRate, Ptot, f, makePlot, modelAdapter);
truncateValues#
function table = truncateValues(table,cols)
Round values in selected columns to six significant digits.
Rounds each value in the given columns of a cell array or table to six significant digits, based on the value's order of magnitude.
Parameters
table: cell or tablea cell array or table where some columns have values that should be truncated.
cols: doubleindex or indices of columns with values to be truncated.
Returns
table: cell or tablethe input with the selected columns rounded.
tunePriorPenaltyWeight#
function sweepTable = tunePriorPenaltyWeight(ecModel,varargin)
Sweep priorPenaltyWeight and report the
fit/reproducibility trade-off, so the value doesn't have to be picked blind.
priorPenaltyWeight charges for moving a kcat away from its prior, and mainly exists to keep large corrections reproducible across seeds rather than landing on an arbitrary point along a flat direction. How strong a charge is enough is model- and data-specific: too little and the search's largest corrections disagree across seeds; too much and the search stops finding real corrections at all. This runs cmaesKcatTuning at every value in candidates, each at every seed in seeds, all against the same tunable set, and reports both fit and cross-seed reproducibility per value.
This is exactly as expensive as it sounds: numel(candidates)*numel(seeds) full cmaesKcatTuning runs, none of them reusable across candidates. Pass a screen you already have to at least avoid recomputing that part.
Parameters
ecModel: structecModel with a populated ec.kcat. Not mutated (MATLAB structs are copied on assignment; every candidate/seed pair starts fresh from this input regardless of what earlier runs did).
Name-Value Arguments
modelAdapter, evoData, tunableMask, screen, targetImpactShare, popsize,
:
nProc, verboseas in cmaesKcatTuning.
candidates: doublepriorPenaltyWeight values to try (default [0.0, 0.01, 0.03, 0.1]).
seeds: doubleseeds to run each candidate at (default [0, 1]). Reproducibility columns pool every pair of seeds, so more than two sharpens them but costs proportionally more.
foldThreshold: doublea kcat counts as "moved" past this fold change from its prior (default 2.0). Matches nChanged's convention loosely (a different quantity, same spirit).
Returns
sweepTable: tableone row per candidate: priorPenaltyWeight, nSeeds, distanceMean, distanceSpread (max minus min across seeds), nChangedMean, nMovers (kcats past foldThreshold in either seed of a pair, summed over every pair), nBothMoved (past it in both), pctDirectionAgree, medianFoldSpread and maxFoldSpread (among movers, the largest fold-change between what two seeds landed on), and fitCost (distanceMean relative to the best candidate's, as a fraction).
See also
cmaesKcatTuning, screenKcatLeverage, selectTunableMask, nChanged