Optimize co-factor swap

Many metabolic enzymes depend on co-factors to function. Keeping balance between co-factors is important for homeostasis and that balance might interact unfavorable with metabolic engineering. And example of such balance is that between the two similar co-factor pairs NAD+/NADH and NADP+/NADPH. These co-factors are not only similar there are even enzymes that catalyze the same reaction but depend on different co-factors. Enzymes can also be enigneered to change their co-factor preference.

This opens an opportunity for using co-factor swaps to optimize production of a target metabolite. Figuring out which reactions should be subjected to co-factor swap can be done using the OptSwap algorithm. Briefly, the algorithm uses a genetic algorithm to test combinations of reactions to co-factor swap and then reports those that results in higher theoretical maximum yield.

We have implemented a variant of OptSwap in cameo and here we test it to reproduce the results in that paper for the aerobic yeast metabolism.

Get the model and import modules.


In [2]:
from cameo import models
model_orig = models.bigg.iJO1366



In [3]:
from cameo.strain_design.heuristic.evolutionary.optimization import CofactorSwapOptimization
from cameo.strain_design.heuristic.evolutionary.objective_functions import product_yield
from cameo.strain_design.heuristic.evolutionary.objective_functions import biomass_product_coupled_yield
from cameo.util import TimeMachine
from cameo.flux_analysis.analysis import flux_variability_analysis as fva

We make a copy of the model for easy testing.


In [4]:
model = model_orig.copy()

Make model changes as indicated in the paper.


In [6]:
for rid in ['FHL', 'CAT', 'SPODM', 'SPODMpp']:
    model.reactions.get_by_id(rid).knock_out()
model.reactions.POR5.lower_bound = 0
model.reactions.EX_glc__D_e.lower_bound = -10
model.reactions.EX_o2_e.lower_bound = -10

In [7]:
model.reactions.BIOMASS_Ec_iJO1366_core_53p95M.lower_bound = 0.1

In the paper they get 0.77 maximum product yield for l-threonine, which we also get


In [8]:
model.objective = model.reactions.EX_thr__L_e
(model.solve().f * 4) / (model.reactions.EX_glc__D_e.flux * 6)


Out[8]:
-0.7697175225044568

Let's run optswap using the CofactorSwapOptimization class in cameo. We use a product-yield function to evaluate how good a solution is, but there are other possibilities like biomass_coupled_product_yield.

GAPD is suggested in the paper as a suitable reactions


In [9]:
py = product_yield(model.reactions.EX_thr__L_e, model.reactions.EX_glc__D_e)
optswap = CofactorSwapOptimization(model=model, objective_function=py)

We also observe GAPD among the best options even though we considered many more reactions than in the original paper


In [10]:
optswap.run(max_evaluations=2000, max_size=2)


Starting optimization at Fri, 23 Sep 2016 10:03:41
The installed widget Javascript is the wrong version.
Finished after 00:03:20
Out[10]:

