diff --git a/b_asic/architecture.py b/b_asic/architecture.py
index a4a33cedf79ce178dde3380da866ee2a2a879706..7f0722a40f81ddcf37f292fb49a95819f975f8aa 100644
--- a/b_asic/architecture.py
+++ b/b_asic/architecture.py
@@ -273,13 +273,13 @@ class Resource(HardwareBlock):
     def is_assigned(self) -> bool:
         return self._assignment is not None
 
-    def assign(self, heuristic: str = "left_edge"):
+    def assign(self, strategy: str = "left_edge"):
         """
         Perform assignment of processes to resource.
 
         Parameters
         ----------
-        heuristic : str
+        strategy : str
             See the specific resource types for more information.
 
         See Also
@@ -426,21 +426,21 @@ class ProcessingElement(Resource):
         return [cast(OperatorProcess, p) for p in self._collection]
 
     def assign(
-        self, heuristic: Literal["left_edge", "graph_color"] = "left_edge"
+        self, strategy: Literal["left_edge", "graph_color"] = "left_edge"
     ) -> None:
         """
         Perform assignment of the processes.
 
         Parameters
         ----------
-        heuristic : {'left_edge', 'graph_color'}, default: 'left_edge'
+        strategy : {'left_edge', 'graph_color'}, default: 'left_edge'
             The assignment algorithm.
 
             * 'left_edge': Left-edge algorithm.
             * 'graph_color': Graph-coloring based on exclusion graph.
         """
         self._assignment = list(
-            self._collection.split_on_execution_time(heuristic=heuristic)
+            self._collection.split_on_execution_time(strategy=strategy)
         )
         if len(self._assignment) > 1:
             self._assignment = None
@@ -561,13 +561,13 @@ class Memory(Resource):
                 pass
         return ""
 
-    def assign(self, heuristic: str = "left_edge") -> None:
+    def assign(self, strategy: str = "left_edge") -> None:
         """
         Perform assignment of the memory variables.
 
         Parameters
         ----------
-        heuristic : str, default: 'left_edge'
+        strategy : str, default: 'left_edge'
             The assignment algorithm. Depending on memory type the following are
             available:
 
@@ -579,7 +579,7 @@ class Memory(Resource):
         """
         if self._memory_type == "RAM":
             self._assignment = self._collection.split_on_execution_time(
-                heuristic=heuristic
+                strategy=strategy
             )
         else:  # "register"
             raise NotImplementedError()
@@ -869,13 +869,13 @@ of :class:`~b_asic.architecture.ProcessingElement`
         else:
             raise ValueError("Resource not in architecture")
 
-    def assign_resources(self, heuristic: str = "left_edge") -> None:
+    def assign_resources(self, strategy: str = "left_edge") -> None:
         """
         Convenience method to assign all resources in the architecture.
 
         Parameters
         ----------
-        heuristic : str, default: "left_edge"
+        strategy : str, default: "left_edge"
             The heurstic to use.
 
         See Also
@@ -885,7 +885,7 @@ of :class:`~b_asic.architecture.ProcessingElement`
 
         """
         for resource in chain(self.memories, self.processing_elements):
-            resource.assign(heuristic=heuristic)
+            resource.assign(strategy=strategy)
 
     def move_process(
         self,
diff --git a/b_asic/resources.py b/b_asic/resources.py
index 9771c040ac4b54fd8a7e238b34f85449e600fc91..2ae8e50a28f1b881d66858d91daa0294bcf8c248 100644
--- a/b_asic/resources.py
+++ b/b_asic/resources.py
@@ -12,6 +12,16 @@ import matplotlib.pyplot as plt
 import networkx as nx
 from matplotlib.axes import Axes
 from matplotlib.ticker import MaxNLocator
+from pulp import (
+    GUROBI,
+    PULP_CBC_CMD,
+    LpBinary,
+    LpProblem,
+    LpStatusOptimal,
+    LpVariable,
+    lpSum,
+    value,
+)
 
 from b_asic._preferences import LATENCY_COLOR, WARNING_COLOR
 from b_asic.codegen.vhdl.common import is_valid_vhdl_identifier
@@ -898,7 +908,7 @@ class ProcessCollection:
 
     def split_on_execution_time(
         self,
-        heuristic: Literal["graph_color", "left_edge"] = "left_edge",
+        strategy: Literal["graph_color", "left_edge"] = "left_edge",
         coloring_strategy: str = "saturation_largest_first",
     ) -> list["ProcessCollection"]:
         """
