{ "cells": [ { "cell_type": "markdown", "id": "cd89e3d0", "metadata": {}, "source": [ "# BoolForge Tutorial 6: Working with Boolean Networks\n", "\n", "While previous tutorials focused on individual Boolean functions, this tutorial\n", "introduces Boolean networks, which combine multiple Boolean functions into a\n", "dynamical system.\n", "\n", "## What you will learn\n", "In this tutorial you will learn how to:\n", "\n", "- create Boolean networks,\n", "- compute basic properties of the wiring diagram,\n", "- compute basic properties of Boolean networks.\n", "\n", "---\n", "## 0. Setup" ] }, { "cell_type": "code", "execution_count": 1, "id": "231f09bb", "metadata": { "execution": { "iopub.execute_input": "2026-01-15T17:23:56.455514Z", "iopub.status.busy": "2026-01-15T17:23:56.455361Z", "iopub.status.idle": "2026-01-15T17:23:57.064921Z", "shell.execute_reply": "2026-01-15T17:23:57.064609Z" }, "lines_to_next_cell": 2 }, "outputs": [], "source": [ "import boolforge\n", "import numpy as np\n", "import networkx as nx\n", "import matplotlib.pyplot as plt" ] }, { "cell_type": "markdown", "id": "ff8d83fb", "metadata": {}, "source": [ "---\n", "## 1. Boolean network theory\n", "\n", "A Boolean network $F = (f_1, \\ldots, f_N)$ is a dynamical system consisting of\n", "$N$ Boolean update functions. Each node can be in one of two states, 0 or 1,\n", "often interpreted as OFF/ON in biological contexts.\n", "\n", "Under synchronous updating, all nodes update simultaneously, yielding a\n", "deterministic state transition graph on $\\{0,1\\}^N$.\n", "\n", "Under asynchronous updating, only one node is updated at a time, yielding a\n", "stochastic transition graph. BoolForge implements both schemes.\n", "\n", "Real biological networks are typically sparsely connected. The **in-degree**\n", "of a node is the number of essential inputs of its update function. The\n", "**wiring diagram** encodes which nodes regulate which others.\n", "\n", "Despite their simplicity, Boolean networks can:\n", "- reproduce complex dynamics (oscillations, multistability),\n", "- predict gene knockout effects,\n", "- identify control strategies,\n", "- scale to genome-wide networks (1000s of nodes)." ] }, { "cell_type": "markdown", "id": "ca790db2", "metadata": {}, "source": [ "---\n", "## 2. Wiring diagrams\n", "\n", "We first construct wiring diagrams, which encode network structure independently\n", "of specific Boolean functions.\n", "\n", "Separating topology (I) from dynamics (F) allows:\n", "- studying structural properties independent of specific Boolean rules,\n", "- swapping different rule sets on the same topology,\n", "- efficient storage (sparse I, local F vs dense full truth table)." ] }, { "cell_type": "code", "execution_count": 2, "id": "5ba84b33", "metadata": { "execution": { "iopub.execute_input": "2026-01-15T17:23:57.066363Z", "iopub.status.busy": "2026-01-15T17:23:57.066242Z", "iopub.status.idle": "2026-01-15T17:23:57.096219Z", "shell.execute_reply": "2026-01-15T17:23:57.095989Z" }, "lines_to_next_cell": 2 }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "W.N: 3\n", "W.variables: ['x0' 'x1' 'x2']\n", "W.indegrees: [1 2 1]\n", "W.outdegrees: [1 2 1]\n", "W.N_constants: 0\n", "W.N_variables: 3\n" ] }, { "data": { "image/png": "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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# Wiring diagram of a 3-node network\n", "I = [\n", " [1],\n", " [0, 2],\n", " [1],\n", "]\n", "\n", "W = boolforge.WiringDiagram(I=I)\n", "\n", "print(\"W.N:\", W.N)\n", "print(\"W.variables:\", W.variables)\n", "print(\"W.indegrees:\", W.indegrees)\n", "print(\"W.outdegrees:\", W.outdegrees)\n", "print(\"W.N_constants:\", W.N_constants)\n", "print(\"W.N_variables:\", W.N_variables)\n", "\n", "DiGraph = W.to_DiGraph()\n", "plt.figure()\n", "nx.draw_networkx(DiGraph, with_labels=True, arrows=True)\n", "plt.axis(\"off\")\n", "plt.show()" ] }, { "cell_type": "markdown", "id": "6e4f360a", "metadata": { "lines_to_next_cell": 2 }, "source": [ "The wiring diagram above uses default variable names $x_0, \\ldots, x_{N-1}$.\n", "The vectors `indegrees` and `outdegrees` describe incoming and outgoing edges\n", "for each node." ] }, { "cell_type": "markdown", "id": "e8123ae0", "metadata": {}, "source": [ "### Example with constants and unequal degrees" ] }, { "cell_type": "code", "execution_count": 3, "id": "b49670c7", "metadata": { "execution": { "iopub.execute_input": "2026-01-15T17:23:57.097399Z", "iopub.status.busy": "2026-01-15T17:23:57.097325Z", "iopub.status.idle": "2026-01-15T17:23:57.114323Z", "shell.execute_reply": "2026-01-15T17:23:57.114099Z" }, "lines_to_next_cell": 2 }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "W.N: 3\n", "W.variables: ['x0' 'x1' 'x2']\n", "W.indegrees: [0 2 1]\n", "W.outdegrees: [2 0 1]\n", "W.N_constants: 1\n", "W.N_variables: 2\n" ] }, { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAgMAAAGFCAYAAABg2vAPAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjUsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvWftoOwAAAAlwSFlzAAAPYQAAD2EBqD+naQAAPfRJREFUeJzt3Qd0lGXaxvE7BQJhQif0Jr13QZpEEBUWkCYg4gqKUmysurZPFyzYcMHFRRRp0pVeBBGp0kRRREA6hBoIoYQEElK+c9+YLCgqSMjMvO//d04OmAmTJ7vKXPOU6wlITU1NFQAA4FqB3h4AAADwLsIAAAAuRxgAAMDlCAMAALgcYQAAAJcjDAAA4HKEAQAAXI4wAACAyxEGAABwOcIAAAAuRxgAAMDlCAMAALgcYQAAAJcjDAAA4HKEAQAAXI4wAACAyxEGAABwOcIAAAAuRxgAAMDlCAMAALgcYQAAAJcjDAAA4HKEAQAAXI4wAACAyxEGAABwOcIAAAAuRxgAAMDlgr09AAAAMlNcQpLsOxEniUkpkjU4UErlyyE5Qtz9cujunx4A4Ao7o2Jl0vpIWbb9mETGxEvqJY8FiEiJvKESUSFcutcvIeUKhonbBKSmpl76vwkAAI5xICZeXpi1WVbtipagwABJTvn9l7ygXx5vUja/DG5fTYrnDRW3IAwAABxp6oZI+dfcLZKUkvqHIeBKoSA4MEAGta0iXeuVEDcgDAAAHOf9ZTtlyOId1/08T7csL49GlBOn4zQBAMBxMwIZEQSUPs+0DZHidMwMAAActUegxdAVkpCU8qdfm3Q2RmK/nSsJh7dL4tFdkpp4Tgp2GyzZSla/7OtCggNlyYBbHb2HgJkBAIBj6GZB3SNwNZJOHJQz66ZLcuwJyVqg5O9/XUqqPa+TcbQQAOCY44N6auBqZS1UVoo9MUWCsodJ3M9fS8KhN6/4dbr5UJ9317FYKRvuzGOHzAwAABxBewT0JEDKhQQ59FEf+9Dfp0k+FysHh/eQoxOeltSUZAkMCbUgcDX0eSeuc+7eAcIAAMARtFBI38UHZgmR/H8bIEknD8uplZ+kPx6z+ANJSYiXfK0HSEBg0DU9d3JKqizbcUycimUCAIDfO5uQZM2CaUKKVJCcDTrKmXUzJLT8LZIcd0rit62UPM17S5a8Rf/S94g8EW9Vxk6sLnbeTwQAcJ39J+IuqxhWuRvfK+d2bZAT84dKyoXzElK8qoTVbfuXv0eqiN1pUKVILnEalgkAAH5PLx36tYCgLJKv1ROSdDrKjg3ma/2kBAQEZPj3cQLCAADA7+ntg1dyfu9G+zU1KdH2ENyo7+PvnPlTAQBcRa8h/vV7/sRje+XU6imSo1oLyVqwjJxYOFxSzsf95e8R8Mv3cSLCAADA7+mmPr2GOE1qcpKcWDBMgjz5JG+Lh22JIDnupMR8Neovf48S+UIduXlQOfOnAgA4jrbnr1y5Ui5cuCC5c+e2jzx58kiuXLkkODhYIiqEy4T1++0Y4Ok10yQxao8U7Pa69QlkDS8tuRt1k1MrJ0iOio0ke5l69pynVk+1Xy9EX+wQOLtlmZw/uNV+n7tR18t6BiLKh4tTEQYAAH4hJiZGmjVrdsXHAgMD5bXhoyU5pYAkHN0lp9d+KmF1/nbZPQM5G3SS+J3rbLmgyEMjJDCbR06vmnjZ88T9+GX67y8NAxow7mvg3OuMuagIAOA3GjZsKOvXr5eUlMt39WfLlk1+/PFHeWVljKzZc8JevDNKUGCANLwpn0x4sL44FXsGAAB+ISEhQWrXrn3FILBmzRopV66cDG5fTYIDr+/44K/p8+nzOhlhAADg0w4cOCAvvviiFC9eXP773/9K1qxZ0x/T3oBPP/1UatWqZf+s1wwPalslQ7//K22rOPr6YkUYAAD4HF3BXr58uXTq1ElKly4tw4cPl27dusn27dvl//7v/9LLg4YOHSpt2rS57M92rVdCnm5ZPkPG8UzLCtKlnnP3CqRhzwAAwGecPXtWJk2aJO+//7789NNPUrlyZXn00Uflvvvuk7CwizcMHj16VMqUKSMPPPCAfd3vtQpO3RAp/5q7RZJSUq9pD0FQYIAtDeiMgBuCgCIMAAC8bufOnTJixAgZO3asxMbGSrt27SwEREREXPHF/vTp05IzZ84/rRc+EBMvL8zaLKt2RduL/B+FgqBfHm9SNr/tEXD60sClCAMAAK/QjYCLFi2yJQD9NX/+/NK7d2/p06ePlCiRse/Id0bFyqT1kXYNsd4+eOkLX8AvhULaI6DHB8uGX5yBcBPCAAAgU508edJmAHQmYPfu3VKnTh157LHHpEuXLnYy4EbTa4j19sFBrw2WSuXLyj/79nRss+DVIgwAADKF9gDoaYCJEydai+A999xjSwH169e/7tsEr9WhQ4ekWLFikj17djl+/LjkyOHMOweuFqcJAAA3jL7oT58+XW699VapUaOGzJ8/X55//nk7LqihoEGDBpkeBJRuPFTnzp2TIUOGiNsxMwAAyHBRUVEyatQoGTlypL0Lb9q0qc0C3H333ZIlSxavji0uLk6KFCkiZ86csX/Oli2b7Nq1S4oWLSpuxcwAACBD6HtLrQrWY4BaEDR48GBp3bq1bNq0SVasWCGdO3f2ehBQn3zyiZ1YuHT24sUXXxQ3Y2YAAHBdzp8/L9OmTbOp92+//VZuuukm6d+/v/Ts2dNuFfS1EwxaW7xnz57LPh8QECDfffddepOh27h7+yQA4C+LjIy0ZQBdDoiOjpY777zT9gTcdddddougL9IX/F8HAaXvi+fNm+faMMDMAADgmmuCtRtgzpw54vF4pFevXtKvXz97x+3rdMPgZ599ZmFFX/yXLl1qAUZ/jooVK0pQUJC4ETMDAICrqgmeMGGCLQVs3bpVqlSpYscEdX+AvpD6Cz1KeP/999vvjxw5IosXL7ajjW5HGAAA/K4dO3bYi/64ceMsEOhpAP1nPSrojSOBGUlDjG4kTE1N9fuf5XoRBgAAl0lOTpaFCxfaLMAXX3xhNcF6LPCRRx7J8Jpgb9KLj/RnTUhIyJTmQ19GGAAAmJiYmPSaYN1kV7duXRk/frw1BTrxxTJteePs2bOO/PmuBWEAAFxOewB0FkCvDtZ3ynpHwJQpU+Tmm28WJ0sLA7GxsTb74WaEAQBwIS3amTVrloWAVatWWfueFu/orYHh4eHiBrpMkDYz4HaEAQBwkaNHj6bXBB8+fNg2AurdAe3atZPgYHe9JFy6TOB27vp/HgBcSHfLr1u3zmYB9Iy9VgL36NHDWgKrVasmbnXpMoHbEQYAwKG0YEdrgrUgaOPGjVKmTBl56623rCY4d+7c4nYsE/wPYQAAHGb//v3ywQcfyMcffywnTpyQVq1ayeeffy533HGHz9YEewPLBP9DGAAAhywFaLWuLgXMnTvX3vWm1QSXLVvW28PzSVmzZrUlk1iWCQgDAODP9IUsrSZ427ZtUrVqVZsV6N69u+TIkcPbw/N5GprOMjNAGAAAf7R9+/b0muD4+Hhp3769hYCmTZu6vlr3WpcKzhIGCAMA4C+0EEjX/nVD4JdffikFChSQxx9/XPr06SPFihXz9vD8dmYglmUCwgAA+DrdBDhmzBirCd63b581A+rSQOfOnSUkJMTbw/NrzAxcRBgAAB/1ww8/pNcEp6SkSNeuXe2ooNNrgjMTYeAiwgAA+FhN8MyZM20pYPXq1Tb9/9JLL1lNsC4LIGOxTHARYQAAfMCRI0fko48+kg8//NB+HxERITNmzJC2bdu6riY4s2cGIiMjxe34NwwAvNgNsHbtWpsF0PsB9Nz7/fffL48++qhUqVLF28NzBZYJLiIMAIAXaoL1imDdD/D9999bKdCQIUPk73//OzXBmYxlgosIAwCQSfQkQFpN8MmTJ60mePDgwdKyZUtqgr2EmYGLCAMAcIOXApYsWWKzAPPmzZNcuXKl1wTrxUHwLsLARYQBALgBzpw5I5988omFAG0L1KuCdXPgvffeS02wD9YRp6amurq5kTAAABno559/Tq8J1r0BHTp0sFMCTZo0cfWLjS/PDKSmplqls5tDGmEAADKgJnj+/Pk2C6BLAuHh4TJgwAB55JFHpGjRot4eHq7yGuMchAEAwF+pCR49erTVBO/fv18aNGggEydOlE6dOlET7EfLBEpPFBQsWFDcijAAANdo48aNNgugxwN1illrgrUboG7dut4eGq5jZsDNCAMAcBUSExOtEVBDwJo1a6R48eLyr3/9Sx588EFqgh0QBmJd3jVAGACAP3D48OH0muCjR4/KbbfdZncHtGnThppgBy0TnGVmAABwKZ3610uCdBZAZwN0/V/bAfv37y+VK1f29vCQgVgmuIgwAAC/0ONlaTXBen1wuXLl5N1337UgoGVBcJ60EwSxLBMAgLvt2bPHaoL1ZMCpU6ekdevW8tZbb0mLFi2oCXY4XerJnj07MwPeHgAAeENKSkp6TbB2BOgFQboZsG/fvnLTTTd5e3jIRB4qiQkDANxXEzx+/HgLATt27JAaNWrYBkGtCQ4NDfX28OClMBDLMgEAON/WrVutJljvCzh//rx07NjRlgUaNWpETbDLhf1yP4GbEQYAOFZSUpItAQwfPlyWLl1qDXP/+Mc/5OGHH6YmGOk8LBMQBgA4T3R0tHz88ce2KTAyMlJuueUWmTRpktUEZ82a1dvDg4/xsExAGADgHN999116TbDSfQDaDVCnTh1vDw0+LIxlAsIAAP+vCf7ss88sBKxbt05KlCghgwYNspMB+fPn9/bw4CczA0ePHhU3IwwA8EuHDh2yimA9CRAVFSXNmzeX2bNny9/+9jcJCgry9vDgRzwsExAGAPhXTfDXX39tswB6P0C2bNnSa4IrVark7eHBT4WxTEAYAOD74uLiZPLkyRYCfvzxRylfvrz8+9//tiCQM2dObw8Pfs7DaQLCAADfrgkeMWKE9QGcPn3abgocMmSILQlQE4yMnBmIZZkAAHyrJnjx4sU2C/D5559bTXDv3r2tJrh06dLeHh4cOjNw7tw5SU5Odu1+E8IAAJ+g7/zHjRtnLYE7d+6UmjVrWldA165dqQlGplxjHBcX59plJ8IAAK/asmVLek1wQkKCFQONHTtWGjZsSE0wMm2ZQOlSAWEAADKxJnju3Lm2FLBs2TIpVKiQPP300/LII49I4cKFvT08uHRm4KyLNxESBgBkmuPHj6fXBB84cMAuCdK2wA4dOlATDK/xEAYIAwBuvA0bNtgswNSpU+0UQFpNcO3atb09NEAuXSZwK8IAgBtC1//TaoLXr18vpUqVktdee0169eol+fLl8/bwgHQeZgYIAwAy1sGDB2XkyJFWE6zLArfffrvMmTNHWrdu7dpjW/BtHsIAYQBAxtQEr1y50mYBZs2aJdmzZ5cHHnjAlgIqVqzo7eEBfyg0NNROrrBMAAB/gZ7LnjRpkoWAzZs32wv/e++9Jz169HDtES34n8DAQMmRIwczAwBwLXbt2mU1wWPGjLF3U1oTrHcFaE0w3QDwRx6X309AGABw1TXBX3zxhc0CLFy4UPLkySN9+vSxD90cCPizMJffT0AYAPCHTp06lV4TrDMCtWrVsouDtCZY9wYATuBhZgAAfkv3AGgAmDBhgiQmJkrnzp2tMrhBgwYsBcCRYSCWmQEAuFgTrMcAdSlg+fLlVg387LPP2q2B1ATD6csEZ5kZAOBmx44dk1GjRlk/gPYENG7c2NoC27dvT00wXDMzEBMTI25FGABc7JtvvrFZgGnTplkhUPfu3a0bQK8PBtwWBvbv3y9uRRgAXFgT/Omnn8rw4cPtzgA9CfD6669bTXDevHm9PTzAK8JYJgDgBnpLoC4D6HKA1gS3bNnSrhFu1aoVNcFwPQ+nCQA4uSZ4xYoVthQwe/Zsq13t2bOn9OvXTypUqODt4QE+w8NpAgBOo+9wJk6caCFgy5YtUqlSJfnPf/5jNcFp17UC+J8wlgkAOMXOnTutJnjs2LH2Lqddu3YWAiIiIugGAP5kZiAxMdE+3HiChjAAOKAmeNGiRbYhUH/Nly+f9O3b12qCS5Ys6e3hAX53jXFeF26kJQwAfurkyZM2A6AzAbt375batWvbP2tNcLZs2bw9PMCvhP2yfEYYAOAXfvzxR6sJ1j0BFy5ckHvuucd+X79+fZYCgAyYGXAjwgDgB/RFX08D6IbAlStXSpEiReS5556zmuBChQp5e3iAY8JArEtPFBAGAB8WFRWVXhN86NAhadq0qRUG3X333ZIlSxZvDw9w5DKBGxEGAB/sBtCaYN0QqC/8wcHBct9991lNcI0aNbw9PMCRPCwTAPAF58+ftzsCdCng22+/ldKlS8sbb7xhNcF58uTx9vAAV8wMxLJMAMAbIiMj02uCo6Oj5c4775T58+fbr9QEA5kjJCTE/ntjZgBApi4FLF++3JYC5syZY1OUaTXB5cuX9/bwANcJCAhw9f0EhAEgE+lfNBMmTLClgK1bt0rlypXt91oTnLZmCcB7SwWxLBMAuFF27Nhh3QDjxo2zQKCnATQENGvWjG4AwEd4mBkAkNGSk5Nl4cKF9qL/xRdfSP78+e1EgNYElyhRwtvDA/ArHsIAgIwSExOTXhO8Z88eqVu3rowfP96aAqkJBnxXGMsEAK7Xpk2bbBZg0qRJkpSUJF26dJHJkydbTTAA3+dhZgDAX60JnjVrloWAVatWSdGiReWFF16wmuCCBQt6e3gArjEMHD16VNyIMAD8BfoXRlpN8OHDh+XWW2+Vzz77TNq1a0dNMODHywQ7d+4UNyIMANfQDbBu3TqbBdAXfn3RT6sJrl69ureHB+A6eVgmAPB7zp07ZzXBWhC0ceNGKVOmjLz11lvywAMPUBMMOIiHMADg1/bv3y8ffPCBfPzxx3LixAm56667ZMGCBVYTHBgY6O3hAchgYZwmAJC2FLB06VJbCpg7d6795ZBWE1yuXDlvDw9AJswMpKamuq4MjDAA/HJTWVpN8LZt26Rq1arWE9C9e3dqggGX8Hg8VhaWkJDguk4QwgBcbfv27ek1wfHx8VYTrCFATwe47Z0B4HZhl1xjTBgAHE6T/+eff26zAIsXL7aa4Mcee8xqgosXL+7t4QHwEs8vs4C6VFCgQAFxE8IAXFUTPHr0aHvnv2/fPrn55pvlk08+kc6dO7vuXQCA3w8DsS7cREgYgOP98MMP6TXBKSkpVhOsRwU1DADAr5cJ3Hi8kDAAx9YEz5w507oBVq9eLcWKFZOXXnpJHnroIQkPD/f28AD4+DKB2xAG4Lia4A8//NA+jhw5Is2aNZPp06dbTXBwMP+6A/h9HpYJAP+lZ4LXrl1rSwH6wq81wT169JBHH33UjggCwNXwMDMA+GdN8JQpUywEfP/991K2bFl5++23rSY4d+7c3h4eAD+TNWtW+yAMAH5ATwKk1QSfPHnSaoIHDx4sLVu2pCYYwHXPDsSyTAD47lLAV199ZRsC582bJzlz5pQHH3xQ+vbtazMCAJBRJwrOMjMA+BZN6OPHj7eWwJ9//lmqVasmI0eOtJrgHDlyeHt4ABzGrTcXEgbgk/SFXwOABgGtCW7fvr2dEGjSpAk1wQBuGA/LBID3a4Lnz59vGwKXLFlifQBPPPGEPPLII9YTAAA3WhjLBIB3nDhxIr0meP/+/VK/fn2ZOHGidOrUSUJCQrw9PAAu4mGZAMhcehxQNwTq8UDdINi1a1fp37+/1KtXz9tDA+DiMLB//35xG8IAMlViYqLMmDHDlgLWrFljtwS+/PLLVhPstlvCAPieMJYJgBvn8OHD8tFHH9kmQK0MjoiIsLsD2rRpQ00wAJ/hYZkAyFg69a/v/nUpQGcDdP3//vvvt6WAKlWqeHt4AHDFmYFYThMA10+PAqbVBOv1weXKlZMhQ4ZYTXCuXLm8PTwA+F0eZgaA67N3716rCdaTAVoT3Lp1a3nzzTfl9ttvpyYYgN+Egbi4OElJSXHV31uEAVwX/Q9GOwF0FkA7AvSdv9YE9+vXT2666SZvDw8ArnmZIDU11WY4024xdAPCAP6SM2fOpNcEb9++XapXr24bBO+9914JDQ319vAA4LqvMfYQBoAr27Ztm80CfPLJJ3aFcMeOHWXUqFHSuHFjaoIBOCoMuAlhAFdVE6w3BWoI0JsDCxYsKP/4xz/k4YcflqJFi3p7eACQocsEym0nCggD+F3R0dHpNcGRkZFyyy23yKRJk6wmOGvWrN4eHgBkOA8zA8BF3333nc0C6PFA1a1bN3n00UelTp063h4aANxQHsIA3F4TPH36dAsBa9eulRIlSsigQYPsZED+/Pm9PTwAyBRhLBPArTXBI0eOtJMAUVFR0rx5c5k1a5bVBAcFBXl7eACQqXLkyGG/MjMAx9MztF9//bXNAuj9ANmyZZO///3v1g1QuXJlbw8PALwmKChIsmfPThiAc2mJxuTJky0EbNq0ScqXLy///ve/LQjkzJnT28MDAJ8Q5sL7CQgDLrBnzx47EaAnA06fPm1LAO+8844tCbipbhMArobHhfcTEAYcXBP85Zdf2izAggULJHfu3NK7d2/p27evlC5d2tvDAwCfDgOxzAzAn+k7/3HjxllN8M6dO6VGjRrWEKjHA6kJBoCrWyY4y8wA/NGWLVssAGhNcEJCghUDjR07Vho2bEhNMABcAw/LBPAnSUlJ6TXBS5culUKFCsnTTz9tNcFFihTx9vAAwC95WCaAPzh+/Lh8/PHH8sEHH8iBAwekUaNG1hbYoUMHaoIBIAOWCY4cOSJuQhjwI99++63NAkydOtWm/vW64P79+0vt2rW9PTQAcAwPywTwNbr+rzXBw4cPl/Xr10upUqXk1VdflV69ekm+fPm8PTwAcBwPywTwFQcPHpQPP/zQaoKPHTsmt99+u8yZM0dat25NTTAA3EBhnCaAt2uCV61alV4TrJWYDzzwgC0FVKxY0dvDAwBX8LBMAG+Ii4uTSZMmWQjYvHmzvfC/99570qNHD2qCAcALYeDcuXN2Yis42B0vk+74KX3U7t27rSZ4zJgxcubMGasJ1rsCtCaYbgAA8O41xnFxcZIrVy5xA8KAF2qCFy9ebBsCFy5cKHny5JFHHnlE+vTpY5sDAQDenxlQulRAGECGOnXqVHpN8K5du6RWrVp2cVDXrl1tbwAAwLfCQKyLThQQBm6wn376yQLAhAkT7Jhg586drTK4QYMGLAUAgA8vE5x10SZCwsANoJtO9Bigbghcvny5FC5cWP75z3/arYH6ewCAfywTuAVhIANpH0BaTbD2BDRu3NjaArUmOEuWLN4eHgDgGmYGYlkmwLXYsGGDbQicNm2aFQJ1797dugFq1qzp7aEBAK6Rh5kB/xaXkCT7TsRJYlKKZA0OlFL5ckiOkBvzI+r6/6effmpLAd98842dBHj99detJjhv3rw35HsCAG680NBQ29NFGPAjO6NiZdL6SFm2/ZhExsRL6iWP6fa8EnlDJaJCuHSvX0LKFbw49XM9dPp/5MiRVhOstwe2bNnSrhG+6667qAkGAAcICAhw3f0EfhsGDsTEywuzNsuqXdESFBggySmXxoCL9DP7Y+Jlwvr9Mm7tPmlSNr8Mbl9NiucNveaa4BUrVtgswOzZsy019uzZU/r16ycVKlTIwJ8KAOALPC6rJA4UPzR1Q6S0GLpC1uw5Yf98pSBwqbTH9ev1z+mfvxraPqWXBVWvXl0iIiJk69at8p///EcOHTpkdcEEAQBwJo/LwoDfzQy8v2ynDFm84y/9WQ0F+vHczM0SfTZBHo0od8Wv01Ig7QYYO3asTRO1a9fOXvw1ENANAADuOFEQyzKBb9J39FcbBFLOn5WTy8ZK/I61kpqUIFkLl5c8tz0oIYXK2uP6PAU8IdKlXomLX5+SIosWLbKlAK0Jzpcvn/Tt29dqgkuWLHlDfy4AgG/xMDPgu3sE/jV3y1V9bWpqihz7bJAkHtsrOet3kKDsOSX2+88lavLzUviBYZIlb1H7upfnbpGqBbLK4llTbCZALw6qU6eO1QZ36dJFsmXLdoN/KgCAL/KwgdA36WbBpD/ZG5Am/ufVknBom+S/+znJUbGxfS60UhM5/OHDcurryVKg7TP2uQtJyXL7C2MkevpAueeee2TixIlSv359lgIAwOXCwsIkOjpa3CLYX44P6qmBlAsJcmTsE/a5wj3fk8AsIfb75HOxcuTjfhKcu6AU7P6WxG9fLYE5cktohYbpzxEUmssCQdyWZZKadEECgrNIigRI1pI1ZMUP2+WWyqW99vMBAHxvZmDfvn3iFn5xmkB7BPT4oL745//bAEk6eVhOrfwk/fGYxR9ISkK85Gs9QAICgyQxardkLVhGAgIu//F030DqhQS5EHMo/XP6vF/sis/UnwcA4Ns8Llsm8IswoIVCaccDQ4pUkJwNOkrst/Pk/IGfJO7nryV+20rJfev96XsBks+elCDPb1sAgz15fnn84pFE+31KqizbcSzTfhYAgH8sE5xlA6HvOJuQZM2Cl8rd+F45t2uDnJg/VFIunJeQ4lUlrG7b9MdTkxIlIOgKFwMFZU1//FKRJ+KtyvhGVRcDAPyLx2WnCXx+ZmD/ibjLKoaVvtDna/WEJJ2OktTEc5Kv9ZOXbfoLCM4qqckXfvtkyYnpj19Kn1/vNAAAQLFM4GP00qErOb93Y/q7fN1DcKkgTx5JPhvzmz+TdPbkL4/nu+rvAwBw5zLBhQsXJDHx8plkp/L5MKC3D/6a9gecWj1FclRrYRsFTywcLinn//fOPmv4TbaJUPsGLvtzh7dLQJaQ9L0Ff/Z9AADu5HHZNcY+/wqo1xBfeuo/NTlJTiwYZu/u87Z42JYIkuNOSsxXo9K/JrRiI0mJOyXx29ekfy45/rTE//y1ZC97sx0rvFTAL98HAIBLw4Bblgp8Pgzopj69hjjN6TXTJDFqj+Rv9YQEhoRK1vDSkrtRN4nbvETO7d5gXxNaoZFkLVJBTnz+npz6eorEblxg7YM6U5C7cffffI+k00fl8X6PyOLFi21aCADgbmFhF6+8Z2bAh0RUCLc+gISju+T02k8lrM7fJFvJ6umP52zQSbIWLvfLcsFZ6xoIv2eQ5KjYRGK/mycnl42RwOw5pWC3wZIlX7HLnjswQKRy7lRZuXKl3HHHHVK4cGF5+OGH5auvvpKkpCQv/LQAAG/zuGyZICA1NfXqOn693EB4+7CVN+z5lwxoKmUKeOSHH36QadOmyaeffip79+6V8PBw6dixo1UVN2nSRIKCgm7YGAAAvmPfvn1SunRp+fLLL6VFixbidH4xM1CuYJg0KZvfZgcykj6fPm/Z8DA7mlirVi1588037cKib775Ru6//35ZsGCBXV1crFgxeeyxx2TVqlV2wyEAwLk8LpsZ8IswoAa3rybBGRwG9Pn0eX9Ng0G9evXknXfesXS4du1a6datm8yaNUuaNm0qxYsXlyeffFLWrFlDMAAAB/IQBnxT8byhMqhtlQx9zlfaVrHn/SMaDBo0aCD//ve/JTIyUr7++mvp1KmTLSU0atRISpUqJU899ZTNJPjBigsA4CqEhIRIcHAwpwl8Udd6JeTpluUz5LmeaVlButQrcU1/JjAw0ALAe++9JwcOHJAVK1ZImzZt0q8+1vWlf/7zn/Ldd98RDADAjwUEBLiqktivwoB6NKKcvNmhmoQEB17zHgL9ev1zb3WoJv0jyl7XOHQzoS4Z/Pe//5XDhw/L0qVL5c4775SxY8dK3bp1pVy5cvLCCy/YpkSCAQD4H4+LwoBfnCa4kgMx8fLCrM2yale0vcin3Wp4JWmP62ZB3SPwZ0sD10OPIy5fvtxOJcycOVNiYmKkfPnydiJBP6pWrXrZPQoAAN9UuXJle5Ony8RO57dh4NJjh5PWR9o1xHr74KU/jL7klsgXKhHlw+W+BiXs1EBm0gIj7SvQ/QW6+fDUqVNSqVKl9GCg/6IBAHzTzTffLDVr1pSPPvpInM7vw8Cl9BpivX1QLx3Suwa0YthXriXWyy70vKoGg9mzZ8uZM2dslkBDQZcuXWz2AADgO2677TYpWLCgTJkyRZzOUWHAX5w/f96qj3UpYe7cubYmVaNGjfQZg7Jlr28/AwDg+rVr186Oj8+bN0+cjjDgZefOnZNFixZZMNB/4eLj46V27do2W9C5c2c7oQAAyHzdu3e3DeLLli0Tp/O70wROkz17dmnfvr1MnTpVjh8/bssIN910kwwcONB+1TWrd9991zoOAACZe5oglp4BZLbQ0FCbDfjss8/k2LFjtk6lNcgvvviilCxZUho2bCjDhg2TgwcPenuoAOCKmwvPuuRoIWHAhxNp165d7XiiBgMtNipQoIA8++yzVoesFycNHz5cjhw54u2hAoAjeVzUM0AY8AM5c+a0tas5c+ZIVFSUjB8/XnLlymU1yEWLFpVmzZrJiBEj7DEAQMbwsEwAX5U7d267TXH+/Pn24j969GjJli2bPP7441KkSBFp3ry5nYmNjo729lABwBHLBKku2GdPGPBjefLkkZ49e9pphKNHj8rIkSPt/oS+fftKoUKFpGXLlhYWtAURAHDtMwMpKSl2HNzpCAMOkT9/fundu7cVG+k+gvfff9+qkfVzWprRqlUrGTdunLUgAgCubmZAuWGpgDDgQOHh4dKnTx+7PEnPyOoJhLi4OOnVq5c9pjctTpgwwVoQAQC/PzOg3LCJkDDgcLpc0L9/f7tuWa9dHjJkiC0b6L4DDQZ33323TJ482RXJFwCuhYcwACfSkwe60XD16tVWYvTGG2/YXgM9qaDBoGPHjlZ6pLMIAOB2YSwTwOm0q2DAgAGybt062bt3r7zyyisWELQGWfsM9I6EGTNmWD0yALiRh5kBuEmpUqXkmWeekQ0bNsju3bvl5Zdfll27dkmnTp1sxuDee++1mxbdsKMWANIQBuBaeh/Cc889Jxs3bpTt27fL888/L1u2bLH7EzQY9OjRwy5USkhI8PZQASBTwkAsywRws/Lly9u9CJs2bZKtW7fK008/bSGhbdu2dlzxgQcekM8//1wSExO9PVQAyHBZsmSRkJAQZgaANJUqVbLlA50l+Omnn+SJJ56w/QatW7e2EwsPPvigLF68WC5cuODtoQJAhvG4pJKYMIBrVqVKFRk0aJBs27bNZg208XDlypVyxx13SOHCheWRRx6Rr776ykqPAMCfhbnk5kLCAP6ygIAAqV69urz++uuyY8cOW0J46KGHrAWxRYsWdpSxX79+snz5cklOTvb2cAHgmnlccnNhQKobbmBAptJ/pb777juZNm2a9RbokUVdStDTCXpksVGjRnaHAgD4ultuucWWSceMGSNORhjADaX/eq1fv95CgX4cOnTIblfs3LmzBYMGDRoQDAD4rJYtW9ptsfr3l5MRBpBp9PavtWvX2n9Un332mV2opOVHGgy07KhevXq29AAAvqJDhw7WsaInp5yMMACv0D0EWouswWD69OkSFRUlJUuWtNkCDQa1a9cmGADwuvvvv99aWletWiVOxvwsvCIoKEiaNm1qVy3r0oGePrjzzjtl7NixUrduXSlXrpy88MIL8sMPP9hSAwB4QxinCYDMCwa33XabjBw50pYO9DRCRESEfPjhh1KrVi2pWLGivPTSS7J582aCAYBM5XHJaQLCAHxKcHCwHUscNWqU3ai4cOFCO32gMwh6jFE7DgYOHGiNiACQGTMDsZQOAd6tAtWlAz3So3sK5s+fb5sMhw4daqGgWrVq8uqrr1rHAQDcCB5mBgDfkTVrVqs+Hj9+vAWDOXPm2EzB22+/LRUqVJCaNWvK4MGD7bZFAMjIMBAXF2enoZyMMAC/ky1bNrssadKkSXLs2DGZOXOm7SvQJkTdeFinTh0LCboDGACud5lAaSBwMsIA/Fr27NnteuWpU6fK8ePH7aiiXsOs+wr015tvvlneffdda0EEgL96jfFZhy8VEAbgGKGhoVZgpIVGOmMwZcoUKVasmF3DrB0GDRs2lGHDhsnBgwe9PVQAfsJDGAD8+z/grl272hKCBoOJEydKgQIF5Nlnn7XWwyZNmsjw4cPtKCMA/NkygdNPFBAG4Hg5c+aU7t2726ZD3XyomxBz5colTz31lN2s2KxZMxkxYoQ9BgCXYmYAcCC9cETrRfWYor74jx492jYkPv7443aBUvPmzeWjjz6S6Ohobw8VgA+FgVhmBgBnypMnj/Ts2VMWLVpkBUfagKg3KPbt29euXNbbyjQsxMTEeHuoALy8THCWmQHA+fLnzy+9e/e2KmTdR6CNh0lJSfa5ggULSqtWrWTcuHFy6tQpbw8VQCZvTFaEAcBlwsPDpU+fPrJ06VI5fPiwnUDQM8a9evWyx9q0aSMTJkyQM2fOeHuoADLh7pTQ0FCWCQA30+WC/v37y4oVK+TAgQMyZMgQWzbQfQcaDO6++26ZPHmy4/+iANwszAU3FxIGgKukJw90o+Hq1autxOiNN96wvQZ6UkGDQceOHa30yOlNZYDbeFxwPwFhAPgLtKtgwIABsm7dOqs9fuWVVywgdOnSxfoM7rnnHpkxY4bEx8d7e6gAMiAMxDp89i8glQvigQyzZ88emx3Qj++//15y5Mhh9yhoONAbGPUYIwD/0qRJE6s3144SpyIMADeIXq2s1cgaDH788Udbd2zXrp0FAz22GBIS4u0hArgKd911l20i1Nk+pyIMAJlg27ZtFgymTZsmW7dutQZE3XyowaBFixZ2RTMA39S5c2c5ffq0LF68WJyKMABksi1btthsgQaD7du3W/mR3ryo+w0iIiIkS5Ys3h4igEvoseKff/5Z1qxZI07FBkIgk1WpUkUGDRpkswWbNm2yxsOVK1fKHXfcIYULF5aHH35YvvrqKys9AuB9Hk4TALhRAgICpHr16vL666/b/oKNGzfKQw89JEuWLLGlAz3K2K9fP1m+fLkkJyd7e7iAa4WFhTn+NAFhAPCRYFCrVi158803Zffu3bJhwwYrNlqwYIEtHRQrVkwee+wxWbVqlaSkpHh7uICreJgZAOCNYFC3bl155513ZN++fbJ27Vrp1q2bzJo1S5o2bWodB08++aR9nmAA3HgeF4QBNhACfkJf+DUA6OZDPZmgFyppMNCdzrr5sF69ehYkAGSscePG2Q2nFy5ckODgYHEiwgDgh3QPgdYiazCYPn26REVFScmSJe2oogaD2rVrEwyADDJ9+nQL3SdPnpTcuXOLExEGAAcEAz2NoEcVtRQlOjpaypQpY8FAP2rUqEEwAK7DokWLrHhILyvT/TtORBgAHESPI+rpAw0GM2fOtBsWy5cvnx4MqlatSjAArtHq1aulcePGVhhWqVIlcSLCAOBQur6pfQW6lKCbD0+dOmV/kaUtJTj1LzUgo23atElq1qwp33zzje3NcSJOEwAOpU2GejnSmDFjbE/B/Pnz7S+yoUOHSuXKlaVatWry6quvWscBgD8+TaCc3DVAGABcQO8+aN26td26psFgzpw5Vnj09ttvS4UKFexdzxtvvCG7du3y9lABnywdUk4+XkgYAFxGr1HWa5UnTZokx44ds70FFStWlNdee03KlSsnderUsZCwd+9ebw8V8KmZgbOEAQBOlD17drskaerUqXL8+HHbX6D3tg8cONB+rV+/vrz77rsSGRnp7aECXv3vJDAwkGUCAM6n97XrWWotNNIZgylTptj9CC+++KJ1GDRs2FCGDRsmBw8e9PZQgUwVEBDg+BZCwgCA39C/+Lp27WpLCBoMJk6cKAUKFJBnn33WWg+bNGkiw4cPtxZEwA08hAEAbpYzZ07p3r27bTrUzYe6CTFXrlzy1FNP2cxBs2bNZMSIEfYY4OQwEMsyAQCIVbHqbYp6TFFf/EePHm0bEh9//HEpUqSING/eXD766CNrQQScdqLgLDMDAHC5PHny2OUtWtV69OhRGTlypG2y6tu3rxQqVEhatmxpYUFbEAF/52GZAAD+WP78+aV3797y5Zdf2j6C999/36qR9XMFCxaUVq1a2c1v2oII+OvMQCzLBABwdcLDw6VPnz6ydOlSOXz4sJ1AiIuLk169etljbdq0kQkTJsiZM2e8PVTgqnmYGQCAv0aXC/r37y8rVqywG9+GDBliywa670CDwd133y2TJ0929DsuOIOHMAAA109PHuhGQ70BTkuMtP5Y9xroSQUNBh07drTSI51FAHxNGMsEAJCxtKtgwIABsm7dOqs9fuWVVywg6G2K2megNyvOmDFD4uPjvT1UwDAzAAA3UKlSpeSZZ56RDRs2yO7du+Xll1+2C5M6depkMwb33nuvzJ49W86fP+/tocLFPIQBAMgceh/Cc889Jxs3bpTt27fL888/L1u2bLH7EzQY9OjRQ+bNmycJCQneHipcukyQmpoqThSQ6tSfDIBjbNu2ze5MmDZtmmzdutUaEHXzoS4ntGjRwq5oBm6kCRMm2MZXnaEKCQkRpyEMAPArOlOgGw01GOjsgZYf6cyB7jeIiIiQLFmyeHuIcKBZs2ZJhw4d7HZP7dVwGsIAAL+kf3Vt3rzZQoGGA91nkC9fPjuVoDMGt956qwQHB3t7mHCIJUuWyO23324bXnWfi9MQBgD4Pf1r7IcffkgPBvoXdtpxRQ0GestiUFCQt4cJP7Zu3Tq55ZZbLIBWrVpVnIYwAMBR9K+0b7/91kKBfuiRRS0/0tMJGgwaNWpkdygA1+Knn36SatWqyZo1aywUOA1hAIBj6V9v69evTw8Ghw4dstsVO3fubMGgQYMGBANclf3799vywOLFi225wGkIAwBcISUlRdauXWuhQE8m6IVKWn6kwUA3H9arV08CAgK8PUz4qBMnTtjGwZkzZ9qGVachDABwneTkZKtF1mAwffp0iYqKkpIlS9psgQaD2rVrEwxwGe22yJYtm4wfP96OGDoN82MAXEc3EzZt2tSuWtalA71h8c4775SxY8dK3bp1pVy5cvLCCy/YpkTeL0Fpt4AeW3VqCyFhAIC4PRhoP8HIkSNt6eDLL7+0f/7www+lVq1aUrFiRXnppZdsFznBwN08Dq4kJgwAwC+0l0AbDUeNGmU3Ki5cuNBOH+gMQvXq1aVKlSoycOBAa0GE+4Q5+OZCwgAAXIFOCevSwZgxY2xPwfz5822T4dChQy0U6DGzV199VXbs2OHtoSKTeJgZAAD30rsPWrdubZvHNBjMmTPHZgrefvttqVChgtSsWVMGDx5sLYhwLo+DwwCnCQDgLzp37pwsWrTImg/1NsX4+Hg7iaAnEvTIYunSpb09RGSgFi1a2PHCqVOnitMQBgAgA2gQWLBggR1X1F81KOiyQlowKFGihLeHiOt09913S1JSki0ZOQ3LBACQAUJDQ+1FXwuNjh07JlOmTJFixYrJiy++aB0GDRs2lGHDhsnBgwe9PVT8RR4HLxMQBgDgBrxodO3a1drqNBhMnDhRChQoIM8++6y1HurFScOHD7ejjPAfYZwmAAD8FTlz5pTu3bvbpkPdfKibEHPlyiVPPfWUFC1aVJo1ayYjRoywx+DbPMwMAACuV+7cua3KVtec9cV/9OjRVnH7+OOP2wVKzZs3l48++kiio6O9PVT8ThhgZgAAkGHy5MkjPXv2tNMIWnCkDYh6g2Lfvn3tyuWWLVtaWIiJifH2UHHJMgEzAwCAG0KPq/Xu3duqkHUfgTYe6q51/VzBggWlVatWMm7cODl16pS3h+pqnl+WCZx4CI8wAAA+JDw8XPr06WOXJx0+fNhOIMTFxUmvXr3ssTZt2siECRPkzJkz3h6qK8NAamqqHSN1GsIAAPgoXS7o37+/rFixQg4cOCBDhgyxZQPdd6DBQM+9T5482bHr2L64TKCcuFRAGAAAP6AnD3Sj4erVqyUyMlLeeOMN22ugJxU0GHTs2NGaEHUWATduZkARBgAAXqddBQMGDJB169bJ3r175ZVXXrGAoN0G2mdwzz33yIwZMxw5ne0LYSDWgTMxhAEA8GOlSpWSZ555RjZs2CC7d++Wl19+2S5M6tSpk80YdOvWTWbPni3nz5/39lD9HssEAACfd9NNN8lzzz0nGzdulO3bt8vzzz8vW7dulfbt21sw6NGjh12olJCQ4O2h+iWPg5cJuKgIABxu27ZtdmeC7inQcKANiLr5UJcT9CY+vaIZf06Pdmo/hF5GpfdQOAlhAABcZMuWLfZipsFAZw/0xU1nDvR2xYiICMmSJYu3h+izkpKS7H+fMWPGWGGUk7BMAAAuUqVKFRk0aJDNFmzatMkaD1euXCl33HGHFC5cWB5++GH56quv7IUPlwsODrb6aCcuExAGAMCFAgICpHr16vL666/Ljh07bJ/BQw89JEuWLLGlAz3K2K9fP1m+fLkkJyd7e7g+I8yhNxcSBgDA5TQY1KpVS9588007kfDNN99YsdGCBQts6aBYsWLy2GOPyapVqyQlJUXczOPQmwsJAwCAy4JBvXr15J133pF9+/bJ2rVr7XjirFmzpGnTptZx8OSTT8qaNWtcGQw8Dg0DbCAEAPwpfeHXYKCbD/Vkgl6opMFAd9Xr5kMNEBoknK5Ro0ZSvnx5GTt2rDgJYQAAcE10D4HWImswmD59ukRFRUnJkiXtqKIGg9q1azs2GNxxxx2SM2dOC0ROQhgAAFxXMNDTCHpUUSuQo6OjpUyZMhYM9KNGjRqOCgYdO3a0mueFCxeKkxAGAAAZQo8j6ukDDQYzZ860GxZ1Sj0tGFStWtXvg8EDDzxgdc9ff/21OAkbCAEAGXYOX48ljho1ym5U1HfPusb+/vvv2zFG7TgYOHCgtSD6K49DNxASBgAAGU6b+u68805r69M9BfPnz7dNhkOHDrVQUK1aNXn11Vet48DfwkAsPQMAAFwbvfugdevWMn78eAsGc+bMsZmCt99+WypUqCA1a9aUwYMH2/S7P5QOnWVmAACAv07rfNu2bSuTJk2SY8eO2d6CihUrWhNiuXLlpE6dOvLWW2/J3r17xRd5WCYAACDjZM+e3S5Jmjp1qhw/ftyOKuo1zHp3gv568803y7vvviuRkZHiS2EgPj7ecRXNhAEAgNeFhoZagZGe39cZgylTplgN8osvvmgdBrfccosMGzZMDh486NVxZg0NkyzhpWXtjiOy5fBpiUtwxoVOHC0EAPisM2fOyLx582zWYNGiRZKYmCiNGze2o4qdOnWymxZvtJ1RsTJpfaQs235MImPi5dIXTT0oWSJvqERUCJfu9UtIuYJh4o8IAwAAv3Dq1CmZO3euBYPFixdbr4Hel6DBQMuAChYsmKHf70BMvLwwa7Os2hUtQYEBkpzy+y+XaY83KZtfBrevJsXzhoo/IQwAAPzOyZMnZfbs2VZwpNcu60tZs2bNLBh06NBBChQocMU/p8sQuhdBlx7+yNQNkfKvuVskKSX1D0PAlUJBcGCADGpbRbrWKyH+gjAAAPBrWoGstyrqjMHSpUut5fC2226zexJ0g2LevHnt6/R65rJly9rSgjYI6ibFK3l/2U4Zsvj6+w+eblleHo0oJ/6AMAAAcIy044oaDLQaOSgoSG6//XabMdDjiq+99pqFBV1S0EBQunTp38wIPDdzc4aN560O1aSLH8wQEAYAAI6klch6eZIGg1WrVtlSQhoNCYUKFbJAUKpUqfQ9Ai2GrpCEpJQ/fe5z+36QuC3LJeHgVkmOPSFBOXJLtpI1JFfT+yTYc3EmQoUEB8qSAbf6/B4CwgAAwPE0DOhmw0vpDEGePHksEFSqVEl6jF4va/acuKo9AkfGPSkp52IltGJjCc5bRJJOHZXY7+ZLQJYQKdJzuAR58qTvIWh4Uz6Z8GB98WXB3h4AAAA32ooVK34TBPS9sN6s2KpVK1m87kc7NXC18tz2kIQUrywBAf+r68leuo5ETX5OzmycL3ma9rDPabDQ5911LFbKhvvusUPCAADA8dJuSsyVK5dtItQ7EfRX/ecWLVpYj4C+i7+QcF6OjH3CvrZwz/ckMEuI/T75XKwc+bifBOcuKAW7vyXZSlT9zffQzwVmC5Ok6AOXfV6fd+K6SBnYtor4KsIAAMDx9JKkESNGSO7cua/4+GNfLLN38frin/9vA+TohGfk1MpPJG/z3vZ4zOIPJCUhXvK1HiABgUFXfI6UxHOScuGcBIbmvOzz+rzLdhyTgUIYAADAq1cq/14QOJuQZM2CaUKKVJCcDTrKmXUzJLT8LZIcd0rit62UPM17S5a8RX/3e5zZMEckOUlCKzb5zWORJ+KtujhHiG++7PrmqAAAyCT7T8RdVjGscje+V87t2iAn5g+VlAvnJaR4VQmr2/Z3n+N85E9yevUUCwLZS9X4zeP6/PtOxEmVIrnEF3FREQDA1RKvcJQwICiL5Gv1hCSdjpLUxHOSr/WTtunwSi6cOCDHZ74uWfKXlHx3PXZN38dXEAYAAK6WNfjKL4Xn9260X1OTEiXp5OErfk3SmeMSNe1lCQwJlfB7Btqv1/p9fIHvjgwAgExQKl8Ou33wUonH9sqp1VMkR7UWkrVgGTmxcLiknI+77GuSz52RqGkvSWrSBQnv8splZUO/FvDL9/FVhAEAgKvlCAm2a4jTpCYnyYkFwyTIk0/ytnjYlgiS405KzFej0r8mJfG8HPt0oLUP6ozAH20sVCXyhfrs5kFFGAAAuF5EhXDrA1Cn10yTxKg9kr/VEzbtnzW8tORu1E3iNi+Rc7s32NdEzxsiiUd2WAPhhegDcvanZekf8TvWXvbc+rwR5cPFl/luTAEAIJN0r19Cxq3dJwlHd8nptZ9KWJ2/SbaS1dMfz9mgk8TvXGfLBUUeGmFhQcX9+KV9XCooZ7gdSby0Z+C+Br59WRF3EwAAIHJNdxNcLX+5m4BlAgAARGRw+2oS/MtSQUbR59Pn9XWEAQAAROya4UEZfH/AK22r+Pz1xYowAADAL7rWKyFPtywvGeGZlhWkSz3f3iuQhj0DAAD8ytQNkfKvuVskKSX1mvYQ6B4BXRrQGQF/CQKKMAAAwBUciImXF2ZtllW7ou1F/o9CQdrjTcrmtz0C/rA0cCnCAAAAf2BnVKxMWh9p1xDr7YOXvmgG/FIopD0CenywbHiY+CPCAAAAV0mvIdbbB/XSIb1rQCuGfblZ8GoRBgAAcDlOEwAA4HKEAQAAXI4wAACAyxEGAABwOcIAAAAuRxgAAMDlCAMAALgcYQAAAJcjDAAA4HKEAQAAXI4wAACAyxEGAABwOcIAAAAuRxgAAMDlCAMAALgcYQAAAJcjDAAA4HKEAQAAXI4wAACAyxEGAABwOcIAAAAuRxgAAMDlCAMAALgcYQAAAJcjDAAA4HKEAQAAXI4wAACAuNv/A3oKbtDZeDGOAAAAAElFTkSuQmCC", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "I = [\n", " [],\n", " [0, 2],\n", " [0],\n", "]\n", "\n", "W = boolforge.WiringDiagram(I=I)\n", "\n", "print(\"W.N:\", W.N)\n", "print(\"W.variables:\", W.variables)\n", "print(\"W.indegrees:\", W.indegrees)\n", "print(\"W.outdegrees:\", W.outdegrees)\n", "print(\"W.N_constants:\", W.N_constants)\n", "print(\"W.N_variables:\", W.N_variables)\n", "\n", "DiGraph = W.to_DiGraph()\n", "plt.figure()\n", "nx.draw_networkx(DiGraph, with_labels=True, arrows=True)\n", "plt.axis(\"off\")\n", "plt.show()" ] }, { "cell_type": "markdown", "id": "d0a9632c", "metadata": {}, "source": [ "This wiring diagram encodes a **feed-forward loop**, one of the most common *network motifs* in \n", "transcriptional networks. It can:\n", "- filter transient signals (coherent FFL with AND gate),\n", "- accelerate response (incoherent FFL),\n", "\n", "See Mangan & Alon, PNAS, 2003 for a detailed analysis.\n", "`BoolForge` enables the identification of all feed-forward loops:" ] }, { "cell_type": "code", "execution_count": 4, "id": "1b0a98aa", "metadata": { "execution": { "iopub.execute_input": "2026-01-15T17:23:57.115390Z", "iopub.status.busy": "2026-01-15T17:23:57.115326Z", "iopub.status.idle": "2026-01-15T17:23:57.116832Z", "shell.execute_reply": "2026-01-15T17:23:57.116630Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "W.get_ffls() [[0, 2, 1]]\n" ] } ], "source": [ "print(\"W.get_ffls()\", W.get_ffls())" ] }, { "cell_type": "markdown", "id": "f65a0795", "metadata": {}, "source": [ "This tells us that `W` contains one FFL, in which $x_0$ regulates both $x_1$ and $x_2$, \n", "while $x_1$ is also regulated by $x_2$.\n", "\n", "`BoolForge` can also identify all feedback loops. For this, we consider another wiring diagram:" ] }, { "cell_type": "code", "execution_count": 5, "id": "1c965bbc", "metadata": { "execution": { "iopub.execute_input": "2026-01-15T17:23:57.117781Z", "iopub.status.busy": "2026-01-15T17:23:57.117721Z", "iopub.status.idle": "2026-01-15T17:23:57.134227Z", "shell.execute_reply": "2026-01-15T17:23:57.134028Z" } }, "outputs": [ { "data": { "image/png": "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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "W2.get_fbls() [[0, 1, 2], [0, 1]]\n" ] } ], "source": [ "I2 = [\n", " [2,1],\n", " [0],\n", " [1],\n", "]\n", "\n", "W2 = boolforge.WiringDiagram(I=I2)\n", "plt.figure()\n", "nx.draw_networkx(DiGraph, with_labels=True, arrows=True)\n", "plt.axis(\"off\")\n", "plt.show()\n", "\n", "print(\"W2.get_fbls()\", W2.get_fbls())" ] }, { "cell_type": "markdown", "id": "04f21649", "metadata": {}, "source": [ "The function `.get_fbls()` identifies all simple cycles in the wiring diagram. \n", "In this case, there exists a 2-cycle $x_0 \\leftrightarrow x_1$ and a 3-cycle $x_0 \\to x_1 \\to x_2 \\to x_0$." ] }, { "cell_type": "markdown", "id": "52703802", "metadata": {}, "source": [ "---\n", "## 3. Creating Boolean networks\n", "\n", "To create a Boolean network, we must specify:\n", "\n", "1. A wiring diagram `I`, describing who regulates whom.\n", "2. A list `F` of Boolean update functions (or truth tables), one per node." ] }, { "cell_type": "code", "execution_count": 6, "id": "8f567ec6", "metadata": { "execution": { "iopub.execute_input": "2026-01-15T17:23:57.135322Z", "iopub.status.busy": "2026-01-15T17:23:57.135258Z", "iopub.status.idle": "2026-01-15T17:23:57.865429Z", "shell.execute_reply": "2026-01-15T17:23:57.865198Z" }, "lines_to_next_cell": 2 }, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
x0(t)x1(t)x2(t)x0(t+1)x1(t+1)x2(t+1)
0000000
1001010
2010101
3011111
4100010
5101010
6110111
7111111
\n", "
" ], "text/plain": [ " x0(t) x1(t) x2(t) x0(t+1) x1(t+1) x2(t+1)\n", "0 0 0 0 0 0 0\n", "1 0 0 1 0 1 0\n", "2 0 1 0 1 0 1\n", "3 0 1 1 1 1 1\n", "4 1 0 0 0 1 0\n", "5 1 0 1 0 1 0\n", "6 1 1 0 1 1 1\n", "7 1 1 1 1 1 1" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "I = [\n", " [1],\n", " [0, 2],\n", " [1],\n", "]\n", "\n", "F = [\n", " [0, 1],\n", " [0, 1, 1, 1],\n", " [0, 1],\n", "]\n", "\n", "bn = boolforge.BooleanNetwork(F=F, I=I)\n", "\n", "bn.to_truth_table()" ] }, { "cell_type": "markdown", "id": "39949b1c", "metadata": {}, "source": [ "The full truth table of a Boolean network has size $N \\times 2^N$ and therefore grows exponentially with the number of nodes. \n", "In practice, however, `BoolForge` never stores this object explicitly. \n", "Instead, a Boolean network is represented internally by its wiring diagram `I` and the list of update functions `F`, \n", "which is far more memory-efficient – especially for sparse networks with few regulators per node.\n", "\n", "When a Boolean network is constructed from `F` and `I`, \n", "`BoolForge` automatically performs a series of consistency checks to guard against common modeling errors. \n", "For example, it verifies that each update function has the correct length, \n", "namely $2^n$, where $n$ is the number of regulators of the corresponding node as specified in `I`. \n", "If any of these checks fail, an informative error is raised immediately, \n", "helping ensure that the resulting network is well-defined.\n", "\n" ] }, { "cell_type": "markdown", "id": "de6b2544", "metadata": {}, "source": [ "### Creating networks from strings\n", "\n", "Alternatively, " ] }, { "cell_type": "code", "execution_count": 7, "id": "59683aeb", "metadata": { "execution": { "iopub.execute_input": "2026-01-15T17:23:57.866751Z", "iopub.status.busy": "2026-01-15T17:23:57.866610Z", "iopub.status.idle": "2026-01-15T17:23:57.869939Z", "shell.execute_reply": "2026-01-15T17:23:57.869724Z" }, "lines_to_next_cell": 2 }, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
x(t)y(t)z(t)x(t+1)y(t+1)z(t+1)
0000000
1001010
2010101
3011111
4100010
5101010
6110111
7111111
\n", "
" ], "text/plain": [ " x(t) y(t) z(t) x(t+1) y(t+1) z(t+1)\n", "0 0 0 0 0 0 0\n", "1 0 0 1 0 1 0\n", "2 0 1 0 1 0 1\n", "3 0 1 1 1 1 1\n", "4 1 0 0 0 1 0\n", "5 1 0 1 0 1 0\n", "6 1 1 0 1 1 1\n", "7 1 1 1 1 1 1" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "string = \"\"\"\n", "x = y\n", "y = x OR z\n", "z = y\n", "\"\"\"\n", "\n", "bn_str = boolforge.BooleanNetwork.from_string(string, separator=\"=\")\n", "bn_str.to_truth_table()" ] }, { "cell_type": "markdown", "id": "08859f49", "metadata": { "lines_to_next_cell": 2 }, "source": [ "This flexible interface enables loading Boolean networks from standard formats\n", "such as `.bnet` files, by first loading the string stored in the file and then loading the Boolean network." ] }, { "cell_type": "markdown", "id": "e408c587", "metadata": {}, "source": [ "### Interoperability with CANA" ] }, { "cell_type": "code", "execution_count": 8, "id": "cf3a7fef", "metadata": { "execution": { "iopub.execute_input": "2026-01-15T17:23:57.871032Z", "iopub.status.busy": "2026-01-15T17:23:57.870970Z", "iopub.status.idle": "2026-01-15T17:23:57.872895Z", "shell.execute_reply": "2026-01-15T17:23:57.872694Z" }, "lines_to_next_cell": 2 }, "outputs": [], "source": [ "cana_bn = bn.to_cana()\n", "bn_from_cana = boolforge.BooleanNetwork.from_cana(cana_bn)\n", "\n", "assert (\n", " np.all([np.all(bn.F[i].f == bn_from_cana.F[i].f) for i in range(bn.N)])\n", " and np.all([np.all(bn.I[i] == bn_from_cana.I[i]) for i in range(bn.N)])\n", " and np.all(bn.variables == bn_from_cana.variables)\n", "), \"BooleanNetwork CANA conversion failed\"" ] }, { "cell_type": "markdown", "id": "9ce55e5c", "metadata": {}, "source": [ "---\n", "## 4. Boolean networks with constants\n", "\n", "Nodes may be:\n", "\n", "- **source nodes** (no regulators),\n", "- **constant nodes** (fixed to 0 or 1),\n", "- **regular nodes** (regulated by others).\n", "\n", "Constant nodes are removed internally, and their values are propagated." ] }, { "cell_type": "code", "execution_count": 9, "id": "b730b0ae", "metadata": { "execution": { "iopub.execute_input": "2026-01-15T17:23:57.873879Z", "iopub.status.busy": "2026-01-15T17:23:57.873819Z", "iopub.status.idle": "2026-01-15T17:23:57.875835Z", "shell.execute_reply": "2026-01-15T17:23:57.875633Z" }, "lines_to_next_cell": 2 }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "bn.variables: [np.str_('x0'), np.str_('x1'), np.str_('x2')]\n", "bn.constants: {np.str_('x3'): {'value': 0, 'regulatedNodes': [np.str_('x1')]}}\n", "bn.F: [BooleanFunction(f=[0, 0, 0, 1]), BooleanFunction(f=[0, 1]), BooleanFunction(f=[0, 1])]\n", "bn.I: [array([1, 2]), array([0]), array([2])]\n" ] } ], "source": [ "F = [\n", " [0, 0, 0, 1], # regular\n", " [0, 1, 1, 1], # regular\n", " [0, 1], # source\n", " [0], # constant\n", "]\n", "\n", "I = [\n", " [1, 2],\n", " [0, 3],\n", " [2],\n", " [],\n", "]\n", "\n", "bn = boolforge.BooleanNetwork(F, I)\n", "\n", "print(\"bn.variables:\", bn.variables)\n", "print(\"bn.constants:\", bn.constants)\n", "print(\"bn.F:\", bn.F)\n", "print(\"bn.I:\", bn.I)" ] }, { "cell_type": "markdown", "id": "2781a525", "metadata": { "lines_to_next_cell": 2 }, "source": [ "The constant node is removed, and its value is propagated into downstream\n", "update functions." ] }, { "cell_type": "markdown", "id": "37ce9e4a", "metadata": {}, "source": [ "### Effect of constant value = 1" ] }, { "cell_type": "code", "execution_count": 10, "id": "d1db8a14", "metadata": { "execution": { "iopub.execute_input": "2026-01-15T17:23:57.876915Z", "iopub.status.busy": "2026-01-15T17:23:57.876855Z", "iopub.status.idle": "2026-01-15T17:23:57.878766Z", "shell.execute_reply": "2026-01-15T17:23:57.878559Z" }, "lines_to_next_cell": 2 }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "bn.F: [BooleanFunction(f=[0, 0, 0, 1]), BooleanFunction(f=[1, 1]), BooleanFunction(f=[0, 1])]\n", "bn.I: [array([1, 2]), array([0]), array([2])]\n", "bn.variables: [np.str_('x0'), np.str_('x1'), np.str_('x2')]\n" ] } ], "source": [ "F = [\n", " [0, 0, 0, 1],\n", " [0, 1, 1, 1],\n", " [0, 1],\n", " [1],\n", "]\n", "\n", "I = [\n", " [1, 2],\n", " [0, 3],\n", " [2],\n", " [],\n", "]\n", "\n", "bn = boolforge.BooleanNetwork(F, I)\n", "\n", "print(\"bn.F:\", bn.F)\n", "print(\"bn.I:\", bn.I)\n", "print(\"bn.variables:\", bn.variables)" ] }, { "cell_type": "markdown", "id": "a866a0c8", "metadata": { "lines_to_next_cell": 2 }, "source": [ "Although $x_1$ becomes fixed at 1 after one update, it is not treated as a\n", "constant node because $x_1 = 0$ remains a valid initial condition." ] }, { "cell_type": "markdown", "id": "16cdac0c", "metadata": {}, "source": [ "---\n", "## 5. Boolean network properties\n", "\n", "The class `BooleanNetwork` inherits all properties of `WiringDiagram`." ] }, { "cell_type": "code", "execution_count": 11, "id": "aad3d059", "metadata": { "execution": { "iopub.execute_input": "2026-01-15T17:23:57.879828Z", "iopub.status.busy": "2026-01-15T17:23:57.879760Z", "iopub.status.idle": "2026-01-15T17:23:57.881350Z", "shell.execute_reply": "2026-01-15T17:23:57.881123Z" }, "lines_to_next_cell": 2 }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "bn.N: 3\n", "bn.indegrees: [np.int64(2), np.int64(1), np.int64(1)]\n", "bn.outdegrees: [np.int64(1), np.int64(1), np.int64(2)]\n", "bn.N_constants: 1\n", "bn.N_variables: 3\n" ] } ], "source": [ "print(\"bn.N:\", bn.N)\n", "print(\"bn.indegrees:\", bn.indegrees)\n", "print(\"bn.outdegrees:\", bn.outdegrees)\n", "print(\"bn.N_constants:\", bn.N_constants)\n", "print(\"bn.N_variables:\", bn.N_variables)" ] }, { "cell_type": "markdown", "id": "7d188e1f", "metadata": {}, "source": [ "---\n", "## Outlook\n", "\n", "In the remaining tutorials, we build on this foundation to study the dynamical\n", "behavior of Boolean networks, including attractors, basins of attraction,\n", "and stability under perturbations." ] } ], "metadata": { "jupytext": { "cell_metadata_filter": "-all", "main_language": "python", "notebook_metadata_filter": "-all" }, "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.12.3" } }, "nbformat": 4, "nbformat_minor": 5 }