cartesian
Advanced tools
| build: | ||
| image: latest | ||
| python: | ||
| version: 3.6 | ||
| setup_py_install: true | ||
| requirements_file: requirements-dev.txt |
| <!-- Alabaster (krTheme++) Hacks --> | ||
| <!-- CSS Adjustments (I'm very picky.) --> | ||
| <style type="text/css"> | ||
| /* Rezzy requires precise alignment. */ | ||
| img.logo {margin-left: -20px!important;} | ||
| /* "Quick Search" should be capitalized. */ | ||
| div#searchbox h3 {text-transform: capitalize;} | ||
| /* Make the document a little wider, less code is cut-off. */ | ||
| div.document {width: 1008px;} | ||
| /* Much-improved spacing around code blocks. */ | ||
| div.highlight pre {padding: 11px 14px;} | ||
| /* Remain Responsive! */ | ||
| @media screen and (max-width: 1008px) { | ||
| div.sphinxsidebar {display: none;} | ||
| div.document {width: 100%!important;} | ||
| /* Have code blocks escape the document right-margin. */ | ||
| div.highlight pre {margin-right: -30px;} | ||
| } | ||
| </style> |
| <h3>Useful Links</h3> | ||
| <ul> | ||
| <li><a href="http://github.com/Ohjeah/cartesian">cartesian @ GitHub</a></li> | ||
| <li><a href="http://pypi.python.org/pypi/cartesian">cartesian @ PyPI</a></li> | ||
| <li><a href="http://github.com/Ohjeah/cartesian/issues">Issue Tracker</a></li> | ||
| </ul> |
+46
| import os | ||
| import sys | ||
| import datetime | ||
| sys.path.insert(0, os.path.abspath("../")) | ||
| import cartesian | ||
| project = "Cartesian" | ||
| copyright = "{}, Markus Quade".format(datetime.datetime.now().year) | ||
| author = "Markus Quade" | ||
| version = release = cartesian.__version__ | ||
| master_doc = "index" | ||
| extensions = [ | ||
| "sphinxcontrib.apidoc", | ||
| "sphinx.ext.autodoc", | ||
| "sphinx.ext.todo", | ||
| "sphinx.ext.viewcode", | ||
| "sphinx.ext.autosummary", | ||
| "sphinx.ext.napoleon", | ||
| ] | ||
| apidoc_module_dir = "../cartesian" | ||
| apidoc_excluded_paths = ["tests"] | ||
| autodoc_default_flags = ["members"] | ||
| autodoc_member_order = "bysource" | ||
| autoclass_content = "init" | ||
| language = None | ||
| exclude_patterns = ["_build"] | ||
| pygments_style = "sphinx" | ||
| add_module_names = True | ||
| add_function_parentheses = False | ||
| todo_include_todos = True | ||
| html_theme = "sphinx_rtd_theme" | ||
| html_show_sourcelink = False | ||
| html_show_sphinx = False | ||
| html_show_copyright = True | ||
| default_role = "any" |
| Overview | ||
| -------- | ||
| .. include:: ../README.rst | ||
| API Documentation | ||
| ----------------- | ||
| .. toctree:: | ||
| :maxdepth: 4 | ||
| api/modules | ||
| Indices and tables | ||
| ------------------ | ||
| * :ref:`genindex` | ||
| * :ref:`modindex` | ||
| * :ref:`search` |
| # Minimal makefile for Sphinx documentation | ||
| # | ||
| # You can set these variables from the command line. | ||
| SPHINXOPTS = | ||
| SPHINXBUILD = sphinx-build | ||
| SPHINXPROJ = cartesian | ||
| SOURCEDIR = . | ||
| BUILDDIR = _build | ||
| # Put it first so that "make" without argument is like "make help". | ||
| help: | ||
| @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) | ||
| .PHONY: help Makefile | ||
| # Catch-all target: route all unknown targets to Sphinx using the new | ||
| # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). | ||
| %: Makefile | ||
| @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) |
| [tool.black] | ||
| line-length = 111 | ||
| py36 = true | ||
| include = '\.pyi?$' | ||
| exclude = ''' | ||
| /( | ||
| \.git | ||
| | \.hg | ||
| | \.mypy_cache | ||
| | \.tox | ||
| | \.venv | ||
| | _build | ||
| | buck-out | ||
| | build | ||
| | dist | ||
| )/ | ||
| ''' |
+36
| **cartesian**: is a lightweight implementation of Cartesian genetic | ||
| programming with symbolic regression in mind. | ||
| |image0| |image1| |PyPI| |DOI| |Documentation Status| |Code style: | ||
| black| | ||
| -------------- | ||
| It is meant to be used in conjunction with | ||
| `deap <https://github.com/DEAP/deap>`__ or | ||
| `glyph <https://github.com/Ambrosys/glyph>`__. | ||
| The basic components are provided: | ||
| - data structure | ||
| - 1+4 Algorithm | ||
| - symbolic, ephemeral random and structure-based constants | ||
| Installation | ||
| ------------ | ||
| cartesian is available on PyPI | ||
| ``pip install cartesian`` | ||
| .. |image0| image:: https://travis-ci.org/Ohjeah/cartesian.svg?branch=master | ||
| :target: https://travis-ci.org/Ohjeah/cartesian | ||
| .. |image1| image:: https://codecov.io/gh/Ohjeah/cartesian/branch/master/graph/badge.svg | ||
| :target: https://codecov.io/gh/Ohjeah/cartesian | ||
| .. |PyPI| image:: https://img.shields.io/pypi/v/cartesian.svg | ||
| :target: https://pypi.python.org/pypi/cartesian | ||
| .. |DOI| image:: https://zenodo.org/badge/79949716.svg | ||
| :target: https://zenodo.org/badge/latestdoi/79949716 | ||
| .. |Documentation Status| image:: https://readthedocs.org/projects/cartesian/badge/?version=latest | ||
| :target: https://cartesian.readthedocs.io/en/latest/?badge=latest | ||
| .. |Code style: black| image:: https://img.shields.io/badge/code%20style-black-000000.svg | ||
| :target: https://github.com/ambv/black |
+5
-2
| .pytest_cache | ||
| _build | ||
| .idea | ||
| .vscode | ||
| docs/api | ||
| # Byte-compiled / optimized / DLL files | ||
@@ -7,3 +10,3 @@ __pycache__/ | ||
| *$py.class | ||
| doc | ||
| # C extensions | ||
@@ -10,0 +13,0 @@ *.so |
+5
-1
| language: python | ||
| cache: | ||
| directories: | ||
| - $HOME/.cache/pip | ||
| - tests/.hypothesis | ||
| python: | ||
| - "3.6" | ||
| install: | ||
| - pip install -r requirements-dev.txt | ||
| - pip install --upgrade -r requirements-dev.txt | ||
| script: | ||
@@ -7,0 +11,0 @@ - py.test |
@@ -1,4 +0,4 @@ | ||
| Metadata-Version: 2.1 | ||
| Metadata-Version: 1.2 | ||
| Name: cartesian | ||
| Version: 0.1.7 | ||
| Version: 0.1.8 | ||
| Summary: Minimal cartesian genetic programming for symbolic regression. | ||
@@ -9,23 +9,39 @@ Home-page: https://github.com/ohjeah/cartesian | ||
| License: MIT | ||
| Description: **cartesian**: is a lightweight implementation of Cartesian genetic programming with symbolic regression in mind. | ||
| Description: **cartesian**: is a lightweight implementation of Cartesian genetic | ||
| programming with symbolic regression in mind. | ||
| [](https://travis-ci.org/Ohjeah/cartesian) [](https://codecov.io/gh/Ohjeah/cartesian) [](https://pypi.python.org/pypi/cartesian) [](https://zenodo.org/badge/latestdoi/79949716) | ||
| [](https://github.com/ambv/black) | ||
| |image0| |image1| |PyPI| |DOI| |Documentation Status| |Code style: | ||
| black| | ||
| *** | ||
| -------------- | ||
| It is meant to be used in conjunction with [deap](https://github.com/DEAP/deap) or [glyph](https://github.com/Ambrosys/glyph). | ||
| It is meant to be used in conjunction with | ||
| `deap <https://github.com/DEAP/deap>`__ or | ||
| `glyph <https://github.com/Ambrosys/glyph>`__. | ||
| The basic components are provided: | ||
| - data structure | ||
| - 1+4 Algorithm | ||
| - symbolic, ephemeral random and structure-based constants | ||
| - data structure | ||
| - 1+4 Algorithm | ||
| - symbolic, ephemeral random and structure-based constants | ||
| Installation | ||
| ------------ | ||
| ## Installation | ||
| cartesian is available on PyPI | ||
| `pip install cartesian` | ||
| ``pip install cartesian`` | ||
| .. |image0| image:: https://travis-ci.org/Ohjeah/cartesian.svg?branch=master | ||
| :target: https://travis-ci.org/Ohjeah/cartesian | ||
| .. |image1| image:: https://codecov.io/gh/Ohjeah/cartesian/branch/master/graph/badge.svg | ||
| :target: https://codecov.io/gh/Ohjeah/cartesian | ||
| .. |PyPI| image:: https://img.shields.io/pypi/v/cartesian.svg | ||
| :target: https://pypi.python.org/pypi/cartesian | ||
| .. |DOI| image:: https://zenodo.org/badge/79949716.svg | ||
| :target: https://zenodo.org/badge/latestdoi/79949716 | ||
| .. |Documentation Status| image:: https://readthedocs.org/projects/cartesian/badge/?version=latest | ||
| :target: https://cartesian.readthedocs.io/en/latest/?badge=latest | ||
| .. |Code style: black| image:: https://img.shields.io/badge/code%20style-black-000000.svg | ||
| :target: https://github.com/ambv/black | ||
| Platform: UNKNOWN | ||
@@ -36,5 +52,4 @@ Classifier: Programming Language :: Python | ||
| Classifier: Intended Audience :: Science/Research | ||
| Classifier: License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3) | ||
| Classifier: License :: OSI Approved :: MIT License | ||
| Classifier: Topic :: Scientific/Engineering :: Mathematics | ||
| Requires-Python: >=3.6 | ||
| Description-Content-Type: text/markdown |
@@ -5,1 +5,2 @@ scikit-learn[alldeps] | ||
| numpy | ||
| dataclasses |
@@ -5,6 +5,8 @@ .editorconfig | ||
| .gitignore | ||
| .readthedocs.yml | ||
| .travis.yml | ||
| LICENSE | ||
| MANIFEST.in | ||
| README.md | ||
| README.rst | ||
| pyproject.toml | ||
| requirements-dev.txt | ||
@@ -24,2 +26,7 @@ requirements.txt | ||
| cartesian.egg-info/top_level.txt | ||
| docs/Makefile | ||
| docs/conf.py | ||
| docs/index.rst | ||
| docs/_templates/hacks.html | ||
| docs/_templates/sidebarintro.html | ||
| examples/basic.py | ||
@@ -26,0 +33,0 @@ examples/ephemeral_constant.py |
@@ -7,2 +7,4 @@ from pkg_resources import get_distribution, DistributionNotFound | ||
| pass | ||
| from .sklearn_api import Symbolic | ||
| from .cgp import Primitive, Symbol, Structural, Constant, Ephemeral |
+15
-34
@@ -7,7 +7,8 @@ import math | ||
| from scipy.optimize import OptimizeResult, minimize | ||
| from joblib import Parallel, delayed | ||
| from .cgp import Base, point_mutation, compile, to_polish, Constant | ||
| # from joblib import Parallel, delayed | ||
| from .cgp import point_mutation, compile, to_polish, Constant | ||
| def return_opt_result(f, individual): | ||
@@ -17,5 +18,6 @@ """ | ||
| :param f: `callable(individual` | ||
| :param f: callable(individual) | ||
| :param individual: instance of cartesian.cgp.Base | ||
| :type individual: instance of cartesian.cgp.Cartesian | ||
| :return: OptimizeResult | ||
@@ -30,14 +32,6 @@ """ | ||
| def oneplus( | ||
| fun, | ||
| random_state=None, | ||
| cls=None, | ||
| lambda_=4, | ||
| max_iter=100, | ||
| max_nfev=None, | ||
| f_tol=0, | ||
| n_jobs=1, | ||
| seed=None, | ||
| fun, random_state=None, cls=None, lambda_=4, max_iter=100, max_nfev=None, f_tol=0, n_jobs=1, seed=None | ||
| ): | ||
| """ | ||
| 1 + lambda algorithm. | ||
| """1 + lambda algorithm. | ||
| In each generation, create lambda offspring and compare their fitness to the parent individual. | ||
@@ -47,3 +41,3 @@ The fittest individual carries over to the next generation. In case of a draw, the offspring is prefered. | ||
| :param fun: `callable(individual)`, function to be optimized | ||
| :param fun: callable(individual), function to be optimized | ||
| :param random_state: an instance of np.random.RandomState, a seed integer or None | ||
@@ -59,6 +53,3 @@ :param cls: The base class for individuals | ||
| :return: scipy.optimize.OptimizeResult with non-standard attributes | ||
| res.x = values for constants | ||
| res.expr = expression | ||
| res.fun = best value for the function | ||
| :return: scipy.optimize.OptimizeResult with non-standard attributes res.x = values for constants res.expr = expression res.fun = best value for the function | ||
| """ | ||
@@ -71,5 +62,3 @@ max_iter = max_nfev if max_nfev else max_iter | ||
| nfev = best_res.nfev | ||
| res = OptimizeResult( | ||
| expr=best, x=best_res.x, fun=best_res.fun, nit=0, nfev=nfev, success=False | ||
| ) | ||
| res = OptimizeResult(expr=best, x=best_res.x, fun=best_res.fun, nit=0, nfev=nfev, success=False) | ||
| if best_res.fun <= f_tol: | ||
@@ -80,16 +69,9 @@ res["success"] = True | ||
| for i in range(1, max_iter): | ||
| offspring = [ | ||
| point_mutation(best, random_state=random_state) for _ in range(lambda_) | ||
| ] | ||
| offspring = [point_mutation(best, random_state=random_state) for _ in range(lambda_)] | ||
| # with Parallel(n_jobs=n_jobs) as parallel: | ||
| # offspring_fitness = parallel(delayed(return_opt_result)(fun, o) for o in offspring) | ||
| offspring_fitness = [return_opt_result(fun, o) for o in offspring] | ||
| best, best_res = min( | ||
| zip(offspring + [best], offspring_fitness + [best_res]), | ||
| key=lambda x: x[1].fun, | ||
| ) | ||
| best, best_res = min(zip(offspring + [best], offspring_fitness + [best_res]), key=lambda x: x[1].fun) | ||
| nfev += sum(of.nfev for of in offspring_fitness) | ||
| res = OptimizeResult( | ||
| expr=best, x=best_res.x, fun=best_res.fun, nit=i, nfev=nfev, success=False | ||
| ) | ||
| res = OptimizeResult(expr=best, x=best_res.x, fun=best_res.fun, nit=i, nfev=nfev, success=False) | ||
| if res.fun <= f_tol: | ||
@@ -130,4 +112,3 @@ res["success"] = True | ||
| def optimize_constants(fun): | ||
| """Wrap a measure with constant optimization. | ||
| """ | ||
| """Wrap a measure with constant optimization.""" | ||
@@ -134,0 +115,0 @@ @wraps(fun) |
+154
-134
@@ -6,4 +6,4 @@ import itertools | ||
| from operator import attrgetter | ||
| from collections import namedtuple | ||
| from dataclasses import dataclass | ||
| from sklearn.base import TransformerMixin | ||
@@ -16,6 +16,5 @@ from sklearn.utils.validation import check_random_state | ||
| class Primitive(object): | ||
| """Base class""" | ||
| def __init__(self, name, function, arity): | ||
| """Basic build block for cartesian programs. | ||
| def __init__(self, name, function, arity): | ||
| """ | ||
| :param name: for text representation | ||
@@ -31,69 +30,71 @@ :param function: | ||
| class Symbol(Primitive): | ||
| """ | ||
| Base class for variables. | ||
| Will always be used in the boilerplate ensuring a uniform signature, even if variable is not used in the genotype. | ||
| """ | ||
| arity = 0 | ||
| def __init__(self, name): | ||
| """Base class for variables. | ||
| def __init__(self, name): | ||
| Will always be used in the boilerplate ensuring a uniform signature. | ||
| Even if variable is not used in the genotype. | ||
| Args: | ||
| name: name of the primitive | ||
| """ | ||
| self.name = name | ||
| self.arity = 0 | ||
| class Constant(Symbol): | ||
| """ | ||
| Base class for symbolic constants. | ||
| """Base class for symbolic constants. | ||
| Will be used for constant optimization. | ||
| Boilerplate: will only appear when used. | ||
| """ | ||
| pass | ||
| class Ephemeral(Primitive): | ||
| """ | ||
| Base class for ERC's. | ||
| ERC's are terminals, but they are implemented as zero arity functions, because they do not need to appear in the | ||
| argument list of the lambda expression. | ||
| def __init__(self, name, fun): | ||
| """Base class for ERC's. | ||
| .. Note :: | ||
| ERC's are terminals, but they are implemented as zero arity functions, as they do not need to appear in the | ||
| argument list of the lambda expression. | ||
| Compilation behaviour: Each individual has a dict to store its numeric values. Each position in the code block will | ||
| only execute the function once. Values are lost during copying. | ||
| Note: | ||
| Compilation behaviour: Each individual has a dict to store its numeric values. | ||
| Each position in the code block will only execute the function once. | ||
| Values are lost during copying. | ||
| """ | ||
| def __init__(self, name, function): | ||
| Args: | ||
| name: for text representation | ||
| fun: callback, should return a random numeric values. | ||
| """ | ||
| :param name: for text representation | ||
| :param function: callback(), should return a random numeric values. | ||
| """ | ||
| super().__init__(name, function, 0) | ||
| super().__init__(name, fun, 0) | ||
| class Structural(Primitive): | ||
| """ | ||
| Structural constants are operators which take the graph representation of its arguments | ||
| and convert it to a numeric value. | ||
| """ | ||
| def __init__(self, name, fun, arity): | ||
| """Structural constants are operators which take the graph representation of its arguments | ||
| and convert it to a numeric value. | ||
| def __init__(self, name, function, arity): | ||
| Args: | ||
| name: for text representation | ||
| fun: | ||
| arity: | ||
| """ | ||
| :param name: for text representation | ||
| :param function: | ||
| :param arity: | ||
| """ | ||
| self.name = name | ||
| self._function = function | ||
| self._function = fun | ||
| self.arity = arity | ||
| def function(self, *args): | ||
| return self._function(* map(self.get_len, args)) | ||
| return self._function(*map(self.get_len, args)) | ||
| @staticmethod | ||
| def get_len(expr, tokens="(,"): | ||
| """ Get the length of a tree by parsing its polish notation representation | ||
| Args: | ||
| expr: a formula in polish notation | ||
| tokens: symbols to split the expression at | ||
| Returns: | ||
| length of expr | ||
| """ | ||
| Get the length of a tree by parsing its polish notation representation | ||
| :param expr: | ||
| :param tokens: to split at | ||
| :return: length of expr | ||
| """ | ||
| regex = "|".join("\\{}".format(t) for t in tokens) | ||
@@ -103,36 +104,54 @@ return len(re.split(regex, expr)) | ||
| PrimitiveSet = namedtuple( | ||
| "PrimitiveSet", "operators terminals max_arity mapping imapping context symbols" | ||
| ) | ||
| @dataclass | ||
| class PrimitiveSet: | ||
| """A container holding the primitives and pre-compiled helper attributes. | ||
| Args: | ||
| operators: all non-terminal primitives (arity > 0) | ||
| terminals: all terminals | ||
| max_arity: maximum arity of all terminals. Determines the number of links for each register | ||
| mapping: sorted and indexed list of the primitive set | ||
| imapping: inverse of mapping | ||
| context: links names of primitives to their functions | ||
| symbols: all sybolic constants | ||
| def create_pset(primitives): | ||
| """Create immutable PrimitiveSet with some attributes for quick lookups""" | ||
| terminals = [p for p in primitives if p.arity == 0] | ||
| symbols = [p for p in primitives if isinstance(p, Symbol)] | ||
| non_symbols = [p for p in terminals if not isinstance(p, Symbol)] | ||
| operators = [p for p in primitives if p.arity > 0] | ||
| if operators: | ||
| max_arity = max(operators, key=attrgetter("arity")).arity | ||
| else: | ||
| max_arity = 0 | ||
| mapping = { | ||
| i: prim | ||
| for i, prim in enumerate( | ||
| sorted(symbols, key=attrgetter("name")) + | ||
| sorted(non_symbols, key=attrgetter("name")) + | ||
| sorted(operators, key=attrgetter("name")) | ||
| """ | ||
| operators: list | ||
| terminals: list | ||
| mapping: dict | ||
| imapping: dict | ||
| context: dict | ||
| symbols: list | ||
| max_arity: int | ||
| @classmethod | ||
| def create(cls, primitives): | ||
| """Create immutable PrimitiveSet with some attributes for quick lookups""" | ||
| terminals = [p for p in primitives if p.arity == 0] | ||
| symbols = [p for p in primitives if isinstance(p, Symbol)] | ||
| non_symbols = [p for p in terminals if not isinstance(p, Symbol)] | ||
| operators = [p for p in primitives if p.arity > 0] | ||
| if operators: | ||
| max_arity = max(operators, key=attrgetter("arity")).arity | ||
| else: | ||
| max_arity = 0 | ||
| mapping = { | ||
| i: prim | ||
| for i, prim in enumerate( | ||
| sorted(symbols, key=attrgetter("name")) | ||
| + sorted(non_symbols, key=attrgetter("name")) | ||
| + sorted(operators, key=attrgetter("name")) | ||
| ) | ||
| } | ||
| imapping = {v: k for k, v in mapping.items()} | ||
| context = {f.name: f.function for f in operators} | ||
| return cls( | ||
| operators=operators, | ||
| terminals=terminals, | ||
| imapping=imapping, | ||
| max_arity=max_arity, | ||
| mapping=mapping, | ||
| context=context, | ||
| symbols=symbols, | ||
| ) | ||
| } | ||
| imapping = {v: k for k, v in mapping.items()} | ||
| context = {f.name: f.function for f in operators} | ||
| return PrimitiveSet( | ||
| operators=operators, | ||
| terminals=terminals, | ||
| imapping=imapping, | ||
| max_arity=max_arity, | ||
| mapping=mapping, | ||
| context=context, | ||
| symbols=symbols, | ||
| ) | ||
@@ -179,7 +198,6 @@ | ||
| class Base(TransformerMixin): | ||
| def __init__(self, code, outputs): | ||
| self.n_inputs = len(self.pset.terminals) | ||
| self.inputs = list(range(self.n_inputs)) | ||
| self.symbols = self.inputs[:len(self.pset.symbols)] | ||
| self.symbols = self.inputs[: len(self.pset.symbols)] | ||
| self.code = code | ||
@@ -192,6 +210,4 @@ self.outputs = outputs | ||
| def mapping(self): | ||
| return { | ||
| i: (el, c, r, l) | ||
| for i, el, c, r, l in _make_map(self.inputs, * self.code, self.outputs) | ||
| } | ||
| """Helper dictionary to index the cartesian registers.""" | ||
| return {i: (el, c, r, l) for i, el, c, r, l in _make_map(self.inputs, *self.code, self.outputs)} | ||
@@ -202,6 +218,7 @@ def __getitem__(self, index): | ||
| def __setitem__(self, index, item): | ||
| el, c, r, l = self.mapping[index] | ||
| l[r] = item | ||
| el, c, r, lst = self.mapping[index] | ||
| lst[r] = item | ||
| def __len__(self): | ||
| """Returs the number of registers in self.""" | ||
| return self.n_columns * self.n_rows + self.n_out + self.n_inputs | ||
@@ -228,3 +245,4 @@ | ||
| def format(self, x): | ||
| @staticmethod | ||
| def format(x): | ||
| return "{}".format(x) | ||
@@ -238,10 +256,16 @@ | ||
| def transform(self, x, y=None): | ||
| return self._transform(* x.T) | ||
| return self._transform(*x.T) | ||
| @classmethod | ||
| def create(cls, random_state=None): | ||
| """ | ||
| """Creates a new individual. | ||
| Each gene is picked with a uniform distribution from all allowed inputs or functions. | ||
| :param random_state: an instance of np.random.RandomState, a seed integer or None | ||
| :return: A random new class instance. | ||
| Args: | ||
| random_state: an instance of np.random.RandomState, a seed integer or None | ||
| Returns: | ||
| a new (random) individual | ||
| """ | ||
@@ -263,5 +287,3 @@ random_state = check_random_state(random_state) | ||
| outputs = [ | ||
| random_state.choice( | ||
| cls._valid_inputs[_out_index(cls.n_rows, cls.n_columns, n_in, o)] | ||
| ) | ||
| random_state.choice(cls._valid_inputs[_out_index(cls.n_rows, cls.n_columns, n_in, o)]) | ||
| for o in range(cls.n_out) | ||
@@ -273,10 +295,6 @@ ] | ||
| class Cartesian(type): | ||
| """ | ||
| Meta class to set class parameters and primitive set. | ||
| """ | ||
| """Meta class to set class parameters and primitive set.""" | ||
| def __new__(mcs, name, primitive_set, n_columns=3, n_rows=1, n_back=1, n_out=1): | ||
| valid_inputs = _get_valid_inputs( | ||
| n_rows, n_columns, n_back, len(primitive_set.terminals), n_out | ||
| ) | ||
| valid_inputs = _get_valid_inputs(n_rows, n_columns, n_back, len(primitive_set.terminals), n_out) | ||
| dct = dict( | ||
@@ -295,5 +313,3 @@ pset=primitive_set, | ||
| def __init__(cls, name, primitive_set, n_columns=3, n_rows=1, n_back=1, n_out=1): | ||
| valid_inputs = _get_valid_inputs( | ||
| n_rows, n_columns, n_back, len(primitive_set.terminals), n_out | ||
| ) | ||
| valid_inputs = _get_valid_inputs(n_rows, n_columns, n_back, len(primitive_set.terminals), n_out) | ||
| dct = dict( | ||
@@ -311,10 +327,14 @@ pset=primitive_set, | ||
| def point_mutation(individual, random_state=None): | ||
| """Picks a gene at random and mutates it. | ||
| The mutation is either rewiring, i.e. changing the inputs, or changing the operator (head of gene). | ||
| Args: | ||
| individual: instance of Base | ||
| random_state: an instance of np.random.RandomState, a seed integer or None | ||
| Returns: | ||
| mutated individual | ||
| """ | ||
| Randomly pick a gene in individual and mutate it. | ||
| The mutation is either rewiring, i.e. changing the inputs, or changing the operator (head of gene) | ||
| :param individual: instance of Base | ||
| :type individual: instance of Cartesian | ||
| :param random_state: an instance of np.random.RandomState, a seed integer or None | ||
| :return: new instance of Base | ||
| """ | ||
| random_state = check_random_state(random_state) | ||
@@ -329,5 +349,3 @@ n_terminals = len(individual.pset.terminals) | ||
| if j == 0: # function | ||
| new_j = individual.pset.imapping[ | ||
| random_state.choice(individual.pset.operators) | ||
| ] | ||
| new_j = individual.pset.imapping[random_state.choice(individual.pset.operators)] | ||
| else: # input | ||
@@ -344,14 +362,16 @@ new_j = random_state.choice(individual._valid_inputs[i]) | ||
| def to_polish(c, return_args=True): | ||
| """ | ||
| Return polish notation of expression encoded by c. | ||
| Optionally return the used arguments. | ||
| """Generates the polish notation of expression encoded by c. | ||
| Resolve the outputs recursively. | ||
| Resolves the outputs recursively. | ||
| .. Note :: | ||
| Note: | ||
| Function has side-effects on the individual c. | ||
| See Symbols for details | ||
| :param c: instance of Base | ||
| :type c: instance of Cartesian | ||
| Args: | ||
| c: instance of base | ||
| return_args: optionally return the used arguments too | ||
| Returns: | ||
| polish notation of expression encoded by c | ||
| """ | ||
@@ -380,12 +400,7 @@ primitives = c.pset.mapping | ||
| elif isinstance(primitive, Structural): | ||
| return c.format( | ||
| primitive.function( | ||
| * [h(a) for a, _ in zip(gene, range(primitive.arity))] | ||
| ) | ||
| ) | ||
| 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))), | ||
| primitive.name, ", ".join(h(a) for a, _ in zip(gene, range(primitive.arity))) | ||
| ) | ||
@@ -401,5 +416,4 @@ | ||
| def boilerplate(c, used_arguments=()): | ||
| """ | ||
| Return the overhead needed to compile the polish notation. | ||
| def _boilerplate(c, used_arguments=()): | ||
| """Generates the overhead needed to compile the polish notation. | ||
@@ -409,5 +423,8 @@ If used_arguments are provided, the boilerplate will only include | ||
| :param c: instance of Base | ||
| :type c: instance of Cartesian | ||
| :param used_arguments: list of terminals actually used in c. | ||
| Args: | ||
| c: instance of Base | ||
| used_arguments: list of terminals actually used in c. | ||
| Returns: overhead needed to compile the polish notation. | ||
| """ | ||
@@ -426,7 +443,10 @@ mapping = c.pset.mapping | ||
| def compile(c): | ||
| """Transform an individual into a lambda function | ||
| Args: | ||
| c: instance of Base | ||
| Returns:lambda function | ||
| """ | ||
| :param c: instance of Base | ||
| :type c: instance of Cartesian | ||
| :return: lambda function | ||
| """ | ||
| polish, args = to_polish(c, return_args=True) | ||
@@ -436,4 +456,4 @@ for t in c.pset.symbols: | ||
| args.add(t) | ||
| bp = boilerplate(c, used_arguments=args) | ||
| bp = _boilerplate(c, used_arguments=args) | ||
| code = "({})".format(", ".join(polish)) if len(polish) > 1 else polish[0] | ||
| return eval(bp + code, c.pset.context) |
+52
-38
@@ -6,3 +6,3 @@ import numpy as np | ||
| from .cgp import create_pset, Symbol, Primitive, Constant, compile, Cartesian | ||
| from .cgp import PrimitiveSet, Symbol, Primitive, Constant, compile, Cartesian | ||
| from .algorithm import oneplus, optimize | ||
@@ -18,9 +18,9 @@ | ||
| except: | ||
| except (AttributeError, TypeError, IndexError): | ||
| return np.ones(shape) * yhat | ||
| class evaluate: # ugly construct s.th. you can pickle it and use joblib | ||
| class _Evaluate: # ugly construct s.th. you can pickle it and use joblib | ||
| def __init__(self, x, y, metric): | ||
| """Wraps metric for optimization""" | ||
| self.n_samples, *out = y.shape | ||
@@ -34,7 +34,5 @@ self.multi_output = True if out else False | ||
| if self.multi_output: | ||
| yhat = np.array( | ||
| [_ensure_1d(i, self.n_samples) for i in f(* self.x.T, *consts)] | ||
| ).T | ||
| yhat = np.array([_ensure_1d(i, self.n_samples) for i in f(*self.x.T, *consts)]).T | ||
| else: | ||
| yhat = _ensure_1d(f(* self.x.T, *consts), self.n_samples) | ||
| yhat = _ensure_1d(f(*self.x.T, *consts), self.n_samples) | ||
| return self.metric(self.y, yhat) | ||
@@ -47,4 +45,2 @@ | ||
| class Symbolic(BaseEstimator, RegressorMixin): | ||
| """Wraps the 1 + lambda algorithm in sklearn api""" | ||
| def __init__( | ||
@@ -61,25 +57,28 @@ self, | ||
| f_tol=0, | ||
| seed=None, | ||
| seeded_individual=None, | ||
| random_state=None, | ||
| n_jobs=1, | ||
| metric=mean_squared_error, | ||
| metric=None, | ||
| ): | ||
| """Wraps the 1 + lambda algorithm in sklearn api. | ||
| Note: | ||
| n_costs provides a convenience method to create Symbols. | ||
| All constants can be directly passed via the operators. | ||
| Args: | ||
| operators: list of primitives | ||
| n_const: number of symbolic constants | ||
| n_rows: number of rows in the code block | ||
| n_columns: number of columns in the code block | ||
| n_back: number of rows to look back for connections | ||
| max_iter: maximum number of generations | ||
| max_nfev: maximum number of function evaluations. Important, if fun is another optimizer | ||
| lambda_: number of offspring per generation | ||
| f_tol: Absolute error in metric(ind) between iterations that is acceptable for convergence | ||
| seed: an individual used to hot-start the optimization | ||
| random_state: an instance of np.random.RandomState, an integer used as seed, or None | ||
| n_jobs: number of jobs for joblib embarrassingly easy parallel | ||
| metric: callable(individual), function to be optimized | ||
| """ | ||
| :param operators: list of primitive excluding terminals | ||
| :param n_const: number of symbolic constants | ||
| :param n_rows: number of rows in the code block | ||
| :param n_columns: number of columns in the code block | ||
| :param n_back: number of rows to look back for connections | ||
| :param metric: what to optimize for | ||
| :param fun: `callable(individual)`, function to be optimized | ||
| :param random_state: an instance of np.random.RandomState, a seed integer or None | ||
| :param cls: The base class for individuals | ||
| :type cls: (optional) instance of cartesian.cgp.Cartesian | ||
| :param seed: (optional) can be passed instead of cls. | ||
| :param lambda_: number of offspring per generation | ||
| :param max_iter: maximum number of generations | ||
| :param max_nfev: maximum number of function evaluations. Important, if fun is another optimizer | ||
| :param f_tol: threshold for precision | ||
| :param n_jobs: number of jobs for joblib embarrassingly easy parallel | ||
| """ | ||
| self.operators = DEFAULT_PRIMITIVES or operators | ||
@@ -99,8 +98,17 @@ self.constants = [Constant("c_{}".format(i)) for i in range(n_const)] | ||
| self.f_tol = f_tol | ||
| self.metric = metric | ||
| self.metric = metric if metric is not None else mean_squared_error | ||
| self.random_state = check_random_state(random_state) | ||
| self.n_jobs = n_jobs | ||
| self.seed = seed | ||
| self.seeded_individual = seeded_individual | ||
| def fit(self, x, y): | ||
| """Trains the model given the regression task. | ||
| Args: | ||
| x (np.ndarray): input data matrix for fitting of size (number_of_input_points, number_of_features) | ||
| y (np.ndarray): target data vector for fitting of size (number_of_input_points) | ||
| Returns: self | ||
| """ | ||
| x = check_array(x) | ||
@@ -110,3 +118,3 @@ _, self.n_out = y.reshape(y.shape[0], -1).shape | ||
| terminals = [Symbol("x_{}".format(i)) for i in range(n_features)] | ||
| self.pset = create_pset(self.operators + terminals + self.constants) | ||
| self.pset = PrimitiveSet.create(self.operators + terminals + self.constants) | ||
| cls = Cartesian( | ||
@@ -121,3 +129,3 @@ str(hash(self)), | ||
| self.res = oneplus( | ||
| evaluate(x, y, self.metric), | ||
| _Evaluate(x, y, self.metric), | ||
| random_state=self.random_state, | ||
@@ -130,3 +138,3 @@ cls=cls, | ||
| n_jobs=self.n_jobs, | ||
| seed=self.seed, | ||
| seed=self.seeded_individual, | ||
| ) | ||
@@ -137,8 +145,14 @@ self.model = compile(self.res.expr) | ||
| def predict(self, x): | ||
| """Use the fitted model f to make a prediction. | ||
| Args: | ||
| x: input data matrix for scoring | ||
| Returns: predicted target data vector | ||
| """ | ||
| if self.n_out > 1: | ||
| yhat = np.array( | ||
| [_ensure_1d(i, x.shape[0]) for i in self.model(* x.T, * self.res.x)] | ||
| ).T | ||
| yhat = np.array([_ensure_1d(i, x.shape[0]) for i in self.model(*x.T, *self.res.x)]).T | ||
| else: | ||
| yhat = _ensure_1d(self.model(* x.T, * self.res.x), x.shape[0]) | ||
| yhat = _ensure_1d(self.model(*x.T, *self.res.x), x.shape[0]) | ||
| return yhat |
| def make_it(x): | ||
| """ | ||
| Ensure that x is an iterator. | ||
| """Ensures that x is an iterator. | ||
| If x is not iterable, wrap it as a one-elemened tuple. | ||
@@ -10,3 +10,3 @@ """ | ||
| except TypeError: | ||
| x = x, | ||
| x = (x,) | ||
| return iter(x) |
@@ -13,3 +13,3 @@ import numpy as np | ||
| ] | ||
| pset = create_pset(primitives) | ||
| pset = PrimitiveSet.create(primitives) | ||
| rng = check_random_state(42) | ||
@@ -16,0 +16,0 @@ x = rng.normal(size=(100, 2)) |
@@ -15,3 +15,3 @@ import numpy as np | ||
| ] | ||
| pset = create_pset(primitives) | ||
| pset = PrimitiveSet.create(primitives) | ||
| x = rng.normal(size=(100, 2)) | ||
@@ -18,0 +18,0 @@ y = x[:, 1] * x[:, 0] + 0.3 |
@@ -15,3 +15,3 @@ import numpy as np | ||
| ] | ||
| pset = create_pset(primitives) | ||
| pset = PrimitiveSet.create(primitives) | ||
| x = rng.normal(size=(100, 2)) | ||
@@ -18,0 +18,0 @@ y = x[:, 1] * x[:, 0] + 0.3 |
@@ -15,3 +15,3 @@ import numpy as np | ||
| ] | ||
| pset = create_pset(primitives) | ||
| pset = PrimitiveSet.create(primitives) | ||
| rng = check_random_state(42) | ||
@@ -18,0 +18,0 @@ x = rng.normal(size=(100, 2)) |
+30
-15
@@ -1,4 +0,4 @@ | ||
| Metadata-Version: 2.1 | ||
| Metadata-Version: 1.2 | ||
| Name: cartesian | ||
| Version: 0.1.7 | ||
| Version: 0.1.8 | ||
| Summary: Minimal cartesian genetic programming for symbolic regression. | ||
@@ -9,23 +9,39 @@ Home-page: https://github.com/ohjeah/cartesian | ||
| License: MIT | ||
| Description: **cartesian**: is a lightweight implementation of Cartesian genetic programming with symbolic regression in mind. | ||
| Description: **cartesian**: is a lightweight implementation of Cartesian genetic | ||
| programming with symbolic regression in mind. | ||
| [](https://travis-ci.org/Ohjeah/cartesian) [](https://codecov.io/gh/Ohjeah/cartesian) [](https://pypi.python.org/pypi/cartesian) [](https://zenodo.org/badge/latestdoi/79949716) | ||
| [](https://github.com/ambv/black) | ||
| |image0| |image1| |PyPI| |DOI| |Documentation Status| |Code style: | ||
| black| | ||
| *** | ||
| -------------- | ||
| It is meant to be used in conjunction with [deap](https://github.com/DEAP/deap) or [glyph](https://github.com/Ambrosys/glyph). | ||
| It is meant to be used in conjunction with | ||
| `deap <https://github.com/DEAP/deap>`__ or | ||
| `glyph <https://github.com/Ambrosys/glyph>`__. | ||
| The basic components are provided: | ||
| - data structure | ||
| - 1+4 Algorithm | ||
| - symbolic, ephemeral random and structure-based constants | ||
| - data structure | ||
| - 1+4 Algorithm | ||
| - symbolic, ephemeral random and structure-based constants | ||
| Installation | ||
| ------------ | ||
| ## Installation | ||
| cartesian is available on PyPI | ||
| `pip install cartesian` | ||
| ``pip install cartesian`` | ||
| .. |image0| image:: https://travis-ci.org/Ohjeah/cartesian.svg?branch=master | ||
| :target: https://travis-ci.org/Ohjeah/cartesian | ||
| .. |image1| image:: https://codecov.io/gh/Ohjeah/cartesian/branch/master/graph/badge.svg | ||
| :target: https://codecov.io/gh/Ohjeah/cartesian | ||
| .. |PyPI| image:: https://img.shields.io/pypi/v/cartesian.svg | ||
| :target: https://pypi.python.org/pypi/cartesian | ||
| .. |DOI| image:: https://zenodo.org/badge/79949716.svg | ||
| :target: https://zenodo.org/badge/latestdoi/79949716 | ||
| .. |Documentation Status| image:: https://readthedocs.org/projects/cartesian/badge/?version=latest | ||
| :target: https://cartesian.readthedocs.io/en/latest/?badge=latest | ||
| .. |Code style: black| image:: https://img.shields.io/badge/code%20style-black-000000.svg | ||
| :target: https://github.com/ambv/black | ||
| Platform: UNKNOWN | ||
@@ -36,5 +52,4 @@ Classifier: Programming Language :: Python | ||
| Classifier: Intended Audience :: Science/Research | ||
| Classifier: License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3) | ||
| Classifier: License :: OSI Approved :: MIT License | ||
| Classifier: Topic :: Scientific/Engineering :: Mathematics | ||
| Requires-Python: >=3.6 | ||
| Description-Content-Type: text/markdown |
@@ -9,2 +9,6 @@ -e . | ||
| twine>=1.11.0rc1 | ||
| setuptools>=38.6.0 | ||
| setuptools>=38.6.0 | ||
| sphinx | ||
| #alabaster | ||
| sphinx_rtd_theme | ||
| sphinxcontrib-apidoc |
+1
-0
@@ -5,1 +5,2 @@ scikit-learn[alldeps] | ||
| numpy | ||
| dataclasses |
+2
-3
@@ -17,3 +17,3 @@ from pathlib import Path | ||
| "Intended Audience :: Science/Research", | ||
| "License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)", | ||
| "License :: OSI Approved :: MIT License", | ||
| "Topic :: Scientific/Engineering :: Mathematics", | ||
@@ -23,3 +23,3 @@ ] | ||
| REQUIRED = f.readlines() | ||
| with open(CURRENT_DIR / "README.md", "r", encoding="utf8") as f: | ||
| with open(CURRENT_DIR / "README.rst", "r", encoding="utf8") as f: | ||
| LONG_DESCRIPTION = f.read() | ||
@@ -30,3 +30,2 @@ setup( | ||
| long_description=LONG_DESCRIPTION, | ||
| long_description_content_type="text/markdown", | ||
| author=AUTHOR, | ||
@@ -33,0 +32,0 @@ author_email=EMAIL, |
@@ -12,3 +12,3 @@ import operator | ||
| operators = [Primitive("neg", operator.neg, 1)] | ||
| pset = create_pset(terminals + operators) | ||
| pset = PrimitiveSet.create(terminals + operators) | ||
| return pset | ||
@@ -15,0 +15,0 @@ |
@@ -12,7 +12,7 @@ from sklearn.utils.validation import check_random_state | ||
| res = oneplus(fun, random_state=rng, lambda_=4, max_iter=2, f_tol=-1, cls=cls) | ||
| assert res.success == False | ||
| assert not res.success | ||
| res = oneplus(fun, random_state=rng, lambda_=4, max_iter=0, f_tol=0, cls=cls) | ||
| assert res.success == True | ||
| assert res.success | ||
| res = oneplus(fun, random_state=rng, lambda_=4, max_nfev=1, f_tol=-1, cls=cls) | ||
| assert res.success == False | ||
| assert not res.success | ||
@@ -27,3 +27,3 @@ | ||
| def fun(f, consts=()): | ||
| return np.sum((y - f(* x.T, *consts))) | ||
| return np.sum((y - f(*x.T, *consts))) | ||
@@ -43,5 +43,5 @@ res = oneplus(fun, seed=individual) | ||
| def fun(f, consts=()): | ||
| return np.sum((f(* x.T, consts)) ** 2) | ||
| return np.sum((f(*x.T, consts)) ** 2) | ||
| res = fun(ind) | ||
| assert res.x < 1e-6 |
@@ -10,3 +10,3 @@ import operator | ||
| from cartesian.cgp import * | ||
| from cartesian.cgp import _get_valid_inputs | ||
| from cartesian.cgp import _get_valid_inputs, _boilerplate | ||
@@ -49,6 +49,4 @@ | ||
| def test_boilerplate(individual): | ||
| assert boilerplate(individual) == "lambda x_0, x_1, c:" | ||
| assert boilerplate( | ||
| individual, used_arguments=[individual.pset.terminals[0]] | ||
| ) == "lambda x_0:" | ||
| assert _boilerplate(individual) == "lambda x_0, x_1, c:" | ||
| assert _boilerplate(individual, used_arguments=[individual.pset.terminals[0]]) == "lambda x_0:" | ||
@@ -100,3 +98,3 @@ | ||
| operators = [Ephemeral("c", random.random)] | ||
| pset = create_pset(terminals + operators) | ||
| pset = PrimitiveSet.create(terminals + operators) | ||
| MyClass = Cartesian("MyClass", pset) | ||
@@ -118,3 +116,3 @@ ind1 = MyClass([[2, 0]], [2]) | ||
| primitives = [Symbol("x_0"), sc] | ||
| pset = create_pset(primitives) | ||
| pset = PrimitiveSet.create(primitives) | ||
| MyClass = Cartesian("MyClass", pset) | ||
@@ -121,0 +119,0 @@ ind = MyClass([[[1, 0, 0]], [[1, 0, 0]], [[1, 0, 0]]], [2]) |
-20
| **cartesian**: is a lightweight implementation of Cartesian genetic programming with symbolic regression in mind. | ||
| [](https://travis-ci.org/Ohjeah/cartesian) [](https://codecov.io/gh/Ohjeah/cartesian) [](https://pypi.python.org/pypi/cartesian) [](https://zenodo.org/badge/latestdoi/79949716) | ||
| [](https://github.com/ambv/black) | ||
| *** | ||
| It is meant to be used in conjunction with [deap](https://github.com/DEAP/deap) or [glyph](https://github.com/Ambrosys/glyph). | ||
| The basic components are provided: | ||
| - data structure | ||
| - 1+4 Algorithm | ||
| - symbolic, ephemeral random and structure-based constants | ||
| ## Installation | ||
| cartesian is available on PyPI | ||
| `pip install cartesian` |
Alert delta unavailable
Currently unable to show alert delta for PyPI packages.
48407
13.87%40
21.21%957
2.03%