@@ -906,13 +916,13 @@ class ProcessCollection:
 
         Parameters
         ----------
-        heuristic : {'graph_color', 'left_edge'}, default: 'left_edge'
-            The heuristic used when splitting based on execution times.
+        strategy : {'graph_color', 'left_edge'}, default: 'left_edge'
+            The strategy used when splitting based on execution times.
 
         coloring_strategy : str, default: 'saturation_largest_first'
             Node ordering strategy passed to
             :func:`networkx.algorithms.coloring.greedy_color`.
-            This parameter is only considered if *heuristic* is set to 'graph_color'.
+            This parameter is only considered if *strategy* is set to 'graph_color'.
             One of
 
             * 'largest_first'
@@ -927,59 +937,61 @@ class ProcessCollection:
         -------
         A list of new ProcessCollection objects with the process splitting.
         """
-        if heuristic == "graph_color":
+        if strategy == "graph_color":
             return self._graph_color_assignment(coloring_strategy)
-        elif heuristic == "left_edge":
+        elif strategy == "left_edge":
             return self._left_edge_assignment()
         else:
-            raise ValueError(f"Invalid heuristic '{heuristic}'")
+            raise ValueError(f"Invalid strategy '{strategy}'")
 
     def split_on_ports(
         self,
-        heuristic: str = "left_edge",
+        strategy: str = "left_edge",
         read_ports: int | None = None,
         write_ports: int | None = None,
         total_ports: int | None = None,
         processing_elements: list["ProcessingElement"] | None = None,
-        amount_of_sets: int | None = None,
+        max_colors: int | None = None,
+        solver: PULP_CBC_CMD | GUROBI | None = None,
     ) -> list["ProcessCollection"]:
         """
         Split based on concurrent read and write accesses.
 
-        Different heuristic methods can be used.
+        Different strategy methods can be used.
 
         Parameters
         ----------
-        heuristic : str, default: "left_edge"
-            The heuristic used when splitting this :class:`ProcessCollection`.
+        strategy : str, default: "left_edge"
+            The strategy used when splitting this :class:`ProcessCollection`.
             Valid options are:
-
-            * "ilp_graph_color"
-            * "ilp_min_input_mux"
-            * "greedy_graph_color"
-            * "equitable_graph_color"
-            * "left_edge"
-            * "left_edge_min_pe_to_mem"
-            * "left_edge_min_mem_to_pe"
-
+                * "ilp_graph_color"
+                * "ilp_min_input_mux"
+                * "greedy_graph_color"
+                * "equitable_graph_color"
+                * "left_edge"
+                * "left_edge_min_pe_to_mem"
+                * "left_edge_min_mem_to_pe"
         read_ports : int, optional
             The number of read ports used when splitting process collection based on
             memory variable access.
-
         write_ports : int, optional
             The number of write ports used when splitting process collection based on
             memory variable access.
-
         total_ports : int, optional
             The total number of ports used when splitting process collection based on
             memory variable access.
-
         processing_elements : list of ProcessingElement, optional
-            The currently used PEs, only required if heuristic = "min_mem_to_pe",
+            The currently used PEs,
+            only required if strategy = "left_edge_min_mem_to_pe",
             "ilp_graph_color" or "ilp_min_input_mux".
-
-        amount_of_sets : int, optional
-            amount of sets to split to, only required if heuristics = "ilp_min_input_mux".
+        max_colors : int, optional
+            The maximum amount of colors to split based on,
+            only required if strategy is an ILP method.
+        solver : PuLP MIP solver object, optional
+            Only used if strategy is an ILP method.
+            Valid options are:
+                * PULP_CBC_CMD() - preinstalled with the package
+                * GUROBI() - required licence but likely faster
 
         Returns
         -------
@@ -988,77 +1000,80 @@ class ProcessCollection:
         read_ports, write_ports, total_ports = _sanitize_port_option(
             read_ports, write_ports, total_ports
         )
-        if heuristic == "ilp_graph_color":
+        if strategy == "ilp_graph_color":
             return self._split_ports_ilp_graph_color(
-                read_ports, write_ports, total_ports
+                read_ports, write_ports, total_ports, max_colors, solver
             )
-        elif heuristic == "ilp_min_input_mux":
+        elif strategy == "ilp_min_input_mux":
             if processing_elements is None:
                 raise ValueError(
-                    "processing_elements must be provided if heuristic = 'ilp_min_input_mux'"
+                    "processing_elements must be provided if strategy = 'ilp_min_input_mux'"
                 )
