From dcf1ac9fc452bdbff01c5a3083ffdab2c2528da9 Mon Sep 17 00:00:00 2001 From: mq Date: Mon, 31 Jul 2017 15:04:10 +0200 Subject: [PATCH] structural constants & tests --- cartesian/cgp.py | 27 +++++++++++++++++++++++++-- examples/structural_constant.py | 32 ++++++++++++++++++++++++++++++++ tests/conftest.py | 5 +++++ tests/test_cgp.py | 12 ++++++++++++ 4 files changed, 74 insertions(+), 2 deletions(-) create mode 100644 examples/structural_constant.py diff --git a/cartesian/cgp.py b/cartesian/cgp.py index 2bae657..bd159c8 100644 --- a/cartesian/cgp.py +++ b/cartesian/cgp.py @@ -1,6 +1,7 @@ import itertools import copy import sys +import re from operator import attrgetter from collections import namedtuple @@ -22,6 +23,7 @@ class Terminal(Primitive): def __init__(self, name): self.name = name + class Constant(Terminal): pass @@ -30,6 +32,21 @@ class Ephemeral(Primitive): def __init__(self, name, function): super().__init__(name, function, 0) + +class Structual(Primitive): + def __init__(self, name, function, arity): + self.name = name + self._function = function + self.arity = arity + + def function(self, *args): + return self._function(*map(self.get_len, args)) + + @staticmethod + def get_len(expr, tokens=("(,")): + regex = "|".join("\\{}".format(t) for t in tokens) + return len(re.split(regex, expr)) + # class PrimitiveSet: # def __init__(self, primitives): # self.operators = [p for p in primitives if p.arity > 0] @@ -111,6 +128,9 @@ def __copy__(self): def clone(self): return copy.copy(self) + def format(self, x): + return "{}".format(x) + def fit(self, x, y=None, **fit_params): self._transform = compile(self) self.fit_params = fit_params @@ -184,17 +204,20 @@ def h(g): gene = make_it(c[g]) primitive = primitives[next(gene)] + # refactor to primitive.format() ? side-effects? if primitive.arity == 0: if isinstance(primitive, Terminal): used_arguments.add(primitive) elif isinstance(primitive, Ephemeral): if g not in c.memory: - c.memory[g] = "{0:.2f}".format(primitive.function()) + c.memory[g] = c.format(primitive.function()) return c.memory[g] - return primitive.name + elif isinstance(primitive, Structual): + return c.format(primitive.function(*[h(a) for a, _ in zip(gene, range(primitive.arity))])) + else: return "{}({})".format(primitive.name, ", ".join(h(a) for a, _ in zip(gene, range(primitive.arity)))) diff --git a/examples/structural_constant.py b/examples/structural_constant.py new file mode 100644 index 0000000..b559a54 --- /dev/null +++ b/examples/structural_constant.py @@ -0,0 +1,32 @@ +import numpy as np +from sklearn.utils.validation import check_random_state + +from cartesian.algorithm import oneplus +from cartesian.cgp import * + +rng = check_random_state(1337) + +primitives = [ + Primitive("add", np.add, 2), + Primitive("mul", np.multiply, 2), + Terminal("x_0"), + Terminal("x_1"), + Structual("SC", (lambda x, y: min(x, y)/max(x, y)), 2), +] + +pset = create_pset(primitives) + +x = rng.normal(size=(100, 2)) +y = x[:, 1] * x[:, 0] + 0.3 +y += 0.05 * rng.normal(size=y.shape) + + +def func(individual): + f = compile(individual) + yhat = f(*x.T) + return np.sqrt(np.mean((y - yhat)**2))/(y.max() - y.min()) + + +MyCartesian = Cartesian("MyCartesian", pset, n_rows=3, n_columns=4, n_out=1, n_back=1) +res = oneplus(func, cls=MyCartesian, f_tol=0.01, random_state=rng, max_nfev=50000, n_jobs=1) +print(res) diff --git a/tests/conftest.py b/tests/conftest.py index 2052c53..7db6ace 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -20,3 +20,8 @@ def individual(request): code = [[[3, 1]]] outputs = [3] return MyCartesian(code, outputs) + +@pytest.fixture +def sc(): + s = Structual("SC", (lambda x, y: x/y), 2) + return s diff --git a/tests/test_cgp.py b/tests/test_cgp.py index 3320c84..34563bc 100644 --- a/tests/test_cgp.py +++ b/tests/test_cgp.py @@ -96,3 +96,15 @@ def test_ephemeral_constant(): assert s1 != s2 ind3 = point_mutation(ind1) assert not ind3.memory # empty dict + + +def test_structural_constant_cls(sc): + assert 0.5 == sc.function("x", "f(x)") + +def test_structural_constant_to_polish(sc): + primitives = [Terminal("x_0"), sc] + pset = create_pset(primitives) + + MyClass = Cartesian("MyClass", pset) + ind = MyClass([[[1, 0, 0]], [[1, 0, 0]], [[1, 0, 0]]], [2]) + assert to_polish(ind, return_args=False) == ["1.0"]