def __init__(self, name, layout_constraint, entity_constraint, orig_layout_constraint=None, orig_entity_constraint=None, sample_new_num_count=None, is_pg=False): super(Layout, self).__init__(name, level="Layout", node_type="and", is_pg=is_pg) self.layout_constraint = layout_constraint self.entity_constraint = entity_constraint self.number = Number(min_level=layout_constraint["Number"][0], max_level=layout_constraint["Number"][1]) self.position = Position(pos_type=layout_constraint["Position"][0], pos_list=layout_constraint["Position"][1]) self.uniformity = Uniformity(min_level=layout_constraint["Uni"][0], max_level=layout_constraint["Uni"][1]) self.number.sample() self.position.sample(self.number.get_value()) self.uniformity.sample() # store initial layout_constraint and entity_constraint for answer generation if orig_layout_constraint is None: self.orig_layout_constraint = copy.deepcopy(self.layout_constraint) else: self.orig_layout_constraint = orig_layout_constraint if orig_entity_constraint is None: self.orig_entity_constraint = copy.deepcopy(self.entity_constraint) else: self.orig_entity_constraint = orig_entity_constraint if sample_new_num_count is None: self.sample_new_num_count = dict() most_num = len(self.position.values) for i in range(layout_constraint["Number"][0], layout_constraint["Number"][1] + 1): self.sample_new_num_count[i] = [comb(most_num, i + 1), []] else: self.sample_new_num_count = sample_new_num_count
class Layout(AoTNode): """Layout is the highest level of the hierarchy that has attributes (Number, Position and Uniformity). To copy a Layout, please use deepcopy such that newly instantiated and separated attributes are created. """ def __init__(self, name, layout_constraint, entity_constraint, orig_layout_constraint=None, orig_entity_constraint=None, sample_new_num_count=None, is_pg=False): super(Layout, self).__init__(name, level="Layout", node_type="and", is_pg=is_pg) self.layout_constraint = layout_constraint self.entity_constraint = entity_constraint self.number = Number(min_level=layout_constraint["Number"][0], max_level=layout_constraint["Number"][1]) self.position = Position(pos_type=layout_constraint["Position"][0], pos_list=layout_constraint["Position"][1]) self.uniformity = Uniformity(min_level=layout_constraint["Uni"][0], max_level=layout_constraint["Uni"][1]) self.number.sample() self.position.sample(self.number.get_value()) self.uniformity.sample() # store initial layout_constraint and entity_constraint for answer generation if orig_layout_constraint is None: self.orig_layout_constraint = copy.deepcopy(self.layout_constraint) else: self.orig_layout_constraint = orig_layout_constraint if orig_entity_constraint is None: self.orig_entity_constraint = copy.deepcopy(self.entity_constraint) else: self.orig_entity_constraint = orig_entity_constraint if sample_new_num_count is None: self.sample_new_num_count = dict() most_num = len(self.position.values) for i in range(layout_constraint["Number"][0], layout_constraint["Number"][1] + 1): self.sample_new_num_count[i] = [comb(most_num, i + 1), []] else: self.sample_new_num_count = sample_new_num_count self.num_count = dict() for i in self.sample_new_num_count.keys(): self.num_count[i] = 1 def reset_num_count(self): for i in self.num_count.keys(): if self.sample_new_num_count[i][0] > 0: self.num_count[i] = 1 def add_new(self, *bboxes): """Add new entities into this level. Arguments: *bboxes(tuple of bbox): bboxes of new entities """ name = self.number.get_value() uni = self.uniformity.get_value() for i in range(len(bboxes)): name += i bbox = bboxes[i] new_entity = copy.deepcopy(self.children[0]) new_entity.name = str(name) new_entity.bbox = bbox if not uni: new_entity.resample() self._insert(new_entity) def resample(self, change_number=False): self._resample(change_number) def _sample(self): """Though Layout is an "and" node, we do not enumerate all possible configurations, but rather we treat it as a sampling process such that different configurtions are sampled. After the sampling, the lower level Entities are instantiated. Returns: new_node(Layout): a separated node with independent attributes """ pos = self.position.get_value() new_node = copy.deepcopy(self) new_node.is_pg = True if self.uniformity.get_value(): node = Entity(name=str(0), bbox=pos[0], entity_constraint=self.entity_constraint) new_node._insert(node) for i in range(1, len(pos)): bbox = pos[i] node = copy.deepcopy(node) node.name = str(i) node.bbox = bbox new_node._insert(node) else: for i in range(len(pos)): bbox = pos[i] node = Entity(name=str(i), bbox=bbox, entity_constraint=self.entity_constraint) new_node._insert(node) return new_node def _resample(self, change_number): """Resample each attribute for every child. This function is called across rows. Arguments: change_number(bool): whether to resample a number """ if change_number: self.number.sample() del self.children[:] self.position.sample(self.number.get_value()) pos = self.position.get_value() if self.uniformity.get_value(): node = Entity(name=str(0), bbox=pos[0], entity_constraint=self.entity_constraint) self._insert(node) for i in range(1, len(pos)): bbox = pos[i] node = copy.deepcopy(node) node.name = str(i) node.bbox = bbox self._insert(node) else: for i in range(len(pos)): bbox = pos[i] node = Entity(name=str(i), bbox=bbox, entity_constraint=self.entity_constraint) self._insert(node) def _update_constraint(self, rule_group): """Update the constraint of the layout. If one constraint is not satisfied, return None such that this structure is disgarded. Arguments: rule_group(list of Rule): all rules to apply to this layout Returns: Layout(Layout): a new Layout node with independent attributes """ num_min = self.layout_constraint["Number"][0] num_max = self.layout_constraint["Number"][1] uni_min = self.layout_constraint["Uni"][0] uni_max = self.layout_constraint["Uni"][1] type_min = self.entity_constraint["Type"][0] type_max = self.entity_constraint["Type"][1] size_min = self.entity_constraint["Size"][0] size_max = self.entity_constraint["Size"][1] color_min = self.entity_constraint["Color"][0] color_max = self.entity_constraint["Color"][1] new_constraints = rule_constraint(rule_group, num_min, num_max, uni_min, uni_max, type_min, type_max, size_min, size_max, color_min, color_max) new_layout_constraint, new_entity_constraint = new_constraints new_num_min = new_layout_constraint["Number"][0] new_num_max = new_layout_constraint["Number"][1] if new_num_min > new_num_max: return None new_uni_min = new_layout_constraint["Uni"][0] new_uni_max = new_layout_constraint["Uni"][1] if new_uni_min > new_uni_max: return None new_type_min = new_entity_constraint["Type"][0] new_type_max = new_entity_constraint["Type"][1] if new_type_min > new_type_max: return None new_size_min = new_entity_constraint["Size"][0] new_size_max = new_entity_constraint["Size"][1] if new_size_min > new_size_max: return None new_color_min = new_entity_constraint["Color"][0] new_color_max = new_entity_constraint["Color"][1] if new_color_min > new_color_max: return None new_layout_constraint = copy.deepcopy(self.layout_constraint) new_layout_constraint["Number"][:] = [new_num_min, new_num_max] new_layout_constraint["Uni"][:] = [new_uni_min, new_uni_max] new_entity_constraint = copy.deepcopy(self.entity_constraint) new_entity_constraint["Type"][:] = [new_type_min, new_type_max] new_entity_constraint["Size"][:] = [new_size_min, new_size_max] new_entity_constraint["Color"][:] = [new_color_min, new_color_max] return Layout(self.name, new_layout_constraint, new_entity_constraint, self.orig_layout_constraint, self.orig_entity_constraint, self.sample_new_num_count) def reset_constraint(self, attr): attr_name = attr.lower() instance = getattr(self, attr_name) instance.min_level = self.layout_constraint[attr][0] instance.max_level = self.layout_constraint[attr][1] def _sample_new(self, attr_name, min_level, max_level, layout): if attr_name == "Number": while True: value_level = self.number.sample_new(min_level, max_level) if layout.sample_new_num_count[value_level][0] == 0: continue new_num = self.number.get_value(value_level) new_value_idx = self.position.sample_new(new_num) set_new_value_idx = set(new_value_idx) if set_new_value_idx not in layout.sample_new_num_count[ value_level][1]: layout.sample_new_num_count[value_level][0] -= 1 layout.sample_new_num_count[value_level][1].append( set_new_value_idx) break self.number.set_value_level(value_level) self.position.set_value_idx(new_value_idx) pos = self.position.get_value() del self.children[:] for i in range(len(pos)): bbox = pos[i] node = Entity(name=str(i), bbox=bbox, entity_constraint=self.entity_constraint) self._insert(node) elif attr_name == "Position": new_value_idx = self.position.sample_new(self.number.get_value()) layout.position.previous_values.append(new_value_idx) self.position.set_value_idx(new_value_idx) pos = self.position.get_value() for i in range(len(pos)): bbox = pos[i] self.children[i].bbox = bbox elif attr_name == "Type": for index in range(len(self.children)): new_value_level = self.children[index].type.sample_new( min_level, max_level) self.children[index].type.set_value_level(new_value_level) layout.children[index].type.previous_values.append( new_value_level) elif attr_name == "Size": for index in range(len(self.children)): new_value_level = self.children[index].size.sample_new( min_level, max_level) self.children[index].size.set_value_level(new_value_level) layout.children[index].size.previous_values.append( new_value_level) elif attr_name == "Color": for index in range(len(self.children)): new_value_level = self.children[index].color.sample_new( min_level, max_level) self.children[index].color.set_value_level(new_value_level) layout.children[index].color.previous_values.append( new_value_level) else: raise ValueError("Unsupported operation") def _sample_new_value(self, attr_name, min_level, max_level, attr_uni, mode_3): ret = [] if attr_name == "Number": previous_num = self.number.get_value() while True: value_level = self.number.sample_new(min_level, max_level) if mode_3 == '3-Position-Number' and self.sample_new_num_count[ value_level][0] == 1: continue if self.num_count[value_level] == 1: self.num_count[value_level] = 0 break new_num = self.number.get_value(value_level) if previous_num >= new_num: select = list( np.random.choice(previous_num, new_num, replace=False)) else: rest = new_num select = [] while previous_num < rest: select += range(previous_num) rest -= previous_num if rest > 0: select += list( np.random.choice(previous_num, rest, replace=False)) ret = [value_level, select] t = 1 if mode_3 == '3-Position-Number': t += 1 for i in range(t): while True: new_value_idx = self.position.sample_new(new_num) set_new_value_idx = set(new_value_idx) if set_new_value_idx not in self.sample_new_num_count[ value_level][1]: self.sample_new_num_count[value_level][0] -= 1 self.sample_new_num_count[value_level][1].append( set_new_value_idx) ret.append(new_value_idx) break if sum(self.num_count.values()) == 1: self.reset_num_count() elif attr_name == "Position": new_value_idx = self.position.sample_new(self.number.get_value()) ret = [new_value_idx] elif attr_name == "Type": if attr_uni: new_value_level = self.children[0].type.sample_new( min_level, max_level) ret = [new_value_level] else: for index in range(len(self.children)): new_value_level = self.children[index].type.sample_new( min_level, max_level) ret.append(new_value_level) elif attr_name == "Size": if attr_uni: new_value_level = self.children[0].size.sample_new( min_level, max_level) ret = [new_value_level] else: for index in range(len(self.children)): new_value_level = self.children[index].size.sample_new( min_level, max_level) ret.append(new_value_level) elif attr_name == "Color": if attr_uni: new_value_level = self.children[0].color.sample_new( min_level, max_level) ret = [new_value_level] else: for index in range(len(self.children)): new_value_level = self.children[index].color.sample_new( min_level, max_level) ret.append(new_value_level) else: raise ValueError("Unsupported operation") return ret def _apply_new_value(self, attr_name, value): L = len(value) if attr_name == "Number": number_value = value[0] select = value[1] if L == 4 and self.position.isChanged: position_value = value[3] else: position_value = value[2] self.number.set_value_level(number_value) self.position.set_value_idx(position_value) pos = self.position.get_value() new_entity_list = [ copy.deepcopy(self.children[idx]) for idx in select ] del self.children[:] for i in range(len(pos)): bbox = pos[i] self._insert(new_entity_list[i]) self.children[i].bbox = bbox elif attr_name == "Position": self.position.set_value_idx(value[0]) self.position.isChanged = True pos = self.position.get_value() for i in range(len(pos)): bbox = pos[i] self.children[i].bbox = bbox elif attr_name == "Type": for index in range(len(self.children)): self.children[index].type.set_value_level(value[index % L]) elif attr_name == "Size": for index in range(len(self.children)): self.children[index].size.set_value_level(value[index % L]) elif attr_name == "Color": for index in range(len(self.children)): self.children[index].color.set_value_level(value[index % L]) else: raise ValueError("Unsupported operation")