{ "cells": [ { "cell_type": "markdown", "id": "f3962d29", "metadata": {}, "source": [ "# BoolForge Tutorial 3: Canalization\n", "\n", "Canalization is a key property of biological Boolean functions that confers\n", "robustness: when a canalizing variable takes its canalizing value, the output\n", "is determined regardless of other inputs. This \"buffering\" mechanism is thought\n", "to protect organisms from genetic and environmental perturbations.\n", "\n", "Discovered by C.H. Waddington in 1942 in developmental biology, canalization\n", "has since been formalized in Boolean network theory and found to be prevalent\n", "in empirically-derived gene regulatory networks.\n", "\n", "## What you will learn\n", "In this tutorial you will:\n", "\n", "- determine if a Boolean function is canalizing, $k$-canalizing, and nested canalizing,\n", "- compute the canalizing layer structure of any Boolean function,\n", "- compute properties related to collective canalization, such as canalizing strength, \n", "effective degree and input redundancy.\n", "\n", "---\n", "## 0. Setup" ] }, { "cell_type": "code", "execution_count": 1, "id": "d29e3d00", "metadata": { "execution": { "iopub.execute_input": "2026-01-15T17:23:53.250507Z", "iopub.status.busy": "2026-01-15T17:23:53.250200Z", "iopub.status.idle": "2026-01-15T17:23:53.988602Z", "shell.execute_reply": "2026-01-15T17:23:53.988283Z" }, "lines_to_next_cell": 2 }, "outputs": [], "source": [ "import boolforge\n", "import numpy as np\n", "import matplotlib.pyplot as plt" ] }, { "cell_type": "markdown", "id": "56eefe5b", "metadata": {}, "source": [ "---\n", "## 1. Canalizing variables and layers\n", "\n", "A Boolean function $f(x_1, \\ldots, x_n)$ is *canalizing* if there exists at least one\n", "*canalizing variable* $x_i$ and a *canalizing input value* $a \\in \\{0,1\\}$ such that\n", "\n", "$$\n", "f(x_1,\\ldots,x_i=a,\\ldots,x_n)=b,\n", "$$\n", "\n", "where $b \\in \\{0,1\\}$ is a constant, the *canalized output*.\n", "\n", "A Boolean function is *k-canalizing* if it has at least k conditionally canalizing variables. \n", "This is checked recursively: after fixing a canalizing variable $x_i$ to its non-canalizing input value $\\bar a$, \n", "the subfunction $f(x_1,\\ldots,x_{i-1},x_{i+1},\\ldots,x_n)$ must itself contain another canalizing variable, and so on. \n", "For a given function, the maximal possible value of k is defined as its *canalizing depth*. \n", "If all variables are conditionally canalizing (i.e., if the canalizing depth is $n$), \n", "the function is called a *nested canalizing* function (*NCF*). \n", "Biological networks are heavily enriched for NCFs as we explore in a later tutorial.\n", "\n", "Per (He and Macauley, Physica D, 2016), any Boolean function can be decomposed \n", "into a unique standard monomial form by recursively identifying and removing all \n", "conditionally canalizing variables (this set of variables is called a *canalizing layer*). \n", "Each variable of a Boolean function appears in exactly one layer, \n", "or (if it is not conditionally canalizing) it is part of the non-canalizing core function \n", "that has to be evaluated only if all conditionally canalizing variables receive their non-canalizing input value. \n", "The *canalizing layer structure* $[k_1,\\ldots,k_r]$ describes the number of variables in each canalizing layer. \n", "We thus have $r\\geq 0$, $k_i\\geq 1$ and $k_1+\\cdots+k_r$.\n", "\n", "In the following code, we define four 3-input functions with different canalizing properties.\n", "\n", "### 1.1 Examples" ] }, { "cell_type": "code", "execution_count": 2, "id": "afc5dc4f", "metadata": { "execution": { "iopub.execute_input": "2026-01-15T17:23:53.990841Z", "iopub.status.busy": "2026-01-15T17:23:53.990595Z", "iopub.status.idle": "2026-01-15T17:23:53.993168Z", "shell.execute_reply": "2026-01-15T17:23:53.992951Z" }, "lines_to_next_cell": 2 }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "x0\tx1\tx2\t|\tf\tg\th\tk\n", "---------------------------------------------------------\n", "0\t0\t0\t|\t0\t1\t0\t0\n", "0\t0\t1\t|\t1\t0\t0\t0\n", "0\t1\t0\t|\t1\t0\t0\t0\n", "0\t1\t1\t|\t0\t1\t1\t1\n", "1\t0\t0\t|\t1\t1\t0\t1\n", "1\t0\t1\t|\t0\t1\t0\t1\n", "1\t1\t0\t|\t0\t1\t0\t1\n", "1\t1\t1\t|\t1\t1\t0\t1\n" ] } ], "source": [ "# Non-canalizing XOR function\n", "f = boolforge.BooleanFunction(\"(x0 + x1 + x2) % 2\")\n", "\n", "# 1-canalizing function\n", "g = boolforge.BooleanFunction(\"(x0 | (x1 & x2 | ~x1 & ~x2)) % 2\")\n", "\n", "# Nested canalizing function with all variables in one layer\n", "h = boolforge.BooleanFunction(\"~x0 & x1 & x2\")\n", "\n", "# Nested canalizing function with two canalizing layers\n", "k = boolforge.BooleanFunction(\"x0 | (x1 & x2)\")\n", "\n", "labels = [\"f\", \"g\", \"h\", \"k\"]\n", "boolforge.display_truth_table(f, g, h, k, labels=labels)" ] }, { "cell_type": "markdown", "id": "0961b763", "metadata": {}, "source": [ "### 1.2 Canalizing depth and nested canalization\n", "\n", "For each function, we can determine whether it is canalizing and/or nested canalizing. \n", "This is determined by the canalizing depth (the number of conditionally canalizing variables), \n", "which we can also directly compute. \n", "As a reminder, an $n$-input function is canalizing if \n", "its canalizing depth is non-zero and nested canalizing if its canalizing depth equals $n$." ] }, { "cell_type": "code", "execution_count": 3, "id": "e3b145a1", "metadata": { "execution": { "iopub.execute_input": "2026-01-15T17:23:53.994251Z", "iopub.status.busy": "2026-01-15T17:23:53.994157Z", "iopub.status.idle": "2026-01-15T17:23:53.996847Z", "shell.execute_reply": "2026-01-15T17:23:53.996522Z" }, "lines_to_next_cell": 2 }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Canalizing depth of f: 0\n", "f is canalizing: False\n", "f is nested canalizing: False\n", "\n", "Canalizing depth of g: 1\n", "g is canalizing: True\n", "g is nested canalizing: False\n", "\n", "Canalizing depth of h: 3\n", "h is canalizing: True\n", "h is nested canalizing: True\n", "\n", "Canalizing depth of k: 3\n", "k is canalizing: True\n", "k is nested canalizing: True\n", "\n" ] } ], "source": [ "for func, label in zip([f, g, h, k], labels):\n", " depth = func.get_canalizing_depth()\n", " print(f\"Canalizing depth of {label}: {depth}\")\n", "\n", " print(f\"{label} is canalizing:\", func.is_canalizing())\n", " print(f\"{label} is nested canalizing:\", func.is_k_canalizing(k=func.n))\n", " print()" ] }, { "cell_type": "markdown", "id": "bfba54c8", "metadata": {}, "source": [ "### 1.3 Canalizing layer structure\n", "\n", "The full canalizing layer structure includes canalizing input values, canalized\n", "output values, the order of canalizing variables, the layer structure, and the\n", "remaining non-canalizing core function." ] }, { "cell_type": "code", "execution_count": 4, "id": "116d266c", "metadata": { "execution": { "iopub.execute_input": "2026-01-15T17:23:53.998275Z", "iopub.status.busy": "2026-01-15T17:23:53.998187Z", "iopub.status.idle": "2026-01-15T17:23:54.000608Z", "shell.execute_reply": "2026-01-15T17:23:54.000387Z" }, "lines_to_next_cell": 2 }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Canalizing input values of f: []\n", "Canalized output values of f: []\n", "Order of canalizing variables of f: []\n", "Layer structure of f: []\n", "Number of layers of f: 0\n", "Core function of f: [0 1 1 0 1 0 0 1]\n", "\n", "Canalizing input values of g: [1]\n", "Canalized output values of g: [1]\n", "Order of canalizing variables of g: [0]\n", "Layer structure of g: [1]\n", "Number of layers of g: 1\n", "Core function of g: [1 0 0 1]\n", "\n", "Canalizing input values of h: [1 0 0]\n", "Canalized output values of h: [0 0 0]\n", "Order of canalizing variables of h: [0 1 2]\n", "Layer structure of h: [3]\n", "Number of layers of h: 1\n", "Core function of h: [1]\n", "\n", "Canalizing input values of k: [1 0 0]\n", "Canalized output values of k: [1 0 0]\n", "Order of canalizing variables of k: [0 1 2]\n", "Layer structure of k: [1, 2]\n", "Number of layers of k: 2\n", "Core function of k: [1]\n", "\n" ] } ], "source": [ "for func, label in zip([f, g, h, k], labels):\n", " info = func.get_layer_structure()\n", " print(f\"Canalizing input values of {label}: {info['CanalizingInputs']}\")\n", " print(f\"Canalized output values of {label}: {info['CanalizedOutputs']}\")\n", " print(f\"Order of canalizing variables of {label}: {info['OrderOfCanalizingVariables']}\")\n", " print(f\"Layer structure of {label}: {info['LayerStructure']}\")\n", " print(f\"Number of layers of {label}: {info['NumberOfLayers']}\")\n", " print(f\"Core function of {label}: {info['CoreFunction']}\")\n", " print()" ] }, { "cell_type": "markdown", "id": "37bb3697", "metadata": {}, "source": [ "Consider, for example, the output for `k`. The canalizing input values corresponding to\n", "$x_0, x_1, x_2$ are $1,0,0$, respectively, with the same canalized outputs. That is,\n", "\n", "- Layer 1: $x_0$ (if $x_0=1$, then $k=1$, regardless of $x_1$ and $x_2$)\n", "- Layer 2: $x_1, x_2$ (if $x_0=0$ and $x_1=0$ or $x_2=0$, then $k=0$)" ] }, { "cell_type": "markdown", "id": "4bced039", "metadata": {}, "source": [ "---\n", "## 2. Collective canalization\n", "\n", "Collective canalization treats canalization as a property of the function rather\n", "than individual variables (Reichhardt & Bassler, J. Phys. A, 2007).\n", "\n", "A Boolean function is *$k$-set canalizing* if there exists a set of $k$ variables\n", "whose fixed values determine the output irrespective of the remaining inputs.\n", "\n", "Individual canalization asks: \"Which single variables can determine output?\"\n", "Collective canalization asks: \"Which SETS of variables can determine output?\"\n", "\n", "A 2-set canalizing example: If $k(x_0,x_1,x_2) = x_0 \\| (x_1 \\& x_2)$,\n", "- $\\{x_0,x_1\\}$ can determine the output: if $(x_0,x_1)=(1,0)$, $k=1$ ($x_2$ irrelevant),\n", "- $\\{x_1,x_2\\}$ can determine the output: if $(x_1,x_2)=(1,1)$, $k=1$ ($x_0$ irrelevant)\n", "\n", "The proportion of such $k$-sets, the $k$-set canalizing proportion denoted $P_k(f)$, is used to define the canalizing strength.\n", "It is fairly obvious that\n", "\n", "- nested canalizing functions of a single layer such as `h` are the non-degenerate functions with highest k-set canalizing proportion $P_k(f) = 1-1/2^k$, and\n", "- $P_{k-1}(f) \\leq P_k(f)$, i.e., more knowledge about a function's inputs cannot result in less knowledge about its output,\n", "- the $n-1$-set canalizing proportion $P_{n-1}(f)$ is 1 minus the function's normalized average sensitivity.\n", "\n", "We can compute the $k$-set canalizing proportions for the four 3-input functions:" ] }, { "cell_type": "code", "execution_count": 5, "id": "18549eae", "metadata": { "execution": { "iopub.execute_input": "2026-01-15T17:23:54.001788Z", "iopub.status.busy": "2026-01-15T17:23:54.001705Z", "iopub.status.idle": "2026-01-15T17:23:54.004618Z", "shell.execute_reply": "2026-01-15T17:23:54.004423Z" }, "lines_to_next_cell": 2 }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1-set canalizing proportion of f: 0.0\n", "2-set canalizing proportion of f: 0.0\n", "Normalized average sensitivity of f: 1.0\n", "3-set canalizing proportion of f: 1.0\n", "\n", "1-set canalizing proportion of g: 0.16666666666666666\n", "2-set canalizing proportion of g: 0.5\n", "Normalized average sensitivity of g: 0.5\n", "3-set canalizing proportion of g: 1.0\n", "\n", "1-set canalizing proportion of h: 0.5\n", "2-set canalizing proportion of h: 0.75\n", "Normalized average sensitivity of h: 0.25\n", "3-set canalizing proportion of h: 1.0\n", "\n", "1-set canalizing proportion of k: 0.16666666666666666\n", "2-set canalizing proportion of k: 0.5833333333333334\n", "Normalized average sensitivity of k: 0.4166666666666667\n", "3-set canalizing proportion of k: 1.0\n", "\n" ] } ], "source": [ "for func, label in zip([f, g, h, k], labels):\n", " print(f\"1-set canalizing proportion of {label}: {func.get_kset_canalizing_proportion(k=1)}\")\n", " print(f\"2-set canalizing proportion of {label}: {func.get_kset_canalizing_proportion(k=2)}\")\n", " print(f\"Normalized average sensitivity of {label}: {func.get_average_sensitivity(EXACT=True)}\")\n", " print(f\"3-set canalizing proportion of {label}: {func.get_kset_canalizing_proportion(k=3)}\")\n", " print()" ] }, { "cell_type": "markdown", "id": "c9aa878a", "metadata": {}, "source": [ "### 2.1 Canalizing strength\n", "\n", "The *canalizing strength* summarizes collective canalization as a weighted average of\n", "the $k$-set canalizing proportions (Kadelka et al., Adv Appl Math, 2023). It ranges from:\n", "\n", "- 1 for maximally canalizing non-degenerate functions (namely, nested canalizing functions of a single canalizing layer such as `h`),\n", "- 0 for linear functions such as `f`,\n", "\n", "For all other non-degenerate Boolean functions it is within $(0,1)$.\n", "\n", "It helps to consider the canalizing strength as a probability: \n", "Given that I know a random number of function inputs (drawn uniformly at random from $1,\\ldots,n-1$), \n", "how likely am I to already know the function output?" ] }, { "cell_type": "code", "execution_count": 6, "id": "0e1d645c", "metadata": { "execution": { "iopub.execute_input": "2026-01-15T17:23:54.005673Z", "iopub.status.busy": "2026-01-15T17:23:54.005603Z", "iopub.status.idle": "2026-01-15T17:23:54.007723Z", "shell.execute_reply": "2026-01-15T17:23:54.007530Z" }, "lines_to_next_cell": 2 }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Canalizing strength of f: 0.0\n", "\n", "Canalizing strength of g: 0.5\n", "\n", "Canalizing strength of h: 1.0\n", "\n", "Canalizing strength of k: 0.5555555555555556\n", "\n" ] } ], "source": [ "for func, label in zip([f, g, h, k], labels):\n", " strength = func.get_canalizing_strength()\n", " print(f\"Canalizing strength of {label}: {strength}\")\n", " print()" ] }, { "cell_type": "markdown", "id": "eb1cd212", "metadata": {}, "source": [ "---\n", "### 2.2. Distribution of canalizing strength\n", "\n", "An enumeration of all non-degenerate 3-input Boolean functions reveals the distribution of the canalizing strength. \n", "Note that this brute-force code can also run (in less than a minute) for all $2^{2^4}=2^{16}=65,536$ \n", "4-input functions but will take days for all $2^{2^5}=2^{32}=4,294,967,296$ 5-input functions." ] }, { "cell_type": "code", "execution_count": 7, "id": "a32dea2d", "metadata": { "execution": { "iopub.execute_input": "2026-01-15T17:23:54.008761Z", "iopub.status.busy": "2026-01-15T17:23:54.008688Z", "iopub.status.idle": "2026-01-15T17:23:54.308371Z", "shell.execute_reply": "2026-01-15T17:23:54.308169Z" }, "lines_to_next_cell": 2 }, "outputs": [ { "data": { "image/png": "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9sWpeuPGjUtTyC6++OI0tW/x4sVp+/nnn1+45pprKky/rl+/fuH2229PU4JvuOEG06+rqa6HDh2apl3++te/Lrz33nvly8qVK4v4XdS+et6UWUvVU88LFixIs+4uv/zywrx58wq/+93vCnvttVfhlltuKeJ3UfvqOf5NjvX8P//zP2mK8B/+8IfCgQcemGacsnnx72q81UVcYmQYNmxYev3OO++k7bGOY11vOv36Bz/4QboWxltlmH5dRHH+e/v27dNFM071mz17dvm2L33pS+kP+8YeeOCBQseOHdP+cfrZ5MmTi1Dq2l/X++23X/oHtekS/1BRtb/TGxNkqq+eZ86cmW7XEC/McSr2rbfemqa+U3X1vG7dusKNN96Ywkvjxo0L7dq1K1x22WWFjz76qEilz8Mf//jHSv/eltVt/BrretNjjjjiiPRzib/PY8eOrdYy1on/qb72HgCA6mOMDACQLUEGAMiWIAMAZEuQAQCyJcgAANkSZACAbAkyAEC2BBngc6tTp0548MEH0+v4jKb4flufmt2hQ4dw5513VnMJa8+DD2PdLlu2rNhFgZIhyECm4kMFBwwYEA444IDQqFGj9Pyj+JDHTZ9zUtNiOd57771w2GGHbdP+zz77bHpwXzGVYpg68cQTw5VXXlnsYkDJ8/RryFBs9YgPG2zRokX4yU9+kp7ku27duvDoo4+G/v37h9dee61oZYsP4tuep77vueeeIQfr169PrSGekgylxb9IyNBll12WLqrPPPNMOPvss0PHjh3DoYceGgYNGhRmz55dvt+wYcNSyNl1111TS0k8btWqVeXbx40bl8JQDEBdunQJu+22W/jKV76SWlQ2bjE55ZRTwh577BGaN28evvSlL4Xnn39+s2XbtGvpggsuSO83XWI3SWWtIXHbL3/5y/BP//RPYZdddgkHH3xweOihhyp8Rnwf1zdu3Dg9kftXv/rVFrtc4pNYbrzxxtC+ffvUerX33nuH7373u+UtH++8804YOHBgedk2rpv4WYccckg6bsGCBWHNmjXh+9//fthnn31SvXbv3r38e9nWOv3000/T58f9WrVqFa6++urQt2/f0KdPn/I6e/zxx8NPf/rT8jLFei0zZ86c9NT3WD/HHntsmDdv3hZ/X6A2E2QgMx9++GGYMmVKanmJF9JNxYtjmdh6cNddd4VXXnklXeynT58errrqqgr7//3vfw+33357+K//+q/wxBNPpIt1vFCXWblyZbrIPvnkkykkxQBx2mmnpfXbIl6M40W8bLniiivCXnvtFTp37rzZY2666abw9a9/Pbz44ovps84777z0fUfz588P//zP/5wu+n/605/CJZdcEq699totluE3v/lNGD58ePj5z38eXn/99TSeJwa8aOLEiWHfffcNN998c3kZN66b//iP/0jBKtZhLPfll18eZs2aFcaPH5/K9y//8i8pqMTzbmudxnPed999YezYseGpp54KK1asKB9jVFZnPXr0CBdddFF5mWIQLRO/3zvuuCM899xzoX79+uHf/u3ftulnAbVStT6SEqhyTz/9dHr67MSJE7f72AkTJhRatWpV/j4+lTae64033ihfN2LEiELr1q03e47169cXmjZtWnj44YfL18VzTJo0Kb2eP39+ev/CCy985tjf/OY36cnDTz75ZIWnmQ8fPrzCua677rry96tWrUrrfv/736f3V199deGwww6rcN5rr7027bO5Jxnfcccd6cn0a9eurXT7pmXYuG7mzp1bvu6dd94p1KtXr/Duu+9W2Pfkk08uDB48eJvrNL7+yU9+Uv4+Puk6PsV546eLxycKX3HFFZU+iXjq1Knl6yZPnpzWffLJJ5V+b1DbaZGBzGzPA+unTp0aTj755NQN0rRp03D++eeHDz74ILUYlIndEwceeGD5+7Zt24alS5eWv1+yZElqGYgtMbFrqVmzZql7KrYybI8XXnghff7PfvazNL5nS7p27Vr+OrY6xc8sK1PsRvniF79YYf+jjz56i+eLrSaffPJJGhgdv5dJkyal7p2tadiwYYWyvPTSS2msTOzKi11GZUvsBnrzzTe3qU6XL1+e6nTjMsdxRd26dQvbauMyxXNHG//MYGdisC9kJgaKOGZiawN645iKr371q+HSSy8Nt956a2jZsmXqHurXr19Yu3ZtuthGDRo0qHBcPPfGYSl2K8XwE7s79ttvvzRWJHZ7xHNszwyrr33ta+HCCy9Mn781lZVpw4YNYUfFbpkYgGKwe+yxx9JYoThIOgaQTT9rY02aNCkfMxPFABdDRxyjEr9uLAaaLZV/ewLo1mx8/rLyfZ76gZxpkYHMxEDSu3fvMGLEiPDxxx9/ZnvZgNd4sY0XtziW4phjjkmtCIsWLdruz4tjOOLA1DhWJQ4ojkHmb3/72zYfv3r16nDmmWemMTFx8PHn1alTpzQ2ZGNxQPLWxFASp6fHMUNxcG4c5xJbWMpaXmJLy9Z84QtfSPvF1o+DDjqowrKtM7Viq1br1q0rlDmec9MB1NtaJtjZCTKQoRhi4kUudk/EgaxxoOmrr76aLtKxtSSKF9c4Jfvuu+8Ob731Vhp4Onr06B1qAYrHxvM//fTTaeBtDAXbKg7GXbhwYSrb+++/n1pn4rI9LTqbni+2RsWZPn/5y1/CAw88kGYKRRu3nmwsbh8zZkx4+eWXU13893//d/oeYgtT2cypOCj33Xff3WJIi2Ewfv/f+ta30iDhOPA4zhwbMmRImDx58jZ/D/H+P/GY3/72t6mlKA6A/uijjyqUP5Yp1ndsWYtl0uIClRNkIENxrEf8P/g49fh73/teuvlcnCIdb4Y3atSotM/hhx+eWkDiDJm4Pc6SiRfP7RUDQLzIHnnkkWmMS2ydibN3tlXsvomzbuIU5jieo2yZOXNm2BH7779/+PWvf52CRBwrEr/fsllLsbWoMnEm1z333JPG5sRjYhfTww8/nKY+R3HGUgwMcVzL1u5rE2caxSAT6z22DsXZU7F1JU7t3lYxhH3zm99M54nBM3ZLxVa2OJ28TJzlFLuvYr3FMm3vmCTYWdSJI36LXQiAzyOOAYqtTbHlJ0extSXecyZOOf/Rj35U7OJAVgz2BbIzcuTINHMptqjEMTxx4G68v0su4g34/vCHP6SbC8Yb7MWZXLGb6txzzy120SA7ggyQnTgm6JZbbkk3yYtdOrGbZ/DgwSEX8UaFcdxO7D6KjeKx6y92d8VWGWD76FoCALJlsC8AkC1BBgDIliADAGRLkAEAsiXIAADZEmQAgGwJMgBAtgQZACBbggwAEHL1/wBRcKhaagWw6QAAAABJRU5ErkJggg==", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "n = 3\n", "all_functions = boolforge.get_left_side_of_truth_table(2**n)\n", "\n", "canalizing_strengths = []\n", "for binary_vector in all_functions:\n", " func = boolforge.BooleanFunction(f=binary_vector)\n", " if not func.is_degenerate():\n", " canalizing_strengths.append(func.get_canalizing_strength())\n", "\n", "fig, ax = plt.subplots()\n", "ax.hist(canalizing_strengths, bins=50)\n", "ax.set_xlabel(\"Canalizing strength\")\n", "ax.set_ylabel(\"Count\")\n", "plt.show()" ] }, { "cell_type": "markdown", "id": "d35794f5", "metadata": {}, "source": [ "---\n", "## 3. Canalization as a measure of input redundancy\n", "\n", "Canalization, symmetry and redundancy are related concepts. \n", "A highly symmetry Boolean function with few (e.g., one) symmetry groups\n", "exhibits high input redundancy and is on average more canalizing, irrespective of the measure of canalization. \n", "Recently, it was shown that almost all Boolean functions (except the linear functions)\n", "exhibit some level of *input redundancy* (Gates et al., PNAS, 2021). \n", "The input redundancy of a variable is defined as 1 minus its *edge effectiveness*, \n", "which describes the proportion of times that this variable is needed to determine the output of the function. \n", "Edge effectiveness is very similar to the activity of a variable \n", "but is not the same (the difference is defined as *excess canalization*).\n", "The sum of all edge effectivenesses of the inputs of a function is known as its *effective degree*.\n", "The average input redundancy serves as a measure of the canalization in a function.\n", "\n", "In `BoolForge`, all these quantities can be computed, however not directly.\n", "Instead, they are computed using the `CANA` package, \n", "which must be installed (`pip install cana`) to enjoy this functionality. \n", "To exemplify this, we reconsider the four 3-input functions from above." ] }, { "cell_type": "code", "execution_count": 8, "id": "090e0384", "metadata": { "execution": { "iopub.execute_input": "2026-01-15T17:23:54.309538Z", "iopub.status.busy": "2026-01-15T17:23:54.309410Z", "iopub.status.idle": "2026-01-15T17:23:54.313051Z", "shell.execute_reply": "2026-01-15T17:23:54.312846Z" }, "lines_to_next_cell": 2 }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Edge effectiveness of f: [1.0, 1.0, 1.0]\n", "Activities of f: [1. 1. 1.]\n", "Excess canalization of f: [0. 0. 0.]\n", "Effective degree of f: 3.0\n", "Average edge effectiveness of f: 1.0\n", "Normalized input redundancy of f: 0.0\n", "\n", "Edge effectiveness of g: [0.625, 0.625, 0.625]\n", "Activities of g: [0.4949 0.499 0.499 ]\n", "Excess canalization of g: [0.1301 0.126 0.126 ]\n", "Effective degree of g: 1.875\n", "Average edge effectiveness of g: 0.625\n", "Normalized input redundancy of g: 0.375\n", "\n", "Edge effectiveness of h: [0.41666666666666663, 0.41666666666666663, 0.41666666666666663]\n", "Activities of h: [0.2501 0.2508 0.2408]\n", "Excess canalization of h: [0.16656667 0.16586667 0.17586667]\n", "Effective degree of h: 1.25\n", "Average edge effectiveness of h: 0.4166666666666667\n", "Normalized input redundancy of h: 0.5833333333333334\n", "\n", "Edge effectiveness of k: [0.8125, 0.375, 0.375]\n", "Activities of k: [0.7453 0.2533 0.2481]\n", "Excess canalization of k: [0.0672 0.1217 0.1269]\n", "Effective degree of k: 1.5625\n", "Average edge effectiveness of k: 0.5208333333333334\n", "Normalized input redundancy of k: 0.4791666666666667\n", "\n" ] } ], "source": [ "for func, label in zip([f, g, h, k], labels):\n", " edge_eff = func.get_edge_effectiveness()\n", " activities = func.get_activities()\n", " effective_degree = func.get_effective_degree()\n", " input_redundancy = func.get_input_redundancy()\n", "\n", " print(f\"Edge effectiveness of {label}: {edge_eff}\")\n", " print(f\"Activities of {label}: {activities}\")\n", " print(f\"Excess canalization of {label}: {edge_eff - activities}\")\n", " print(f\"Effective degree of {label}: {effective_degree}\")\n", " print(f\"Average edge effectiveness of {label}: {effective_degree / func.n}\")\n", " print(f\"Normalized input redundancy of {label}: {input_redundancy}\")\n", " print()" ] }, { "cell_type": "markdown", "id": "00d6ed82", "metadata": {}, "source": [ "---\n", "## 4. Summary and next steps\n", "\n", "In this tutorial you learned how to:\n", "\n", "- compute canalizing depth and identify nested canalizing functions,\n", "- compute the canalizing layer structure and interpret layers and core functions,\n", "- quantify collective canalization via $k$-set canalizing proportions,\n", "- summarize canalization via canalizing strength,\n", "- relate canalization to redundancy-based measures such as edge effectiveness.\n", "\n", "Canalization provides a structural explanation for why many biological Boolean\n", "rules are robust to perturbations.\n", "\n", "**Next steps:** Subsequent tutorials will explore random Boolean functions with\n", "prescribed canalization properties and the impact of canalization on Boolean\n", "network dynamics and robustness." ] } ], "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 }