-            if amount_of_sets is None:
+            if max_colors is None:
                 raise ValueError(
-                    "amount_of_sets must be provided if heuristic = 'ilp_min_input_mux'"
+                    "max_colors must be provided if strategy = 'ilp_min_input_mux'"
                 )
             return self._split_ports_ilp_min_input_mux_graph_color(
                 read_ports,
                 write_ports,
                 total_ports,
                 processing_elements,
-                amount_of_sets,
+                max_colors,
+                solver,
             )
-        elif heuristic == "ilp_min_output_mux":
+        elif strategy == "ilp_min_output_mux":
             if processing_elements is None:
                 raise ValueError(
-                    "processing_elements must be provided if heuristic = 'ilp_min_output_mux'"
+                    "processing_elements must be provided if strategy = 'ilp_min_output_mux'"
                 )
-            if amount_of_sets is None:
+            if max_colors is None:
                 raise ValueError(
-                    "amount_of_sets must be provided if heuristic = 'ilp_min_output_mux'"
+                    "max_colors must be provided if strategy = 'ilp_min_output_mux'"
                 )
             return self._split_ports_ilp_min_output_mux_graph_color(
                 read_ports,
                 write_ports,
                 total_ports,
                 processing_elements,
-                amount_of_sets,
+                max_colors,
+                solver,
             )
-        elif heuristic == "ilp_min_total_mux":
+        elif strategy == "ilp_min_total_mux":
             if processing_elements is None:
                 raise ValueError(
-                    "processing_elements must be provided if heuristic = 'ilp_min_total_mux'"
+                    "processing_elements must be provided if strategy = 'ilp_min_total_mux'"
                 )
-            if amount_of_sets is None:
+            if max_colors is None:
                 raise ValueError(
-                    "amount_of_sets must be provided if heuristic = 'ilp_min_total_mux'"
+                    "max_colors must be provided if strategy = 'ilp_min_total_mux'"
                 )
             return self._split_ports_ilp_min_total_mux_graph_color(
                 read_ports,
                 write_ports,
                 total_ports,
                 processing_elements,
-                amount_of_sets,
+                max_colors,
+                solver,
             )
-        elif heuristic == "greedy_graph_color":
+        elif strategy == "greedy_graph_color":
             return self._split_ports_greedy_graph_color(
                 read_ports, write_ports, total_ports
             )
-        elif heuristic == "equitable_graph_color":
+        elif strategy == "equitable_graph_color":
             return self._split_ports_equitable_graph_color(
                 read_ports, write_ports, total_ports
             )
-        elif heuristic == "left_edge":
+        elif strategy == "left_edge":
             return self.split_ports_sequentially(
                 read_ports,
                 write_ports,
                 total_ports,
                 sequence=sorted(self),
             )
-        elif heuristic == "left_edge_min_pe_to_mem":
+        elif strategy == "left_edge_min_pe_to_mem":
             if processing_elements is None:
                 raise ValueError(
-                    "processing_elements must be provided if heuristic = 'left_edge_min_pe_to_mem'"
+                    "processing_elements must be provided if strategy = 'left_edge_min_pe_to_mem'"
                 )
             return self._split_ports_sequentially_minimize_pe_to_memory_connections(
                 read_ports,
@@ -1067,10 +1082,10 @@ class ProcessCollection:
                 sequence=sorted(self),
                 processing_elements=processing_elements,
             )
-        elif heuristic == "left_edge_min_mem_to_pe":
+        elif strategy == "left_edge_min_mem_to_pe":
             if processing_elements is None:
                 raise ValueError(
-                    "processing_elements must be provided if heuristic = 'left_edge_min_mem_to_pe'"
+                    "processing_elements must be provided if strategy = 'left_edge_min_mem_to_pe'"
                 )
             return self._split_ports_sequentially_minimize_memory_to_pe_connections(
                 read_ports,
@@ -1080,7 +1095,7 @@ class ProcessCollection:
                 processing_elements=processing_elements,
             )
         else:
-            raise ValueError("Invalid heuristic provided.")
+            raise ValueError("Invalid strategy provided.")
 
     def split_ports_sequentially(
         self,
@@ -1408,16 +1423,9 @@ class ProcessCollection:
         read_ports: int,
         write_ports: int,
         total_ports: int,
+        max_colors: int | None = None,
+        solver: PULP_CBC_CMD | GUROBI | None = None,
     ) -> list["ProcessCollection"]:
