feat: implement Fast-SNP, speed up loopless FVA #1444
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
TL;DR
This PR implements Fast-SNP algorithm for adding loopless constraints. It consists of two key optimizations:
As a result a 10-100x (or more) speed-up for loopless FVA is achieved.
This PR is somewhat similar to #841, but differs in implementation.
Code modifications
One of the key optimizations is that
add_loopless
andflux_variability_analysis
functions use only reactions that can be a part of a cycle (we call them cyclic). This greatly improves performance due to the number of cyclic reactions is usually much smaller than the number of internal reactions.Function
find_cyclic_reactions
We add a new function
find_cyclic_reactions
which finds all reactions in the model that can be a part of a cycle.Also for each such reaction we identify if it can have positive and negative flux in a cycle. This is tested by restricting boundary reaction fluxes to zero and optimizing flux through each of the internal reactions.
There are two methods (
method
parameter) that can be called:basic
: a straightforward procedure that iterates over all of the reactions;optimized
: an optimized version inspired by Fast-SNP random weights idea, which discovers many cyclic reactions in one go.The second method uses less LP programs and usually works at least 2x faster. It is a custom procedure, so we don't have a citation for the algorithm.
Function
add_loopless
We introduced new
method
parameter. Ifmethod="fastSNP"
we:find_cyclic_reactions
.If
method="original"
then the current SVD-based algorithm is ran on all of the internal reactions.Function
flux_variability_analysis
We made some optimizations for this function if
loopless=True
.find_cyclic_reactions
function. Reactions which are not cyclic are optimized using the basic FVA mode—their extreme values are guaranteed to be achievable without using cycle fluxes. This greatly improves the performance of the function even when using the CycleFreeFlux method.add_loopless
regime to this function, which adds loopless constraints (re-using the calculated cyclic reactions andadd_loopless
function) to solve loopless FVA for the cyclic reactions. In this regime the exact FVA bounds are calculated (unlike for the CycleFreeFlux method).Performance
FVA quick example
For the quick demonstration of the performance improvement we assembled the following script, which solves FVA for a single reaction with the original and new Fast-SNP loopless constraints.
Here are the example run times for the tested model/reaction pairs (CPLEX solver was used for MILP):
iIT341
, reactionACKr
:iAF692
, reactionMDHy
:iNJ661
, reactionNADTRHD_copy1
:The fastest complete loopless FVA
The most efficient way to calculate loopless FVA for all of the reactions is to directly call
flux_variability_analysis
function, without using explicitadd_loopless
:Discussion points
There are several points regarding the API we wanted to highlight for discussion:
flux_variability_analysis(model, loopless=True, params_add_loopless={})
is the most efficient way to calculate loopless FVA. The functionflux_variability_analysis
identifies which method (add_loopless
-based or CycleFreeFlux) will be used based on theparams_add_loopless
presence. This form enables backward-compatibility, but is a bit clunky. A better way could be to makeloopless
parameter deprecated and replace it with a new string-basedmethod
parameter to control the behavior, with the corresponding backward compatibility-handling procedure that setsmethod="CycleFreeFlux"
ifloopless=True
is present.add_loopless
function the originaluse_cyclic=True
to enable that, however we decided not to implement it. Our thinking is thatmethod=original
is provided for the reproducibility of the previous behavior, but ultimately shouldn't be used: the Fast-SNP algorithm should always provide a better performance.flux_variability_analysis(loopless=True)
can still be ran when the full loopless FVA is not feasible. Thus we decided to implement running CycleFreeFlux only for the cyclic reactions, which significantly improves the performance. This changes the behavior, but should produce the same results as before.We would be happy to hear your feedback and are ready to implement any additional changes to API/docs/tests/etc that are needed for this PR to be merged.