{ "cells": [ { "cell_type": "markdown", "id": "6db486e6-359f-49d1-9d05-c914e29d71a2", "metadata": {}, "source": [ "# 4a. Numerical solution of FFLs\n", "\n", "
" ] }, { "cell_type": "code", "execution_count": 1, "id": "162d234d-7849-42b1-bb31-0bb3d3092ddf", "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", "
\n", " \n", " Loading BokehJS ...\n", "
\n" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "application/javascript": [ "(function(root) {\n", " function now() {\n", " return new Date();\n", " }\n", "\n", " const force = true;\n", "\n", " if (typeof root._bokeh_onload_callbacks === \"undefined\" || force === true) {\n", " root._bokeh_onload_callbacks = [];\n", " root._bokeh_is_loading = undefined;\n", " }\n", "\n", "const JS_MIME_TYPE = 'application/javascript';\n", " const HTML_MIME_TYPE = 'text/html';\n", " const EXEC_MIME_TYPE = 'application/vnd.bokehjs_exec.v0+json';\n", " const CLASS_NAME = 'output_bokeh rendered_html';\n", "\n", " /**\n", " * Render data to the DOM node\n", " */\n", " function render(props, node) {\n", " const script = document.createElement(\"script\");\n", " node.appendChild(script);\n", " }\n", "\n", " /**\n", " * Handle when an output is cleared or removed\n", " */\n", " function handleClearOutput(event, handle) {\n", " const cell = handle.cell;\n", "\n", " const id = cell.output_area._bokeh_element_id;\n", " const server_id = cell.output_area._bokeh_server_id;\n", " // Clean up Bokeh references\n", " if (id != null && id in Bokeh.index) {\n", " Bokeh.index[id].model.document.clear();\n", " delete Bokeh.index[id];\n", " }\n", "\n", " if (server_id !== undefined) {\n", " // Clean up Bokeh references\n", " const cmd_clean = \"from bokeh.io.state import curstate; print(curstate().uuid_to_server['\" + server_id + \"'].get_sessions()[0].document.roots[0]._id)\";\n", " cell.notebook.kernel.execute(cmd_clean, {\n", " iopub: {\n", " output: function(msg) {\n", " const id = msg.content.text.trim();\n", " if (id in Bokeh.index) {\n", " Bokeh.index[id].model.document.clear();\n", " delete Bokeh.index[id];\n", " }\n", " }\n", " }\n", " });\n", " // Destroy server and session\n", " const cmd_destroy = \"import bokeh.io.notebook as ion; ion.destroy_server('\" + server_id + \"')\";\n", " cell.notebook.kernel.execute(cmd_destroy);\n", " }\n", " }\n", "\n", " /**\n", " * Handle when a new output is added\n", " */\n", " function handleAddOutput(event, handle) {\n", " const output_area = handle.output_area;\n", " const output = handle.output;\n", "\n", " // limit handleAddOutput to display_data with EXEC_MIME_TYPE content only\n", " if ((output.output_type != \"display_data\") || (!Object.prototype.hasOwnProperty.call(output.data, EXEC_MIME_TYPE))) {\n", " return\n", " }\n", "\n", " const toinsert = output_area.element.find(\".\" + CLASS_NAME.split(' ')[0]);\n", "\n", " if (output.metadata[EXEC_MIME_TYPE][\"id\"] !== undefined) {\n", " toinsert[toinsert.length - 1].firstChild.textContent = output.data[JS_MIME_TYPE];\n", " // store reference to embed id on output_area\n", " output_area._bokeh_element_id = output.metadata[EXEC_MIME_TYPE][\"id\"];\n", " }\n", " if (output.metadata[EXEC_MIME_TYPE][\"server_id\"] !== undefined) {\n", " const bk_div = document.createElement(\"div\");\n", " bk_div.innerHTML = output.data[HTML_MIME_TYPE];\n", " const script_attrs = bk_div.children[0].attributes;\n", " for (let i = 0; i < script_attrs.length; i++) {\n", " toinsert[toinsert.length - 1].firstChild.setAttribute(script_attrs[i].name, script_attrs[i].value);\n", " toinsert[toinsert.length - 1].firstChild.textContent = bk_div.children[0].textContent\n", " }\n", " // store reference to server id on output_area\n", " output_area._bokeh_server_id = output.metadata[EXEC_MIME_TYPE][\"server_id\"];\n", " }\n", " }\n", "\n", " function register_renderer(events, OutputArea) {\n", "\n", " function append_mime(data, metadata, element) {\n", " // create a DOM node to render to\n", " const toinsert = this.create_output_subarea(\n", " metadata,\n", " CLASS_NAME,\n", " EXEC_MIME_TYPE\n", " );\n", " this.keyboard_manager.register_events(toinsert);\n", " // Render to node\n", " const props = {data: data, metadata: metadata[EXEC_MIME_TYPE]};\n", " render(props, toinsert[toinsert.length - 1]);\n", " element.append(toinsert);\n", " return toinsert\n", " }\n", "\n", " /* Handle when an output is cleared or removed */\n", " events.on('clear_output.CodeCell', handleClearOutput);\n", " events.on('delete.Cell', handleClearOutput);\n", "\n", " /* Handle when a new output is added */\n", " events.on('output_added.OutputArea', handleAddOutput);\n", "\n", " /**\n", " * Register the mime type and append_mime function with output_area\n", " */\n", " OutputArea.prototype.register_mime_type(EXEC_MIME_TYPE, append_mime, {\n", " /* Is output safe? */\n", " safe: true,\n", " /* Index of renderer in `output_area.display_order` */\n", " index: 0\n", " });\n", " }\n", "\n", " // register the mime type if in Jupyter Notebook environment and previously unregistered\n", " if (root.Jupyter !== undefined) {\n", " const events = require('base/js/events');\n", " const OutputArea = require('notebook/js/outputarea').OutputArea;\n", "\n", " if (OutputArea.prototype.mime_types().indexOf(EXEC_MIME_TYPE) == -1) {\n", " register_renderer(events, OutputArea);\n", " }\n", " }\n", " if (typeof (root._bokeh_timeout) === \"undefined\" || force === true) {\n", " root._bokeh_timeout = Date.now() + 5000;\n", " root._bokeh_failed_load = false;\n", " }\n", "\n", " const NB_LOAD_WARNING = {'data': {'text/html':\n", " \"
\\n\"+\n", " \"

\\n\"+\n", " \"BokehJS does not appear to have successfully loaded. If loading BokehJS from CDN, this \\n\"+\n", " \"may be due to a slow or bad network connection. Possible fixes:\\n\"+\n", " \"

\\n\"+\n", " \"\\n\"+\n", " \"\\n\"+\n", " \"from bokeh.resources import INLINE\\n\"+\n", " \"output_notebook(resources=INLINE)\\n\"+\n", " \"\\n\"+\n", " \"
\"}};\n", "\n", " function display_loaded() {\n", " const el = document.getElementById(\"p1001\");\n", " if (el != null) {\n", " el.textContent = \"BokehJS is loading...\";\n", " }\n", " if (root.Bokeh !== undefined) {\n", " if (el != null) {\n", " el.textContent = \"BokehJS \" + root.Bokeh.version + \" successfully loaded.\";\n", " }\n", " } else if (Date.now() < root._bokeh_timeout) {\n", " setTimeout(display_loaded, 100)\n", " }\n", " }\n", "\n", " function run_callbacks() {\n", " try {\n", " root._bokeh_onload_callbacks.forEach(function(callback) {\n", " if (callback != null)\n", " callback();\n", " });\n", " } finally {\n", " delete root._bokeh_onload_callbacks\n", " }\n", " console.debug(\"Bokeh: all callbacks have finished\");\n", " }\n", "\n", " function load_libs(css_urls, js_urls, callback) {\n", " if (css_urls == null) css_urls = [];\n", " if (js_urls == null) js_urls = [];\n", "\n", " root._bokeh_onload_callbacks.push(callback);\n", " if (root._bokeh_is_loading > 0) {\n", " console.debug(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n", " return null;\n", " }\n", " if (js_urls == null || js_urls.length === 0) {\n", " run_callbacks();\n", " return null;\n", " }\n", " console.debug(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n", " root._bokeh_is_loading = css_urls.length + js_urls.length;\n", "\n", " function on_load() {\n", " root._bokeh_is_loading--;\n", " if (root._bokeh_is_loading === 0) {\n", " console.debug(\"Bokeh: all BokehJS libraries/stylesheets loaded\");\n", " run_callbacks()\n", " }\n", " }\n", "\n", " function on_error(url) {\n", " console.error(\"failed to load \" + url);\n", " }\n", "\n", " for (let i = 0; i < css_urls.length; i++) {\n", " const url = css_urls[i];\n", " const element = document.createElement(\"link\");\n", " element.onload = on_load;\n", " element.onerror = on_error.bind(null, url);\n", " element.rel = \"stylesheet\";\n", " element.type = \"text/css\";\n", " element.href = url;\n", " console.debug(\"Bokeh: injecting link tag for BokehJS stylesheet: \", url);\n", " document.body.appendChild(element);\n", " }\n", "\n", " for (let i = 0; i < js_urls.length; i++) {\n", " const url = js_urls[i];\n", " const element = document.createElement('script');\n", " element.onload = on_load;\n", " element.onerror = on_error.bind(null, url);\n", " element.async = false;\n", " element.src = url;\n", " console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n", " document.head.appendChild(element);\n", " }\n", " };\n", "\n", " function inject_raw_css(css) {\n", " const element = document.createElement(\"style\");\n", " element.appendChild(document.createTextNode(css));\n", " document.body.appendChild(element);\n", " }\n", "\n", " const js_urls = [\"https://cdn.bokeh.org/bokeh/release/bokeh-3.1.0.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-gl-3.1.0.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-widgets-3.1.0.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-tables-3.1.0.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-mathjax-3.1.0.min.js\"];\n", " const css_urls = [];\n", "\n", " const inline_js = [ function(Bokeh) {\n", " Bokeh.set_log_level(\"info\");\n", " },\n", "function(Bokeh) {\n", " }\n", " ];\n", "\n", " function run_inline_js() {\n", " if (root.Bokeh !== undefined || force === true) {\n", " for (let i = 0; i < inline_js.length; i++) {\n", " inline_js[i].call(root, root.Bokeh);\n", " }\n", "if (force === true) {\n", " display_loaded();\n", " }} else if (Date.now() < root._bokeh_timeout) {\n", " setTimeout(run_inline_js, 100);\n", " } else if (!root._bokeh_failed_load) {\n", " console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n", " root._bokeh_failed_load = true;\n", " } else if (force !== true) {\n", " const cell = $(document.getElementById(\"p1001\")).parents('.cell').data().cell;\n", " cell.output_area.append_execute_result(NB_LOAD_WARNING)\n", " }\n", " }\n", "\n", " if (root._bokeh_is_loading === 0) {\n", " console.debug(\"Bokeh: BokehJS loaded, going straight to plotting\");\n", " run_inline_js();\n", " } else {\n", " load_libs(css_urls, js_urls, function() {\n", " console.debug(\"Bokeh: BokehJS plotting callback run at\", now());\n", " run_inline_js();\n", " });\n", " }\n", "}(window));" ], "application/vnd.bokehjs_load.v0+json": "(function(root) {\n function now() {\n return new Date();\n }\n\n const force = true;\n\n if (typeof root._bokeh_onload_callbacks === \"undefined\" || force === true) {\n root._bokeh_onload_callbacks = [];\n root._bokeh_is_loading = undefined;\n }\n\n\n if (typeof (root._bokeh_timeout) === \"undefined\" || force === true) {\n root._bokeh_timeout = Date.now() + 5000;\n root._bokeh_failed_load = false;\n }\n\n const NB_LOAD_WARNING = {'data': {'text/html':\n \"
\\n\"+\n \"

\\n\"+\n \"BokehJS does not appear to have successfully loaded. If loading BokehJS from CDN, this \\n\"+\n \"may be due to a slow or bad network connection. Possible fixes:\\n\"+\n \"

\\n\"+\n \"\\n\"+\n \"\\n\"+\n \"from bokeh.resources import INLINE\\n\"+\n \"output_notebook(resources=INLINE)\\n\"+\n \"\\n\"+\n \"
\"}};\n\n function display_loaded() {\n const el = document.getElementById(\"p1001\");\n if (el != null) {\n el.textContent = \"BokehJS is loading...\";\n }\n if (root.Bokeh !== undefined) {\n if (el != null) {\n el.textContent = \"BokehJS \" + root.Bokeh.version + \" successfully loaded.\";\n }\n } else if (Date.now() < root._bokeh_timeout) {\n setTimeout(display_loaded, 100)\n }\n }\n\n function run_callbacks() {\n try {\n root._bokeh_onload_callbacks.forEach(function(callback) {\n if (callback != null)\n callback();\n });\n } finally {\n delete root._bokeh_onload_callbacks\n }\n console.debug(\"Bokeh: all callbacks have finished\");\n }\n\n function load_libs(css_urls, js_urls, callback) {\n if (css_urls == null) css_urls = [];\n if (js_urls == null) js_urls = [];\n\n root._bokeh_onload_callbacks.push(callback);\n if (root._bokeh_is_loading > 0) {\n console.debug(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n return null;\n }\n if (js_urls == null || js_urls.length === 0) {\n run_callbacks();\n return null;\n }\n console.debug(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n root._bokeh_is_loading = css_urls.length + js_urls.length;\n\n function on_load() {\n root._bokeh_is_loading--;\n if (root._bokeh_is_loading === 0) {\n console.debug(\"Bokeh: all BokehJS libraries/stylesheets loaded\");\n run_callbacks()\n }\n }\n\n function on_error(url) {\n console.error(\"failed to load \" + url);\n }\n\n for (let i = 0; i < css_urls.length; i++) {\n const url = css_urls[i];\n const element = document.createElement(\"link\");\n element.onload = on_load;\n element.onerror = on_error.bind(null, url);\n element.rel = \"stylesheet\";\n element.type = \"text/css\";\n element.href = url;\n console.debug(\"Bokeh: injecting link tag for BokehJS stylesheet: \", url);\n document.body.appendChild(element);\n }\n\n for (let i = 0; i < js_urls.length; i++) {\n const url = js_urls[i];\n const element = document.createElement('script');\n element.onload = on_load;\n element.onerror = on_error.bind(null, url);\n element.async = false;\n element.src = url;\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n document.head.appendChild(element);\n }\n };\n\n function inject_raw_css(css) {\n const element = document.createElement(\"style\");\n element.appendChild(document.createTextNode(css));\n document.body.appendChild(element);\n }\n\n const js_urls = [\"https://cdn.bokeh.org/bokeh/release/bokeh-3.1.0.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-gl-3.1.0.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-widgets-3.1.0.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-tables-3.1.0.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-mathjax-3.1.0.min.js\"];\n const css_urls = [];\n\n const inline_js = [ function(Bokeh) {\n Bokeh.set_log_level(\"info\");\n },\nfunction(Bokeh) {\n }\n ];\n\n function run_inline_js() {\n if (root.Bokeh !== undefined || force === true) {\n for (let i = 0; i < inline_js.length; i++) {\n inline_js[i].call(root, root.Bokeh);\n }\nif (force === true) {\n display_loaded();\n }} else if (Date.now() < root._bokeh_timeout) {\n setTimeout(run_inline_js, 100);\n } else if (!root._bokeh_failed_load) {\n console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n root._bokeh_failed_load = true;\n } else if (force !== true) {\n const cell = $(document.getElementById(\"p1001\")).parents('.cell').data().cell;\n cell.output_area.append_execute_result(NB_LOAD_WARNING)\n }\n }\n\n if (root._bokeh_is_loading === 0) {\n console.debug(\"Bokeh: BokehJS loaded, going straight to plotting\");\n run_inline_js();\n } else {\n load_libs(css_urls, js_urls, function() {\n console.debug(\"Bokeh: BokehJS plotting callback run at\", now());\n run_inline_js();\n });\n }\n}(window));" }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# Colab setup ------------------\n", "import os, sys, subprocess\n", "if \"google.colab\" in sys.modules:\n", " cmd = \"pip install --upgrade colorcet biocircuits watermark\"\n", " process = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n", " stdout, stderr = process.communicate()\n", "# ------------------------------\n", "\n", "import numpy as np\n", "import scipy.integrate\n", "\n", "import biocircuits\n", "\n", "import colorcet\n", "colors = colorcet.b_glasbey_category10\n", "\n", "import bokeh.io\n", "import bokeh.layouts\n", "import bokeh.models\n", "import bokeh.plotting\n", "\n", "bokeh.io.output_notebook()" ] }, { "cell_type": "markdown", "id": "26935a9c-3e37-41d1-af40-4994411da5cb", "metadata": {}, "source": [ "It is useful to write functions to solve for the dynamics of FFLs in response to a step up and a step down to quickly explore the dynamics of the various sub-architectures of the FFL.\n", "\n", "As a reminder, the architectures for the various FFLs are shown below.\n", "\n", "
\n", "\n", "![FFL classes](../chapters/figs/FFL_classes.png)\n", "\n", "
\n", "\n", "Each architecture may feature AND or OR logic for the regulation of Z by X and Y. As we worked out in the chapter, the dimensionless dynamical equations for an FFL are\n", "\n", "\\begin{align}\n", "\\frac{\\mathrm{d}y}{\\mathrm{d}t} &= \\beta\\,f_y\\left(\\kappa x; n_{xy}\\right) - y,\\\\[1em]\n", "\\gamma^{-1}\\,\\frac{\\mathrm{d}z}{\\mathrm{d}t} &= f_z(x, y; n_{xz}, n_{yz}) - z,\n", "\\end{align}\n", "\n", "where the function $f_y$ specifies how the expression of Y is regulated by the concentration of X and the function $f_z$ specifies how the expression of Z is regulated by the concentrations of X and Y. We can use the regulatory functions included in the `biocircuits` when we code up the ODEs for FFLs.\n", "\n", "It is convenient to define a function that will give back a function that we can use as the right-hand side we need to specify to `scipy.integrate.odeint()`. Remember that `odeint()` requires a function of the form `func(yz, t, *args)`, where `yz` is an array of length two containing the values of $y$ and $z$. For convenience, our function will return a function with call signature `rhs(yz, t, x)`, where `x` is the value of $x$ at a given time point." ] }, { "cell_type": "code", "execution_count": 2, "id": "dda8957f-6e69-4dfb-8555-97707cc76989", "metadata": {}, "outputs": [], "source": [ "def ffl_rhs(beta, gamma, kappa, n_xy, n_xz, n_yz, ffl, logic):\n", " \"\"\"Return a function with call signature fun(yz, x) that computes\n", " the right-hand side of the dynamical system for an FFL. Here,\n", " `yz` is a length two array containing concentrations of Y and Z.\n", " \"\"\"\n", " if ffl[:2].lower() in (\"c1\", \"c3\", \"i1\", \"i3\"):\n", " fy = lambda x: biocircuits.act_hill(x, n_xy)\n", " else:\n", " fy = lambda x: biocircuits.rep_hill(x, n_xy)\n", "\n", " if ffl[:2].lower() in (\"c1\", \"i4\"):\n", " if logic.lower() == \"and\":\n", " fz = lambda x, y: biocircuits.aa_and(x, y, n_xz, n_yz)\n", " else:\n", " fz = lambda x, y: biocircuits.aa_or(x, y, n_xz, n_yz)\n", " elif ffl[:2].lower() in (\"c4\", \"i1\"):\n", " if logic.lower() == \"and\":\n", " fz = lambda x, y: biocircuits.ar_and(x, y, n_xz, n_yz)\n", " else:\n", " fz = lambda x, y: biocircuits.ar_or(x, y, n_xz, n_yz)\n", " elif ffl[:2].lower() in (\"c2\", \"i3\"):\n", " if logic.lower() == \"and\":\n", " fz = lambda x, y: biocircuits.ar_and(y, x, n_yz, n_xz)\n", " else:\n", " fz = lambda x, y: biocircuits.ar_or(y, x, n_yz, n_xz)\n", " else:\n", " if logic.lower() == \"and\":\n", " fz = lambda x, y: biocircuits.rr_and(x, y, n_xz, n_yz)\n", " else:\n", " fz = lambda x, y: biocircuits.rr_or(x, y, n_xz, n_yz)\n", "\n", " def rhs(yz, t, x):\n", " y, z = yz\n", " dy_dt = beta * fy(kappa * x) - y\n", " dz_dt = gamma * (fz(x, y) - z)\n", "\n", " return np.array([dy_dt, dz_dt])\n", "\n", " return rhs" ] }, { "cell_type": "markdown", "id": "27eb4507-ce6c-440c-817e-44555d32b8ba", "metadata": {}, "source": [ "To study the dynamics, we will investigate how the circuit responds to a step up in concentration of X, assuming all concentrations are initially zero, and how a circuit at steady state with nonzero concentration of X responds to a step down in X to zero. This case is particularly relevant for a C1-FFL and an I1-FFL, since in the absence of X (and leakage), the steady state levels of both Y and Z are zero. For other FFLs, the steady state concentrations of Y or Z absent X can be nonzero. In this case, you can think of the sudden rise in X being associated also with a sudden rise of effectors that allow Y and Z to turn on.\n", "\n", "Now we can write a function to solve the ODEs. Because the steps are discontinuous, we need to solve the ODEs in a piecewise manner. We specify that the step up starts at $t = 0$, and we will allow the time of the step down to be specified. The magnitude of the step up, $x_0$ will also be specified." ] }, { "cell_type": "code", "execution_count": 3, "id": "dd76008d-bc79-40c1-8023-21f1b9961944", "metadata": {}, "outputs": [], "source": [ "def solve_ffl(beta, gamma, kappa, n_xy, n_xz, n_yz, ffl, logic, t, t_step_down, x_0):\n", " \"\"\"Solve an FFL. The dynamics are given by\n", " `rhs`, the output of `ffl_rhs()`.\n", " \"\"\"\n", " if t[0] != 0:\n", " raise RuntimeError(\"time must start at zero.\")\n", "\n", " rhs = ffl_rhs(beta, gamma, kappa, n_xy, n_xz, n_yz, ffl, logic)\n", "\n", " # Integrate if we do not step down\n", " if t[-1] < t_step_down:\n", " return scipy.integrate.odeint(rhs, np.zeros(2), t, args=(x_0,))\n", "\n", " # Integrate up to step down\n", " t_during_step = np.concatenate((t[t < t_step_down], (t_step_down,)))\n", " yz_during_step = scipy.integrate.odeint(\n", " rhs, np.zeros(2), t_during_step, args=(x_0,)\n", " )\n", "\n", " # Integrate after step\n", " t_after_step = np.concatenate(((t_step_down,), t[t > t_step_down]))\n", " yz_after_step = scipy.integrate.odeint(\n", " rhs, yz_during_step[-1, :], t_after_step, args=(0,)\n", " )\n", "\n", " # Concatenate solutions\n", " if t_step_down in t:\n", " return np.vstack((yz_during_step[:-1, :], yz_after_step))\n", " else:\n", " return np.vstack((yz_during_step[:-1, :], yz_after_step[1:, :]))" ] }, { "cell_type": "markdown", "id": "0058c186-5164-48f6-966e-bce13269eec9", "metadata": {}, "source": [ "Finally, we can write a function to solve and plot the dynamics of an FFL for a unit step. " ] }, { "cell_type": "code", "execution_count": 4, "id": "0cbb6557-6ca1-44e7-865a-dcbf1962e7f1", "metadata": {}, "outputs": [], "source": [ "def plot_ffl(\n", " beta=1.0,\n", " gamma=1.0,\n", " kappa=1.0,\n", " n_xy=1.0,\n", " n_xz=1.0,\n", " n_yz=1.0,\n", " ffl=\"c1\",\n", " logic=\"and\",\n", " t=np.linspace(0, 20, 200),\n", " t_step_down=10.0,\n", " x_0=1.0,\n", " normalized=False,\n", "):\n", " yz = solve_ffl(\n", " beta, gamma, kappa, n_xy, n_xz, n_yz, ffl, logic, t, t_step_down, x_0\n", " )\n", " y, z = yz.transpose()\n", "\n", " # Generate x-values\n", " if t[-1] > t_step_down:\n", " t_x = np.array([-t_step_down / 10, 0, 0, t_step_down, t_step_down, t[-1]])\n", " x = np.array([0, 0, x_0, x_0, 0, 0], dtype=float)\n", " else:\n", " t_x = np.array([-t[-1] / 10, 0, 0, t[-1]])\n", " x = np.array([0, 0, x_0, x_0], dtype=float)\n", "\n", " # Add left part of y and z-values\n", " t = np.concatenate(((t_x[0],), t))\n", " y = np.concatenate(((0,), y))\n", " z = np.concatenate(((0,), z))\n", "\n", " # Normalize if necessary\n", " if normalized:\n", " x /= x.max()\n", " y /= y.max()\n", " z /= z.max()\n", " \n", " # Set up figure\n", " p = bokeh.plotting.figure(\n", " frame_height=175,\n", " frame_width=550,\n", " x_axis_label=\"dimensionless time\",\n", " y_axis_label=f\"{'norm. ' if normalized else ''}dimensionless conc.\",\n", " x_range=[t.min(), t.max()],\n", " )\n", "\n", " # Column data sources\n", " cds = bokeh.models.ColumnDataSource(dict(t=t, y=y, z=z))\n", " cds_x = bokeh.models.ColumnDataSource(dict(t=t_x, x=x))\n", "\n", " # Populate glyphs\n", " p.line(source=cds_x, x=\"t\", y=\"x\", line_width=2, color=colors[0], legend_label=\"x\")\n", " p.line(source=cds, x=\"t\", y=\"y\", line_width=2, color=colors[1], legend_label=\"y\")\n", " p.line(source=cds, x=\"t\", y=\"z\", line_width=2, color=colors[2], legend_label=\"z\")\n", "\n", " # Allow vanishing lines by clicking legend\n", " p.legend.click_policy = \"hide\"\n", "\n", " return p" ] }, { "cell_type": "markdown", "id": "1cd98440-a697-4d2e-a89f-c280091d8457", "metadata": {}, "source": [ "We can take this for a spin to see the response of an I1-FFL." ] }, { "cell_type": "code", "execution_count": 5, "id": "679896ae-cebc-48e7-9d30-21f20495ab2b", "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", "
\n" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "application/javascript": [ "(function(root) {\n", " function embed_document(root) {\n", " const docs_json = {\"11454553-81ab-4d12-a117-4890dd5079f2\":{\"version\":\"3.1.0\",\"title\":\"Bokeh Application\",\"defs\":[],\"roots\":[{\"type\":\"object\",\"name\":\"Figure\",\"id\":\"p1002\",\"attributes\":{\"x_range\":{\"type\":\"object\",\"name\":\"Range1d\",\"id\":\"p1011\",\"attributes\":{\"start\":-1.0,\"end\":20.0}},\"y_range\":{\"type\":\"object\",\"name\":\"DataRange1d\",\"id\":\"p1004\"},\"x_scale\":{\"type\":\"object\",\"name\":\"LinearScale\",\"id\":\"p1015\"},\"y_scale\":{\"type\":\"object\",\"name\":\"LinearScale\",\"id\":\"p1017\"},\"title\":{\"type\":\"object\",\"name\":\"Title\",\"id\":\"p1007\"},\"renderers\":[{\"type\":\"object\",\"name\":\"GlyphRenderer\",\"id\":\"p1060\",\"attributes\":{\"data_source\":{\"type\":\"object\",\"name\":\"ColumnDataSource\",\"id\":\"p1051\",\"attributes\":{\"selected\":{\"type\":\"object\",\"name\":\"Selection\",\"id\":\"p1052\",\"attributes\":{\"indices\":[],\"line_indices\":[]}},\"selection_policy\":{\"type\":\"object\",\"name\":\"UnionRenderers\",\"id\":\"p1053\"},\"data\":{\"type\":\"map\",\"entries\":[[\"t\",{\"type\":\"ndarray\",\"array\":{\"type\":\"bytes\",\"data\":\"AAAAAAAA8L8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAJEAAAAAAAAAkQAAAAAAAADRA\"},\"shape\":[6],\"dtype\":\"float64\",\"order\":\"little\"}],[\"x\",{\"type\":\"ndarray\",\"array\":{\"type\":\"bytes\",\"data\":\"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAPA/AAAAAAAA8D8AAAAAAAAAAAAAAAAAAAAA\"},\"shape\":[6],\"dtype\":\"float64\",\"order\":\"little\"}]]}}},\"view\":{\"type\":\"object\",\"name\":\"CDSView\",\"id\":\"p1061\",\"attributes\":{\"filter\":{\"type\":\"object\",\"name\":\"AllIndices\",\"id\":\"p1062\"}}},\"glyph\":{\"type\":\"object\",\"name\":\"Line\",\"id\":\"p1057\",\"attributes\":{\"x\":{\"type\":\"field\",\"field\":\"t\"},\"y\":{\"type\":\"field\",\"field\":\"x\"},\"line_color\":\"#1f77b3\",\"line_width\":2}},\"nonselection_glyph\":{\"type\":\"object\",\"name\":\"Line\",\"id\":\"p1058\",\"attributes\":{\"x\":{\"type\":\"field\",\"field\":\"t\"},\"y\":{\"type\":\"field\",\"field\":\"x\"},\"line_color\":\"#1f77b3\",\"line_alpha\":0.1,\"line_width\":2}},\"muted_glyph\":{\"type\":\"object\",\"name\":\"Line\",\"id\":\"p1059\",\"attributes\":{\"x\":{\"type\":\"field\",\"field\":\"t\"},\"y\":{\"type\":\"field\",\"field\":\"x\"},\"line_color\":\"#1f77b3\",\"line_alpha\":0.2,\"line_width\":2}}}},{\"type\":\"object\",\"name\":\"GlyphRenderer\",\"id\":\"p1071\",\"attributes\":{\"data_source\":{\"type\":\"object\",\"name\":\"ColumnDataSource\",\"id\":\"p1048\",\"attributes\":{\"selected\":{\"type\":\"object\",\"name\":\"Selection\",\"id\":\"p1049\",\"attributes\":{\"indices\":[],\"line_indices\":[]}},\"selection_policy\":{\"type\":\"object\",\"name\":\"UnionRenderers\",\"id\":\"p1050\"},\"data\":{\"type\":\"map\",\"entries\":[[\"t\",{\"type\":\"ndarray\",\"array\":{\"type\":\"bytes\",\"data\":\"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\"},\"shape\":[201],\"dtype\":\"float64\",\"order\":\"little\"}],[\"y\",{\"type\":\"ndarray\",\"array\":{\"type\":\"bytes\",\"data\":\"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\"},\"shape\":[201],\"dtype\":\"float64\",\"order\":\"little\"}],[\"z\",{\"type\":\"ndarray\",\"array\":{\"type\":\"bytes\",\"data\":\"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\"},\"shape\":[201],\"dtype\":\"float64\",\"order\":\"little\"}]]}}},\"view\":{\"type\":\"object\",\"name\":\"CDSView\",\"id\":\"p1072\",\"attributes\":{\"filter\":{\"type\":\"object\",\"name\":\"AllIndices\",\"id\":\"p1073\"}}},\"glyph\":{\"type\":\"object\",\"name\":\"Line\",\"id\":\"p1068\",\"attributes\":{\"x\":{\"type\":\"field\",\"field\":\"t\"},\"y\":{\"type\":\"field\",\"field\":\"y\"},\"line_color\":\"#ff7e0e\",\"line_width\":2}},\"nonselection_glyph\":{\"type\":\"object\",\"name\":\"Line\",\"id\":\"p1069\",\"attributes\":{\"x\":{\"type\":\"field\",\"field\":\"t\"},\"y\":{\"type\":\"field\",\"field\":\"y\"},\"line_color\":\"#ff7e0e\",\"line_alpha\":0.1,\"line_width\":2}},\"muted_glyph\":{\"type\":\"object\",\"name\":\"Line\",\"id\":\"p1070\",\"attributes\":{\"x\":{\"type\":\"field\",\"field\":\"t\"},\"y\":{\"type\":\"field\",\"field\":\"y\"},\"line_color\":\"#ff7e0e\",\"line_alpha\":0.2,\"line_width\":2}}}},{\"type\":\"object\",\"name\":\"GlyphRenderer\",\"id\":\"p1081\",\"attributes\":{\"data_source\":{\"id\":\"p1048\"},\"view\":{\"type\":\"object\",\"name\":\"CDSView\",\"id\":\"p1082\",\"attributes\":{\"filter\":{\"type\":\"object\",\"name\":\"AllIndices\",\"id\":\"p1083\"}}},\"glyph\":{\"type\":\"object\",\"name\":\"Line\",\"id\":\"p1078\",\"attributes\":{\"x\":{\"type\":\"field\",\"field\":\"t\"},\"y\":{\"type\":\"field\",\"field\":\"z\"},\"line_color\":\"#2ba02b\",\"line_width\":2}},\"nonselection_glyph\":{\"type\":\"object\",\"name\":\"Line\",\"id\":\"p1079\",\"attributes\":{\"x\":{\"type\":\"field\",\"field\":\"t\"},\"y\":{\"type\":\"field\",\"field\":\"z\"},\"line_color\":\"#2ba02b\",\"line_alpha\":0.1,\"line_width\":2}},\"muted_glyph\":{\"type\":\"object\",\"name\":\"Line\",\"id\":\"p1080\",\"attributes\":{\"x\":{\"type\":\"field\",\"field\":\"t\"},\"y\":{\"type\":\"field\",\"field\":\"z\"},\"line_color\":\"#2ba02b\",\"line_alpha\":0.2,\"line_width\":2}}}}],\"toolbar\":{\"type\":\"object\",\"name\":\"Toolbar\",\"id\":\"p1009\",\"attributes\":{\"tools\":[{\"type\":\"object\",\"name\":\"PanTool\",\"id\":\"p1033\"},{\"type\":\"object\",\"name\":\"WheelZoomTool\",\"id\":\"p1034\"},{\"type\":\"object\",\"name\":\"BoxZoomTool\",\"id\":\"p1035\",\"attributes\":{\"overlay\":{\"type\":\"object\",\"name\":\"BoxAnnotation\",\"id\":\"p1036\",\"attributes\":{\"syncable\":false,\"level\":\"overlay\",\"visible\":false,\"left_units\":\"canvas\",\"right_units\":\"canvas\",\"bottom_units\":\"canvas\",\"top_units\":\"canvas\",\"line_color\":\"black\",\"line_alpha\":1.0,\"line_width\":2,\"line_dash\":[4,4],\"fill_color\":\"lightgrey\",\"fill_alpha\":0.5}}}},{\"type\":\"object\",\"name\":\"SaveTool\",\"id\":\"p1037\"},{\"type\":\"object\",\"name\":\"ResetTool\",\"id\":\"p1038\"},{\"type\":\"object\",\"name\":\"HelpTool\",\"id\":\"p1039\"}]}},\"left\":[{\"type\":\"object\",\"name\":\"LinearAxis\",\"id\":\"p1026\",\"attributes\":{\"ticker\":{\"type\":\"object\",\"name\":\"BasicTicker\",\"id\":\"p1029\",\"attributes\":{\"mantissas\":[1,2,5]}},\"formatter\":{\"type\":\"object\",\"name\":\"BasicTickFormatter\",\"id\":\"p1028\"},\"axis_label\":\"dimensionless conc.\",\"major_label_policy\":{\"type\":\"object\",\"name\":\"AllLabels\",\"id\":\"p1027\"}}}],\"below\":[{\"type\":\"object\",\"name\":\"LinearAxis\",\"id\":\"p1019\",\"attributes\":{\"ticker\":{\"type\":\"object\",\"name\":\"BasicTicker\",\"id\":\"p1022\",\"attributes\":{\"mantissas\":[1,2,5]}},\"formatter\":{\"type\":\"object\",\"name\":\"BasicTickFormatter\",\"id\":\"p1021\"},\"axis_label\":\"dimensionless time\",\"major_label_policy\":{\"type\":\"object\",\"name\":\"AllLabels\",\"id\":\"p1020\"}}}],\"center\":[{\"type\":\"object\",\"name\":\"Grid\",\"id\":\"p1025\",\"attributes\":{\"axis\":{\"id\":\"p1019\"}}},{\"type\":\"object\",\"name\":\"Grid\",\"id\":\"p1032\",\"attributes\":{\"dimension\":1,\"axis\":{\"id\":\"p1026\"}}},{\"type\":\"object\",\"name\":\"Legend\",\"id\":\"p1063\",\"attributes\":{\"click_policy\":\"hide\",\"items\":[{\"type\":\"object\",\"name\":\"LegendItem\",\"id\":\"p1064\",\"attributes\":{\"label\":{\"type\":\"value\",\"value\":\"x\"},\"renderers\":[{\"id\":\"p1060\"}]}},{\"type\":\"object\",\"name\":\"LegendItem\",\"id\":\"p1074\",\"attributes\":{\"label\":{\"type\":\"value\",\"value\":\"y\"},\"renderers\":[{\"id\":\"p1071\"}]}},{\"type\":\"object\",\"name\":\"LegendItem\",\"id\":\"p1084\",\"attributes\":{\"label\":{\"type\":\"value\",\"value\":\"z\"},\"renderers\":[{\"id\":\"p1081\"}]}}]}}],\"frame_width\":550,\"frame_height\":175}}],\"callbacks\":{\"type\":\"map\"}}};\n", " const render_items = [{\"docid\":\"11454553-81ab-4d12-a117-4890dd5079f2\",\"roots\":{\"p1002\":\"c8e2648f-850c-454e-b81c-d84d7b3290ce\"},\"root_ids\":[\"p1002\"]}];\n", " root.Bokeh.embed.embed_items_notebook(docs_json, render_items);\n", " }\n", " if (root.Bokeh !== undefined) {\n", " embed_document(root);\n", " } else {\n", " let attempts = 0;\n", " const timer = setInterval(function(root) {\n", " if (root.Bokeh !== undefined) {\n", " clearInterval(timer);\n", " embed_document(root);\n", " } else {\n", " attempts++;\n", " if (attempts > 100) {\n", " clearInterval(timer);\n", " console.log(\"Bokeh: ERROR: Unable to run BokehJS code because BokehJS library is missing\");\n", " }\n", " }\n", " }, 10, root)\n", " }\n", "})(window);" ], "application/vnd.bokehjs_exec.v0+json": "" }, "metadata": { "application/vnd.bokehjs_exec.v0+json": { "id": "p1002" } }, "output_type": "display_data" } ], "source": [ "# Parameter values\n", "beta = 5\n", "gamma = 1\n", "kappa = 1\n", "n_xy, n_yz = 3, 3\n", "n_xz = 5\n", "\n", "# Plot\n", "p = plot_ffl(beta, gamma, kappa, n_xy, n_xz, n_yz, ffl=\"i1\", logic=\"and\")\n", "bokeh.io.show(p)" ] }, { "cell_type": "markdown", "id": "029da1ee-779d-4a6c-bc37-53c3cd7547e7", "metadata": {}, "source": [ "The functionality of the `solve_ffl()` and `plot_ffl()` functions are available in the `biocircuits` packages as `biocircuits.apps.solve_ffl()` and `biocircuits.apps.plot_ffl()`. The `biocircuits.apps.plot_ffl()` also outputs the `ColumnDataSource`s `cds` and `cds_x` that are used in constructing the FFL explorer app, so its API does differ from the function defined above." ] }, { "cell_type": "markdown", "id": "31f026f3-85b3-45e1-bdeb-6604a0d4899f", "metadata": {}, "source": [ "## Computing environment" ] }, { "cell_type": "code", "execution_count": 6, "id": "761ebfff-174d-4478-8dfb-d41f472b2298", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Python implementation: CPython\n", "Python version : 3.10.10\n", "IPython version : 8.10.0\n", "\n", "numpy : 1.23.5\n", "scipy : 1.10.0\n", "bokeh : 3.1.0\n", "biocircuits: 0.1.9\n", "jupyterlab : 3.5.3\n", "\n" ] } ], "source": [ "%load_ext watermark\n", "%watermark -v -p numpy,scipy,bokeh,biocircuits,jupyterlab" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.10.10" } }, "nbformat": 4, "nbformat_minor": 5 }