{ "cells": [ { "cell_type": "markdown", "id": "Nti1VE8-Dl_5", "metadata": { "id": "Nti1VE8-Dl_5" }, "source": [ "# BoolForge Tutorial #4: Generating random Boolean functions\n", "\n", "In this tutorial, we will focus on the random generation of Boolean functions with defined properties, which enables a wealth of computational studies. \n", "You will learn how to generate random n-input Boolean functions under flexible constraints, including:\n", "- specified canalizing properties (canalizing depth, canalizing layer structure, etc),\n", "- bias, absolute bias or a specific Hamming weight,\n", "- linearity requirements,\n", "- degeneracy requirements.\n", "\n", "To ensure familiarity with these concepts, we highly recommended to first work the previous tutorials.\n", "\n", "The function `boolforge.random_function(n,*args)` is the only function needed to be called (correctly) to generate a random Boolean function under specific constraints. Apart from the degree `n`, it takes a number of optional arguments that specify these constraints. By default, this function creates an n-input non-degenerated Boolean function. In other words, the generated function actually depends on all its inputs, i.e., each input's activity and edge effectiveness is strictly positive." ] }, { "cell_type": "code", "execution_count": 167, "id": "a1a56603", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "x0\tx1\tx2\t|\tf_random_non_degenerated\n", "--------------------------------------------------------\n", "0\t0\t0\t|\t1\n", "0\t0\t1\t|\t0\n", "0\t1\t0\t|\t0\n", "0\t1\t1\t|\t1\n", "1\t0\t0\t|\t1\n", "1\t0\t1\t|\t1\n", "1\t1\t0\t|\t1\n", "1\t1\t1\t|\t0\n", "Is f degenerated? False\n", "Activities of the variables of f: [0.75 0.75 0.75]\n", "Edge effectiveness of the variables of f: [0.8333333333333334, 0.8333333333333334, 0.8333333333333334]\n" ] } ], "source": [ "import boolforge\n", "import numpy as np\n", "import matplotlib.pyplot as plt\n", "\n", "n = 3\n", "f = boolforge.random_function(n)\n", "\n", "boolforge.display_truth_table(f,labels='f_random_non_degenerated')\n", "\n", "print('Is f degenerated?',f.is_degenerated())\n", "print('Activities of the variables of f:',f.get_activities(EXACT=True))\n", "print(f'Edge effectiveness of the variables of f: {f.get_edge_effectiveness()}')" ] }, { "cell_type": "markdown", "id": "203b8d7d", "metadata": {}, "source": [ "The rest of this tutorial describes the various constraints. Each constraint defines a specific family of n-input Boolean functions, from which `boolforge.random_function(n,*args)` samples *uniformly at random*. In other words, `boolforge.random_function(n,*args)` selects each function satisfying a given set of constraints with equal probability." ] }, { "cell_type": "markdown", "id": "788b1861", "metadata": {}, "source": [ "## Linear functions\n", "\n", "If we set the optional argument `LINEAR=True`, the generated function is a linear function (also known as parity or XOR function), which we can verify by computing the activities or edge effectiveness of its inputs (all 1), or the normalized average sensitivity (1) or the canalizing strength (0)." ] }, { "cell_type": "code", "execution_count": 42, "id": "21a919ec", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "x0\tx1\tx2\t|\tf_linear\n", "----------------------------------------\n", "0\t0\t0\t|\t0\n", "0\t0\t1\t|\t1\n", "0\t1\t0\t|\t1\n", "0\t1\t1\t|\t0\n", "1\t0\t0\t|\t1\n", "1\t0\t1\t|\t0\n", "1\t1\t0\t|\t0\n", "1\t1\t1\t|\t1\n", "Activities of the variables of f: [1. 1. 1.]\n", "Edge effectiveness of the variables of f: [1.0, 1.0, 1.0]\n", "Normalized average sensitivity of f: 1.0\n", "Canalizing strength of f: 0.0\n" ] } ], "source": [ "f = boolforge.random_function(n,LINEAR=True)\n", "\n", "boolforge.display_truth_table(f,labels='f_linear')\n", "\n", "print('Activities of the variables of f:',f.get_activities(EXACT=True))\n", "print(f'Edge effectiveness of the variables of f: {f.get_edge_effectiveness()}')\n", "print('Normalized average sensitivity of f:',f.get_average_sensitivity(EXACT=True))\n", "print(f'Canalizing strength of f: {f.get_canalizing_strength()[0]}')" ] }, { "cell_type": "markdown", "id": "f1d8dda8", "metadata": {}, "source": [ "## Functions with defined canalizing properties\n", "\n", "If `LINEAR=False` (the default), we can specify the canalizing layer structure `layer_structure`. This specifies the number of conditionally canalizing variables in each layer of the randomly generated function. If the optional argument `EXACT_DEPTH=True` (default is False), then this describes the exact layer structure, i.e., the core function cannot be canalizing. If `EXACT_DEPTH=False` (the default), it is possible that the core function is canalizing, meaning that the last described layer in `layer_structure` may have more conditionally canalizing variables, or that there are additional canalizing layers. \n", "\n", "Before generating any random function, `random_function()` goes through a number of checks ensuring that the provided optional arguments make sense. For example, it checks that the provided layer structure $(k_1,\\ldots,k_r)$ satisfies\n", "- $k_i\\geq 1$, \n", "- $k_1 + \\cdots + k_r \\leq n$, and\n", "- if $k_1 + \\cdots + k_r = n$, then $k_r \\geq 2$ because the last layer of a nested canalizing function must always contain two or more variables." ] }, { "cell_type": "code", "execution_count": 74, "id": "970e5586", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "x0\tx1\tx2\t|\tf\tg\th\tk\n", "---------------------------------------------------------\n", "0\t0\t0\t|\t1\t0\t1\t0\n", "0\t0\t1\t|\t0\t0\t0\t0\n", "0\t1\t0\t|\t0\t1\t0\t1\n", "0\t1\t1\t|\t0\t0\t0\t0\n", "1\t0\t0\t|\t0\t0\t0\t1\n", "1\t0\t1\t|\t0\t0\t0\t1\n", "1\t1\t0\t|\t0\t0\t0\t1\n", "1\t1\t1\t|\t0\t1\t0\t1\n", "Canalizing depth of f: 3\n", "Layer structure of f: [3]\n", "Number of canalizing layers of f: 1\n", "Non-canalizing core function of f: [1]\n", "\n", "Canalizing depth of g: 1\n", "Layer structure of g: [1]\n", "Number of canalizing layers of g: 1\n", "Non-canalizing core function of g: [1 0 0 1]\n", "\n", "Canalizing depth of h: 3\n", "Layer structure of h: [3]\n", "Number of canalizing layers of h: 1\n", "Non-canalizing core function of h: [1]\n", "\n", "Canalizing depth of k: 3\n", "Layer structure of k: [1, 2]\n", "Number of canalizing layers of k: 2\n", "Non-canalizing core function of k: [1]\n", "\n" ] } ], "source": [ "# a random canalizing function\n", "f = boolforge.random_function(n,layer_structure=[1])\n", "\n", "# a random function with exact canalizing depth 1\n", "g = boolforge.random_function(n,layer_structure=[1],EXACT_DEPTH=True)\n", "\n", "# a random nested canalizing function with one layer\n", "h = boolforge.random_function(n,layer_structure=[3])\n", "\n", "# a random nested canalizing function with two layers\n", "k = boolforge.random_function(n,layer_structure=[1,2])\n", "\n", "labels = ['f','g','h','k']\n", "\n", "boolforge.display_truth_table(f,g,h,k,labels=labels)\n", "\n", "for func,label in zip([f,g,h,k],labels):\n", " canalizing_info = func.get_layer_structure()\n", " print(f'Canalizing depth of {label}: {func.get_canalizing_depth()}') \n", " print(f'Layer structure of {label}: {canalizing_info['LayerStructure']}')\n", " print(f'Number of canalizing layers of {label}: {canalizing_info['NumberOfLayers']}')\n", " print(f'Non-canalizing core function of {label}: {canalizing_info['CoreFunction']}')\n", " print()\n", "\n" ] }, { "cell_type": "markdown", "id": "7c443612", "metadata": {}, "source": [ "Repeated evaluation of this block of code shows that the canalizing depth of `f` is either 1 or 3 (note that a canalizing depth of $n-1$ is never possible for a non-degenerated function). On the contrary, the canalizing depth of `g` is always 1 because we set `EXACT_DEPTH=True`. The 2-input core function of `g` is one of the two linear functions, each with 50% probability. Likewise, the core function for the other functions is simply [0] or [1], each with 50% probability. Functions `h` and `k` are nested canalizing, i.e., their canalizing depth is 3. Their layer structure is exactly as specified.\n", "\n", "If we do not care about the specific layer structure but only about the canalizing depth, we specify the optional argument `depth` instead of `layer_structure`." ] }, { "cell_type": "code", "execution_count": 91, "id": "c6348491", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "x0\tx1\tx2\t|\tf\tg\th\tk\n", "---------------------------------------------------------\n", "0\t0\t0\t|\t1\t0\t0\t1\n", "0\t0\t1\t|\t0\t0\t1\t1\n", "0\t1\t0\t|\t1\t1\t0\t1\n", "0\t1\t1\t|\t0\t1\t0\t1\n", "1\t0\t0\t|\t1\t1\t1\t0\n", "1\t0\t1\t|\t1\t0\t0\t1\n", "1\t1\t0\t|\t1\t0\t0\t1\n", "1\t1\t1\t|\t0\t0\t0\t1\n", "Canalizing depth of f: 3\n", "\n", "Canalizing depth of g: 0\n", "\n", "Canalizing depth of h: 1\n", "\n", "Canalizing depth of k: 3\n", "\n" ] } ], "source": [ "# any function has at least canalizing depth 0 so this is the same as boolforge.random_function(n)\n", "f = boolforge.random_function(n,depth=0)\n", "\n", "#a random non-canalizing function\n", "g = boolforge.random_function(n,depth=0,EXACT_DEPTH=True)\n", "\n", "#a random canalizing function\n", "h = boolforge.random_function(n,depth=1)\n", "\n", "#a random nested canalizing function\n", "k = boolforge.random_function(n,depth=n)\n", "\n", "labels = ['f','g','h','k']\n", "\n", "boolforge.display_truth_table(f,g,h,k,labels=labels)\n", "\n", "for func,label in zip([f,g,h,k],labels):\n", " canalizing_info = func.get_layer_structure()\n", " print(f'Canalizing depth of {label}: {func.get_canalizing_depth()}') \n", " print()" ] }, { "cell_type": "markdown", "id": "99660fd9", "metadata": {}, "source": [ "As before, repeated evaluation of this block of code shows that the canalizing depth of `f` can be 0, 1, or 3. Note that specifying `depth=0` without `EXACT_DEPTH=True` does not restrict the space of functions at all. On the contrary, the canalizing depth of `g` is always 0 (i.e., g does not contain any canalizing variables) because we set `EXACT_DEPTH=True`. Function `h` is canalizing and may be nested canalizing (because we specified that the minimal canalizing depth is 1), and `k` is always nested canalizing (i.e., it has canalizing depth $n=3$)." ] }, { "cell_type": "markdown", "id": "2901dee5", "metadata": { "id": "2901dee5" }, "source": [ "## Allowing degenerated functions \n", "\n", "It is possible that an n-input Boolean function does not depend on all its variables. For example, the function $f(x,y) = x$ depends on $x$ but not on $y$. By default, such degenerated functions are never generated by `boolforge.random_function()`. To enable the generation of possibly degenerated functions, we set `ALLOW_DEGENERATED_FUNCTIONS=True`. Although hardly of any practical value, we can even restrict the random generation to degenerated functions only, using `boolforge.random_degenerated_function(n,*args)`. \n", "\n", "Since degenerated functions occur much more frequently at low degree, we set `n=2`, generate a large number of random, possibly degenerated functions and compare a histogram of the observed number of essential variables to the expected proportions." ] }, { "cell_type": "code", "execution_count": 111, "id": "67a5a393", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Error: [ 0.0046 -0.001 -0.0036]\n" ] }, { "data": { "image/png": "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79/vvF976qZOnUq4AbIowg3hRiwON5auUAzfOHz4sAkDujWFK51BVrduXXP94Ycflk8++USmTJkic+fOdQYbV64BQ0OHhhWthVJ79+6VH3/8UZYsWeJ8jOZiHVbSHpqffvrJhB8NO75st55ft+ZIq50AAKRGuLGBS5cumf9qzYzut+VKa1+UhghdEVoDyKFDhzJ0jqeeesrU2aR21113maDij3YDAOAtwo0NVK9e3YQBHRpKq+dE61R0+vxXX30l999/v6mbadWqldtjtm3bZoKK0iEfrbfROhx19913y88//2zqdzzRmh7txdmwYYPHYSnHVH3XrTLS0249v267kbqdAACkhXBjA4UKFZIRI0bI0KFDTcC49957zZikFgzruKQumKibjmqxr4aUkSNHSlRUlBlm0tlLDhMmTJBixYqZ2Wq68rM+z7EGkc6C0hlUWkSsNTEFChQwYUdrbF5//XWzgam+Zu/eveW1114zBcVatBwfH29qdsqXL29mSa1YscKEKy0o/rt26+tpcbFusaFt1vNq75PWAQEAYIvtF5C2iRMnmr22dFq19na0a9fODPdo6OjTp4+MGzfOBBs1fvx4E2A0OLjSehydTaWLK+qqz1988YWzx0WniGuvjPbm6HRwrYnRqeRlypRxPl9reXRm1IABA6Rq1apmx3fHVHEddtLzajGwntuxUGNa7dap4Up7kj766CP59NNPTWDSrTZ0bSMAANLCbCmwejAAn2K2FLOlxOLZUvTcAAAAWyHcAAAAW6GgGNKiRQvn1gwAAAQ6em4AAICtEG4AAICtEG4AAICtEG4AAICtEG4AAICtEG4AAICtEG4AAICtEG4AAICtEG4AAICtEG4AAICtEG4AAICtEG4AAICtEG4AAICtEG4AAICtEG4AAICtEG4AAICtEG4AAICtEG4AAICtEG4AAICtEG4AAICtEG4AAICtEG4AAICtEG4AAICtEG4AAICtEG4AAICtEG4AAICtEG4AAICtEG4AAICtEG4AAICtEG4AAICtEG4AAICtEG4AAICt+CTcXLhwwRcvAwAAkPnhZurUqRIbG+u83a1bNylWrJiULVtW9u7de/stAgAAyMxwM2/ePAkNDTXXv/nmG3P56quvpH379jJy5MjbaQsAAMBty+XtE06dOuUMNytWrDA9N23atJGwsDBp1KjR7bcIAAAg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tHE7d1xcnHnewYMHze3mzZun1K1b92/fb5kyZVImTZrkdqxBgwYpAwYMcN7W96nvNy1//fVXSv78+VO2bNnidrxPnz4pPXr0MNc7duyYEh0d7fH5gwcPTmnVqlVKcnLyTfd99913KSEhIeYcripWrJjy5ptvmuvaNj1/YmKi8/6RI0emNGrUyHlbP48hQ4a4vca6devMZ3b+/Pk031uHDh1Shg8ffsvXSf0d0LY56N9Dz3HgwIE0nzNw4MCUrl27un1vSpYsmXL16lW3x+l3SV8/LR9++GFKsWLFnLcXLFhgzr1t2zbnMW2HHvv+++89fo+17UuXLnV7Xf2+RURE/O3fCsgM9NwAt0nrbvRfrPov2IzSXg/9F7aDDiHpUIJrL5HWsWjPhCvtaXDIlSuX6RFwtEOHZNatW2eGpBwXHT5w9GA4aC/ErSQmJppepSZNmrgd19vevOfDhw+bHoL77rvPrU3aW+Boz9NPPy3Lli0zw0fau7Blyxa3YRidHabDL88884x8/fXXzvv0vepQi35Grq999OhRt/eqvQeFChVy3tZhk9Sf6d/RIaOJEyeav48OGep5Vq9eLcePH0/3a2iviA55bdu2zdzWng3taXL8fdScOXPM30aH2fQcb7311k3n0DZor9utfPvtt6YHS4fR9L1rL5P27unfwvW706BBA+dtbYcOj3r6+16+fNl8pjps5vpZv/TSS87P+lZ/KyAz5MqUswA21qxZMzNMM3r0aPN/6q40sKSk6D+C/8/169dveo3cuXO73daaDE/HdLgivfTHXoepNHylpj/qDlqLkRkcdR46nKM/tK60jkdpfdBvv/0mX375pRlq0R/lgQMHmuEw/fHXsKLDTvqDrcNrWvekNTv62vqetD4mNddpzrf7mapXX33VDGFpXYujlkVrlrwpxtZaLR0yXLp0qZk+r//VYOegAU+H9nQoUAOshhI9rw5Ruvq7v50GKB3e1NfWISQNY5s2bTLBRNubkcJjx9/x7bfflkaNGrndpyFc3epvBWQGwg3gA1q3ob0NjqJOB/1X96lTp0zAccy28eXaNPovfw1XSguQtdZECzkdPzAfffSR6a3Qf5lnlBbnasGq1mI0b97ceVxvN2zYMN2vU716dRNitPfB9XVS088sKirKXJo2bWrqijTcONrSvXt3c3nooYekXbt2pqZG36t+zvo+b6e2Q3tBtGfmVvR9P/DAA6ZWSGk40iJhfX/e0MJp7Z3S4uRff/3VrcZFz6GF4lr86+DaA5Ve+n3Q9mlIcvQMpq7bcXx3tC7L8ffUOhmtu9GC4dS0V1G/D9pmfQ9pSetvpQEL8DfCDeAD+i94/T96LaR0pbORzpw5I6+88or5P3gt6tV/zer/8fuCDl3oDBf9EdLZNjrbqHfv3uY+7fHQf13rj6f+iOqPig4Naa+AznRx/Cs7PTRgaJGzzubREKczmDSkpS6KvRXtfdDeCC0i1h9cnTaekJBgfsj189Awo0WqOhSjw3RXr141RbKOH1gttNbeGS2Q1h9qLbbVHhDtmdFeAe3h0NlD+llr0a4OpWkvUZcuXcxwXXpoMNLeEe3x0KEWTz/E+nlrD4QOmWnBt7ZLC7W9DTcPPvig6VHRS8uWLU1gcD2HDtfpcJfOmNIZXrp+jV73RqVKlUxPoRa9ay9eWkXj2qM1ePBg8/3VgKgBWXuU0gqv48ePN8NNOuNMQ4v+rTQc6fdv2LBht/xbAZmBmhvAR3QadOohDv1h1imyGkJ02rPOqElrJlFGe4z0oq+tww06Q8Uxu8jR26I9EW3atDEBTIdP9AfGtb4nPfSHTH+0dDaUvo6GND2X/gh7Q2tVdFaNzprSz0Z/GDWAOH60tedEh/dq165teqQ0gGkYc4QjDS4aVLQ+RAOIDl/pe9FeMb2uz4mOjjbhRntCdIhLexrSS/82ek4NKtqD5KmO5l//+pfpKdKhSA2v+qOtocpb+n40cGi9UOoeEJ0Zp+FHez106EdrZFx7cdJLvxcaNHRoUpcK0DDqaUq2Dk89//zzZraW1lJpsIuNjU3zdXXKvQZkDbn6fdCeOF3p2PF3vNXfCsgMQVpVnClnAgAAyATEaAAAYCuEGwAAYCuEGwAAYCuEGwAAYCuEGwAAYCuEGwAAYCuEGwAAYCuEGwAAYCuEGwAAYCuEGwAAYCuEGwAAIHby/wCQL5Pj7ODerQAAAABJRU5ErkJggg==", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "n = 2\n", "nsim = 10000\n", "count_essential_variables = np.zeros(n+1,dtype=int)\n", "for _ in range(nsim):\n", " f = boolforge.random_function(n,ALLOW_DEGENERATED_FUNCTIONS=True)\n", " count_essential_variables[f.get_number_of_essential_variables()] += 1\n", "\n", "#2 constant \"2-input\" functions, 4 1-variable \"2-input\" functions, 10 non-degenerated 2-input functions\n", "expected_proportions = [2/16,4/16,10/16] \n", "\n", "x = np.arange(n+1)\n", "width = 0.4\n", "\n", "fig,ax = plt.subplots()\n", "ax.bar(np.arange(n+1)-width/2,count_essential_variables/nsim,width=width,label='observed')\n", "ax.bar(np.arange(n+1)+width/2,expected_proportions,width=width,label='expected')\n", "ax.legend(frameon=False,loc='best')\n", "ax.set_xticks(x)\n", "ax.set_xlabel('Number of essential variables')\n", "ax.set_ylabel(f'Proportion of {n}-input functions')\n", "\n", "print('Error:',count_essential_variables/nsim - expected_proportions)\n" ] }, { "cell_type": "markdown", "id": "e978926c", "metadata": {}, "source": [ "## Functions with specific Hamming weight\n", "\n", "The Hamming weight of a Boolean function is the number of 1s in its truth table. BoolForge allows for the generation of random n-input functions with a specific Hamming weight $w\\in\\{0,1,\\ldots,2^n\\}$. The additional optional parameters `ALLOW_DEGENERATED_FUNCTIONS` and `EXACT_DEPTH` specify whether degenerated and canalizing functions are allowed. By default, canalizing functions are allowed, while degenerated functions are not. Since all functions with Hamming weight $w\\in\\{0,1,2^n-1,2^n\\}$ are canalizing, we require $2\\leq w\\leq 2^n-2$ whenever canalizing functions are not permissible (i.e., whenever`EXACT_DEPTH=True`)." ] }, { "cell_type": "code", "execution_count": 137, "id": "8275851d", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "x0\tx1\tx2\t|\tf\tg\th\n", "-------------------------------------------------\n", "0\t0\t0\t|\t1\t1\t0\n", "0\t0\t1\t|\t0\t0\t0\n", "0\t1\t0\t|\t0\t1\t0\n", "0\t1\t1\t|\t1\t0\t1\n", "1\t0\t0\t|\t1\t1\t1\n", "1\t0\t1\t|\t1\t1\t0\n", "1\t1\t0\t|\t0\t0\t0\n", "1\t1\t1\t|\t1\t1\t0\n", "Hamming weight of f: 5\n", "Canalizing depth of f: 0\n", "Number of essential variables of f: 3\n", "\n", "Hamming weight of g: 5\n", "Canalizing depth of g: 0\n", "Number of essential variables of g: 3\n", "\n", "Hamming weight of h: 2\n", "Canalizing depth of h: 0\n", "Number of essential variables of h: 3\n", "\n" ] } ], "source": [ "n=3\n", "\n", "#a random 3-input function with Hamming weight 5\n", "f = boolforge.random_function(n,hamming_weight=5)\n", "\n", "#a random non-canalizing 3-input function with Hamming weight 5\n", "g = boolforge.random_function(n,hamming_weight=5,EXACT_DEPTH=True)\n", "\n", "#a random, possibly degenerated function with Hamming weight 2\n", "h = boolforge.random_function(n,hamming_weight=2,ALLOW_DEGENERATED_FUNCTIONS=True)\n", "\n", "\n", "labels = ['f','g','h']\n", "\n", "boolforge.display_truth_table(f,g,h,labels=labels)\n", "\n", "for func,label in zip([f,g,h],labels):\n", " canalizing_info = func.get_layer_structure()\n", " print(f'Hamming weight of {label}: {func.get_hamming_weight()}') \n", " print(f'Canalizing depth of {label}: {func.get_canalizing_depth()}') \n", " print(f'Number of essential variables of {label}: {func.get_number_of_essential_variables()}') \n", " print()" ] }, { "cell_type": "markdown", "id": "628dc116", "metadata": {}, "source": [ "## Biased functions\n", "\n", "While specifying the Hamming weight fixes the exact number of 1s in the truth table of a generated function, specifying the bias or absolute bias acts slightly differently. The bias $p$ describes the probability of selecting a 1 at any position in the truth table and can be modified using the optional argument `bias`. Instead of specifying the bias, the absolute bias may also be specified. Unbiased functions generated using $p=0.5$ have an absolute bias of $0$, the default. If, for example, we set `absolute_bias=0.5` and specify to use absolute bias (`USE_ABSOLUTE_BIAS=True`, default is False), the bias used to generate the function is either 0.25 or 0.75, both with probability 50%. Generally, if we set `USE_ABSOLUTE_BIAS=True; absolute_bias=a` for $a\\in [0,1]$, the bias is either $(1+a)/2$ or $(1-a)/2$, both with probability 50%. \n", "\n", "To display these different modes, we repeatedly generate random Boolean functions under three different constraints (`f` with bias $p=0.75$, `g` with absolute bias 0.5, and `h` an unbiased function, i.e., with bias $p=0.5$), and compare the empirical Hamming weight distribution of the three families of functions." ] }, { "cell_type": "code", "execution_count": null, "id": "6b9df367", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Text(0, 0.5, 'Proportion of 4-input functions')" ] }, "execution_count": 159, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAkAAAAGwCAYAAABB4NqyAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjUsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvWftoOwAAAAlwSFlzAAAPYQAAD2EBqD+naQAAQu1JREFUeJzt3Qd4VNXW8PFFSehNOoiEJjWA0qSj8FJEqVIVQhEFRVGKwL1UgUuRKiAogtLBgoigVAEVgnRERS4gCkgvIfSSzPes/X4z70waBM5kJnP+v+c5OqfM2XuGZGZl77X3TuFwOBwCAABgIyl9XQEAAICkRgAEAABshwAIAADYDgEQAACwHQIgAABgOwRAAADAdgiAAACA7aT2dQX8UXR0tJw8eVIyZcokKVKk8HV1AADAfdCpDa9cuSL58uWTlCkTbuMhAIqDBj8FChTwdTUAAMADOH78uDz66KMJXkMAFAdt+XG+gZkzZ/Z1dQAAwH2IjIw0DRjO7/GEEADFwdntpcEPARAAAMnL/aSvkAQNAABshwAIAADYDgEQAACwHQIgAABgOwRAAADAdgiAAACA7RAAAQAA2yEAAgAAtkMABAAAbIcACAAA2A4BEAAAsB3WArNQyIBVSVbWX2MaJ+r6OnXqSPny5WXy5MnxXhMSEiJvvfWW2QAACGS0AMFlx44d8sorr3jt/g6HQ4YMGSJ58+aVdOnSSb169eTQoUMJPkeDMl3ULub2+uuvewR3Mc93797da68DAJD8EQDBJWfOnJI+fXqv3X/cuHHy/vvvy8yZM+Xnn3+WDBkySIMGDeTmzZsJBmWnTp1ybevWrTPHW7Vq5XFdt27dPK7TsgAAiA8BkI3cvXtXevbsKVmyZJEcOXLI4MGDTauMe2uLexfZxIkTJTQ01AQqBQoUkNdee02uXr3qOv/333/L888/L9myZTPXlC5dWr799ts4y9Zy9N6DBg2Spk2bStmyZWXevHly8uRJWb58eYJBWZ48eVzbypUrpUiRIlK7dm2P6zRwc78uc+bMD/luAQACGTlANjJ37lzp2rWrbN++XXbu3Gm6ux577DHTehKXlClTmhabQoUKyZ9//mkCoHfeeUc++OADc167oW7fvi0//PCDCYB+//13yZgxY5z3Onr0qJw+fdp0ezlpIFalShUJDw+Xtm3b3rP+WtaCBQukd+/eppvL3cKFC805DX40KNPgzputWQC8kyuZ2PxG4EERANmItuJMmjTJBA/FixeX/fv3m/34AiD3ZGhtHRo5cqTJrXEGQMeOHZOWLVuaViJVuHDheMvW4Eflzp3b47juO8/di7YURURESKdOnTyOt2/fXgoWLCj58uWTX375Rfr37y8HDx6UZcuW3dd9AQD2QwBkI0899ZRHy0nVqlVlwoQJEhUVJalSpYp1/fr162X06NHyxx9/SGRkpOlC03yd69evm9aVN998U3r06CFr1641LTsaDGnXlrfMnj1bGjVqZAIdd+6J2xqMaZJ13bp15ciRI6a7DACAmMgBQpz++usvee6550xA8+WXX8quXbtk+vTprq4o9fLLL5uusQ4dOpjWpIoVK8rUqVPjvJ92TakzZ854HNd957mEaL6RBmRa5r1ot5o6fPjwfbxSAIAdEQDZiI68crdt2zYpVqxYnK0/GvBER0ebFiJtOXr88cdNwnJc3WraLabdTX369JFZs2bFWbbmEWmgs2HDBtcxbVXSOmlL1L188sknkitXLmnc+N75AXv37jX/15YgAADiQgBkI5qzownEmh+zePFi01rTq1evOK8tWrSo3Llzx1yjrTzz5883w9dj5gitWbPGJDjv3r1bNm7cKCVLlozzftr1ptdrHtGKFStMi1HHjh1Nd1azZs1c12nX1bRp0zyeq4GYBkBhYWGSOrVnr612c40YMcIEbNpqpffW+9aqVcur3XEAgOSNHCAL+fvoBQ0Mbty4IZUrVzatPhr8xDfxYbly5cww+LFjx8rAgQNNQKH5QHoPJ80d0pFgJ06cMMPOGzZsaJKq46MjyK5du2bK1GTmGjVqyOrVqyVt2rQeAc358+c9nqddXxq8denSJdY9g4ODzXkdYq/31hYpzUXS4fYAAMQnhcN9Ihi4umZ0iPbly5eZTwYALMQwePjL9zddYAAAwHYIgAAAgO0QAAEAANshAAIAALZDAAQAAGyHAAgAANgOARAAALAdJkIEAAQc5hvCvdACBEOXkdDlKpzraHlDp06dPJa9SEr3U3adOnXMch0AgMBHC5CVhmVJwrIuix1oUPbVV18lSeCkC7oGBQV5tYzp06fLe++9J6dPnzbLjehaa7o0SXw+/fRT6dy5s8exNGnSyM2bN71aTwAIdLQAAf/fI488IpkyZfLa/ZcuXWoWox06dKhZ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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "n=4\n", "\n", "nsim = 10000\n", "count_hamming_weights = np.zeros((3,2**n+1),dtype=int)\n", "for _ in range(nsim):\n", " #a random 3-input function with bias p=0.75\n", " f = boolforge.random_function(n,bias=0.75)\n", "\n", " #a random 3-input function with absolute bias 0.5 (i.e., bias p=0.25 or p=0.75)\n", " g = boolforge.random_function(n,absolute_bias=0.5,USE_ABSOLUTE_BIAS=True)\n", "\n", " #a random 3-input function with absolute bias 0.5 (but the absolute_bias is erroneously not used because USE_ABSOLUTE_BIAS=True is missing)\n", " h = boolforge.random_function(n,absolute_bias=0.5)\n", "\n", " count_hamming_weights[0,f.get_hamming_weight()] += 1\n", " count_hamming_weights[1,g.get_hamming_weight()] += 1\n", " count_hamming_weights[2,h.get_hamming_weight()] += 1\n", "\n", "labels=['bias 0.75','absolute bias 0.5','random']\n", "\n", "x = np.arange(2**n+1)\n", "width = 0.3\n", "\n", "fig,ax = plt.subplots()\n", "for i,label in enumerate(labels):\n", " ax.bar(x-width+width*i,count_hamming_weights[i]/nsim,width=width,label=labels[i])\n", "ax.legend(frameon=False,loc='best')\n", "ax.set_xticks(x)\n", "ax.set_xlabel('Hamming weight')\n", "ax.set_ylabel(f'Proportion of {n}-input functions')\n", "\n" ] }, { "cell_type": "markdown", "id": "e805a7db", "metadata": {}, "source": [ "This plot exemplifies the difference between bias and absolute bias: Specifying the bias shifts the mode of the Hamming weight distribution to the value of `bias`. Specifying the absolute bias yields random functions with a bimodal Hamming weight distribution. Note that `absolute_bias=0.5` is ignored in the generation of `h`. The desired use of absolute bias must be specified by `USE_ABSOLUTE_BIAS=True`.\n", "\n", "In the above plot, we notice a lack of functions with Hamming weight 0 and $16=2^n$. These constant functions are degenerated and thus not generated unless we set `ALLOW_DEGENERATED_FUNCTIONS=True`, which as we see below slightly modifies the resulting Hamming weight distributions." ] }, { "cell_type": "code", "execution_count": 157, "id": "9ea3e02b", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Text(0, 0.5, 'Proportion of 4-input functions')" ] }, "execution_count": 157, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "nsim = 10000\n", "count_hamming_weights = np.zeros((3,2**n+1),dtype=int)\n", "for _ in range(nsim):\n", " #a random 3-input function with bias p=0.75\n", " f = boolforge.random_function(n,bias=0.75,ALLOW_DEGENERATED_FUNCTIONS=True)\n", "\n", " #a random 3-input function with absolute bias 0.5 (i.e., bias p=0.25 or p=0.75)\n", " g = boolforge.random_function(n,absolute_bias=0.5,USE_ABSOLUTE_BIAS=True,ALLOW_DEGENERATED_FUNCTIONS=True)\n", "\n", " #a random 3-input function with absolute bias 0.5 (but the absolute_bias is erroneously not used because USE_ABSOLUTE_BIAS=True is missing)\n", " h = boolforge.random_function(n,absolute_bias=0.5,ALLOW_DEGENERATED_FUNCTIONS=True)\n", "\n", " count_hamming_weights[0,f.get_hamming_weight()] += 1\n", " count_hamming_weights[1,g.get_hamming_weight()] += 1\n", " count_hamming_weights[2,h.get_hamming_weight()] += 1\n", "\n", "labels=['bias 0.75','absolute bias 0.5','random']\n", "\n", "x = np.arange(2**n+1)\n", "width = 0.3\n", "\n", "fig,ax = plt.subplots()\n", "for i,label in enumerate(labels):\n", " ax.bar(x-width+width*i,count_hamming_weights[i]/nsim,width=width,label=labels[i])\n", "ax.legend(frameon=False,loc='best')\n", "ax.set_xticks(x)\n", "ax.set_xlabel('Hamming weight')\n", "ax.set_ylabel(f'Proportion of {n}-input functions')" ] } ], "metadata": { "colab": { "provenance": [ { "file_id": "1uYGafWSuMhd9QxcQkz2tTMTeFsLb_VHA", "timestamp": 1696210918065 } ] }, "kernelspec": { "display_name": "envpy312", "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.12.3" } }, "nbformat": 4, "nbformat_minor": 5 }