Skip to content

Commit

Permalink
Merge pull request #386 from Happy-Algorithms-League/feature/logical-…
Browse files Browse the repository at this point in the history
…operators

Support evolution for boolean expressions
  • Loading branch information
jakobj authored Feb 14, 2024
2 parents 2d708e6 + 27d1450 commit bc8ca62
Show file tree
Hide file tree
Showing 2 changed files with 131 additions and 3 deletions.
8 changes: 5 additions & 3 deletions cgp/node_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

try:
from sympy.core import expr as sympy_expr # noqa: F401
from sympy.logic import boolalg as sympy_boolalg # noqa: F401

sympy_available = True
except ModuleNotFoundError:
Expand Down Expand Up @@ -91,6 +92,7 @@ def check_to_sympy(cls: Type["OperatorNode"]) -> None:
genome = _create_genome(cls)

f = CartesianGraph(genome).to_sympy()
assert isinstance(f, sympy_expr.Expr)
x = [1.0]
f.subs("x_0", x[0]).evalf()
assert isinstance(f, sympy_expr.Expr) or isinstance(f, sympy_boolalg.Boolean)
if isinstance(f, sympy_expr.Expr):
x = [1.0]
f.subs("x_0", x[0]).evalf()
126 changes: 126 additions & 0 deletions examples/example_parity.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
"""
Evolving boolean expressions
============================
Example demonstrating the use of Cartesian genetic programming for
generating boolean expressions from a truth table.
"""

# The docopt str is added explicitly to ensure compatibility with
# sphinx-gallery.
docopt_str = """
Usage:
example_parity.py [--max-generations=<N>]
Options:
-h --help
--max-generations=<N> Maximum number of generations [default: 300]
"""

from docopt import docopt

import cgp

args = docopt(docopt_str)

# %%
# We first define a truth table (here 3bit parity generator).

truth_table = {
(0, 0, 0): 0,
(0, 0, 1): 1,
(0, 1, 0): 1,
(0, 1, 1): 0,
(1, 0, 0): 1,
(1, 0, 1): 0,
(1, 1, 0): 0,
(1, 1, 1): 1,
}


# %%
# Then we define the objective function for the evolution. It check whether the
# output of our expression matches the expected value for all input
# combinations.


def objective(individual):

if not individual.fitness_is_None():
return individual

f = individual.to_func()
fitness = 0
for message, parity_bit in truth_table.items():
y = f(*message)
fitness += float(y == bool(parity_bit))

individual.fitness = fitness

return individual


class AND2(cgp.OperatorNode):
"""A node that ands its two inputs."""

_arity = 2
_def_output = "bool(x_0) and bool(x_1)"
_def_numpy_output = "np.logical_and(x_0.astype(bool), x_1.astype(bool))"
_def_sympy_output = "x_0 & x_1"
_def_torch_output = "torch.logical_and(x_0.bool(), x_1.bool())"


class OR2(cgp.OperatorNode):
"""A node that ors its two inputs."""

_arity = 2
_def_output = "bool(x_0) or bool(x_1)"
_def_numpy_output = "np.logical_or(x_0.astype(bool), x_1.astype(bool))"
_def_sympy_output = "x_0 | x_1"
_def_torch_output = "torch.logical_or(x_0.bool(), x_1.bool())"


class NOT(cgp.OperatorNode):
"""A node that nots its input."""

_arity = 1
_def_output = "not bool(x_0)"
_def_numpy_output = "np.logical_not(x_0.astype(bool))"
_def_sympy_output = "Not(x_0)"
_def_torch_output = "torch.logical_not(x_0.bool())"


class XOR2(cgp.OperatorNode):
"""A node that xors its two inputs."""

_arity = 2
_def_output = "bool(x_0) != bool(x_1)"
_def_numpy_output = "np.logical_xor(x_0.astype(bool), x_1.astype(bool))"
_def_sympy_output = "Xor(x_0, x_1)"
_def_torch_output = "torch.logical_xor(x_0.bool(), x_1.bool())"


genome_params = {
"n_inputs": 3,
"primitives": (AND2, OR2, NOT, XOR2),
}

# create population that will be evolved
pop = cgp.Population(genome_params=genome_params)


# %%
# Next, we perform the evolution mostly relying on the libraries default
# hyperparameters.
pop = cgp.evolve(
objective,
pop,
termination_fitness=8.0, # eight rows in truth table, so max fitness is 8
print_progress=True,
)


# %%
# After finishing the evolution, we log the final evolved expression.
f = pop.champion.to_sympy(simplify=True)
print(f"Final expression: {f}")

0 comments on commit bc8ca62

Please sign in to comment.