Generate

This module provides functions for generating random Boolean functions and Boolean networks with specified structural and dynamical properties.

The generate module enables the systematic creation of Boolean functions and networks that satisfy particular constraints, such as specified canalization depth, sensitivity range, bias, or connectivity. Generated instances can be used for statistical analysis, benchmarking, or simulation studies.

Several generation routines leverage Numba acceleration for efficient sampling and evaluation of large function spaces. While Numba is recommended to achieve near-native performance, it is not required for functionality; all functions have pure Python fallbacks.

This module complements boolean_function and boolean_network by facilitating reproducible generation of synthetic test cases and large ensembles of random networks.

Example

>>> from boolforge import generate
>>> generate.random_function(n=3)
>>> str(generate.random_network(N=5, n=2))
boolforge.generate.random_function(n: int, depth: int = 0, EXACT_DEPTH: bool = False, UNIFORM_STRUCTURE: bool = True, layer_structure: list[int] | None = None, PARITY: bool = False, ALLOW_DEGENERATE_FUNCTIONS: bool = False, bias: float = 0.5, absolute_bias: float = 0, USE_ABSOLUTE_BIAS: bool = False, hamming_weight: int | None = None, *, rng=None) BooleanFunction[source]

Generate a random Boolean function under flexible structural constraints.

This function acts as a high-level generator that unifies several common ensembles of Boolean functions, including parity functions, canalizing functions of specified depth or layer structure, functions with fixed Hamming weight, and biased random functions. The first applicable generation rule (in the order described below) is applied.

Selection logic (first applicable rule is used)

  1. If PARITY is True, return a random parity function (see random_parity_function).

  2. Else, if layer_structure is provided, return a Boolean function with the specified canalizing layer structure using random_k_canalizing_function_with_specific_layer_structure. Exactness of the canalizing depth is controlled by EXACT_DEPTH.

  3. Else, if depth > 0, return a k-canalizing function with k = min(depth, n) using random_k_canalizing_function. If EXACT_DEPTH is True, the function has exactly this depth; otherwise, its canalizing depth is at least k.

    If UNIFORM_STRUCTURE is True, canalizing layer structures are sampled uniformly at random (up to the imposed constraints). If False, canalized outputs are sampled independently and uniformly as bitstrings, which biases the distribution toward more symmetric layer structures.

  4. Else, if hamming_weight is provided, repeatedly sample Boolean functions with the specified Hamming weight until additional constraints implied by EXACT_DEPTH and ALLOW_DEGENERATE_FUNCTIONS are satisfied.

  5. Else, generate a random Boolean function using a Bernoulli model with either:

    • fixed bias bias, or

    • an automatically chosen bias determined by absolute_bias if USE_ABSOLUTE_BIAS is True.

    Additional constraints on canalization and degeneracy are enforced depending on EXACT_DEPTH and ALLOW_DEGENERATE_FUNCTIONS.

Parameters

nint

Number of input variables. Must be a positive integer.

depthint, optional

Requested canalizing depth. Used only if layer_structure is None and depth > 0. If EXACT_DEPTH is True, the function has exactly this canalizing depth (clipped at n); otherwise, its depth is at least depth. Default is 0.

EXACT_DEPTHbool, optional

Enforce exact canalizing depth where applicable. If depth == 0, setting EXACT_DEPTH=True enforces that the function is non-canalizing. Default is False.

UNIFORM_STRUCTUREbool, optional

If True (default), canalizing layer structures are sampled uniformly at random in canalizing-function branches. If False, canalized outputs are sampled independently as bitstrings, inducing a bias toward more symmetric structures.

layer_structurelist[int] or None, optional

Explicit canalizing layer structure [k1, ..., kr]. If provided, this takes precedence over depth. Default is None.

PARITYbool, optional

If True, ignore all other options and return a random parity function. Default is False.

ALLOW_DEGENERATE_FUNCTIONSbool, optional

If True, functions with non-essential variables may be returned in random-generation branches. If False, non-degenerate functions are enforced whenever possible. Default is False.

biasfloat, optional

Probability of a 1 when sampling truth-table entries independently. Used only if USE_ABSOLUTE_BIAS is False and no other branch applies. Must lie in [0, 1]. Default is 0.5.

absolute_biasfloat, optional

Absolute deviation from 0.5 used to determine the bias when USE_ABSOLUTE_BIAS is True. The bias is chosen uniformly from {0.5*(1 - absolute_bias), 0.5*(1 + absolute_bias)}. Must lie in [0, 1]. Default is 0.

USE_ABSOLUTE_BIASbool, optional

If True, ignore bias and determine the bias using absolute_bias. Default is False.

hamming_weightint or None, optional

If provided, enforce that the Boolean function has exactly this many ones in its truth table. Additional constraints are enforced depending on EXACT_DEPTH and ALLOW_DEGENERATE_FUNCTIONS. Default is None.

rngint, numpy.random.Generator, numpy.random.RandomState, random.Random, or None, optional

Random number generator or seed specification. Passed to utils._coerce_rng.

Returns

BooleanFunction

A randomly generated Boolean function of arity n.

Raises

TypeError

If parameters have invalid types.

ValueError

If parameter values or combinations are invalid.

Notes

For any fixed combination of parameters, this function samples uniformly at random from the set of Boolean functions satisfying the corresponding constraints. Non-uniformity arises only when explicitly requested via UNIFORM_STRUCTURE=False.

Extremely biased functions are often degenerate or highly canalizing; under restrictive parameter choices, some branches may reject repeatedly before returning a valid function.

