def factory(cli_arg: str, main_config: tp.Dict[str, str], batch_input_root: str, **kwargs) -> BlockConstantDensity: """ Factory to create :class:`BlockConstantDensity` derived classes from the command line definition of batch criteria. """ attr = cd.Parser()(cli_arg) kw = sgp.ScenarioGeneratorParser.reparse_str(kwargs['scenario']) if kw['dist_type'] == "SS" or kw['dist_type'] == "DS": r = range(kw['arena_x'], kw['arena_x'] + attr['cardinality'] * attr['arena_size_inc'], attr['arena_size_inc']) dims = [core.utils.ArenaExtent(Vector3D(x, x / 2.0, 0)) for x in r] elif kw['dist_type'] == "PL" or kw['dist_type'] == "RN": r = range(kw['arena_x'], kw['arena_x'] + attr['cardinality'] * attr['arena_size_inc'], attr['arena_size_inc']) dims = [core.utils.ArenaExtent(Vector3D(x, x, 0)) for x in r] else: raise NotImplementedError( "Unsupported block dstribution '{0}': Only SS,DS,QS,RN supported". format(kw['dist_type'])) def __init__(self) -> None: BlockConstantDensity.__init__(self, cli_arg, main_config, batch_input_root, attr["target_density"], dims, kw['dist_type']) return type( cli_arg, # type: ignore (BlockConstantDensity, ), {"__init__": __init__})
def at_point(self, x: float = None, y: float = None): r""" Calculate the block acquisition probability density at an (X,Y) point within the arena. .. math:: \frac{1}{{\sqrt{z + -\log{\rho_b ^ {\rho_b / 2}}}} where :math:`z` is the distance of the (X,Y) point to the center of the nest, and :math:`\rho_b` is the block density at (X,Y). """ if x is None and y is not None: # Calculating marginal PDF of X pt = Vector3D(self.cluster.extent.center.x, y) elif x is not None and y is None: # Calculating marginal PDF of Y pt = Vector3D(x, self.cluster.extent.center.y) else: # Normal case assert x is not None and y is not None pt = Vector3D(x, y) # assert not self.nest.extent.contains(pt), "{0} inside nest@{1}".format(str(pt), # str(self.nest.extent)) # No acquisitions possible if the cluster never had any blocks in it during simulation. if self.rho is None: return 0.0 z = self.dist_measure.to_nest(pt) if z < 0: z = 0 return 1.0 / ((math.sqrt(z) + self.rho)**2) * self.norm_factor
def __init__(self, scenario: str, nest: Nest): self.scenario = scenario self.nest = nest if 'RN' in self.scenario or 'PL' in self.scenario: # Our model assumes all robots finish foraging EXACTLY at the nest center, and the # implementation has robots pick a random point between where they enter the nest and # the center, in order to reduce congestion. # # This has the effect of making the expected distance the robots travel after entering # the nest but before dropping their object LESS than the distance the model # assumes. So, we calculate the average distance from any point in the square defined by # HALF the nest span in X,Y (HALF being a result of uniform random choice in X,Y) to the # nest center: # https://math.stackexchange.com/questions/15580/what-is-average-distance-from-center-of-square-to-some-point edge = nest.extent.xsize() / 2.0 self.nest_factor = edge / 6.0 * (math.sqrt(2.0) + math.log(1 + math.sqrt(2.0))) elif 'SS' in self.scenario: res, _ = si.nquad( lambda x, y: (self.nest.extent.center - Vector3D(x, y)).length(), [[ self.nest.extent.center.x, self.nest.extent.center.x + self.nest.extent.xsize() / 2.0 ], [ self.nest.extent.center.y - self.nest.extent.ysize() / 4.0, self.nest.extent.center.y + self.nest.extent.ysize() / 4.0, ]], opts={'limit': 100}) self.nest_factor = res / (nest.extent.area()) elif 'DS' in self.scenario: res, _ = si.nquad( lambda x, y: (self.nest.extent.center - Vector3D(x, y)).length(), [[ self.nest.extent.center.x - self.nest.extent.xsize() / 4.0, self.nest.extent.center.x + self.nest.extent.xsize() / 4.0 ], [ self.nest.extent.center.y - self.nest.extent.ysize() / 8.0, self.nest.extent.center.y + self.nest.extent.ysize() / 8.0, ]], opts={'limit': 100}) eff_area = nest.extent.xsize() / 2.0 * nest.extent.ysize() / 4.0 self.nest_factor = res / eff_area
def from_df(cls, clusters_df: pd.DataFrame, cluster_id: int) -> 'BlockCluster': col_stem = 'cluster' + str(cluster_id) xmin = clusters_df.filter(regex=col_stem + '_xmin').iloc[-1].values[0] xmax = clusters_df.filter(regex=col_stem + '_xmax').iloc[-1].values[0] ymin = clusters_df.filter(regex=col_stem + '_ymin').iloc[-1].values[0] ymax = clusters_df.filter(regex=col_stem + '_ymax').iloc[-1].values[0] avg_blocks = clusters_df.filter(regex='cum_avg_' + col_stem + '_block_count').iloc[-1].values[0] return BlockCluster(ll=Vector3D(xmin, ymin), ur=Vector3D(xmax, ymax), cluster_id=cluster_id, avg_blocks=avg_blocks)
def gen_attr_changelist(self) -> tp.List[XMLAttrChangeSet]: """ Generate list of sets of changes to input file to set the # blocks for a set of arena sizes such that the blocks density is constant. Blocks are approximated as point masses. """ if not self.already_added: for changeset in self.attr_changes: for c in changeset: if c.path == ".//arena" and c.attr == "size": x, y, z = c.value.split(',') dims = Vector3D(float(x), float(y), float(z)) extent = core.utils.ArenaExtent(dims) # Always need at least 1 block n_blocks = max( 2, extent.area() * (self.target_density / 100.0)) changeset.add( XMLAttrChange( ".//arena_map/blocks/distribution/manifest", "n_cube", "{0}".format(int(n_blocks / 2.0)))) changeset.add( XMLAttrChange( ".//arena_map/blocks/distribution/manifest", "n_ramp", "{0}".format(int(n_blocks / 2.0)))) break self.already_added = True return self.attr_changes
def __init__(self, cmdopts: dict, criteria: bc.IConcreteBatchCriteria, exp_num: int): # Get nest position spec = ExperimentSpec(criteria, exp_num, cmdopts) res = sgp.ScenarioGeneratorParser.reparse_str(cmdopts['scenario']) pose = np.NestPose(res['dist_type'], [spec.arena_dim]) for path, attr, val in pose.gen_attr_changelist()[0]: if 'nest' in path and 'center' in attr: x, y = val.split(',') center = Vector3D(float(x), float(y), 0.0) if 'nest' in path and 'dims' in attr: x, y = val.split(',') dims = Vector3D(float(x), float(y), 0.0) self.extent = ArenaExtent(dims, center - dims / 2.0)
def at_point(self, x: float = None, y: float = None): r""" Calculate the block density at an (X,Y) point within the extent of the block cluster. """ assert x is not None and y is not None pt = Vector3D(x, y) # No density outside cluster extent if not self.cluster.extent.contains(pt): return 0.0 # No density inside nest elif self.nest.extent.contains(pt): return 0.0 return self.rho_b * self.norm_factor
def _nest_to_cluster(self, cluster: rep.BlockCluster, nest: core.utils.ArenaExtent, scenario: str) -> float: dist_measure = DistanceMeasure2D(scenario, nest=nest) density = BlockAcqDensity(nest=nest, cluster=cluster, dist_measure=dist_measure) # Compute expected value of X coordinate of average distance from nest to acquisition # location. ll = cluster.extent.ll ur = cluster.extent.ur evx = density.evx_for_region(ll=ll, ur=ur) # Compute expected value of Y coordinate of average distance from nest to acquisition # location. evy = density.evy_for_region(ll=ll, ur=ur) # Compute expected distance from nest to block acquisitions dist = dist_measure.to_nest(Vector3D(evx, evy)) return dist
def __init__(self, cmdopts: dict, nest: Nest, sim_opath: str) -> None: clusters_df = core.utils.pd_csv_read( os.path.join(sim_opath, 'block-clusters.csv')) n_clusters = len([c for c in clusters_df.columns if 'xmin' in c]) # Create extents from clusters self.clusters = set() # RN block distribution has a single cluster, but the nest is in the middle of it, which # makes density calculations much trickier when integrating across/up to the nest (modeled # as a single point). We break it into an equivalent set of 4 smaller clusters ringing the # nest to avoid computational issues. if 'RN' in cmdopts['scenario']: cluster = BlockCluster.from_df(clusters_df, 0) ll1 = cluster.extent.ll ur1 = Vector3D(nest.extent.ll.x, cluster.extent.ur.y) c1 = BlockCluster(ll=ll1, ur=ur1, cluster_id=0, avg_blocks=cluster.avg_blocks / 4.0) ll2 = Vector3D(nest.extent.ll.x, cluster.extent.ll.y) ur2 = Vector3D(nest.extent.ur.x, nest.extent.ll.y) c2 = BlockCluster(ll=ll2, ur=ur2, cluster_id=1, avg_blocks=cluster.avg_blocks / 4.0) ll3 = Vector3D(nest.extent.ll.x, nest.extent.ur.y) ur3 = Vector3D(nest.extent.ur.x, cluster.extent.ur.y) c3 = BlockCluster(ll=ll3, ur=ur3, cluster_id=2, avg_blocks=cluster.avg_blocks / 4.0) ll4 = Vector3D(nest.extent.ur.x, cluster.extent.ll.y) ur4 = cluster.extent.ur c4 = BlockCluster(ll=ll4, ur=ur4, cluster_id=3, avg_blocks=cluster.avg_blocks / 4.0) self.clusters = set([c1, c2, c3, c4]) else: # General case for c in range(0, n_clusters): self.clusters |= set([BlockCluster.from_df(clusters_df, c)])
def __init__(self, x_range: range, y_range: range, z: int, dist_type: str) -> None: super().__init__( [ArenaExtent(Vector3D(x, y, z)) for x in x_range for y in y_range], dist_type=dist_type)
def __init__(self, sqrange: range, z: int, dist_type: str) -> None: super().__init__([ArenaExtent(Vector3D(x, x, z)) for x in sqrange], dist_type=dist_type)