Join%20Chat

logo

Parameter Sweeping for Biochemical Reactions

This tutorial

The code for this tutorial is here: vilar.py

Motivation

Stochastic simulations of biochemical reaction networks can capture intrinsic noise. This matters when the number of molecular species is low. These simulations are expensive, and the noise makes parameter evaluation harder. For this reason, Fredrik Wrede proposes a machine-learning-based interactive method for model exploration through parameter sweeps on a multi-node CPU network. See the corresponding video.

Our idea is similar, but it uses a different kind of human interaction. Here the user designs a multi-objective fitness function, and optimization then guides the parameter sweep. The goal is not to find a single optimum. Instead, we introduce some "bias" into the sweep so it moves toward "interesting" model properties. This is especially useful in high-dimensional parameter spaces, where exhaustive scanning is not practical. The two approaches could also be combined. The time series generated during optimization could feed the machine-learning-based interactive method.

The Vilar Oscillator

In Mechanisms of noise-resistance in genetic oscillators Jose M.G.Vilar presented a biochemical model of a "circadian clock" that lets organisms maintain an internal sense of daily time. This model can be simulated using GillesPy2, see VilarOscillator.py. The Vilar model has 15 parameters, and we want to understand:

  • Is the oscillating behavior of the model dependent on specific parameter settings?

  • Can we find parameters which can affect the oscillating property of the model negatively?

  • Or does the model have "self-regulating" properties preserving the steady oscillation?

Multi-Objective Function

The core idea is to use multi-objective optimization, which requires a fitness function. The question is how to analyze the result of one simulation run, which produces time series for each species, so that we can characterize the "steady oscillation" property.

    # multi processing result list
    results = mp.Manager().list()

    class fcmaes_problem():

        def __init__(self):
            self.bounds = get_bounds(VilarOscillator(), 100)
            self.dim = len(self.bounds.ub)

        def fitness(self, x):
            model = VilarOscillator()
            set_params(model, x)
            res = model.run(algorithm = "SSA")
            # store params, result tuple
            results.append((x, res))
            R = res['R'] # time series for R
            r_mean = np.mean(R)
            r_over = np.array([r for r in R if r > r_mean])
            ilocs_max = argrelextrema(r_over, np.greater_equal, order=3)[0]
            freq = len(ilocs_max) / len(R)
            peak_dists = np.array([ilocs_max[i] - ilocs_max[i-1] for i in range(1, len(ilocs_max))])
            sdev_peak_dist = np.std(peak_dists)
            peaks = (r_over - r_mean)[ilocs_max]
            sdev_amp = np.std(peaks)
            # maximize sdev_peak_dist and sdev_amp
            return [-sdev_peak_dist, -sdev_amp, freq]----

There are more sophisticated options, for example FFT-based analysis. Here we keep it simple and use scipys argrelextrema to identify maxima in the R species. From these peaks we compute the standard deviation of the amplitude and the standard deviation of the peak time distances. Small values for these standard deviations indicate a steady oscillation, so we use them as objectives to maximize. We also add "frequency" as a third objective. This increases the set of non-dominated solutions, the so-called pareto front.

The optimizer can handle different scalings of both parameters and objectives. We could also define objectives as constraints to exclude specific model properties from the sweep. Constraints are given priority while they are violated and are ignored once they are satisfied. Calling a multi-objective optimizer and plotting the result is straightforward:

    popsize = 64 # population size of the evolutionary algorithm
    max_evaluations = popsize*16 # maximum number of evaluation
    # popsize = 256 # population size of the evolutionary algorithm
    # max_evaluations = popsize*96 # maximum number of evaluation
    nobj = 3 # number of objectives
    ncon = 0 # number of constraints
    # stores all values; if capacity is reached, content is replaced by the pareto front
    store = mode.store(problem.dim, nobj, capacity=max_evaluations)
    # perform the parameter sweep by multi objective optimization
    xs, ys = mode.minimize(mode.wrapper(problem.fitness, 3, interval=64, store=store),
                                    nobj, ncon,
                                    problem.bounds, popsize = popsize,
                                    max_evaluations = max_evaluations,
                                    nsga_update=True, workers=min(popsize, mp.cpu_count()))
    # save last population of the evolutionary algorithm
    np.savez_compressed("sweep", xs=xs, ys=ys)
    # save all evaluations
    xs, ys = store.get_xs(), store.get_ys()
    np.savez_compressed("sweep_all", xs=xs, ys=ys)
    # show results
    for x, res in results[:10]:
        print(list(x), list(res['R']))
    moretry.plot("sweep_all", ncon, xs, ys) # plot 2d
    plot3d(xs, ys, "sweep_3d") # plot 3d

