class Design(NamedIDObject): def __init__(self, comp_list, routing_list, fabric, position_type, name='', constraint_generators=(), optimizers=()): ''' adj_dict :: {str : [(str, int)]} adj_dict[x] := out edges of x with the their width fabric :: Fabric position_type :: str -> Fabric -> PositionBase constraints_gen :: [([Component] -> [Wire] -> fabric -> z3.Bool)] constraint_generators := an iterable of keys, functions that generate hard constraints optimizers :: [([Component] -> [Wire] -> fabric -> (z3.Bool, z3.Object), bool)] optimizers := [k, f, b] where k is the key f(components, wires) := an Iterable of functions that generate hard constraint / optimizing parameters pairs, b := a bool which indicating whether Optimizing parameter is minimized or maximized ''' super().__init__(name) self._fabric = fabric self._position_type = position_type self._comp_list = comp_list #is kinda redundant to keep this around but it might be useful self._routing_list = routing_list self._comps = dict() self._wires = dict() self._p_constraints = ValidContainer() self._cg = dict() self._opt = dict() self._pinned_comps = [] self._r_constraints = ValidContainer() self._rcg = dict() self._ropt = dict() self._max_degree = 0 for k,f in constraint_generators: self.add_constraint_generator(k,f) for k,f,b in optimizers: self.add_optimizer(k,f,b) #build graph self._gen_graph() def _gen_graph(self): #reset constraints self._reset_constraints() for comp_dict in self._comp_list: name = comp_dict['name'] width = comp_dict['width'] height = comp_dict['height'] if comp_dict['x'] is not None and comp_dict['y'] is not None: self._comps[name] = Component(name, True, width, height, comp_dict['x'], comp_dict['y']) self._pinned_comps.append(name) else: self._comps[name] = Component(name, False, width, height) #need to generate positons for each component self._gen_pos() def _gen_pos(self): #reset constraints self._reset_constraints() for c in self.components: if c.width and c.height: c.pos = self._position_type(c.name, self.fabric, c.width, c.height) else: c.pos = self._position_type(c.name, self.fabric) # also find maximum (in or out) degree if self._max_degree < c.degree: self._max_degree = c.degree def get_sorted_components(self, descend): '''returns components sorted by their degree in descending order if descend = True''' return sorted(list(self._comps.values()), key = lambda c: c.degree, reverse=descend) @property def components(self): return iter(self._comps.values()) @property def wires(self): return iter(self._wires.values()) @property def fabric(self): return self._fabric @fabric.setter def fabric(self, fabric): if self.fabric != fabirc: self._fabric = fabric #position representation is dependent on fabric self._gen_pos() @property def constraints(self): '''returns all hard constraints''' if self._pinned_comps: return z3.And(self.p_constraints, self.g_constraints, self.o_constraints, self.r_constraints, self.pinned_constraints) else: return z3.And(self.p_constraints, self.g_constraints, self.o_constraints, self.r_constraints) @property def max_degree(self): return self._max_degree def _reset_constraints(self): self._reset_p_constraints() self._reset_g_constraints() self._reset_o_constraints() ''' ----------------------------------------------------------------------- Position Related Stuff ----------------------------------------------------------------------- ''' @property def position_type(self): return self._position_type @position_type.setter def position_type(self, position_type): if position_type != self.position_type: self._position_type = position_type #regenerate positions for each node self._gen_pos() @property def p_constraints(self): if not self._p_constraints.valid: cl = [] for c in self.components: if not c.is_fixed: cl.append(c.pos.invariants) self._p_constraints.data = z3.And(*cl) return self._p_constraints.data def _reset_p_constraints(self): self._p_constraints.mark_invalid() @property def pinned_constraints(self): if self._pinned_comps: c = [] for src_name in self._pinned_comps: comp = self._comps[src_name] c.append(comp.pos.x == self._position_type.pos_repr(comp.x)) c.append(comp.pos.y == self._position_type.pos_repr(comp.y)) c.append(comp.pos.horiz_var == comp.pos.width) c.append(comp.pos.vert_var == comp.pos.height) c.append(z3.And(comp.pos._d0 == True, comp.pos._d90 == False, comp.pos._d180 == False, comp.pos._d270 == False)) return z3.And(c) else: return [] ''' ----------------------------------------------------------------------- General constraints related stuff ----------------------------------------------------------------------- ''' @property def constraint_generators(self): return set((k, f) for k,(f,_) in self._cg.items()) def add_constraint_generator(self, k, f): self._cg[k] = (f, ValidContainer()) def remove_constraint_generator(self, k): del self._cg[k] @property def g_constraints(self): cl = [] for k,(f, c) in self._cg.items(): if not c.valid: c.data = f(self.components, self.wires, self.fabric) cl.append(c.data) return z3.And(cl) def _reset_g_constraints(self): for _,c in self._cg.values(): c.mark_invalid() ''' ----------------------------------------------------------------------- Optimization Related Stuff ----------------------------------------------------------------------- ''' @property def optimizers(self): return set((k, f, b) for k,(f,_,b) in self._cg.items()) def add_optimizer(self, k, f, minimize): self._opt[k] = (f, ValidContainer(), minimize) def remove_optimizer(self, k): del self._opt[k] @property def o_constraints(self): cl = [] for f,c,m in self._opt.values(): # f := functiom # c := ValidContainer(constraints, parameter) # m := minimize flag if not c.valid: c.data = f(self.components, self.wires, self.fabric) if c.data[0]: #check that list nonempty to avoid appending an empty list cl.append(c.data[0]) return z3.And(cl) @property def opt_parameters(self): cl = [] for f,c,m in self._opt.values(): # f := functiom # c := ValidContainer(constraints, parameter) # m := minimize flag if not c.valid: c.data = f(self.components, self.wires, self.fabric) cl.append((c.data[1], m)) return cl def _reset_o_constraints(self): for _,c,_ in self._opt.values(): c.mark_invalid() ''' ----------------------------------------------------------------------- Pad-Routing Related Stuff ----------------------------------------------------------------------- ''' @property def r_constraints(self): cl = [] for k,(f, c) in self._rcg.items(): if not c.valid: c.data = f(self._comps, self._routing_list) cl.append(c.data) return z3.And(cl) def add_pad_cg(self, k, f): ''' Adds a constraint generator for pad-level connectivity ''' self._rcg[k] = (f, ValidContainer()) def remove_pad_cg(self, k): del self._rcg[k] @property def r_opt_param(self): cl = [] for k,(f, c) in self._ropt.items(): if not c.valid: c.data = f(self._comps, self._routing_list) cl.append(c.data) return cl def add_pad_opt(self, k, f): ''' Adds a constraint generator for pad-level connectivity ''' self._ropt[k] = (f, ValidContainer()) def remove_pad_opt(self, k): del self._ropt[k]
class Design(NamedIDObject): def __init__(self, adj_dict, fabric, position_type, name='', constraint_generators=(), optimizers=()): ''' adj_dict :: {str : [(str, int)]} adj_dict[x] := out edges of x with the their width fabric :: Fabric position_type :: str -> Frabric -> PositionBase constraints_gen :: [([Component] -> [Wire] -> fabric -> z3.Bool)] constraint_generators := an iterable of keys, functions that generate hard constraints optimizers :: [([Component] -> [Wire] -> fabric -> (z3.Bool, z3.Object), bool)] optimizers := [k, f, b] where k is the key f(components, wires) := an Iterable of functions that generate hard constraint / optimizing parameters pairs, b := a bool which indicating whether Optimizing parameter is minimized or maximized ''' super().__init__(name) self._fabric = fabric self._position_type = position_type self._adj_dict = adj_dict #is kinda redundent to keep this around but it might be useful self._comps = dict() self._wires = dict() self._p_constraints = ValidContainer() self._cg = dict() self._opt = dict() self._max_degree = 0 for k, f in constraint_generators: self.add_constraint_generator(k, f) for k, f, b in optimizers: self.add_optimizer(k, f, b) #build graph self._gen_graph() def _gen_graph(self): #reset constraints self._reset_constraints() for src_name, adj_list in self._adj_dict.items(): if not isinstance(src_name, str): raise TypeError( 'component_graph must be a dictionary of str to [(str, int)]' ) if src_name not in self._comps: self._comps[src_name] = Component(src_name) src = self._comps[src_name] for pair in adj_list: if not isinstance(pair, tuple) or len(pair) != 2: raise TypeError( 'component_graph must be a dictionary of str to [(str, int)]' ) dst_name = pair[0] width = pair[1] if not isinstance(dst_name, str) or not isinstance(width, int): raise TypeError( 'component_graph must be a dictionary of str to [(str, int)]' ) if dst_name not in self._comps: self._comps[dst_name] = Component(dst_name) dst = self._comps[dst_name] self._wires[(src_name, dst_name)] = Wire(src, dst, width) #need to generate positons for each component self._gen_pos() def _gen_pos(self): #reset constraints self._reset_constraints() for c in self.components: c.pos = self._position_type(c.name, self.fabric) # also find maximum (in or out) degree if self._max_degree < c.degree: self._max_degree = c.degree def get_sorted_components(self, descend): '''returns components sorted by their degree in descending order if descend = True''' return sorted(list(self._comps.values()), key=lambda c: c.degree, reverse=descend) @property def components(self): return iter(self._comps.values()) @property def wires(self): return iter(self._wires.values()) @property def fabric(self): return self._fabric @fabric.setter def fabric(self, fabric): if self.fabric != fabirc: self._fabric = fabric #position representation is dependent on fabric self._gen_pos() @property def constraints(self): '''returns all hard constraints''' return z3.And(self.p_constraints, self.g_constraints, self.o_constraints) @property def max_degree(self): return self._max_degree def _reset_constraints(self): self._reset_p_constraints() self._reset_g_constraints() self._reset_o_constraints() ''' ----------------------------------------------------------------------- Position Related Stuff ----------------------------------------------------------------------- ''' @property def position_type(self): return self._position_type @position_type.setter def position_type(self, position_type): if position_type != self.position_type: self._position_type = position_type #regenerate positions for each node self._gen_pos() @property def p_constraints(self): if not self._p_constraints.valid: cl = [] for c in self.components: cl.append(c.pos.invariants) self._p_constraints.data = z3.And(*cl) return self._p_constraints.data def _reset_p_constraints(self): self._p_constraints.mark_invalid() ''' ----------------------------------------------------------------------- General constraints related stuff ----------------------------------------------------------------------- ''' @property def constraint_generators(self): return set((k, f) for k, (f, _) in self._cg.items()) def add_constraint_generator(self, k, f): self._cg[k] = (f, ValidContainer()) def remove_constraint_generator(self, k): del self._cg[k] @property def g_constraints(self): cl = [] for k, (f, c) in self._cg.items(): if not c.valid: c.data = f(self.components, self.wires, self.fabric) cl.append(c.data) return z3.And(cl) def _reset_g_constraints(self): for _, c in self._cg.values(): c.mark_invalid() ''' ----------------------------------------------------------------------- Optimization Related Stuff ----------------------------------------------------------------------- ''' @property def optimizers(self): return set((k, f, b) for k, (f, _, b) in self._cg.items()) def add_optimizer(self, k, f, minimize): self._opt[k] = (f, ValidContainer(), minimize) def remove_optimizer(self, k): del self._opt[k] @property def o_constraints(self): cl = [] for f, c, m in self._opt.values(): # f := functiom # c := ValidContainer(constraints, parameter) # m := minimize flag if not c.valid: c.data = f(self.components, self.wires, self.fabric) if c.data[0]: #check that list nonempty to avoid appending an empty list cl.append(c.data[0]) return z3.And(cl) @property def opt_parameters(self): cl = [] for f, c, m in self._opt.values(): # f := functiom # c := ValidContainer(constraints, parameter) # m := minimize flag if not c.valid: c.data = f(self.components, self.wires, self.fabric) cl.append((c.data[1], m)) return cl def _reset_o_constraints(self): for _, c, _ in self._opt.values(): c.mark_invalid()