From 44710324bd06e74ea46fed24a75c7899a8db3ad5 Mon Sep 17 00:00:00 2001 From: Simon Bjurek <simbj106@student.liu.se> Date: Tue, 8 Apr 2025 18:10:33 +0200 Subject: [PATCH] Refactor resource allocation/assignment methods and add ability to use custom solver --- b_asic/architecture.py | 22 +- b_asic/resources.py | 214 ++++++++++--------- examples/ldlt_matrix_inverse.py | 2 +- examples/memory_constrained_scheduling.py | 4 +- test/integration/test_sfg_to_architecture.py | 79 +++++-- test/unit/test_architecture.py | 2 +- test/unit/test_resources.py | 57 +++-- 7 files changed, 219 insertions(+), 161 deletions(-) diff --git a/b_asic/architecture.py b/b_asic/architecture.py index a4a33ced..7f0722a4 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 9771c040..2ae8e50a 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 de766277..fba600e1 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 7ae0d015..8b3af165 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 4caa3211..11c4763a 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 1a520aa6..67f2eab7 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 990f506c..e7a93ba9 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 -- GitLab