def random_choice(seq, n=1): """ """ if n == 1: return seq[random_pick(indices(seq))] else: return [seq[i] for i in random_pick(indices(seq), size=n)]
def random_walk(self, iters): """ Generate new plan states by random walking and stop after given iterations. Note that this is not searching because the walking is not directed by the objective value. Args: iters (int): how many steps before stopping """ # instantialize a transition object to handle actions transition = Transition(silent=self.silent, pr_actions=[1., 0., 0., 0.]) for i in range(iters): if not self.silent: print("\n\n------------------- Iter: %d --------------------" % i) # randomly pick a action with different weighting # apply the picked action to picked room and get new state action = random_pick(transition.actions, p=transition.pr_actions) if not self.silent: print("\nSelected action:", action) getattr(transition, action)(self) if not self.silent: print("\nRoom states after action:"); self._pprint_rooms() # parse the stats and evaluate the stats # plot the plan every 20 iterations self._parse() self._evaluate() if i % self.plot_freq == 0: self._plot_intermediate(i) self.plot_fig.ioff()
def _slice_a_subsequence(self, seq): """ Randomly slice a consecutive subsequence out of given sequence. """ n = len(seq) if n == 1: return seq else: start = random_pick(n) length = random_pick(range(1, n)) end = start + length if end < n: sub_seq = seq[start:end] else: sub_seq = seq[start:n] + seq[0:end - n - 1] return sub_seq
def random_walk(self, iters): """ Generate new plan states by random walking and stop after given iterations. Note that this is not searching because the walking is not directed by the objective value. Args: iters (int): how many steps before stopping """ # instantialize a transition object to handle actions transition = Transition(silent=self.silent, pr_actions=[1., 0., 0., 0.]) for i in range(iters): if not self.silent: print("\n\n------------------- Iter: %d --------------------" % i) # randomly pick a action with different weighting # apply the picked action to picked room and get new state action = random_pick(transition.actions, p=transition.pr_actions) if not self.silent: print("\nSelected action:", action) getattr(transition, action)(self) if not self.silent: print("\nRoom states after action:") self._pprint_rooms() # parse the stats and evaluate the stats # plot the plan every 20 iterations self._parse() self._evaluate() if i % self.plot_freq == 0: self._plot_intermediate(i) self.plot_fig.ioff()
def split(self, plan): """ Split a room into 2 same-function rooms by a random x or y axis inside this room's x, y ranges. """ room = plan._pick_a_room() axis = random_pick(['x', 'y']) if not self.silent: print("Split room:", room.rid) # randomly pick a split line left = room.stats["min_"+axis] + 0.5 right = room.stats["max_"+axis] - 0.5 if right - left == 0: if not self.silent: print("This room has width 1 at axis %s" % axis) return split_line = random_pick(np.arange(left, right)) # split the grids xys_1, xys_2 = [], [] for xy in room.xys: if getattr(plan.grids[xy], axis) <= split_line: xys_1.append(xy) else: xys_2.append(xy) # create 2 new rooms based on split 2 groups of xys # append the new rooms to self.rooms and set original room to None room_1 = Room(function=room.function, xys=xys_1, rid=plan.room_count) room_2 = Room(function=room.function, xys=xys_2, rid=plan.room_count + 1) plan.rooms += [room_1, room_2] plan.rooms[room.rid] = None plan.room_count += 2 # update the rids of split xys for xy in xys_1: plan.grids[xy].rid = room_1.rid for xy in xys_2: plan.grids[xy].rid = room_2.rid if not self.silent: print("Into 2 rooms: %d and %d" % (room_1.rid, room_2.rid))
def merge(self, plan): """ Merge 2 same-function, adjacent rooms together. """ # find all pairs of adjacent, same-function rooms keyed by function groups = self._group_rooms_by_function(plan) pairs = {function:[] for function in plan.functions} for function in plan.functions: for rid in groups[function]: for adjacent_rid in plan.rooms[rid].find_adjacent_rids(plan.grids): if adjacent_rid in groups[function]: pairs[function].append((rid, adjacent_rid)) # purge the function without adjacent pairs # if no function contains adjacent pairs, exit this action. pairs = purge_dict(pairs) if not pairs: if not self.silent: print("No pairs of adjacent same-function rooms found.") return # randomly pick a function by weighting. (we may want to using weights # to control the chance of merge for different room function) # redo picking if the picked function has no adjacent pairs picked_function = "" while picked_function not in pairs.keys(): picked_function = random_pick(plan.functions, p=plan.pr_merge) # randomly pick a pair of adjacent rooms # create a new room with same function and merged xys picked_pair = random_choice(pairs[picked_function]) room_new = Room( function=picked_function, rid = plan.room_count, xys=plan.rooms[picked_pair[0]].xys + plan.rooms[picked_pair[1]].xys ) plan.room_count += 1 if not self.silent: print("Merge rooms %d and %d" % (plan.rooms[picked_pair[0]].rid, plan.rooms[picked_pair[1]].rid)) # update self.rooms plan.rooms[picked_pair[0]] = None plan.rooms[picked_pair[1]] = None plan.rooms.append(room_new) if not self.silent: print("into room %d" % room_new.rid) # update the rid of merged xys for xy in room_new.xys: plan.grids[xy].rid = room_new.rid
def _random_pick_walls_to_expand(self, walls): """ Randomly pick {a portion of a wall, a wall, multiple walls} by probabilities {0.2, 0.7, 0.1} to expand outward """ # choose whether to expand multiple walls or just one wall. The prob # of picking 1 is 0.9, and the rest 0.1 prob is evenly distributed # among 2 to n_walls. n_walls = len(walls) pr_pick = [0.9]+[0.1/(n_walls-1) for i in range(n_walls-1)] n_pick = random_pick(range(n_walls), p=pr_pick) # if we select multiple walls to expand: if n_pick > 1: outward_xys = flatten(random_choice(walls, n=n_pick)) if not self.silent: print("Selected number of walls to expand:", n_pick) print("Surrounding xys to be taken: ", outward_xys) # if we select 1 wall to expand, we further choose whether to expand # the whole wall or a portion of wall. The prob to select whole wall # is 0.7, and select a portion is 0.3. else: n_pick = random_pick(["portion", "whole"], p=[0.3, 0.7]) if n_pick == "whole": outward_xys = random_choice(walls) if not self.silent: print("Selected number of walls to expand:", 1) print("Surrounding xys to be taken: ", outward_xys) else: outward_xys = self._slice_a_subsequence(random_choice(walls)) if not self.silent: print("A portion of 1 wall to expand.") print("Surrounding xys to be taken: ", outward_xys) return outward_xys
def _divide_xys(self, n): """ Randomly divide the xys into n continuous groups. Args: n (int): number of groups to have Returns: groups (list): the list of divided xys groups """ groups = [[] for i in range(n)] # randomly pick n xys(points) in plan xys = list(self.grids.keys()) idx = random_pick(indices(xys), size=n, replace=False) points = [xys[i] for i in idx] # assign each xy to the nearest point(l1 version of voronoi). for xy in xys: dists = [l1_dist((xy, point)) for point in points] min_dist = min(dists) groups[dists.index(min_dist)].append(xy) return groups
def _pick_a_room(self, ): """ Randomly pick a valid room. """ rooms = self._purge_room() return random_pick(rooms)
import numpy as np from numpy.random import choice as random_pick from Transition import Transition from Room import Room from Grid import Grid from Wall import Wall import Visual # ---------------------------- global functions ------------------------------ indices = lambda seq: range(len(seq)) flatten = lambda l: list(set([item for sublist in l for item in sublist])) purge_dict = lambda d: dict((k, v) for k, v in d.items() if v) l1_dist = lambda xys: abs(xys[0][0] - xys[1][0]) + abs(xys[0][1] - xys[1][1]) random_choice = lambda seq: seq[random_pick(indices(seq))] class Plan(object): """ The plan class implements the real-world plan objects. It's consisted of different types of rooms, and each room is consisted of a number of unit grids and enclosing walls. Besides the state attributes, it also has stats attribtues like total area, space efficiency etc. Inputs: ------- - function_params (dict): requirements for each function as a dict { 'function':{
from pprint import pprint import numpy as np from numpy.random import choice as random_pick from Transition import Transition from Room import Room from Grid import Grid from Wall import Wall import Visual # ---------------------------- global functions ------------------------------ indices = lambda seq: range(len(seq)) flatten = lambda l: list(set([item for sublist in l for item in sublist])) purge_dict = lambda d: dict((k, v) for k, v in d.items() if v) l1_dist = lambda xys: abs(xys[0][0] - xys[1][0]) + abs(xys[0][1] - xys[1][1]) random_choice = lambda seq: seq[random_pick(indices(seq))] class Plan(object): """ The plan class implements the real-world plan objects. It's consisted of different types of rooms, and each room is consisted of a number of unit grids and enclosing walls. Besides the state attributes, it also has stats attribtues like total area, space efficiency etc. Inputs: ------- - function_params (dict): requirements for each function as a dict { 'function':{ 'area':total area of each function,