Example #1
0
    def setUp(self):
        # config and mutation
        self.config = {
            "cartesian": {
                "rows": 4,
                "columns": 4,
                "levels_back": 2,
                "num_inputs": 4,
                "num_outputs": 4
            },
            "function_nodes": [{
                "type": "FUNCTION",
                "name": "ADD",
                "arity": 2
            }, {
                "type": "FUNCTION",
                "name": "SUB",
                "arity": 2
            }, {
                "type": "FUNCTION",
                "name": "MUL",
                "arity": 2
            }, {
                "type": "FUNCTION",
                "name": "DIV",
                "arity": 2
            }, {
                "type": "FUNCTION",
                "name": "COS",
                "arity": 1
            }, {
                "type": "FUNCTION",
                "name": "SIN",
                "arity": 1
            }, {
                "type": "FUNCTION",
                "name": "RAD",
                "arity": 1
            }],
            "mutation": {
                "methods": ["POINT_MUTATION"],
                "probability": 1.0
            }
        }
        self.mutator = CartesianMutation(self.config)

        # make cartesian
        self.data = [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12],
                     [13, 14, 15, 16]]
        self.chromosome = [[0, 0, 2], [0, 0, 3], [3, 4, 5], [0, 1, 2],
                           [0, 1, 3], [2, 5, 7], [2, 6, 9], [0, 5, 7],
                           [2, 11, 8], [0, 11, 8]]
        self.output_nodes = [4, 9, 12, 13]
        self.cartesian = Cartesian(config={},
                                   rows=1,
                                   columns=14,
                                   levels_back=0,
                                   func_nodes=self.chromosome,
                                   input_nodes=self.data,
                                   output_nodes=self.output_nodes)
Example #2
0
 def setUp(self):
     # make cartesian
     self.data = [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12],
                  [13, 14, 15, 16]]
     self.chromosome = [[0, 0, 2], [0, 0, 3], [3, 4, 5], [0, 1, 2],
                        [0, 1, 3], [2, 5, 7], [2, 6, 9], [0, 5, 7],
                        [2, 11, 8], [0, 11, 8]]
     self.output_nodes = [4, 9, 12, 13]
     self.cartesian = Cartesian(config={},
                                rows=1,
                                columns=14,
                                levels_back=0,
                                func_nodes=self.chromosome,
                                input_nodes=self.data,
                                output_nodes=self.output_nodes)
Example #3
0
    def generate_new_cartesian(self):
        # cartesian details
        rows = self.config["cartesian"]["rows"]
        columns = self.config["cartesian"]["columns"]

        num_inputs = self.config["cartesian"]["num_inputs"]
        num_outputs = self.config["cartesian"]["num_outputs"]
        num_funcs = (rows * columns)

        # function nodes
        func_nodes = []
        for i in range(num_funcs):
            func_nodes.append(self.gen_random_func_node(i + num_inputs))

        # output nodes
        output_nodes = []
        for i in range(num_outputs):
            output_nodes.append(self.gen_random_output_node())

        # input nodes
        input_nodes = self.prep_input_nodes()

        # create new cartesian obj
        return Cartesian(
            config=self.config,
            rows=self.config["cartesian"]["rows"],
            columns=self.config["cartesian"]["columns"],
            levels_back=self.config["cartesian"]["levels_back"],
            func_nodes=func_nodes,
            input_nodes=input_nodes,
            output_nodes=output_nodes
        )
Example #4
0
 def setUp(self):
     # make cartesian
     self.data = [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]]
     self.chromosome = [
         [0, 0, 2],
         [0, 0, 3],
         [3, 4, 5],
         [0, 1, 2],
         [0, 1, 3],
         [2, 5, 7],
         [2, 6, 9],
         [0, 5, 7],
         [2, 11, 8],
         [0, 11, 8],
     ]
     self.output_nodes = [4, 9, 12, 13]
     self.cartesian = Cartesian(
         config={},
         rows=1,
         columns=14,
         levels_back=0,
         func_nodes=self.chromosome,
         input_nodes=self.data,
         output_nodes=self.output_nodes,
     )