-        from pulp import (
-            LpBinary,
-            LpProblem,
-            LpStatusOptimal,
-            LpVariable,
-            lpSum,
-            value,
-        )
-
         # create new exclusion graph. Nodes are Processes
         exclusion_graph = self.create_exclusion_graph_from_ports(
             read_ports, write_ports, total_ports
@@ -1425,11 +1433,13 @@ class ProcessCollection:
         nodes = list(exclusion_graph.nodes())
         edges = list(exclusion_graph.edges())
 
-        # run a good heuristic to get an upper bound on the amount of colors
-        coloring = nx.coloring.greedy_color(
-            exclusion_graph, strategy="saturation_largest_first"
-        )
-        colors = range(len(set(coloring.values())))
+        if max_colors is None:
+            # get an initial estimate using NetworkX greedy graph coloring
+            coloring = nx.coloring.greedy_color(
+                exclusion_graph, strategy="saturation_largest_first"
+            )
+            max_colors = len(set(coloring.values()))
+        colors = range(max_colors)
 
         # find the minimal amount of colors (memories)
 
@@ -1464,7 +1474,10 @@ class ProcessCollection:
         for color in colors[:-1]:
             problem += c[color + 1] <= c[color]
 
-        status = problem.solve()
+        if solver is None:
+            solver = PULP_CBC_CMD()
+
+        status = problem.solve(solver)
 
         if status != LpStatusOptimal:
             raise ValueError(
@@ -1493,16 +1506,8 @@ class ProcessCollection:
         total_ports: int,
         processing_elements: list["ProcessingElement"],
         amount_of_colors: int,
+        solver: PULP_CBC_CMD | GUROBI | None = None,
     ) -> list["ProcessCollection"]:
-        from pulp import (
-            LpBinary,
-            LpProblem,
-            LpStatusOptimal,
-            LpVariable,
-            lpSum,
-            value,
-        )
-
         # create new exclusion graph. Nodes are Processes
         exclusion_graph = self.create_exclusion_graph_from_ports(
             read_ports, write_ports, total_ports
@@ -1553,7 +1558,10 @@ class ProcessCollection:
         for color in colors[:-1]:
             problem += c[color + 1] <= c[color]
 
-        status = problem.solve()
+        if solver is None:
+            solver = PULP_CBC_CMD()
+
+        status = problem.solve(solver)
 
         if status != LpStatusOptimal:
             raise ValueError(
@@ -1582,16 +1590,8 @@ class ProcessCollection:
         total_ports: int,
         processing_elements: list["ProcessingElement"],
         amount_of_colors: int,
+        solver: PULP_CBC_CMD | GUROBI | None = None,
     ) -> list["ProcessCollection"]:
-        from pulp import (
-            LpBinary,
-            LpProblem,
-            LpStatusOptimal,
-            LpVariable,
-            lpSum,
-            value,
-        )
-
         # create new exclusion graph. Nodes are Processes
         exclusion_graph = self.create_exclusion_graph_from_ports(
             read_ports, write_ports, total_ports
@@ -1642,7 +1642,10 @@ class ProcessCollection:
         for color in colors[:-1]:
             problem += c[color + 1] <= c[color]
 
-        status = problem.solve()
+        if solver is None:
+            solver = PULP_CBC_CMD()
+
+        status = problem.solve(solver)
 
         if status != LpStatusOptimal:
             raise ValueError(
@@ -1671,16 +1674,8 @@ class ProcessCollection:
         total_ports: int,
         processing_elements: list["ProcessingElement"],
         amount_of_colors: int,
+        solver: PULP_CBC_CMD | GUROBI | None = None,
     ) -> list["ProcessCollection"]:
-        from pulp import (
-            LpBinary,
-            LpProblem,
-            LpStatusOptimal,
-            LpVariable,
-            lpSum,
-            value,
-        )
-
         # create new exclusion graph. Nodes are Processes
         exclusion_graph = self.create_exclusion_graph_from_ports(
             read_ports, write_ports, total_ports
@@ -1708,11 +1703,14 @@ class ProcessCollection:
         )
 
         # constraints:
-        #   1 - nodes have exactly one color
-        #   2 - adjacent nodes cannot have the same color
-        #   3 - only permit assignments if color is used
-        #   4 - if node is colored then enable the PE which generates that node (variable)
-        #   5 - if node is colored then enable the PE reads from that node (variable)
+        #   (1) - nodes have exactly one color
+        #   (2) - adjacent nodes cannot have the same color
+        #   (3) - only permit assignments if color is used
+        #   (4) - if node is colored then enable the PE which generates that node
+        #   (5) - if node is colored then enable the PE reads from that node
+        #   (6) - reduce solution space by assigning colors to the largest clique
+        #   (7 & 8) - reduce solution space by ignoring the symmetry caused
+        #       by cycling the graph colors
         for node in nodes:
             problem += lpSum(x[node][i] for i in colors) == 1
         for u, v in edges:
@@ -1729,8 +1727,18 @@ class ProcessCollection:
             pe = _get_destination(node, processing_elements)
             for color in colors:
                 problem += x[node][color] <= z[pe][color]
+        max_clique = next(nx.find_cliques(exclusion_graph))
+        for color, node in enumerate(max_clique):
+            problem += x[node][color] == c[color] == 1
+        for color in colors:
+            problem += c[color] <= lpSum(x[node][color] for node in nodes)
+        for color in colors[:-1]:
+            problem += c[color + 1] <= c[color]
+
+        if solver is None:
+            solver = PULP_CBC_CMD()
 
-        status = problem.solve()
+        status = problem.solve(solver)
 
         if status != LpStatusOptimal:
             raise ValueError(
diff --git a/examples/ldlt_matrix_inverse.py b/examples/ldlt_matrix_inverse.py
index de7662772d6a4a2a7d0486aa48692c28a94b72d3..fba600e15e7451887b4fcbf269e6433a10324241 100644
--- a/examples/ldlt_matrix_inverse.py
+++ b/examples/ldlt_matrix_inverse.py
@@ -117,7 +117,7 @@ mem_vars.show(title="All memory variables")
 direct, mem_vars = mem_vars.split_on_length()
 mem_vars.show(title="Non-zero time memory variables")
 mem_vars_set = mem_vars.split_on_ports(
-    read_ports=1, write_ports=1, total_ports=2, heuristic="greedy_graph_color"
+    read_ports=1, write_ports=1, total_ports=2, strategy="greedy_graph_color"
 )
 
 # %%
diff --git a/examples/memory_constrained_scheduling.py b/examples/memory_constrained_scheduling.py
index 7ae0d015e34b893b4e94ee63ad665acd4b297ebd..8b3af165a25ed2aa5bbdf09c7d993d1463bd710b 100644
--- a/examples/memory_constrained_scheduling.py
+++ b/examples/memory_constrained_scheduling.py
@@ -64,7 +64,7 @@ mem_vars.show(title="All memory variables")
 direct, mem_vars = mem_vars.split_on_length()
 mem_vars.show(title="Non-zero time memory variables")
 mem_vars_set = mem_vars.split_on_ports(
-    read_ports=1, write_ports=1, total_ports=2, heuristic="greedy_graph_color"
+    read_ports=1, write_ports=1, total_ports=2, strategy="greedy_graph_color"
 )
 
 # %%
@@ -121,7 +121,7 @@ pe_out = ProcessingElement(outputs, entity_name='output')
 
 mem_vars.show(title="Non-zero time memory variables")
 mem_vars_set = mem_vars.split_on_ports(
-    heuristic="greedy_graph_color", read_ports=1, write_ports=1, total_ports=2
+    strategy="greedy_graph_color", read_ports=1, write_ports=1, total_ports=2
 )
 
 # %% Allocate memories by graph coloring
diff --git a/test/integration/test_sfg_to_architecture.py b/test/integration/test_sfg_to_architecture.py
index 4caa3211d7d646f463d2cc774ec1b490ab2c3cec..11c4763a7841e0464c438b4c73ef876a12459bf6 100644
--- a/test/integration/test_sfg_to_architecture.py
+++ b/test/integration/test_sfg_to_architecture.py
@@ -126,11 +126,11 @@ def test_pe_and_memory_constrained_schedule():
     mem_vars = schedule.get_memory_variables()
     direct, mem_vars = mem_vars.split_on_length()
     mem_vars_set = mem_vars.split_on_ports(
-        read_ports=1, write_ports=1, total_ports=2, heuristic="greedy_graph_color"
+        read_ports=1, write_ports=1, total_ports=2, strategy="greedy_graph_color"
     )
 
     mem_vars_set = mem_vars.split_on_ports(
-        read_ports=1, write_ports=1, total_ports=2, heuristic="greedy_graph_color"
+        read_ports=1, write_ports=1, total_ports=2, strategy="greedy_graph_color"
     )
 
     memories = []
@@ -204,7 +204,7 @@ def test_different_resource_algorithms():
         read_ports=1,
         write_ports=1,
         total_ports=2,
-        heuristic="left_edge",
+        strategy="left_edge",
         processing_elements=processing_elements,
     )
 
@@ -227,7 +227,7 @@ def test_different_resource_algorithms():
         read_ports=1,
         write_ports=1,
         total_ports=2,
-        heuristic="left_edge_min_pe_to_mem",
+        strategy="left_edge_min_pe_to_mem",
         processing_elements=processing_elements,
     )
 
@@ -250,7 +250,7 @@ def test_different_resource_algorithms():
         read_ports=1,
         write_ports=1,
         total_ports=2,
-        heuristic="left_edge_min_mem_to_pe",
+        strategy="left_edge_min_mem_to_pe",
         processing_elements=processing_elements,
     )
 
@@ -273,7 +273,7 @@ def test_different_resource_algorithms():
         read_ports=1,
         write_ports=1,
         total_ports=2,
-        heuristic="greedy_graph_color",
+        strategy="greedy_graph_color",
         processing_elements=processing_elements,
     )
 