Examples

>>> # Unbiased, non-degenerate random function
>>> f = random_function(n=3)
>>> # Function with canalizing depth at least 2
>>> f = random_function(n=5, depth=2)
>>> # Function with exact canalizing depth 2
>>> f = random_function(n=5, depth=2, EXACT_DEPTH=True)
>>> # Function with a specific canalizing layer structure
>>> f = random_function(n=6, layer_structure=[2, 1])
>>> # Parity function
>>> f = random_function(n=4, PARITY=True)
>>> # Fixed Hamming weight with non-canalizing and non-degenerate constraints
>>> f = random_function(
...     n=5,
...     hamming_weight=10,
...     EXACT_DEPTH=True,
...     ALLOW_DEGENERATE_FUNCTIONS=False
... )
boolforge.generate.random_function_with_bias(n: int, bias: float = 0.5, *, rng=None) BooleanFunction[source]

Generate a random Boolean function with a specified bias.

The Boolean function is represented by its truth table of length 2**n, where each entry is independently set to 1 with probability bias and to 0 otherwise.

Parameters

nint

Number of Boolean variables.

biasfloat, optional

Probability that a given truth-table entry equals 1. Default is 0.5.

rngint, np.random.Generator, np.random.RandomState, random.Random, or None, optional

Random number generator or seed specification. Passed to utils._coerce_rng.

Returns

BooleanFunction

Random Boolean function with the specified bias.

boolforge.generate.random_function_with_exact_hamming_weight(n: int, hamming_weight: int, *, rng=None) BooleanFunction[source]

Generate a random Boolean function with a fixed Hamming weight.

The Boolean function is represented by its truth table of length 2**n, containing exactly hamming_weight entries equal to 1. All such functions are sampled uniformly at random.

Parameters

nint

Number of Boolean variables.

hamming_weightint

Number of truth-table entries equal to 1.

rngint, np.random.Generator, np.random.RandomState, random.Random, or None, optional

Random number generator or seed specification. Passed to utils._coerce_rng.

Returns

BooleanFunction

Random Boolean function with exactly hamming_weight ones in its truth table.

Raises

TypeError

If hamming_weight is not an integer.

ValueError

If hamming_weight is not in the range [0, 2**n].

boolforge.generate.random_parity_function(n: int, *, rng=None) BooleanFunction[source]

Generate a random parity Boolean function.

A parity Boolean function evaluates to the parity (sum modulo 2) of all input variables, optionally shifted by a constant. This function returns either the parity function or its complement, chosen uniformly at random.

Parameters

nint

Number of Boolean variables.

rngint, np.random.Generator, np.random.RandomState, random.Random, or None, optional

Random number generator or seed specification. Passed to utils._coerce_rng.

Returns

BooleanFunction

Random parity Boolean function on n variables.

Raises

ValueError

If n is not a positive integer.

Notes

  • The returned function is either x1 XOR x2 XOR ... XOR xn or its complement.

  • All variables are included symmetrically.

  • Parity functions are never canalizing. All variables must always be known to determine the output; they have maximal average sensitivity.

Examples

>>> f = random_parity_function(3)
>>> sum(f)
4
boolforge.generate.random_non_degenerate_function(n: int, bias: float = 0.5, *, rng=None) BooleanFunction[source]

Generate a random non-degenerate Boolean function.

A Boolean function is non-degenerate if every variable is essential, i.e., the function depends on all n input variables. Functions are sampled repeatedly from the Bernoulli(bias) ensemble until a non-degenerate function is obtained.

Parameters

nint

Number of Boolean variables.

biasfloat, optional

Probability that a truth-table entry equals 1. Default is 0.5.

rngint, np.random.Generator, np.random.RandomState, random.Random, or None, optional

Random number generator or seed specification. Passed to utils._coerce_rng.

Returns

BooleanFunction

Random non-degenerate Boolean function.

Raises

ValueError

If n is not a positive integer.

ValueError

If bias is not strictly between 0 and 1.

Notes

  • For moderate bias values, almost all Boolean functions are non-degenerate.

  • Extremely biased functions are very likely to be degenerate, which may lead to long rejection-sampling times.

boolforge.generate.random_degenerate_function(n: int, bias: float = 0.5, *, rng=None) BooleanFunction[source]

Generate a random degenerate Boolean function.

A Boolean function is degenerate if at least one variable is non-essential, i.e., the function does not depend on that variable. This function constructs a degenerate Boolean function by selecting one variable uniformly at random and enforcing that the output is independent of that variable.

Parameters

nint

Number of Boolean variables.

biasfloat, optional

Probability that a truth-table entry equals 1 for the underlying (n−1)-variable function. Default is 0.5.

rngint, np.random.Generator, np.random.RandomState, random.Random, or None, optional

Random number generator or seed specification. Passed to utils._coerce_rng.

Returns

BooleanFunction

Random degenerate Boolean function on n variables.

Raises

ValueError

If n is not a positive integer.

ValueError

If bias is not strictly between 0 and 1.

Notes

  • Exactly one variable is forced to be non-essential by construction, though additional variables may also be non-essential by chance.

  • The degenerate variable is chosen uniformly at random.

  • The resulting distribution is not uniform over all degenerate Boolean functions.

  • This construction avoids rejection sampling.

boolforge.generate.random_non_canalizing_function(n: int, bias: float = 0.5, *, rng=None) BooleanFunction[source]

Generate a random non-canalizing Boolean function.