Example #5
0
    def setUp(self):
        # config and mutation
        self.config = {
            "cartesian": {
                "rows": 4,
                "columns": 4,
                "levels_back": 2,

                "num_inputs": 4,
                "num_outputs": 4
            },

            "function_nodes": [
                {"type": "FUNCTION", "name": "ADD", "arity": 2},
                {"type": "FUNCTION", "name": "SUB", "arity": 2},
                {"type": "FUNCTION", "name": "MUL", "arity": 2},
                {"type": "FUNCTION", "name": "DIV", "arity": 2},
                {"type": "FUNCTION", "name": "COS", "arity": 1},
                {"type": "FUNCTION", "name": "SIN", "arity": 1},
                {"type": "FUNCTION", "name": "RAD", "arity": 1}
            ],

            "mutation": {
                "methods": ["POINT_MUTATION"],
                "probability": 1.0
            }
        }
        self.mutator = CartesianMutation(self.config)

        # make cartesian
        self.data = [
            [1, 2, 3, 4],
            [5, 6, 7, 8],
            [9, 10, 11, 12],
            [13, 14, 15, 16]
        ]
        self.chromosome = [
            [0, 0, 2],
            [0, 0, 3],
            [3, 4, 5],
            [0, 1, 2],
            [0, 1, 3],
            [2, 5, 7],
            [2, 6, 9],
            [0, 5, 7],
            [2, 11, 8],
            [0, 11, 8]
        ]
        self.output_nodes = [4, 9, 12, 13]
        self.cartesian = Cartesian(
            config={},
            rows=1,
            columns=14,
            levels_back=0,
            func_nodes=self.chromosome,
            input_nodes=self.data,
            output_nodes=self.output_nodes
        )
Example #6
0
class CartesianTests(unittest.TestCase):
    def setUp(self):
        # make cartesian
        self.data = [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12],
                     [13, 14, 15, 16]]
        self.chromosome = [[0, 0, 2], [0, 0, 3], [3, 4, 5], [0, 1, 2],
                           [0, 1, 3], [2, 5, 7], [2, 6, 9], [0, 5, 7],
                           [2, 11, 8], [0, 11, 8]]
        self.output_nodes = [4, 9, 12, 13]
        self.cartesian = Cartesian(config={},
                                   rows=1,
                                   columns=14,
                                   levels_back=0,
                                   func_nodes=self.chromosome,
                                   input_nodes=self.data,
                                   output_nodes=self.output_nodes)

    def test_program(self):
        solution = [
            # input nodes
            [1, 2, 3, 4],
            [5, 6, 7, 8],
            [9, 10, 11, 12],
            [13, 14, 15, 16],

            # function nodes
            [0, 0, 2],
            [0, 0, 3],
            [3, 4, 5],
            [0, 1, 2],
            [0, 1, 3],
            [2, 5, 7],
            [2, 6, 9],
            [0, 5, 7],
            [2, 11, 8],
            [0, 11, 8],

            # output nodes
            4,
            9,
            12,
            13
        ]
        program = self.cartesian.program()
        self.assertEquals(solution, program)

    def test_to_dict(self):
        solution = {
            "columns":
            14,
            "func_nodes": [[0, 0, 2], [0, 0, 3], [3, 4, 5], [0, 1, 2],
                           [0, 1, 3], [2, 5, 7], [2, 6, 9], [0, 5, 7],
                           [2, 11, 8], [0, 11, 8]],
            "func_nodes_len":
            10,
            "input_nodes": [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12],
                            [13, 14, 15, 16]],
            "input_nodes_len":
            4,
            "levels_back":
            0,
            "output_nodes": [4, 9, 12, 13],
            "output_nodes_len":
            4,
            "program": [
                # input nodes
                [1, 2, 3, 4],
                [5, 6, 7, 8],
                [9, 10, 11, 12],
                [13, 14, 15, 16],

                # function nodes
                [0, 0, 2],
                [0, 0, 3],
                [3, 4, 5],
                [0, 1, 2],
                [0, 1, 3],
                [2, 5, 7],
                [2, 6, 9],
                [0, 5, 7],
                [2, 11, 8],
                [0, 11, 8],

                # output nodes
                4,
                9,
                12,
                13
            ],
            "rows":
            1,
            "score":
            None
        }

        cartesian_dict = self.cartesian.to_dict()
        cartesian_dict.pop("id")

        import pprint
        pprint.pprint(cartesian_dict)

        self.assertEquals(cartesian_dict, solution)