@@ -296,7 +296,7 @@ def test_different_resource_algorithms():
         read_ports=1,
         write_ports=1,
         total_ports=2,
-        heuristic="equitable_graph_color",
+        strategy="equitable_graph_color",
         processing_elements=processing_elements,
     )
 
@@ -354,7 +354,7 @@ def test_different_resource_algorithms():
         read_ports=1,
         write_ports=1,
         total_ports=2,
-        heuristic="ilp_graph_color",
+        strategy="ilp_graph_color",
         processing_elements=processing_elements,
     )
 
@@ -372,14 +372,38 @@ def test_different_resource_algorithms():
     assert len(arch.processing_elements) == 5
     assert len(arch.memories) == 4
 
+    # ILP COLOR (amount of colors given)
+    mem_vars_set = mem_vars.split_on_ports(
+        read_ports=1,
+        write_ports=1,
+        total_ports=2,
+        strategy="ilp_graph_color",
+        processing_elements=processing_elements,
+        max_colors=4,
+    )
+
+    memories = []
+    for i, mem in enumerate(mem_vars_set):
+        memory = Memory(mem, memory_type="RAM", entity_name=f"memory{i}")
+        memories.append(memory)
+        memory.assign("graph_color")
+
+    arch = Architecture(
+        processing_elements,
+        memories,
+        direct_interconnects=direct,
+    )
+    assert len(arch.processing_elements) == 5
+    assert len(arch.memories) == 4
+
     # ILP COLOR MIN INPUT MUX
     mem_vars_set = mem_vars.split_on_ports(
         read_ports=1,
         write_ports=1,
         total_ports=2,
-        heuristic="ilp_min_input_mux",
+        strategy="ilp_min_input_mux",
         processing_elements=processing_elements,
-        amount_of_sets=4,
+        max_colors=4,
     )
 
     memories = []
