booldog.utils.boolean_normal_forms

This is a modification of pyboolnet.boolean_normal_forms module (originally PyBoolNet.QuineMcCluskey), specifically the function functions2mindnf, ca line 130.

The original:

expressions = {} for name, func in functions.items():

inputs = inspect.getargspec(func).args

Modified:

expressions = {}
for name, func in functions.items():

    # modification start CB
    if hasattr(func, 'depends'):
        inputs = func.depends
    else:
        inputs = inspect.getargspec(func).args
    # modification end CB

The modification allows the functions in Functions argument to accept input as an k-length list, where k is the number of nodes the node may depend on.

This modification simplifies function creation when using and matrix multiplication of “Activation” and “Inhibition” matrices when defining node update functions.

Example:

Suppose we have a matrix that defines the update functions:

graph_matrix = np.array([[1, 0, 0],
                         [1, 0, 1],
                         [0, 0, 1]])

And the graph state is given by multiplying the matrix with the current state:

node_state = np.matmul(data, network_state)         # line 1

As lambdas (the PyBoolNet format), the functions would be:

{
'f1': lambda x1: x1,
'f2': lambda x1, x2: x1 + x2,
'f3': lambda x3: x3
}

I am unaware of a way to automate producing lambdas like these at scale, especially when node_state is composed of more complicated vector operations. The following code produces functions at scale that have all node states as arguments, calculates the update, and also identifies the dependencies and make them an attribute of the function:

n = data.shape[0]
idx = {f"node{x}":x for x in range(n)}

def function_factory(data, node):
    # actual node dependencies
    args = [f"node{x}" for x in np.nonzero(data[idx[node]])[0]]

    def func(*func_input):
        # derive the network state from the input
        network_state = [0]*n
        for other_node, other_node_state in zip(args, func_input):
            network_state[idx[other_node]] = other_node_state

        # calculate the node state
        node_state = np.matmul(data[idx[node]], network_state)
        return node_state
   func.depends = args
   return func

funcs = {node:function_factory(data,  node) for node in idx.keys()}

Attributes

log

Functions

functions2mindnf(→ Dict[str, str])

Generates and returns a minimal disjunctive normal form (DNF) for the Boolean network represented by functions.

functions2primes(→ dict)

Generates and returns the prime implicants of a Boolean network represented by functions.

Module Contents

booldog.utils.boolean_normal_forms.log
booldog.utils.boolean_normal_forms.functions2mindnf(functions: Dict[str, callable]) Dict[str, str]

Generates and returns a minimal disjunctive normal form (DNF) for the Boolean network represented by functions. The algorithm uses Prekas2012, a Python implementation of the Quine-McCluskey algorithm.

arguments:
  • functions: keys are component names and values are Boolean functions

returns:
  • min_dnf: keys are component names and values are minimal DNF expressions

example:

>>> funcs = {"v1": lambda v1,v2: v1 or not v2, "v2": lambda: 1}
>>> mindnf = functions2primes(funcs)
>>> mindnf["v1"]
((! v2) | v1)
booldog.utils.boolean_normal_forms.functions2primes(functions: Dict[str, callable]) dict

Generates and returns the prime implicants of a Boolean network represented by functions.

arguments:
  • functions: keys are component names and values are Boolean functions

returns:
  • primes: primes implicants

example:

>>> funcs = {"v1": lambda v1, v2: v1 or not v2,
...          "v2": lambda v1, v2: v1 + v2 == 1}
>>> primes = functions2primes(funcs)