Example #7
0
    def setUp(self):
        self.config = {
            "cartesian": {
                "rows": 1,
                "columns": 4,
                "levels_back": 4,

                "num_inputs": 12,
                "num_outputs": 1
            },

            "input_variables": [
                {"type": "INPUT", "name": "x"},
                {"type": "CONSTANT", "name": "0.0"},
                {"type": "CONSTANT", "name": "1.0"},
                {"type": "CONSTANT", "name": "2.0"},
                {"type": "CONSTANT", "name": "3.0"},
                {"type": "CONSTANT", "name": "4.0"},
                {"type": "CONSTANT", "name": "5.0"},
                {"type": "CONSTANT", "name": "6.0"},
                {"type": "CONSTANT", "name": "7.0"},
                {"type": "CONSTANT", "name": "8.0"},
                {"type": "CONSTANT", "name": "9.0"},
                {"type": "CONSTANT", "name": "10.0"}
            ],

            "function_nodes": [
                {"type": "FUNCTION", "name": "ADD", "arity": 2},
                {"type": "FUNCTION", "name": "SUB", "arity": 2},
                {"type": "FUNCTION", "name": "MUL", "arity": 2},
                {"type": "FUNCTION", "name": "DIV", "arity": 2},
                {"type": "FUNCTION", "name": "SIN", "arity": 1},
                {"type": "FUNCTION", "name": "COS", "arity": 1},
                {"type": "FUNCTION", "name": "RAD", "arity": 1}
            ],

            "response_variables" : [{"name": "y"}],

            "data_file": "../../data/sine.dat",

        }
        script_path = os.path.dirname(os.path.realpath(sys.argv[0]))
        load_data(self.config, script_path)

        # add constants
        rows = len(self.config["data"]["y"])
        for i in range(11):
            i = float(i)
            self.config["data"][str(i) + ".0"] = [i for j in range(rows)]

        self.functions = [
            functions.add_function,
            functions.sub_function,
            functions.mul_function,
            functions.div_function,
            functions.sin_function,
            functions.cos_function,
            functions.rad_function
        ]

        self.input_nodes = [
            "x",
            "0.0", "1.0", "2.0", "3.0", "4.0", "5.0",
            "6.0", "7.0", "8.0", "9.0", "10.0"
        ]
        self.func_nodes = [
            [2, 11, 11],    # 12
            [6, 0],         # 13
            [2, 12, 13],    # 14
            [4, 14],        # 15
        ]
        self.output_nodes = [15]

        self.cartesian = Cartesian(
            config={},
            rows=1,
            columns=4,
            levels_back=4,
            func_nodes=self.func_nodes,
            input_nodes=self.input_nodes,
            output_nodes=self.output_nodes
        )