A Boolean function is canalizing if there exists at least one variable and a value of that variable such that fixing it forces the output of the function. This function samples Boolean functions from the Bernoulli(bias) ensemble until a non-canalizing function is obtained.

Parameters

nint

Number of Boolean variables. Must satisfy n > 1.

biasfloat, optional

Probability that a truth-table entry equals 1. Default is 0.5.

rngint, np.random.Generator, np.random.RandomState, random.Random, or None, optional

Random number generator or seed specification. Passed to utils._coerce_rng.

Returns

BooleanFunction

Random non-canalizing Boolean function on n variables.

Raises

ValueError

If n is not an integer greater than 1.

ValueError

If bias is not strictly between 0 and 1.

Notes

  • This function uses rejection sampling.

  • For moderate bias values, almost all Boolean functions are non-canalizing.

  • Extremely biased functions are more likely to be canalizing and may lead to longer sampling times.

References

C. Kadelka, J. Kuipers, and R. Laubenbacher (2017). The influence of canalization on the robustness of Boolean networks. Physica D: Nonlinear Phenomena, 353, 39–47.

boolforge.generate.random_non_canalizing_non_degenerate_function(n: int, bias: float = 0.5, *, rng=None) BooleanFunction[source]

Generate a random Boolean function that is both non-canalizing and non-degenerate.

A Boolean function is non-canalizing if no variable can force the output when fixed, and non-degenerate if every variable is essential. This function samples Boolean functions from the Bernoulli(bias) ensemble until both properties are satisfied.

Parameters

nint

Number of Boolean variables. Must satisfy n > 1.

biasfloat, optional

Probability that a truth-table entry equals 1. Default is 0.5.

rngint, np.random.Generator, np.random.RandomState, random.Random, or None, optional

Random number generator or seed specification. Passed to utils._coerce_rng.

Returns

BooleanFunction

Random Boolean function on n variables that is both non-canalizing and non-degenerate.

Raises

ValueError

If n is not an integer greater than 1.

ValueError

If bias is not strictly between 0 and 1.

Notes

  • This function uses rejection sampling.

  • For moderate bias values and sufficiently large n, almost all Boolean functions are both non-canalizing and non-degenerate.

  • Extremely biased functions are more likely to be canalizing or degenerate and may lead to longer sampling times.

References

C. Kadelka, J. Kuipers, and R. Laubenbacher (2017). The influence of canalization on the robustness of Boolean networks. Physica D: Nonlinear Phenomena, 353, 39–47.

boolforge.generate.sample_canalized_outputs_uniform_structure(n, W, *, rng)[source]

Sample a canalized output bitstring yielding uniform layer-structure weighting.

This function samples a binary vector b of length n representing canalized output values, where consecutive equal values are part of the same canalizing layer. The probability of a given layer structure (k_1, k_2, ..., k_r) is proportional to

1 / (k_1! k_2! … k_r!).

Sampling is performed sequentially using precomputed dynamic-programming weights W, stored in _uniform_structure_weights.

Parameters

nint

Length of the output bitstring to sample.

Wndarray of shape (n+1, n+1)

Dynamic-programming weight table, where W[m, s] gives the total weight of all valid completions with m positions remaining and current layer size s.

rngnumpy.random.Generator

Random number generator used for sampling.

Returns

bndarray of shape (n,), dtype int

Sampled binary canalized output vector.

Notes

The bitstring is generated left-to-right. At each step, the algorithm probabilistically chooses whether to extend the current layer or start a new one, using the weights in W to ensure correct global sampling probabilities. The first bit is chosen uniformly at random.

boolforge.generate.random_k_canalizing_function(n: int, k: int, EXACT_DEPTH: bool = False, UNIFORM_STRUCTURE: bool = True, ALLOW_DEGENERATE_FUNCTIONS: bool = False, *, rng=None) BooleanFunction[source]

Generate a random k-canalizing Boolean function in n variables.

A Boolean function is k-canalizing if it has at least k conditionally canalizing variables. If EXACT_DEPTH is True, the function has exactly k conditionally canalizing variables; otherwise, its canalizing depth may exceed k.

Parameters

nint

Number of Boolean variables.

kint

Requested canalizing depth. Must satisfy 0 <= k <= n. Setting k = n generates a nested canalizing function.

EXACT_DEPTHbool, optional

If True, enforce that the canalizing depth is exactly k. If False (default), the depth is at least k.

UNIFORM_STRUCTUREbool, optional

If True (default), canalized outputs are sampled uniformly over canalizing layer structures. Specifically, layer structures (k_1, ..., k_r) are sampled with probability proportional to 1 / (k_1! ... k_r!), removing the bias toward symmetric structures induced by independent sampling. If k == n, the nested canalizing constraint that the final layer has size at least 2 is enforced. If False, canalized outputs are sampled independently and uniformly as a bitstring, which biases the distribution toward more symmetric layer structures.

ALLOW_DEGENERATE_FUNCTIONSbool, optional

If True, the non-canalizing core function may be degenerate. If False (default), non-degenerate core functions are enforced whenever possible.

rngint, numpy.random.Generator, numpy.random.RandomState, random.Random, or None, optional

Random number generator or seed specification. Passed to utils._coerce_rng.

Returns

BooleanFunction

A Boolean function on n variables with canalizing depth at least k (or exactly k if EXACT_DEPTH=True).

Raises

AssertionError

If n is not a positive integer.

AssertionError

If k does not satisfy 0 <= k <= n.

AssertionError

If EXACT_DEPTH=True and k = n-1 (no such functions exist).