The workers parameter enables parallel simulations through Python multiprocessing. On a 16 core AMD 5950 CPU, this gives about 8 simulations per second. This example stays on one machine, but the same approach could also use dask to distribute optimization runs across multiple nodes, as in this paper, and then merge the results.

The store object keeps all parameter settings together with their objective values. We set capacity=max_evaluations so every evaluation fits. store resolves parallel access conflicts with a multiprocessing.Lock. The simulation time series and their corresponding parameter settings are collected in results = mp.Manager().list().

The computed pareto front shows that the maximal standard deviation of the amplitude is around 260, while the maximal standard deviation of the peak time distance is only around 2.95:

[-2.9529177407085023, -95.4039656757866, 0.04488778054862843] [3963.55534, 5.0, 0.07189, 291.00951, 2333.53036, 176.64492, 100.0, 769.04945, 45.31349, 11.30336, 20.92418, 39.33244, 100.0, 3512.29386, 3351.37915]
[-2.951745715363383, -105.61892834474017, 0.04488778054862843] [3192.0246, 21283.45224, 0.40744, 2282.94153, 2449.13288, 14.40633, 85.15188, 1000.0, 8.95256, 7.73864, 71.91611, 0.02, 10.65094, 908.59928, 7486.3519]
[-2.9056680082890516, -118.76987812832546, 0.04488778054862843] [4750.8944, 21187.26512, 0.30343, 3053.65532, 958.32784, 500.0, 2.02548, 0.1, 17.85172, 5.61477, 70.8133, 101.64201, 38.998, 0.5, 2709.01493]
[-2.829650228424869, -126.14233583095285, 0.04488778054862843] [1844.98312, 8018.1519, 0.39544, 1950.09358, 199.70482, 0.05, 22.21654, 776.71529, 19.2612, 14.99067, 43.73244, 146.38544, 61.3209, 2707.33989, 9282.14773]
[-2.8272034919289215, -133.07568857191342, 0.04488778054862843] [2727.76445, 50000.0, 0.50671, 1304.37028, 2589.98677, 464.17394, 85.61988, 630.31469, 35.83535, 4.50604, 89.76378, 49.71666, 39.89323, 975.28531, 10000.0]
[-2.6299556396765835, -135.47628100402463, 0.04738154613466334] [5000.0, 30524.08898, 0.73944, 3784.11623, 252.9115, 500.0, 37.03655, 832.33873, 12.4825, 7.32541, 39.97521, 78.18922, 82.90321, 4001.87626, 7163.73231]
[-2.55255862921022, -101.80509488110111, 0.0399002493765586] [1679.00163, 49172.18683, 0.0001, 698.72041, 189.6883, 231.44121, 91.28288, 827.96068, 19.55392, 9.63559, 59.86862, 57.8062, 75.01048, 5000.0, 2953.52827]
[-2.4462982238476156, -157.9285458855882, 0.04239401496259352] [1616.84502, 25773.2103, 0.19491, 3311.77833, 1702.56557, 72.51257, 48.11612, 297.08423, 18.25544, 11.40069, 31.05834, 0.02, 18.77209, 2323.45674, 2993.41434]
[-2.434930982366623, -116.0010607440725, 0.0399002493765586] [2909.06717, 8230.83586, 0.34597, 4137.00498, 0.5, 0.05, 53.08472, 0.1, 0.005, 16.94276, 28.20045, 45.02235, 22.52807, 127.21803, 1316.2461]
[-2.3795424396766327, -116.71493049306073, 0.0399002493765586] [0.5, 38009.30647, 0.82832, 4025.69335, 2842.63817, 426.54968, 29.56595, 284.86072, 38.63421, 0.002, 31.32197, 200.0, 95.21752, 0.5, 10000.0]
[-2.3570226039551585, -168.11016430825947, 0.0399002493765586] [2226.31818, 50000.0, 1.0, 0.5, 2520.63258, 63.26119, 91.53521, 131.19077, 32.69053, 6.16228, 100.0, 89.46733, 61.56866, 741.69312, 6533.65796]
[-2.314390067400706, -207.69080176021873, 0.04488778054862843] [1738.84673, 28325.49892, 0.0001, 4153.941, 0.5, 297.23208, 47.08712, 285.88175, 18.25992, 0.002, 75.07047, 127.18269, 40.57461, 0.5, 9832.98613]
[-2.1758618981911515, -180.4157060502754, 0.04239401496259352] [5000.0, 50000.0, 0.09536, 1102.04172, 507.11942, 131.34008, 23.0144, 825.01715, 22.50374, 10.67058, 0.01, 0.02, 92.29957, 1366.8583, 965.63167]
[-1.3942300925673667, -114.8690849039317, 0.03740648379052369] [3966.74083, 8256.54672, 1.0, 827.22444, 3410.66379, 482.81114, 23.71309, 643.26172, 28.77231, 20.0, 42.67218, 0.02, 88.48254, 4560.1138, 7150.24457]
[-1.3420210162897097, -146.56429760802368, 0.03740648379052369] [842.00082, 50000.0, 0.19752, 650.88578, 1492.86002, 64.03229, 100.0, 630.28532, 15.78265, 9.23157, 0.01, 25.39473, 58.1836, 1795.3776, 3223.63713]
[-1.3228756555322954, -216.46190839375674, 0.04239401496259352] [3316.9303, 49172.18683, 0.59904, 698.72041, 189.6883, 0.05, 100.0, 827.96068, 19.55392, 9.63559, 59.86862, 153.03553, 97.46121, 5000.0, 2953.52827]
[-1.2892719737209144, -186.11068615208526, 0.0399002493765586] [2380.31009, 36173.28215, 0.0001, 4032.86154, 2955.06148, 300.79915, 76.97691, 976.55334, 50.0, 0.002, 65.68229, 100.41598, 53.51887, 4936.06836, 7099.12308]
[-1.263700429496724, -147.90483426852552, 0.03740648379052369] [5000.0, 41614.45492, 0.0001, 2042.40438, 3849.19248, 155.90764, 58.34843, 364.50473, 13.89905, 2.23895, 100.0, 152.30698, 55.85457, 4662.94334, 1.0]
[-1.254325848148452, -190.04792775442198, 0.0399002493765586] [3981.57454, 40087.32536, 0.307, 319.81802, 2846.01873, 188.623, 40.72906, 522.07154, 0.005, 11.26821, 35.0033, 97.53761, 19.81748, 2712.01541, 6838.85863]
[-1.1925695879998877, -198.70734605192632, 0.0399002493765586] [5000.0, 25412.65676, 0.0001, 3679.39956, 2835.75002, 208.42794, 97.36303, 894.59821, 23.4597, 4.90817, 19.93437, 95.33524, 15.36248, 2324.47721, 10000.0]
[-1.178030178747903, -151.3895049936495, 0.03740648379052369] [1363.62001, 31011.23828, 0.48028, 4863.33951, 963.52964, 213.70395, 30.89808, 358.17736, 50.0, 13.69323, 57.41104, 109.74502, 88.74388, 1514.7859, 5920.81353]
[-1.0873004286866728, -211.57766747698116, 0.0399002493765586] [0.5, 44679.14821, 0.65303, 1910.94801, 2314.8589, 173.2861, 55.17358, 367.67874, 0.005, 1.1258, 51.20502, 157.59849, 100.0, 4478.88327, 1.0]
[-1.0749676997731399, -222.04714272818285, 0.0399002493765586] [3247.96439, 1352.80086, 0.13737, 2351.74198, 2294.67424, 279.65285, 98.58432, 503.23886, 26.17302, 15.31982, 47.78622, 87.54788, 100.0, 2344.1427, 2758.37033]
[-1.0326308782000686, -194.84854517177067, 0.03740648379052369] [459.55155, 50000.0, 0.76135, 4863.33951, 963.52964, 213.70395, 24.98266, 242.57983, 50.0, 2.9316, 57.41104, 200.0, 96.65515, 1514.7859, 10000.0]
[-1.019803902718557, -234.11829274962687, 0.0399002493765586] [5000.0, 44117.05737, 0.90691, 4137.00498, 0.5, 138.02771, 98.00668, 346.56105, 13.56542, 17.50474, 34.12235, 0.02, 19.33328, 492.70916, 10000.0]
[-0.9354143466934853, -236.54461820422816, 0.04239401496259352] [3932.05576, 35815.82508, 1.0, 1955.83674, 2910.72433, 169.00539, 11.68632, 0.1, 50.0, 12.20671, 98.82335, 0.02, 67.57933, 3636.91003, 9.92914]
[-0.9333240326917549, -254.47453152964408, 0.04239401496259352] [2814.62758, 50000.0, 0.58636, 3204.78046, 1270.30639, 135.21917, 29.06001, 917.14848, 39.93926, 0.002, 0.01, 104.66887, 73.35097, 382.12618, 3387.31236]
[-0.9285592184789413, -244.861113376032, 0.0399002493765586] [3670.84244, 822.05728, 0.201, 3462.9566, 1997.32353, 256.3988, 86.54088, 875.1952, 0.005, 4.11743, 34.55674, 84.21904, 37.88851, 2254.81908, 1.0]
[-0.8637671850678283, -256.2949884552299, 0.04239401496259352] [4185.77373, 22788.5206, 0.53141, 0.5, 0.5, 173.65096, 46.7135, 252.04982, 24.21218, 13.53778, 82.29212, 104.30263, 82.92906, 887.3639, 7338.28102]
[-0.618241233033047, -258.4637305309973, 0.0399002493765586] [2860.71177, 29392.31481, 0.55805, 3573.22623, 0.5, 357.77082, 85.8377, 515.86832, 45.87822, 6.8493, 48.78439, 90.04616, 70.66459, 245.69653, 10000.0]