@@ -401,9 +425,9 @@ def test_different_resource_algorithms():
         read_ports=1,
         write_ports=1,
         total_ports=2,
-        heuristic="ilp_min_output_mux",
+        strategy="ilp_min_output_mux",
         processing_elements=processing_elements,
-        amount_of_sets=4,
+        max_colors=4,
     )
 
     memories = []
@@ -425,9 +449,36 @@ def test_different_resource_algorithms():
         read_ports=1,
         write_ports=1,
         total_ports=2,
-        heuristic="ilp_min_total_mux",
+        strategy="ilp_min_total_mux",
+        processing_elements=processing_elements,
+        max_colors=4,
+    )
+
+    memories = []
+    for i, mem in enumerate(mem_vars_set):
+        memory = Memory(mem, memory_type="RAM", entity_name=f"memory{i}")
+        memories.append(memory)
+        memory.assign("graph_color")
+
+    arch = Architecture(
+        processing_elements,
+        memories,
+        direct_interconnects=direct,
+    )
+    assert len(arch.processing_elements) == 5
+    assert len(arch.memories) == 4
+
+    # ILP COLOR MIN TOTAL MUX (custom solver)
+    from pulp import PULP_CBC_CMD
+
+    mem_vars_set = mem_vars.split_on_ports(
+        read_ports=1,
+        write_ports=1,
+        total_ports=2,
+        strategy="ilp_min_total_mux",
         processing_elements=processing_elements,
-        amount_of_sets=4,
+        max_colors=4,
+        solver=PULP_CBC_CMD(),
     )
 
     memories = []