Notes

For fixed parameter values, this function samples uniformly at random from the ensemble of Boolean functions consistent with the specified constraints. Non-uniformity arises only when UNIFORM_STRUCTURE=False.

The construction follows the standard decomposition of a k-canalizing function into canalizing variables, canalizing inputs and outputs, and a residual core function on n-k variables.

References

He, Q., and Macauley, M. (2016).

Stratification and enumeration of Boolean functions by canalizing depth. Physica D: Nonlinear Phenomena, 314, 1–8.

Dimitrova, E., Stigler, B., Kadelka, C., and Murrugarra, D. (2022).

Revealing the canalizing structure of Boolean functions: Algorithms and applications. Automatica, 146, 110630.

boolforge.generate.random_k_canalizing_function_with_specific_layer_structure(n: int, layer_structure: list, EXACT_DEPTH: bool = False, ALLOW_DEGENERATE_FUNCTIONS: bool = False, *, rng=None) BooleanFunction[source]

Generate a random Boolean function with a specified canalizing layer structure.

The canalizing layer structure is given as a list [k_1, ..., k_r], where each k_i specifies the number of canalizing variables in the i-th layer. The total canalizing depth is sum(layer_structure).

If sum(layer_structure) == n and n > 1, the function is a nested canalizing function and the final layer is required to have size at least 2.

Parameters

nint

Total number of Boolean variables.

layer_structurelist of int

Canalizing layer structure [k_1, ..., k_r]. Each entry must be at least 1. If sum(layer_structure) == n and n > 1, the final entry must satisfy layer_structure[-1] >= 2.

EXACT_DEPTHbool, optional

If True, enforce that the canalizing depth is exactly sum(layer_structure). If False (default), additional canalizing variables may occur in the core function.

ALLOW_DEGENERATE_FUNCTIONSbool, optional

If True, the non-canalizing core function may be degenerate. If False (default), non-degenerate core functions are enforced whenever possible.

rngint, numpy.random.Generator, numpy.random.RandomState, random.Random, or None, optional

Random number generator or seed specification. Passed to utils._coerce_rng.

Returns

BooleanFunction

A Boolean function on n variables with the prescribed canalizing layer structure.

Raises

AssertionError

If n is not a positive integer.

AssertionError

If sum(layer_structure) does not satisfy 0 <= sum(layer_structure) <= n.

AssertionError

If EXACT_DEPTH=True and sum(layer_structure) = n - 1.

AssertionError

If sum(layer_structure) = n > 1 and the final layer has size less than 2.

AssertionError

If any entry of layer_structure is less than 1.

Notes

For fixed parameter values, this function samples uniformly at random from the ensemble of Boolean functions consistent with the specified canalizing layer structure and additional constraints.

The construction follows the standard decomposition of a canalizing function into ordered canalizing layers and a residual core function on the remaining variables.

References

He, Q., and Macauley, M. (2016).

Stratification and enumeration of Boolean functions by canalizing depth. Physica D: Nonlinear Phenomena, 314, 1–8.

Kadelka, C., Kuipers, J., and Laubenbacher, R. (2017).

The influence of canalization on the robustness of Boolean networks. Physica D: Nonlinear Phenomena, 353, 39–47.

boolforge.generate.random_NCF(n: int, UNIFORM_STRUCTURE: bool = True, layer_structure: list | None = None, *, rng=None) BooleanFunction[source]

Generate a random nested canalizing Boolean function in n variables.

A nested canalizing function (NCF) is an n-canalizing Boolean function, i.e., a function whose canalizing depth equals the number of variables. Optionally, a specific canalizing layer structure may be prescribed.

Parameters

nint

Total number of Boolean variables.

UNIFORM_STRUCTUREbool, optional

If True (default) and layer_structure is None, canalizing layer structures are sampled uniformly at random, removing the bias toward symmetric structures induced by independent sampling of canalized outputs. If False, canalized outputs are sampled independently and uniformly as a bitstring, which biases the distribution toward more symmetric layer structures. This parameter is ignored if layer_structure is provided.

layer_structurelist of int or None, optional

Canalizing layer structure [k_1, ..., k_r]. Each entry must be at least 1. If provided, it must satisfy sum(layer_structure) == n. If n > 1, the final entry must satisfy layer_structure[-1] >= 2. If None (default), the layer structure is sampled at random.

rngint, numpy.random.Generator, numpy.random.RandomState, random.Random, or None, optional

Random number generator or seed specification. Passed to utils._coerce_rng.

Returns

BooleanFunction

A nested canalizing Boolean function on n variables.

Raises

AssertionError

If n is not a positive integer.

AssertionError

If layer_structure is provided but does not satisfy sum(layer_structure) == n.

AssertionError

If n > 1 and the final layer has size less than 2.

Notes

For fixed parameter values, this function samples uniformly at random from the ensemble of nested canalizing Boolean functions consistent with the specified constraints. Non-uniformity arises only when UNIFORM_STRUCTURE=False.

This function is a convenience wrapper around random_k_canalizing_function and random_k_canalizing_function_with_specific_layer_structure.

References

He, Q., and Macauley, M. (2016).

Stratification and enumeration of Boolean functions by canalizing depth. Physica D: Nonlinear Phenomena, 314, 1–8.

Kadelka, C., Kuipers, J., and Laubenbacher, R. (2017).

The influence of canalization on the robustness of Boolean networks. Physica D: Nonlinear Phenomena, 353, 39–47.