In particular, the standard deviation of the peak time distances stays stable, although we do see some amplitude deviations.

sweep

Now let us try the opposite direction. We want to identify parameters that stabilize the oscillation and minimize both standard deviations:

        def fitness(self, x):
            ...
            # minimize sdev_peak_dist and sdev_amp
            return [sdev_peak_dist, sdev_amp, freq]

This time the parameter sweep is guided in the opposite direction, and it finds many low-standard-deviation solutions:

[0.33993463423951903, 85.05106462443608, 0.0399002493765586] [4667.13481, 46359.99664, 0.50975, 4963.54422, 11.66221, 0.05, 51.12464, 402.27105, 10.53585, 1.43816, 27.0709, 133.96803, 100.0, 2996.65492, 7754.62117]
[0.4, 74.98656129600823, 0.0399002493765586] [4085.55047, 41534.06242, 0.25192, 266.76638, 4828.90463, 421.50792, 59.04809, 0.1, 26.36669, 16.87143, 60.35491, 86.88787, 78.07726, 2996.71963, 4881.88487]
[0.41032590332414487, 114.08737392406263, 0.03740648379052369] [5000.0, 50000.0, 0.23102, 0.5, 0.5, 249.3216, 0.01, 889.09787, 50.0, 20.0, 50.98781, 149.82714, 0.01, 3304.80047, 38.29661]
[0.410325903324145, 90.36125029876222, 0.03740648379052369] [3922.44784, 50000.0, 0.47131, 5000.0, 225.90615, 268.70649, 59.34419, 955.78435, 50.0, 20.0, 56.17379, 52.21683, 0.01, 2545.12168, 7786.30862]
[0.4422166387140533, 70.73870824202262, 0.0399002493765586] [0.5, 8884.6834, 0.14039, 2121.80486, 5000.0, 9.94685, 0.01, 515.15081, 31.46132, 9.10421, 81.49082, 137.09595, 0.01, 397.43259, 1.0]
[0.45175395145262565, 89.4535012667972, 0.03740648379052369] [3969.552, 35595.20059, 0.47131, 5000.0, 225.90615, 500.0, 59.34419, 669.16249, 45.03994, 4.21045, 56.17379, 52.21683, 18.96992, 522.35537, 8620.9769]
[0.4573660169594892, 83.93870779721752, 0.03740648379052369] [3750.125, 5.0, 0.31521, 1321.85626, 1259.17944, 311.64924, 9.56078, 355.97666, 33.78151, 11.03842, 0.01, 145.53896, 71.08417, 3806.81813, 8101.10279]
[0.47140452079103173, 53.79616476803899, 0.0399002493765586] [4877.2711, 50000.0, 0.37939, 509.91588, 5000.0, 95.47948, 0.01, 1000.0, 50.0, 17.43156, 71.17818, 169.94934, 15.65189, 3889.87649, 38.29661]
[0.4791574237499549, 70.6209285945431, 0.03740648379052369] [2148.41722, 3823.11184, 0.76019, 0.5, 2526.24176, 85.91607, 0.01, 609.97549, 44.41766, 20.0, 65.27637, 49.73482, 26.75771, 73.16505, 10000.0]
[0.48989794855663554, 46.94261789674709, 0.0399002493765586] [4486.38357, 41043.38753, 0.30004, 1153.50072, 1841.51075, 0.05, 48.91541, 388.84663, 34.56827, 19.82925, 46.43581, 194.80536, 11.18324, 857.99832, 3642.3788]
[0.4948716593053934, 65.56794609834562, 0.03740648379052369] [3026.12731, 5.0, 1.0, 4955.40119, 3473.91023, 16.57025, 88.00157, 478.59413, 23.4971, 0.002, 15.32885, 91.05324, 79.769, 315.13728, 10000.0]
[0.498887651569859, 41.541395017500314, 0.0399002493765586] [2401.0277, 7612.15779, 0.31682, 79.56769, 0.5, 471.41371, 95.95611, 336.75528, 3.92945, 17.79122, 7.11661, 191.06115, 36.05853, 299.63769, 152.02355]
[0.5, 63.90305157032801, 0.03740648379052369] [3521.04674, 50000.0, 0.33635, 4672.30343, 4993.83557, 321.52988, 53.77845, 969.06066, 50.0, 20.0, 100.0, 103.33393, 95.49405, 2637.95239, 10000.0]
[0.573488351136175, 41.42293295989553, 0.0399002493765586] [2772.0545, 8312.37421, 0.35097, 3282.00071, 1731.19102, 19.8691, 89.37192, 596.8713, 12.83699, 20.0, 100.0, 200.0, 13.03374, 2080.06046, 3750.0288]
[0.6110100926607787, 40.37712842687058, 0.0399002493765586] [2700.9404, 43773.06096, 0.61493, 93.38473, 4686.28757, 67.37642, 61.25017, 720.21311, 1.1664, 20.0, 36.81052, 171.52996, 39.75133, 1951.84428, 5994.53237]
[0.6226998490772391, 56.23616866987532, 0.03740648379052369] [441.53957, 28266.85623, 0.4996, 0.5, 4241.41337, 321.28582, 50.78861, 1000.0, 9.58572, 12.72225, 63.05839, 36.69093, 88.46005, 4363.74585, 1.0]
[0.6388765649999398, 50.01892974997899, 0.03740648379052369] [2786.56457, 1752.71181, 0.7316, 4583.21976, 0.5, 73.88818, 67.44368, 454.1061, 1.39891, 20.0, 100.0, 200.0, 14.71424, 2165.23854, 9916.8418]
[0.7034898429854359, 44.120239749524885, 0.03740648379052369] [775.25653, 38721.18599, 0.91891, 4604.08801, 4744.04525, 353.08363, 100.0, 901.34442, 16.91242, 2.915, 49.48101, 64.53518, 48.74173, 584.5451, 1388.3146]
[0.7180219742846006, 38.740674281044726, 0.0399002493765586] [1089.1402, 25002.5, 0.65476, 2623.74255, 4494.51243, 187.9306, 21.40189, 991.73702, 2.32626, 6.64121, 96.52458, 79.65669, 60.70325, 3015.74996, 10000.0]
[0.9970370305242863, 114.33385146944336, 0.034912718204488775] [2533.58606, 11377.76193, 0.95959, 245.79493, 3822.78805, 438.11378, 53.71136, 667.52735, 12.34801, 11.26597, 69.41253, 0.02, 43.43411, 595.77608, 7057.76595]
[1.071414482860317, 81.66344572296065, 0.034912718204488775] [5000.0, 5.0, 0.91981, 887.67146, 2719.74535, 286.77574, 11.60726, 0.1, 23.14101, 20.0, 92.87981, 120.10005, 45.65844, 2489.06925, 10000.0]
[1.0986812966989, 79.28378376028063, 0.034912718204488775] [1593.45681, 41602.71265, 0.0001, 1503.00018, 5000.0, 0.05, 27.25116, 504.13417, 50.0, 20.0, 58.40439, 0.02, 9.11069, 3414.72936, 4293.5848]
sweep2

Visualizing all evaluations

If we visualize all stored evaluations from the optimization-driven parameter sweep, there is not much difference between the two runs. First, the run that maximizes the standard deviations:

sweep all

Then the run that minimizes the standard deviations:

sweep all2

Finally, a 3D view:

sweep3d

Conclusion

  • Multi-objective optimization can speed up the parameter sweep of a stochastic biochemical reaction network model.

  • Simulations are executed in parallel utilizing all processor cores.

  • The objective function guides the parameter sweep toward "interesting" model properties or, in our example, tries to destroy them.