From 10a6f4e8ce087c926e4a3f9177d7c8f2beb8d4aa Mon Sep 17 00:00:00 2001 From: Henrik Mettler Date: Thu, 13 Jan 2022 10:38:09 +0100 Subject: [PATCH 1/8] Compare set expression for output to target expression (optional) --- cgp/genome.py | 18 ++++++++++++++++-- test/test_genome.py | 15 +++++++++++++-- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/cgp/genome.py b/cgp/genome.py index db72b9be..70f6fa23 100644 --- a/cgp/genome.py +++ b/cgp/genome.py @@ -2,6 +2,7 @@ from typing import Dict, Generator, List, Optional, Set, Tuple, Type import numpy as np +import sympy from .cartesian_graph import CartesianGraph from .node import Node, OperatorNode @@ -359,8 +360,8 @@ def change_address_gene_of_output_node(self, new_address: int, output_node_idx: self.dna = dna def set_expression_for_output( - self, dna_insert: List[int], hidden_start_node: int = 0, output_node_idx: int = 0 - ): + self, dna_insert: List[int], hidden_start_node: int = 0, output_node_idx: int = 0, + target_expression: Optional[str] = None): """Set an expression for one output node Replaces part of the dna with user defined values starting from the specified hidden @@ -378,6 +379,9 @@ def set_expression_for_output( Index of the output node which will read the last node of the insert. Relative to the first output node. Defaults to 0. + target_expression: str, optional + Expression the output node should compile to. Numbers must be written as float. + Defaults to None. Returns ---------- None @@ -388,6 +392,16 @@ def set_expression_for_output( self.change_address_gene_of_output_node( new_address=last_inserted_node, output_node_idx=output_node_idx ) + if target_expression is not None: + if self._n_outputs > 1: + output_as_sympy = CartesianGraph(self).to_sympy()[output_node_idx] + else: + output_as_sympy = CartesianGraph(self).to_sympy() + + target_expression_as_sympy = sympy.parse_expr(target_expression) + if not output_as_sympy == target_expression_as_sympy: + print(target_expression_as_sympy, output_as_sympy) + raise ValueError('Target expression and set output expression do not match') def reorder(self, rng: np.random.RandomState) -> None: """Reorder the genome diff --git a/test/test_genome.py b/test/test_genome.py index 34ae3b47..c3d8a108 100644 --- a/test/test_genome.py +++ b/test/test_genome.py @@ -843,9 +843,20 @@ def test_set_expression_for_output(genome_params, rng): assert CartesianGraph(genome).to_sympy() == x_0 + x_1 new_dna = [1, 0, 1] - genome.set_expression_for_output(new_dna) + genome.set_expression_for_output(dna_insert=new_dna, target_expression=' x_0 - x_1') assert CartesianGraph(genome).to_sympy() == x_0 - x_1 new_dna = [0, 0, 1, 2, 0, 0, 1, 0, 0, 0, 2, 3] # x_0+x_1; 1.0; 0; x_0+x_1 + 1.0 - genome.set_expression_for_output(new_dna) + genome.set_expression_for_output(dna_insert=new_dna, target_expression=' x_0 + x_1 + 1.0') assert CartesianGraph(genome).to_sympy() == x_0 + x_1 + 1.0 + + with pytest.raises(ValueError): + genome.set_expression_for_output(dna_insert=new_dna, target_expression=' x_0 + x_1 + 1') + + genome2_params = {'n_inputs': 2, 'n_outputs': 2, "primitives": (cgp.Add, cgp.Sub, cgp.ConstantFloat), +} + genome2 = cgp.Genome(**genome2_params) + genome2.randomize(rng) + + genome2.set_expression_for_output(new_dna, output_node_idx=1, target_expression=' x_0 + x_1 + 1.0') + assert CartesianGraph(genome2).to_sympy()[1] == x_0 + x_1 + 1.0 From 6f397a7776be0416fac5dc8361ad86f732e0c1d8 Mon Sep 17 00:00:00 2001 From: Henrik Mettler Date: Thu, 13 Jan 2022 10:52:56 +0100 Subject: [PATCH 2/8] black --- cgp/genome.py | 10 +++++++--- test/test_genome.py | 17 +++++++++++------ 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/cgp/genome.py b/cgp/genome.py index 70f6fa23..8948fc6c 100644 --- a/cgp/genome.py +++ b/cgp/genome.py @@ -360,8 +360,12 @@ def change_address_gene_of_output_node(self, new_address: int, output_node_idx: self.dna = dna def set_expression_for_output( - self, dna_insert: List[int], hidden_start_node: int = 0, output_node_idx: int = 0, - target_expression: Optional[str] = None): + self, + dna_insert: List[int], + hidden_start_node: int = 0, + output_node_idx: int = 0, + target_expression: Optional[str] = None, + ): """Set an expression for one output node Replaces part of the dna with user defined values starting from the specified hidden @@ -401,7 +405,7 @@ def set_expression_for_output( target_expression_as_sympy = sympy.parse_expr(target_expression) if not output_as_sympy == target_expression_as_sympy: print(target_expression_as_sympy, output_as_sympy) - raise ValueError('Target expression and set output expression do not match') + raise ValueError("Target expression and set output expression do not match") def reorder(self, rng: np.random.RandomState) -> None: """Reorder the genome diff --git a/test/test_genome.py b/test/test_genome.py index c3d8a108..c41e933d 100644 --- a/test/test_genome.py +++ b/test/test_genome.py @@ -843,20 +843,25 @@ def test_set_expression_for_output(genome_params, rng): assert CartesianGraph(genome).to_sympy() == x_0 + x_1 new_dna = [1, 0, 1] - genome.set_expression_for_output(dna_insert=new_dna, target_expression=' x_0 - x_1') + genome.set_expression_for_output(dna_insert=new_dna, target_expression=" x_0 - x_1") assert CartesianGraph(genome).to_sympy() == x_0 - x_1 new_dna = [0, 0, 1, 2, 0, 0, 1, 0, 0, 0, 2, 3] # x_0+x_1; 1.0; 0; x_0+x_1 + 1.0 - genome.set_expression_for_output(dna_insert=new_dna, target_expression=' x_0 + x_1 + 1.0') + genome.set_expression_for_output(dna_insert=new_dna, target_expression=" x_0 + x_1 + 1.0") assert CartesianGraph(genome).to_sympy() == x_0 + x_1 + 1.0 with pytest.raises(ValueError): - genome.set_expression_for_output(dna_insert=new_dna, target_expression=' x_0 + x_1 + 1') + genome.set_expression_for_output(dna_insert=new_dna, target_expression=" x_0 + x_1 + 1") - genome2_params = {'n_inputs': 2, 'n_outputs': 2, "primitives": (cgp.Add, cgp.Sub, cgp.ConstantFloat), -} + genome2_params = { + "n_inputs": 2, + "n_outputs": 2, + "primitives": (cgp.Add, cgp.Sub, cgp.ConstantFloat), + } genome2 = cgp.Genome(**genome2_params) genome2.randomize(rng) - genome2.set_expression_for_output(new_dna, output_node_idx=1, target_expression=' x_0 + x_1 + 1.0') + genome2.set_expression_for_output( + new_dna, output_node_idx=1, target_expression=" x_0 + x_1 + 1.0" + ) assert CartesianGraph(genome2).to_sympy()[1] == x_0 + x_1 + 1.0 From bc3b8be261a4fa0801300056e75fe02e06baeba1 Mon Sep 17 00:00:00 2001 From: Henrik Mettler Date: Thu, 13 Jan 2022 11:06:03 +0100 Subject: [PATCH 3/8] make sympy import in genome conditional --- cgp/genome.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/cgp/genome.py b/cgp/genome.py index 8948fc6c..2947821a 100644 --- a/cgp/genome.py +++ b/cgp/genome.py @@ -2,12 +2,18 @@ from typing import Dict, Generator, List, Optional, Set, Tuple, Type import numpy as np -import sympy from .cartesian_graph import CartesianGraph from .node import Node, OperatorNode from .primitives import Primitives +try: + import sympy + + sympy_available = False +except ModuleNotFoundError: + sympy_available = True + try: import torch # noqa: F401 From b9297aeae50e2524d6c6f3b9935b92ed9db1ffca Mon Sep 17 00:00:00 2001 From: Henrik Mettler Date: Thu, 13 Jan 2022 15:12:31 +0100 Subject: [PATCH 4/8] Apply PR-FB: Delay sympy import into, make target_expression non optional, add test case --- cgp/genome.py | 38 ++++++++++++++++++-------------------- test/test_genome.py | 16 +++++++++------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/cgp/genome.py b/cgp/genome.py index 2947821a..cb28fa2f 100644 --- a/cgp/genome.py +++ b/cgp/genome.py @@ -7,13 +7,6 @@ from .node import Node, OperatorNode from .primitives import Primitives -try: - import sympy - - sympy_available = False -except ModuleNotFoundError: - sympy_available = True - try: import torch # noqa: F401 @@ -368,9 +361,9 @@ def change_address_gene_of_output_node(self, new_address: int, output_node_idx: def set_expression_for_output( self, dna_insert: List[int], + target_expression: str, hidden_start_node: int = 0, output_node_idx: int = 0, - target_expression: Optional[str] = None, ): """Set an expression for one output node @@ -381,6 +374,9 @@ def set_expression_for_output( ---------- dna_insert: List[int] dna segment to be inserted at the first hidden nodes. + target_expression: str + Expression the output node should compile to. Numbers must be written as float. + Defaults to None. hidden_start_node: int Index of the hidden node, where the insert starts. Relative to the first hidden node. @@ -389,9 +385,6 @@ def set_expression_for_output( Index of the output node which will read the last node of the insert. Relative to the first output node. Defaults to 0. - target_expression: str, optional - Expression the output node should compile to. Numbers must be written as float. - Defaults to None. Returns ---------- None @@ -402,16 +395,21 @@ def set_expression_for_output( self.change_address_gene_of_output_node( new_address=last_inserted_node, output_node_idx=output_node_idx ) - if target_expression is not None: - if self._n_outputs > 1: - output_as_sympy = CartesianGraph(self).to_sympy()[output_node_idx] - else: - output_as_sympy = CartesianGraph(self).to_sympy() + try: + import sympy + + if target_expression is not None: + if self._n_outputs > 1: + output_as_sympy = CartesianGraph(self).to_sympy()[output_node_idx] + else: + output_as_sympy = CartesianGraph(self).to_sympy() + + target_expression_as_sympy = sympy.parse_expr(target_expression) + if not output_as_sympy == target_expression_as_sympy: + raise ValueError("expression of output and target expression do not match") - target_expression_as_sympy = sympy.parse_expr(target_expression) - if not output_as_sympy == target_expression_as_sympy: - print(target_expression_as_sympy, output_as_sympy) - raise ValueError("Target expression and set output expression do not match") + except ModuleNotFoundError: + raise Warning("Sympy not available, can not compare written output to target") def reorder(self, rng: np.random.RandomState) -> None: """Reorder the genome diff --git a/test/test_genome.py b/test/test_genome.py index c41e933d..c6afd7dd 100644 --- a/test/test_genome.py +++ b/test/test_genome.py @@ -835,23 +835,25 @@ def test_set_expression_for_output(genome_params, rng): genome = cgp.Genome(**genome_params) genome.randomize(rng) - new_dna = [0, 0, 1] - genome.set_expression_for_output(new_dna) - x_0 = sympy.symbols("x_0") x_1 = sympy.symbols("x_1") + + new_dna = [0, 0, 1] + genome.set_expression_for_output(new_dna, target_expression="x_0 + x_1") assert CartesianGraph(genome).to_sympy() == x_0 + x_1 new_dna = [1, 0, 1] - genome.set_expression_for_output(dna_insert=new_dna, target_expression=" x_0 - x_1") + genome.set_expression_for_output(dna_insert=new_dna, target_expression="x_0 - x_1") assert CartesianGraph(genome).to_sympy() == x_0 - x_1 new_dna = [0, 0, 1, 2, 0, 0, 1, 0, 0, 0, 2, 3] # x_0+x_1; 1.0; 0; x_0+x_1 + 1.0 - genome.set_expression_for_output(dna_insert=new_dna, target_expression=" x_0 + x_1 + 1.0") + genome.set_expression_for_output(dna_insert=new_dna, target_expression="x_0 + x_1 + 1.0") assert CartesianGraph(genome).to_sympy() == x_0 + x_1 + 1.0 with pytest.raises(ValueError): - genome.set_expression_for_output(dna_insert=new_dna, target_expression=" x_0 + x_1 + 1") + # setting an int in the str causes an error, since to_sympy() will always treat variables as float + genome.set_expression_for_output(dna_insert=new_dna, target_expression="x_0 + x_1 + 1") + genome.set_expression_for_output(dna_insert=new_dna, target_expression="x_0 + x_1 8 1.0") genome2_params = { "n_inputs": 2, @@ -862,6 +864,6 @@ def test_set_expression_for_output(genome_params, rng): genome2.randomize(rng) genome2.set_expression_for_output( - new_dna, output_node_idx=1, target_expression=" x_0 + x_1 + 1.0" + new_dna, output_node_idx=1, target_expression="x_0 + x_1 + 1.0" ) assert CartesianGraph(genome2).to_sympy()[1] == x_0 + x_1 + 1.0 From 431eef9a14c0d2af5917fb61889d0467db490a30 Mon Sep 17 00:00:00 2001 From: Henrik Mettler Date: Thu, 13 Jan 2022 15:16:23 +0100 Subject: [PATCH 5/8] prettify str --- test/test_genome.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_genome.py b/test/test_genome.py index c6afd7dd..cdbe70f0 100644 --- a/test/test_genome.py +++ b/test/test_genome.py @@ -851,7 +851,7 @@ def test_set_expression_for_output(genome_params, rng): assert CartesianGraph(genome).to_sympy() == x_0 + x_1 + 1.0 with pytest.raises(ValueError): - # setting an int in the str causes an error, since to_sympy() will always treat variables as float + # setting an int in the str causes an error genome.set_expression_for_output(dna_insert=new_dna, target_expression="x_0 + x_1 + 1") genome.set_expression_for_output(dna_insert=new_dna, target_expression="x_0 + x_1 8 1.0") From 8b24d9a4297ace6f9905b2f64f1ba8a76c9707c5 Mon Sep 17 00:00:00 2001 From: Henrik Mettler Date: Tue, 18 Jan 2022 21:33:09 +0100 Subject: [PATCH 6/8] Raise Error if sympy not available, shorten try block --- cgp/genome.py | 20 ++++++++++---------- test/test_genome.py | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/cgp/genome.py b/cgp/genome.py index cb28fa2f..4ecb18c9 100644 --- a/cgp/genome.py +++ b/cgp/genome.py @@ -398,18 +398,18 @@ def set_expression_for_output( try: import sympy - if target_expression is not None: - if self._n_outputs > 1: - output_as_sympy = CartesianGraph(self).to_sympy()[output_node_idx] - else: - output_as_sympy = CartesianGraph(self).to_sympy() + except ModuleNotFoundError: + raise ModuleNotFoundError("Can not check output expression. No module named 'sympy' (extra requirement)") - target_expression_as_sympy = sympy.parse_expr(target_expression) - if not output_as_sympy == target_expression_as_sympy: - raise ValueError("expression of output and target expression do not match") + if target_expression is not None: + if self._n_outputs > 1: + output_as_sympy = CartesianGraph(self).to_sympy()[output_node_idx] + else: + output_as_sympy = CartesianGraph(self).to_sympy() - except ModuleNotFoundError: - raise Warning("Sympy not available, can not compare written output to target") + target_expression_as_sympy = sympy.parse_expr(target_expression) + if not output_as_sympy == target_expression_as_sympy: + raise ValueError("expression of output and target expression do not match") def reorder(self, rng: np.random.RandomState) -> None: """Reorder the genome diff --git a/test/test_genome.py b/test/test_genome.py index cdbe70f0..ffeb43ef 100644 --- a/test/test_genome.py +++ b/test/test_genome.py @@ -853,7 +853,7 @@ def test_set_expression_for_output(genome_params, rng): with pytest.raises(ValueError): # setting an int in the str causes an error genome.set_expression_for_output(dna_insert=new_dna, target_expression="x_0 + x_1 + 1") - genome.set_expression_for_output(dna_insert=new_dna, target_expression="x_0 + x_1 8 1.0") + genome.set_expression_for_output(dna_insert=new_dna, target_expression="x_0 + x_1 * 1.0") genome2_params = { "n_inputs": 2, From b5917ebad4725909f43312e692c3f528f40f3663 Mon Sep 17 00:00:00 2001 From: Henrik Mettler Date: Tue, 18 Jan 2022 21:38:58 +0100 Subject: [PATCH 7/8] black --- cgp/genome.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cgp/genome.py b/cgp/genome.py index 4ecb18c9..88c3a6c6 100644 --- a/cgp/genome.py +++ b/cgp/genome.py @@ -399,7 +399,9 @@ def set_expression_for_output( import sympy except ModuleNotFoundError: - raise ModuleNotFoundError("Can not check output expression. No module named 'sympy' (extra requirement)") + raise ModuleNotFoundError( + "Can not check output expression. No module named 'sympy' (extra requirement)" + ) if target_expression is not None: if self._n_outputs > 1: From d3ddd38b2eeb7123838faa83ce427ace2d368521 Mon Sep 17 00:00:00 2001 From: Henrik Mettler Date: Mon, 28 Feb 2022 17:50:17 +0100 Subject: [PATCH 8/8] Remove traces of old target_expression defaults to None --- cgp/genome.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/cgp/genome.py b/cgp/genome.py index 88c3a6c6..9b48a5ad 100644 --- a/cgp/genome.py +++ b/cgp/genome.py @@ -376,7 +376,6 @@ def set_expression_for_output( dna segment to be inserted at the first hidden nodes. target_expression: str Expression the output node should compile to. Numbers must be written as float. - Defaults to None. hidden_start_node: int Index of the hidden node, where the insert starts. Relative to the first hidden node. @@ -403,15 +402,14 @@ def set_expression_for_output( "Can not check output expression. No module named 'sympy' (extra requirement)" ) - if target_expression is not None: - if self._n_outputs > 1: - output_as_sympy = CartesianGraph(self).to_sympy()[output_node_idx] - else: - output_as_sympy = CartesianGraph(self).to_sympy() + if self._n_outputs > 1: + output_as_sympy = CartesianGraph(self).to_sympy()[output_node_idx] + else: + output_as_sympy = CartesianGraph(self).to_sympy() - target_expression_as_sympy = sympy.parse_expr(target_expression) - if not output_as_sympy == target_expression_as_sympy: - raise ValueError("expression of output and target expression do not match") + target_expression_as_sympy = sympy.parse_expr(target_expression) + if not output_as_sympy == target_expression_as_sympy: + raise ValueError("expression of output and target expression do not match") def reorder(self, rng: np.random.RandomState) -> None: """Reorder the genome