Result:

  • model: iJO1366
  • heuristic: GA
  • objective function: $$yield = \frac{EX_thr__L_e}{EX_glc__D_e}$$
  • simulation method: pfba
  • type: cofactor-swap
    • reactions knockouts fitness
      0 (TRSARr,) (TRSARr,) 0.805496
      1 (MDH, GAPD) (MDH, GAPD) 0.798618
      2 (PGCD, GAPD) (PGCD, GAPD) 0.788334
      3 (GAPD, KARA1) (GAPD, KARA1) 0.788223
      4 (IPMD, GAPD) (IPMD, GAPD) 0.788168
      5 (DHDPRy, GAPD) (DHDPRy, GAPD) 0.788157
      6 (KARA2, GAPD) (KARA2, GAPD) 0.788147
      7 (GTHOr, GAPD) (GTHOr, GAPD) 0.788141
      8 (P5CR, GAPD) (P5CR, GAPD) 0.788137
      9 (HISTD, GAPD) (HISTD, GAPD) 0.788133
      10 (3OAR140, GAPD) (3OAR140, GAPD) 0.788119
      11 (UAPGR, GAPD) (UAPGR, GAPD) 0.788112
      12 (GAPD, DHFR) (GAPD, DHFR) 0.788112
      13 (GAPD, GLUTRR) (GAPD, GLUTRR) 0.788109
      14 (GAPD,) (GAPD,) 0.788108
      15 (HACD2, GAPD) (HACD2, GAPD) 0.788061
      16 (HACD3, GAPD) (HACD3, GAPD) 0.788061
      17 (FADRx, GAPD) (FADRx, GAPD) 0.787821
      18 (ICDHyr, GAPD) (ICDHyr, GAPD) 0.786520
      19 (GND, GAPD) (GND, GAPD) 0.783726
      20 (GLUDy, 3OAR140) (GLUDy, 3OAR140) 0.783205
      21 (GLUDy,) (GLUDy,) 0.783197
      22 (ASAD,) (ASAD,) 0.782383
      23 (PDH,) (PDH,) 0.773008
      24 (MDH,) (MDH,) 0.772544
      25 (GLTPD, AKGDH) (GLTPD, AKGDH) 0.771216
      26 (PGCD,) (PGCD,) 0.769901
      27 (KARA1,) (KARA1,) 0.769811
      28 (SULRi,) (SULRi,) 0.769797
      29 (IPMD,) (IPMD,) 0.769766
      ... ... ... ...
      70 (TDPDRR,) (TDPDRR,) 0.769718
      71 (GLYCLTDx,) (GLYCLTDx,) 0.769718
      72 (OXCOAHDH,) (OXCOAHDH,) 0.769718
      73 (3OAR160,) (3OAR160,) 0.769718
      74 (ALDD19xr,) (ALDD19xr,) 0.769718
      75 (EAR161y,) (EAR161y,) 0.769718
      76 (LCARS,) (LCARS,) 0.769718
      77 (DMPPS,) (DMPPS,) 0.769718
      78 (TAGURr,) (TAGURr,) 0.769718
      79 (ALR2,) (ALR2,) 0.769718
      80 (2DGULRGx,) (2DGULRGx,) 0.769718
      81 (3OAR141,) (3OAR141,) 0.769718
      82 (2DGULRx,) (2DGULRx,) 0.769718
      83 (LALDO2x,) (LALDO2x,) 0.769718
      84 (EAR100x,) (EAR100x,) 0.769718
      85 (PDX5PS,) (PDX5PS,) 0.769718
      86 (EAR140y,) (EAR140y,) 0.769718
      87 (CDGR,) (CDGR,) 0.769718
      88 (3OAR60,) (3OAR60,) 0.769718
      89 (PERD,) (PERD,) 0.769718
      90 (BETALDHx,) (BETALDHx,) 0.769718
      91 (HPYRRx,) (HPYRRx,) 0.769718
      92 (GLYCLTDy,) (GLYCLTDy,) 0.769718
      93 (NADPHQR4,) (NADPHQR4,) 0.769718
      94 (UACMAMO,) (UACMAMO,) 0.769718
      95 (ALDD3y,) (ALDD3y,) 0.769718
      96 (PPPNDO,) (PPPNDO,) 0.769718
      97 (DOGULNR,) (DOGULNR,) 0.769718
      98 (3OAR161,) (3OAR161,) 0.769718
      99 (3OAR181,) (3OAR181,) 0.769718

      100 rows × 3 columns

The created optswap class has properties to check which reactions were tested and to perform swapping, let's list the first 10.


In [16]:
list(optswap.model.swapped_reactions)[0:10]


Out[16]:
['HACD2',
 'LCARS',
 '3OAR180',
 'TAGURr',
 'ALCD19',
 'DHCIND',
 'GLYCDx',
 'BSORy',
 'FLDR2',
 'SSALx']

In [32]:
optswap.model.reactions.EX_thr__L_e.model = optswap.model
optswap.model.objective = optswap.model.reactions.EX_thr__L_e
original = (optswap.model.solve().f * 4) / (-optswap.model.reactions.EX_glc__D_e.flux * 6)
with TimeMachine() as tm:
    optswap.model.swap_reaction('GAPD', tm)
    swapped = (optswap.model.solve().f * 4) / (-optswap.model.reactions.EX_glc__D_e.flux * 6)

print("product/substrate yield without swap: {}\nproduct/substrate yield with swap: {}".format(original, swapped))


product/substrate yield without swap: 0.7697175225044552
product/substrate yield with swap: 0.7881083210964924