boolforge.generate.random_degrees(N: int, n: int | float | list | ndarray, indegree_distribution: str = 'constant', NO_SELF_REGULATION: bool = True, *, rng=None) ndarray[source]

Draw an in-degree vector for a directed network with N nodes.

This function either accepts a user-specified in-degree vector or samples in-degrees independently for each node from a specified distribution.

Parameters

Nint

Number of nodes in the network. Must be a positive integer.

nint, float, list of int, or ndarray of int

Interpretation depends on indegree_distribution:

  • If n is a length-N vector of integers, it is interpreted as a user-specified in-degree sequence and returned after validation.

  • If indegree_distribution is one of {'constant', 'dirac', 'delta'}, then n is a single integer specifying the in-degree of every node.

  • If indegree_distribution == 'uniform', then n is a positive integer upper bound, and each node independently receives an in-degree sampled uniformly from {1, 2, ..., n}.

  • If indegree_distribution == 'poisson', then n is the Poisson rate parameter λ > 0. Each node independently receives a Poisson(λ) draw, truncated to lie in [1, N - int(NO_SELF_REGULATION)].

indegree_distributionstr, optional

Distribution used to generate in-degrees when n is not a vector. Must be one of {'constant', 'dirac', 'delta', 'uniform', 'poisson'}. Default is 'constant'.

NO_SELF_REGULATIONbool, optional

If True (default), self-loops are disallowed in subsequent wiring generation. This is enforced here by capping in-degrees at N-1. If False, in-degrees may be as large as N.

rngint, numpy.random.Generator, numpy.random.RandomState, random.Random, or None, optional

Random number generator or seed specification. Passed to utils._coerce_rng.

Returns

indegreesndarray of int, shape (N,)

In-degree of each node. For sampled distributions, values lie in [1, N - int(NO_SELF_REGULATION)].

Raises

AssertionError

If inputs are malformed, out of range, or an unsupported distribution is requested.

Notes

When sampling is requested, in-degrees for different nodes are generated independently. No attempt is made to enforce graphicality or feasibility of the resulting degree sequence for a particular wiring model; such constraints must be handled downstream.

Examples

>>> random_degrees(5, n=2, indegree_distribution='constant')
array([2, 2, 2, 2, 2])
>>> random_degrees(4, n=2, indegree_distribution='uniform', NO_SELF_REGULATION=True)
array([2, 1, 2, 2])
>>> random_degrees(6, n=1.7, indegree_distribution='poisson')
array([1, 2, 1, 1, 2, 1])
>>> random_degrees(3, n=[1, 2, 1])
array([1, 2, 1])
boolforge.generate.random_edge_list(N: int, indegrees: Sequence[int], NO_SELF_REGULATION: bool, AT_LEAST_ONE_REGULATOR_PER_NODE: bool = False, *, rng=None) list[source]

Generate a random directed edge list for a network with prescribed in-degrees.

Each node i receives exactly indegrees[i] incoming edges, with regulators chosen uniformly at random from the set of admissible source nodes. Optionally, the construction enforces that every node regulates at least one other node.

Parameters

Nint

Number of nodes in the network.

indegreessequence of int

Length-N sequence specifying the number of incoming edges for each node.

NO_SELF_REGULATIONbool

If True, self-loops (edges from a node to itself) are disallowed.

AT_LEAST_ONE_REGULATOR_PER_NODEbool, optional

If True, enforce that every node has at least one outgoing edge. This is achieved by rewiring edges while preserving the prescribed in-degree sequence. Default is False.

rngint, numpy.random.Generator, numpy.random.RandomState, random.Random, or None, optional

Random number generator or seed specification. Passed to utils._coerce_rng.

Returns

edge_listlist of tuple of int

List of directed edges represented as (source, target) pairs.

Raises

ValueError

If N or indegrees are inconsistent.

AssertionError

If sampling constraints cannot be satisfied.

Notes

Regulators for each node are sampled uniformly at random without replacement from the set of admissible source nodes. If AT_LEAST_ONE_REGULATOR_PER_NODE, the algorithm post-processes the initially sampled edge list by replacing edges until every node has at least one outgoing edge, while preserving all in-degrees and respecting the self-regulation constraint.

No guarantee is made that the resulting edge list is uniformly sampled from the space of all directed graphs satisfying the constraints.

boolforge.generate.random_wiring_diagram(N: int, n: int | float | list | ndarray, NO_SELF_REGULATION: bool = True, STRONGLY_CONNECTED: bool = False, indegree_distribution: str = 'constant', AT_LEAST_ONE_REGULATOR_PER_NODE: bool = False, n_attempts_to_generate_strongly_connected_network: int = 1000, *, rng=None) tuple[source]

Generate a random wiring diagram for a directed network with N nodes.

A wiring diagram specifies, for each node, the set of its regulators (incoming neighbors). In-degrees are first generated according to the specified distribution, after which edges are sampled uniformly at random subject to the requested constraints.

Parameters

Nint

Number of nodes in the network.

nint, float, list of int, or ndarray of int

Parameter determining the in-degree sequence. Interpretation depends on indegree_distribution:

  • If a length-N vector is provided, it is interpreted as the in-degree of each node.

  • If indegree_distribution is 'constant' (or 'dirac' / 'delta'), n specifies the in-degree of every node.

  • If indegree_distribution is 'uniform', n specifies the upper bound of a discrete uniform distribution on {1, ..., n}.

  • If indegree_distribution is 'poisson', n is the Poisson rate parameter lambda > 0.

NO_SELF_REGULATIONbool, optional

If True (default), self-loops are disallowed.