Example #8
0
class CartesianMutationTests(unittest.TestCase):
    def setUp(self):
        # config and mutation
        self.config = {
            "cartesian": {
                "rows": 4,
                "columns": 4,
                "levels_back": 2,
                "num_inputs": 4,
                "num_outputs": 4
            },
            "function_nodes": [{
                "type": "FUNCTION",
                "name": "ADD",
                "arity": 2
            }, {
                "type": "FUNCTION",
                "name": "SUB",
                "arity": 2
            }, {
                "type": "FUNCTION",
                "name": "MUL",
                "arity": 2
            }, {
                "type": "FUNCTION",
                "name": "DIV",
                "arity": 2
            }, {
                "type": "FUNCTION",
                "name": "COS",
                "arity": 1
            }, {
                "type": "FUNCTION",
                "name": "SIN",
                "arity": 1
            }, {
                "type": "FUNCTION",
                "name": "RAD",
                "arity": 1
            }],
            "mutation": {
                "methods": ["POINT_MUTATION"],
                "probability": 1.0
            }
        }
        self.mutator = CartesianMutation(self.config)

        # make cartesian
        self.data = [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12],
                     [13, 14, 15, 16]]
        self.chromosome = [[0, 0, 2], [0, 0, 3], [3, 4, 5], [0, 1, 2],
                           [0, 1, 3], [2, 5, 7], [2, 6, 9], [0, 5, 7],
                           [2, 11, 8], [0, 11, 8]]
        self.output_nodes = [4, 9, 12, 13]
        self.cartesian = Cartesian(config={},
                                   rows=1,
                                   columns=14,
                                   levels_back=0,
                                   func_nodes=self.chromosome,
                                   input_nodes=self.data,
                                   output_nodes=self.output_nodes)

    def test_mutate_function_node(self):
        num_input_nodes = len(self.cartesian.input_nodes)
        num_nodes = len(self.cartesian.graph())

        for i in range(100):
            n_addr = random.randint(num_input_nodes, num_nodes - 1)

            # before
            graph_before = copy.deepcopy(self.cartesian.graph())
            # print "BEFORE:", self.cartesian.graph

            # mutate
            g_index = self.mutator.mutate_function_node(n_addr, self.cartesian)

            # after
            graph_after = copy.deepcopy(self.cartesian.graph())
            # print "AFTER:", self.cartesian.graph

            gene_before = graph_before[n_addr][g_index]
            gene_after = graph_after[n_addr][g_index]

            # asserts
            self.assertNotEquals(graph_before, graph_after)
            self.assertNotEquals(gene_before, gene_after)

    def test_mutate_output_node(self):
        num_output_nodes = len(self.cartesian.output_nodes)
        for i in range(100):
            index = random.randint(0, num_output_nodes - 1)
            old_addr = self.cartesian.output_nodes[index]
            new_addr = self.mutator.mutate_output_node(index, self.cartesian)

            self.assertNotEquals(old_addr, new_addr)
            self.assertNotEquals(old_addr, self.cartesian.output_nodes[index])

    def test_point_mutation(self):
        for i in range(100):
            # before
            output_before = copy.deepcopy(self.cartesian.output_nodes)
            graph_before = copy.deepcopy(self.cartesian.graph)
            # print "BEFORE:", graph_before

            # mutate
            self.mutator.point_mutation(self.cartesian)

            # after
            output_after = copy.deepcopy(self.cartesian.output_nodes)
            graph_after = copy.deepcopy(self.cartesian.graph)
            # print "AFTER:", graph_after

            # asserts
            if self.mutator.index["mutated_node"] == "FUNC_NODE":
                self.assertNotEquals(graph_before, graph_after)

            elif self.mutator.index["mutated_node"] == "OUTPUT_NODE":
                index = self.mutator.index["output_node"]
                num_outputs = len(self.cartesian.output_nodes)
                self.assertNotEquals(output_before, output_after)
                self.assertTrue(index >= 0 and index <= num_outputs - 1)

    def test_to_dict(self):
        for i in range(100):
            # print "PROGRAM:", self.cartesian.program()
            self.mutator.mutate(self.cartesian)
            # print "PROGRAM:", self.cartesian.program()

            # import pprint
            # pprint.pprint(self.mutator.to_dict())

            mut_dict = self.mutator.to_dict()
            before_mut = mut_dict["before_mutation"]
            after_mut = mut_dict["after_mutation"]

            self.assertNotEquals(before_mut, after_mut)
            self.assertIsNotNone(mut_dict["method"])
            self.assertIsNotNone(mut_dict["mutation_probability"])
            self.assertIsNotNone(mut_dict["random_probability"])
            self.assertIsNotNone(mut_dict["mutated"])
            self.assertIsNotNone(mut_dict["before_mutation"])
            self.assertIsNotNone(mut_dict["after_mutation"])
Example #9
0
    def setUp(self):
        self.config = {
            "cartesian": {
                "rows": 1,
                "columns": 4,
                "levels_back": 4,
                "num_inputs": 12,
                "num_outputs": 1
            },
            "input_variables": [{
                "type": "INPUT",
                "name": "x"
            }, {
                "type": "CONSTANT",
                "name": "0.0"
            }, {
                "type": "CONSTANT",
                "name": "1.0"
            }, {
                "type": "CONSTANT",
                "name": "2.0"
            }, {
                "type": "CONSTANT",
                "name": "3.0"
            }, {
                "type": "CONSTANT",
                "name": "4.0"
            }, {
                "type": "CONSTANT",
                "name": "5.0"
            }, {
                "type": "CONSTANT",
                "name": "6.0"
            }, {
                "type": "CONSTANT",
                "name": "7.0"
            }, {
                "type": "CONSTANT",
                "name": "8.0"
            }, {
                "type": "CONSTANT",
                "name": "9.0"
            }, {
                "type": "CONSTANT",
                "name": "10.0"
            }],
            "function_nodes": [{
                "type": "FUNCTION",
                "name": "ADD",
                "arity": 2
            }, {
                "type": "FUNCTION",
                "name": "SUB",
                "arity": 2
            }, {
                "type": "FUNCTION",
                "name": "MUL",
                "arity": 2
            }, {
                "type": "FUNCTION",
                "name": "DIV",
                "arity": 2
            }, {
                "type": "FUNCTION",
                "name": "SIN",
                "arity": 1
            }, {
                "type": "FUNCTION",
                "name": "COS",
                "arity": 1
            }, {
                "type": "FUNCTION",
                "name": "RAD",
                "arity": 1
            }],
            "response_variables": [{
                "name": "y"
            }],
            "data_file":
            "../../data/sine.dat",
        }
        script_path = os.path.dirname(os.path.realpath(sys.argv[0]))
        load_data(self.config, script_path)

        # add constants
        rows = len(self.config["data"]["y"])
        for i in range(11):
            i = float(i)
            self.config["data"][str(i) + ".0"] = [i for j in range(rows)]

        self.functions = [
            functions.add_function, functions.sub_function,
            functions.mul_function, functions.div_function,
            functions.sin_function, functions.cos_function,
            functions.rad_function
        ]

        self.input_nodes = [
            "x", "0.0", "1.0", "2.0", "3.0", "4.0", "5.0", "6.0", "7.0", "8.0",
            "9.0", "10.0"
        ]
        self.func_nodes = [
            [2, 11, 11],  # 12
            [6, 0],  # 13
            [2, 12, 13],  # 14
            [4, 14],  # 15
        ]
        self.output_nodes = [15]

        self.cartesian = Cartesian(config={},
                                   rows=1,
                                   columns=4,
                                   levels_back=4,
                                   func_nodes=self.func_nodes,
                                   input_nodes=self.input_nodes,
                                   output_nodes=self.output_nodes)