diff --git a/test/unit/test_architecture.py b/test/unit/test_architecture.py
index 1a520aa645542596ac2a22a10893171d8b3da9ad..67f2eab70e88f9f867972b2ba6a30a3981ea5ac7 100644
--- a/test/unit/test_architecture.py
+++ b/test/unit/test_architecture.py
@@ -216,7 +216,7 @@ def test_move_process(schedule_direct_form_iir_lp_filter: Schedule):
     mvs = schedule_direct_form_iir_lp_filter.get_memory_variables()
     operations = schedule_direct_form_iir_lp_filter.get_operations()
     adders1, adders2 = operations.get_by_type_name(Addition.type_name()).split_on_ports(
-        heuristic="left_edge", total_ports=1
+        strategy="left_edge", total_ports=1
     )
     adders1 = [adders1]  # Fake two PEs needed for the adders
     adders2 = [adders2]  # Fake two PEs needed for the adders
diff --git a/test/unit/test_resources.py b/test/unit/test_resources.py
index 990f506ce4ddf1e88c4aaa17f6b9837430369929..e7a93ba96b53e4ca659cf333d053980f681f8722 100644
--- a/test/unit/test_resources.py
+++ b/test/unit/test_resources.py
@@ -34,7 +34,7 @@ class TestProcessCollectionPlainMemoryVariable:
         self, simple_collection: ProcessCollection
     ):
         collection_split = simple_collection.split_on_ports(
-            heuristic="greedy_graph_color", read_ports=1, write_ports=1, total_ports=2
+            strategy="greedy_graph_color", read_ports=1, write_ports=1, total_ports=2
         )
         assert len(collection_split) == 3
 
@@ -57,93 +57,93 @@ class TestProcessCollectionPlainMemoryVariable:
         self, simple_collection: ProcessCollection
     ):
         split = simple_collection.split_on_ports(
-            heuristic="left_edge", read_ports=1, write_ports=1, total_ports=2
+            strategy="left_edge", read_ports=1, write_ports=1, total_ports=2
         )
         assert len(split) == 3
 
         split = simple_collection.split_on_ports(
-            heuristic="left_edge", read_ports=1, write_ports=2, total_ports=2
+            strategy="left_edge", read_ports=1, write_ports=2, total_ports=2
         )
         assert len(split) == 3
 
         split = simple_collection.split_on_ports(
-            heuristic="left_edge", read_ports=2, write_ports=2, total_ports=2
+            strategy="left_edge", read_ports=2, write_ports=2, total_ports=2
         )
         assert len(split) == 2
 
     def test_split_memory_variable_raises(self, simple_collection: ProcessCollection):
         with pytest.raises(
             ValueError,
-            match="processing_elements must be provided if heuristic = 'ilp_min_input_mux'",
+            match="processing_elements must be provided if strategy = 'ilp_min_input_mux'",
         ):
             simple_collection.split_on_ports(
-                heuristic="ilp_min_input_mux", total_ports=1
+                strategy="ilp_min_input_mux", total_ports=1
             )
 
         with pytest.raises(
             ValueError,
-            match="amount_of_sets must be provided if heuristic = 'ilp_min_input_mux'",
+            match="max_colors must be provided if strategy = 'ilp_min_input_mux'",
         ):
             simple_collection.split_on_ports(
-                heuristic="ilp_min_input_mux",
+                strategy="ilp_min_input_mux",
                 total_ports=1,
                 processing_elements=[],
             )
 
         with pytest.raises(
             ValueError,
-            match="processing_elements must be provided if heuristic = 'ilp_min_output_mux'",
+            match="processing_elements must be provided if strategy = 'ilp_min_output_mux'",
         ):
             simple_collection.split_on_ports(
-                heuristic="ilp_min_output_mux", total_ports=1
+                strategy="ilp_min_output_mux", total_ports=1
             )
 
         with pytest.raises(
             ValueError,
-            match="amount_of_sets must be provided if heuristic = 'ilp_min_output_mux'",
+            match="max_colors must be provided if strategy = 'ilp_min_output_mux'",
         ):
             simple_collection.split_on_ports(
-                heuristic="ilp_min_output_mux",
+                strategy="ilp_min_output_mux",
                 total_ports=1,
                 processing_elements=[],
             )
 
         with pytest.raises(
             ValueError,
-            match="processing_elements must be provided if heuristic = 'ilp_min_total_mux'",
+            match="processing_elements must be provided if strategy = 'ilp_min_total_mux'",
         ):
             simple_collection.split_on_ports(
-                heuristic="ilp_min_total_mux", total_ports=1
+                strategy="ilp_min_total_mux", total_ports=1
             )
 
         with pytest.raises(
             ValueError,
-            match="amount_of_sets must be provided if heuristic = 'ilp_min_total_mux'",
+            match="max_colors must be provided if strategy = 'ilp_min_total_mux'",
         ):
             simple_collection.split_on_ports(
-                heuristic="ilp_min_total_mux",
+                strategy="ilp_min_total_mux",
                 total_ports=1,
                 processing_elements=[],
             )
 
         with pytest.raises(
             ValueError,
-            match="processing_elements must be provided if heuristic = 'left_edge_min_pe_to_mem'",
+            match="processing_elements must be provided if strategy = 'left_edge_min_pe_to_mem'",
         ):
             simple_collection.split_on_ports(
-                heuristic="left_edge_min_pe_to_mem", total_ports=1
+                strategy="left_edge_min_pe_to_mem", total_ports=1
             )
 
         with pytest.raises(
             ValueError,
-            match="processing_elements must be provided if heuristic = 'left_edge_min_mem_to_pe'",
+            match="processing_elements must be provided if strategy = 'left_edge_min_mem_to_pe'",
         ):
             simple_collection.split_on_ports(
-                heuristic="left_edge_min_mem_to_pe", total_ports=1
+                strategy="left_edge_min_mem_to_pe", total_ports=1
             )
 