STRONGLY_CONNECTEDbool, optional

If True, repeatedly resample the wiring diagram until a strongly connected network is obtained, or until the maximum number of attempts is exceeded. Default is False.

indegree_distributionstr, optional

Distribution used to generate in-degrees. Must be one of {'constant', 'dirac', 'delta', 'uniform', 'poisson'}. Default is 'constant'.

AT_LEAST_ONE_REGULATOR_PER_NODEbool, optional

If True, enforce that every node has at least one outgoing edge. This is achieved by rewiring edges while preserving the in-degree sequence. Default is False.

n_attempts_to_generate_strongly_connected_networkint, optional

Maximum number of attempts to generate a strongly connected wiring diagram before raising a RuntimeError. Default is 1000.

rngint, numpy.random.Generator, numpy.random.RandomState, random.Random, or None, optional

Random number generator or seed specification. Passed to utils._coerce_rng.

Returns

WiringDiagram

A wiring diagram object encoding the regulator set of each node.

Raises

RuntimeError

If STRONGLY_CONNECTED=True and a strongly connected wiring diagram cannot be generated within the specified number of attempts.

Notes

In-degrees are generated first using random_degrees, and edges are then sampled uniformly at random subject to the imposed constraints. When STRONGLY_CONNECTED=True or AT_LEAST_ONE_REGULATOR_PER_NODE=True, the resulting distribution is not uniform over all wiring diagrams with the given in-degree sequence.

This function is a high-level convenience wrapper around random_degrees and random_edge_list.

Examples

>>> W = random_wiring_diagram(5, n=2)
>>> W = random_wiring_diagram(10, n=3, STRONGLY_CONNECTED=True)
>>> W = random_wiring_diagram(6, n=[1, 2, 1, 2, 1, 2])
boolforge.generate.rewire_wiring_diagram(I: list | ndarray | WiringDiagram, average_swaps_per_edge: float = 10, DO_NOT_ADD_SELF_REGULATION: bool = True, FIX_SELF_REGULATION: bool = True, *, rng=None) list[source]

Degree-preserving rewiring of a wiring diagram via double-edge swaps.

The wiring diagram is represented in regulator form: I[target] lists all regulators (incoming neighbors) of target. The algorithm performs random double-edge swaps of the form (u -> v, x -> y) -> (u -> y, x -> v), while preserving both the in-degree and out-degree of every node. Parallel edges are disallowed.

Parameters

Ilist of array-like or WiringDiagram

Wiring diagram in regulator representation. For each node target, I[target] contains the regulators of that node. Regulator indices must be integers in {0, ..., N-1}. If a WiringDiagram is provided, its internal adjacency representation is used.

average_swaps_per_edgefloat, optional

Target number of successful double-edge swaps per edge. Larger values typically yield better mixing but increase runtime. Default is 10.

DO_NOT_ADD_SELF_REGULATIONbool, optional

If True (default), proposed swaps that would introduce a self-loop are rejected.

FIX_SELF_REGULATIONbool, optional

If True (default), existing self-loops are kept fixed and excluded from the pool of swappable edges. If False, existing self-loops may be rewired; if DO_NOT_ADD_SELF_REGULATION is True, no new self-loops will be introduced.

rngint, numpy.random.Generator, numpy.random.RandomState, random.Random, or None, optional

Random number generator or seed specification. Passed to utils._coerce_rng.

Returns

WiringDiagram

A new wiring diagram obtained by degree-preserving rewiring of I.

Raises

ValueError

If the input wiring diagram is malformed.

AssertionError

If rewiring constraints cannot be satisfied.

Notes

Both in-degrees and out-degrees of all nodes are preserved exactly. Duplicate edges are never introduced. Control over self-regulation is governed by the two Boolean flags above.

The resulting wiring diagram is not guaranteed to be sampled uniformly from the space of all directed graphs with the same degree sequence; the procedure is intended as a practical degree-preserving randomization method rather than an exact uniform sampler.

Examples

>>> I = random_network(8,3)
>>> J = rewire_wiring_diagram(I)
>>> I.indegrees == J.indegrees
True
>>> I.get_outdegrees() == J.get_outdegrees()
True
boolforge.generate.random_network(N: int | None = None, n: int | float | list | ndarray | None = None, depth: int | list | ndarray = 0, EXACT_DEPTH: bool = False, UNIFORM_STRUCTURE: bool = True, layer_structure: list | None = None, ALLOW_DEGENERATE_FUNCTIONS: bool = False, PARITY: bool = False, bias: float | list | ndarray = 0.5, absolute_bias: float | list | ndarray = 0.0, USE_ABSOLUTE_BIAS: bool = True, hamming_weight: int | list | ndarray | None = None, NO_SELF_REGULATION: bool = True, STRONGLY_CONNECTED: bool = False, indegree_distribution: str = 'constant', AT_LEAST_ONE_REGULATOR_PER_NODE: bool = False, n_attempts_to_generate_strongly_connected_network: int = 1000, I: list | ndarray | None | WiringDiagram | DiGraph = None, *, rng=None) BooleanNetwork[source]

Construct a random Boolean network with configurable wiring and update rules.

The network is built in two stages:

  1. Wiring diagram If I is provided, it is used directly as the wiring diagram, where I[v] lists the regulators of node v. Otherwise, a wiring diagram for N nodes is sampled using random_wiring_diagram, with in-degrees determined by n and indegree_distribution. Self-loops may be disallowed and strong connectivity may be enforced.

  2. Update rules For each node i, a Boolean update function with arity indegrees[i] is generated using random_function subject to the requested constraints on canalizing depth or layer structure, parity, bias or absolute bias, and exact Hamming weight.