Example #10
0
class CartesianMutationTests(unittest.TestCase):
    def setUp(self):
        # config and mutation
        self.config = {
            "cartesian": {
                "rows": 4,
                "columns": 4,
                "levels_back": 2,

                "num_inputs": 4,
                "num_outputs": 4
            },

            "function_nodes": [
                {"type": "FUNCTION", "name": "ADD", "arity": 2},
                {"type": "FUNCTION", "name": "SUB", "arity": 2},
                {"type": "FUNCTION", "name": "MUL", "arity": 2},
                {"type": "FUNCTION", "name": "DIV", "arity": 2},
                {"type": "FUNCTION", "name": "COS", "arity": 1},
                {"type": "FUNCTION", "name": "SIN", "arity": 1},
                {"type": "FUNCTION", "name": "RAD", "arity": 1}
            ],

            "mutation": {
                "methods": ["POINT_MUTATION"],
                "probability": 1.0
            }
        }
        self.mutator = CartesianMutation(self.config)

        # make cartesian
        self.data = [
            [1, 2, 3, 4],
            [5, 6, 7, 8],
            [9, 10, 11, 12],
            [13, 14, 15, 16]
        ]
        self.chromosome = [
            [0, 0, 2],
            [0, 0, 3],
            [3, 4, 5],
            [0, 1, 2],
            [0, 1, 3],
            [2, 5, 7],
            [2, 6, 9],
            [0, 5, 7],
            [2, 11, 8],
            [0, 11, 8]
        ]
        self.output_nodes = [4, 9, 12, 13]
        self.cartesian = Cartesian(
            config={},
            rows=1,
            columns=14,
            levels_back=0,
            func_nodes=self.chromosome,
            input_nodes=self.data,
            output_nodes=self.output_nodes
        )

    def test_mutate_function_node(self):
        num_input_nodes = len(self.cartesian.input_nodes)
        num_nodes = len(self.cartesian.graph())

        for i in range(100):
            n_addr = random.randint(num_input_nodes, num_nodes - 1)

            # before
            graph_before = copy.deepcopy(self.cartesian.graph())
            # print "BEFORE:", self.cartesian.graph

            # mutate
            g_index = self.mutator.mutate_function_node(n_addr, self.cartesian)

            # after
            graph_after = copy.deepcopy(self.cartesian.graph())
            # print "AFTER:", self.cartesian.graph

            gene_before = graph_before[n_addr][g_index]
            gene_after = graph_after[n_addr][g_index]

            # asserts
            self.assertNotEquals(graph_before, graph_after)
            self.assertNotEquals(gene_before, gene_after)

    def test_mutate_output_node(self):
        num_output_nodes = len(self.cartesian.output_nodes)
        for i in range(100):
            index = random.randint(0, num_output_nodes - 1)
            old_addr = self.cartesian.output_nodes[index]
            new_addr = self.mutator.mutate_output_node(index, self.cartesian)

            self.assertNotEquals(old_addr, new_addr)
            self.assertNotEquals(old_addr, self.cartesian.output_nodes[index])

    def test_point_mutation(self):
        for i in range(100):
            # before
            output_before = copy.deepcopy(self.cartesian.output_nodes)
            graph_before = copy.deepcopy(self.cartesian.graph)
            # print "BEFORE:", graph_before

            # mutate
            self.mutator.point_mutation(self.cartesian)

            # after
            output_after = copy.deepcopy(self.cartesian.output_nodes)
            graph_after = copy.deepcopy(self.cartesian.graph)
            # print "AFTER:", graph_after

            # asserts
            if self.mutator.index["mutated_node"] == "FUNC_NODE":
                self.assertNotEquals(graph_before, graph_after)

            elif self.mutator.index["mutated_node"] == "OUTPUT_NODE":
                index = self.mutator.index["output_node"]
                num_outputs = len(self.cartesian.output_nodes)
                self.assertNotEquals(output_before, output_after)
                self.assertTrue(index >= 0 and index <= num_outputs - 1)

    def test_to_dict(self):
        for i in range(100):
            # print "PROGRAM:", self.cartesian.program()
            self.mutator.mutate(self.cartesian)
            # print "PROGRAM:", self.cartesian.program()

            # import pprint
            # pprint.pprint(self.mutator.to_dict())

            mut_dict = self.mutator.to_dict()
            before_mut = mut_dict["before_mutation"]
            after_mut = mut_dict["after_mutation"]

            self.assertNotEquals(before_mut, after_mut)
            self.assertIsNotNone(mut_dict["method"])
            self.assertIsNotNone(mut_dict["mutation_probability"])
            self.assertIsNotNone(mut_dict["random_probability"])
            self.assertIsNotNone(mut_dict["mutated"])
            self.assertIsNotNone(mut_dict["before_mutation"])
            self.assertIsNotNone(mut_dict["after_mutation"])