-        with pytest.raises(ValueError, match="Invalid heuristic provided."):
-            simple_collection.split_on_ports(heuristic="foo", total_ports=1)
+        with pytest.raises(ValueError, match="Invalid strategy provided."):
+            simple_collection.split_on_ports(strategy="foo", total_ports=1)
 
     @matplotlib.testing.decorators.image_comparison(
         ["test_left_edge_cell_assignment.png"]
@@ -160,7 +160,7 @@ class TestProcessCollectionPlainMemoryVariable:
         collection = generate_matrix_transposer(4, min_lifetime=5)
         assignment_left_edge = collection._left_edge_assignment()
         assignment_graph_color = collection.split_on_execution_time(
-            heuristic="graph_color", coloring_strategy="saturation_largest_first"
+            strategy="graph_color", coloring_strategy="saturation_largest_first"
         )
         assert len(assignment_left_edge) == 18
         assert len(assignment_graph_color) == 16
@@ -182,7 +182,7 @@ class TestProcessCollectionPlainMemoryVariable:
             collection = generate_matrix_transposer(
                 rows=rows, cols=cols, min_lifetime=0
             )
-            assignment = collection.split_on_execution_time(heuristic="graph_color")
+            assignment = collection.split_on_execution_time(strategy="graph_color")
             collection.generate_memory_based_storage_vhdl(
                 filename=(
                     "b_asic/codegen/testbench/"
@@ -243,7 +243,6 @@ class TestProcessCollectionPlainMemoryVariable:
             assert register == f"R{i}"
 
     def test_generate_random_interleaver(self):
-        return
         for _ in range(10):
             for size in range(5, 20, 5):
                 collection = generate_random_interleaver(size)
@@ -340,8 +339,8 @@ class TestProcessCollectionPlainMemoryVariable:
         b = PlainMemoryVariable(4, 0, {0: 7}, "cmul4.0")
         c = PlainMemoryVariable(5, 0, {0: 4}, "cmul5.0")
         collection = ProcessCollection([a, b, c], schedule_time=7, cyclic=True)
-        for heuristic in ("graph_color", "left_edge"):
-            assignment = collection.split_on_execution_time(heuristic)
+        for strategy in ("graph_color", "left_edge"):
+            assignment = collection.split_on_execution_time(strategy)
             assert len(assignment) == 2
             a_idx = 0 if a in assignment[0] else 1
             assert b not in assignment[a_idx]
@@ -350,12 +349,12 @@ class TestProcessCollectionPlainMemoryVariable:
     def test_split_on_execution_lifetime_assert(self):
         a = PlainMemoryVariable(3, 0, {0: 10}, "MV0")
         collection = ProcessCollection([a], schedule_time=9, cyclic=True)
-        for heuristic in ("graph_color", "left_edge"):
+        for strategy in ("graph_color", "left_edge"):
             with pytest.raises(
                 ValueError,
                 match="MV0 has execution time greater than the schedule time",
             ):
-                collection.split_on_execution_time(heuristic)
+                collection.split_on_execution_time(strategy)
 
     def test_split_on_length(self):
         # Test 1: Exclude a zero-time access time