Parameters

Nint or None, optional

Number of nodes. Required when I is not provided. Ignored if I is given.

nint, float, list of int, ndarray of int, or None, optional

Controls the in-degree distribution when generating a wiring diagram (ignored if I is given). Interpretation depends on indegree_distribution:

  • 'constant', 'dirac', 'delta': Every node has constant in-degree n.

  • 'uniform': n is an integer upper bound; each node’s in-degree is sampled uniformly from {1, ..., n}.

  • 'poisson': n is a positive rate parameter lambda; in-degrees are Poisson(lambda) draws truncated to [1, N - int(NO_SELF_REGULATION)].

  • If n is a length-N vector of integers, it is taken as the exact in-degree sequence.

depthint, list of int, or ndarray of int, optional

Requested canalizing depth per node. If an integer, it is broadcast to all nodes and clipped at each node’s in-degree. If a vector, it must have length N. Interpreted as a minimum depth unless EXACT_DEPTH=True. Default is 0.

EXACT_DEPTHbool, optional

If True, each Boolean function is generated with exactly the requested canalizing depth (or exactly sum(layer_structure[i]) if a layer structure is provided). If False, the canalizing depth is at least as large as requested. Default is False.

UNIFORM_STRUCTUREbool, optional

Controls how canalized outputs are sampled when generating canalizing functions.

If True (default), canalizing layer structures are sampled uniformly at random, i.e., proportional to the inverse factorials of layer sizes, removing the bias toward symmetric structures induced by independent sampling of canalized outputs.

If False, canalized outputs are sampled independently and uniformly as bitstrings, which biases the distribution toward more symmetric layer structures.

This parameter is ignored when layer_structure is explicitly provided.

layer_structurelist, list of lists, or None, optional

Canalizing layer structure specifications.

  • If None (default), rule generation is controlled by depth and EXACT_DEPTH.

  • If a single list [k1, ..., kr], the same structure is used for all nodes.

  • If a list of lists of length N, layer_structure[i] is used for node i.

In all cases, sum(layer_structure[i]) must not exceed the in-degree of node i. When provided, layer_structure takes precedence over depth.

ALLOW_DEGENERATE_FUNCTIONSbool, optional

If True and depth == 0 and layer_structure is None, degenerate Boolean functions (with non-essential inputs) may be generated, as in classical NK-Kauffman models. If False, generated functions are required to be non-degenerate whenever possible. Default is False.

PARITYbool, optional

If True, parity Boolean functions are generated for all nodes and all other rule parameters are ignored. Default is False.

biasfloat, list of float, or ndarray of float, optional

Probability of output 1 when generating random (non-canalizing) Boolean functions. Used only when depth == 0, layer_structure is None, PARITY is False, and USE_ABSOLUTE_BIAS is False. Scalars are broadcast to length N. Must lie in [0, 1]. Default is 0.5.

absolute_biasfloat, list of float, or ndarray of float, optional

Absolute deviation from 0.5 used when USE_ABSOLUTE_BIAS is True. Scalars are broadcast to length N. Must lie in [0, 1]. Default 0.0.

USE_ABSOLUTE_BIASbool, optional

If True, the bias of each rule is chosen at random from {0.5*(1-absolute_bias), 0.5*(1+absolute_bias)}. If False, bias is used directly. Default is True.

hamming_weightint, list of int, ndarray of int, or None, optional

Exact Hamming weight (number of ones) of each truth table. Scalars are broadcast to length N. Values must lie in {0, ..., 2^k} for a k-input function. Additional restrictions apply when requesting exact depth zero. Default is None.

NO_SELF_REGULATIONbool, optional

If True, self-loops are forbidden in generated wiring diagrams. Ignored if I is provided. Default is True.

STRONGLY_CONNECTEDbool, optional

If True, wiring generation is repeated until a strongly connected directed graph is obtained or the attempt limit is exceeded. Ignored if I is provided. Default is False.

indegree_distributionstr, optional

Distribution used when sampling in-degrees. Must be one of {'constant', 'dirac', 'delta', 'uniform', 'poisson'}. Default 'constant'.

AT_LEAST_ONE_REGULATOR_PER_NODEbool, optional

If True, ensure that each node has at least one outgoing edge in the generated wiring diagram. Default is False.

n_attempts_to_generate_strongly_connected_networkint, optional

Maximum number of attempts to generate a strongly connected wiring diagram before raising an error. Default is 1000.

Ilist, ndarray, WiringDiagram, networkx.DiGraph, or None, optional

Existing wiring diagram. If provided, N and n are ignored and in-degrees are inferred from I. If I is a BooleanNetwork, its wiring diagram is reused and its Boolean update rules are ignored.

rngint, numpy.random.Generator, numpy.random.RandomState, random.Random, or None, optional

Random number generator or seed specification. Passed to utils._coerce_rng.

Returns

BooleanNetwork

A Boolean network with wiring diagram I (given or generated) and Boolean update functions generated according to the specified constraints.

Raises

AssertionError

If input shapes or parameter combinations are invalid.

RuntimeError

If STRONGLY_CONNECTED=True and a strongly connected wiring diagram cannot be generated within the specified number of attempts.

Notes

Constraint precedence for rule generation is: PARITY -> layer_structure -> depth / EXACT_DEPTH -> bias or Hamming-weight constraints.