Example #11
0
class CartesianTests(unittest.TestCase):
    def setUp(self):
        # make cartesian
        self.data = [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]]
        self.chromosome = [
            [0, 0, 2],
            [0, 0, 3],
            [3, 4, 5],
            [0, 1, 2],
            [0, 1, 3],
            [2, 5, 7],
            [2, 6, 9],
            [0, 5, 7],
            [2, 11, 8],
            [0, 11, 8],
        ]
        self.output_nodes = [4, 9, 12, 13]
        self.cartesian = Cartesian(
            config={},
            rows=1,
            columns=14,
            levels_back=0,
            func_nodes=self.chromosome,
            input_nodes=self.data,
            output_nodes=self.output_nodes,
        )

    def test_program(self):
        solution = [
            # input nodes
            [1, 2, 3, 4],
            [5, 6, 7, 8],
            [9, 10, 11, 12],
            [13, 14, 15, 16],
            # function nodes
            [0, 0, 2],
            [0, 0, 3],
            [3, 4, 5],
            [0, 1, 2],
            [0, 1, 3],
            [2, 5, 7],
            [2, 6, 9],
            [0, 5, 7],
            [2, 11, 8],
            [0, 11, 8],
            # output nodes
            4,
            9,
            12,
            13,
        ]
        program = self.cartesian.program()
        self.assertEquals(solution, program)

    def test_to_dict(self):
        solution = {
            "columns": 14,
            "func_nodes": [
                [0, 0, 2],
                [0, 0, 3],
                [3, 4, 5],
                [0, 1, 2],
                [0, 1, 3],
                [2, 5, 7],
                [2, 6, 9],
                [0, 5, 7],
                [2, 11, 8],
                [0, 11, 8],
            ],
            "func_nodes_len": 10,
            "input_nodes": [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]],
            "input_nodes_len": 4,
            "levels_back": 0,
            "output_nodes": [4, 9, 12, 13],
            "output_nodes_len": 4,
            "program": [
                # input nodes
                [1, 2, 3, 4],
                [5, 6, 7, 8],
                [9, 10, 11, 12],
                [13, 14, 15, 16],
                # function nodes
                [0, 0, 2],
                [0, 0, 3],
                [3, 4, 5],
                [0, 1, 2],
                [0, 1, 3],
                [2, 5, 7],
                [2, 6, 9],
                [0, 5, 7],
                [2, 11, 8],
                [0, 11, 8],
                # output nodes
                4,
                9,
                12,
                13,
            ],
            "rows": 1,
            "score": None,
        }

        cartesian_dict = self.cartesian.to_dict()
        cartesian_dict.pop("id")

        import pprint

        pprint.pprint(cartesian_dict)

        self.assertEquals(cartesian_dict, solution)