When EXACT_DEPTH=True and the requested depth is zero, Hamming weights {0, 1, 2^k - 1, 2^k} correspond to canalizing functions and are therefore disallowed.

Examples

>>> # Boolean network with only essential inputs
>>> bn = random_network(N=10, n=2, ALLOW_DEGENERATE_FUNCTIONS=False)
>>> # Classic NK-Kauffman network allowing degenerate rules
>>> bn = random_network(N=10, n=3, ALLOW_DEGENERATE_FUNCTIONS=True)
>>> # Fixed wiring: reuse an existing diagram but resample rules
>>> bn0 = random_network(N=6, n=2)
>>> bn  = random_network(I=bn)
>>> # Exact canalizing depth k for all nodes
>>> bn = random_network(N=8, n=3, depth=1, EXACT_DEPTH=True)
>>> # Nested canalizing update rules with specific layer structure (broadcast)
>>> bn = random_network(N=5, n=3, layer_structure=[1,2])  # same for all nodes
>>> # Parity rules
>>> bn = random_network(N=7, n=2, PARITY=True)
>>> # Poisson in-degrees (truncated), no self-regulation, request strong connectivity
>>> bn = random_network(N=12, n=1.6, indegree_distribution='poisson',
...                     NO_SELF_REGULATION=True, STRONGLY_CONNECTED=True)
>>> # Exact Hamming weights (broadcast)
>>> bn = random_network(N=6, n=3, hamming_weight=4)
>>> # To ensure strong connectivity, set ALLOW_DEGENERATE_FUNCTIONS=False
>>> # and STRONGLY_CONNECTED=True
>>> bn = random_network(N,n,ALLOW_DEGENERATE_FUNCTIONS=False,STRONGLY_CONNECTED=True)
boolforge.generate.random_null_model(bn: BooleanNetwork, wiring_diagram: str = 'fixed', PRESERVE_BIAS: bool = True, PRESERVE_CANALIZING_DEPTH: bool = True, *, rng=None, **kwargs) BooleanNetwork[source]

Generate a randomized Boolean network (null model) from an existing Boolean network while preserving selected structural and dynamical properties.

The returned network has the same number of nodes as bn. Depending on the selected options, the wiring diagram and/or the Boolean update rules are randomized subject to specified invariants.

Wiring diagram randomization

The wiring diagram can be handled in one of three ways:

  • 'fixed' (default): The original wiring diagram bn.I is reused unchanged.

  • 'fixed_indegree': A new wiring diagram is sampled uniformly at random subject to preserving the in-degree of each node. This uses random_wiring_diagram with N = bn.N and n = bn.indegrees.

  • 'fixed_in_and_outdegree': The original wiring diagram is randomized via degree-preserving double-edge swaps using rewire_wiring_diagram, preserving both in-degrees and out-degrees of all nodes.

Rule randomization

Independently of the wiring diagram, Boolean update rules are randomized for each node, optionally preserving properties of the original rules:

  • If PRESERVE_BIAS is True, the exact Hamming weight (number of ones in the truth table) of each rule is preserved.

  • If PRESERVE_CANALIZING_DEPTH is True, the canalizing depth of each rule is preserved exactly.

If both flags are True, both properties are preserved simultaneously. If neither flag is True, rules are regenerated subject only to non-degeneracy and the node’s in-degree.

Parameters

bnBooleanNetwork

Source Boolean network.

wiring_diagram{‘fixed’, ‘fixed_indegree’, ‘fixed_in_and_outdegree’}, optional

Strategy for handling the wiring diagram. Default is 'fixed'.

PRESERVE_BIASbool, optional

If True, preserve the exact Hamming weight of each Boolean rule. Default is True.

PRESERVE_CANALIZING_DEPTHbool, optional

If True, preserve the exact canalizing depth of each Boolean rule. Default is True.

rngint, numpy.random.Generator, numpy.random.RandomState, random.Random, or None, optional

Random number generator or seed specification. Passed to utils._coerce_rng.

**kwargs

Additional keyword arguments forwarded to the wiring-diagram randomization routine:

  • For wiring_diagram == 'fixed_indegree', forwarded to random_wiring_diagram (e.g., NO_SELF_REGULATION, STRONGLY_CONNECTED).

  • For wiring_diagram == 'fixed_in_and_outdegree', forwarded to rewire_wiring_diagram (e.g., average_swaps_per_edge, DO_NOT_ADD_SELF_REGULATION, FIX_SELF_REGULATION).

Returns

BooleanNetwork

A randomized Boolean network satisfying the selected invariants.

Raises

AssertionError

If invalid options are provided.

RuntimeError

If wiring-diagram randomization fails (e.g., strong connectivity cannot be achieved within the allowed number of attempts).

Notes

This function generates null models by selectively preserving structural and dynamical properties of an existing Boolean network. It is intended for hypothesis testing and comparative studies rather than for uniform sampling over all networks satisfying the given constraints.

Examples

>>> # Most restrictive use case: Preserve both wiring and rule properties (default) 
>>> bn_null = random_null_model(bn)
>>> # Preserve in-degrees only and preserve rule bias
>>> bn_null = random_null_model(
...     bn,
...     wiring_diagram='fixed_indegree',
...     PRESERVE_BIAS=True,
...     PRESERVE_CANALIZING_DEPTH=False
... )
>>> # Preserve both in- and out-degrees via rewiring
>>> bn_null = random_null_model(
...     bn,
...     wiring_diagram='fixed_in_and_outdegree',
...     average_swaps_per_edge=15
... )