def copy_garden(garden_state, rows, cols, sector_row, sector_col, prune_win_rows, prune_win_cols, step, prune_rate): garden = Garden(garden_state=garden_state, N=rows, M=cols, sector_rows=sector_row, sector_cols=sector_col, prune_window_rows=prune_win_rows, prune_window_cols=prune_win_cols, irr_threshold=IRR_THRESHOLD, step=step, prune_rate=prune_rate, animate=False) """ Copies the garden from the garden_state Args: garden_state (GardenState): If passed in, the simulator will initialize its state from the passed in state rows (int): Amount rows for the grid modeling the garden (N in paper). cols (int): Amount columns for the grid modeling the garden (M in paper). sector_row (int): Row size of a sector. sector_col (int): Column size of a sector. prune_win_rows (int): Row size of pruning window. prune_win_cols (int): Column size of pruning window. step (int): Distance between adjacent points in grid. prunte_rate (float): Prune rate. Return: A garden created from the garden state. """ return garden
def reset(self): self.garden = \ Garden( plants=self.PlantType.get_random_plants(self.seed, self.rows, self.cols, self.sector_rows, self.sector_cols), N=self.rows, M=self.cols, sector_rows=self.sector_rows, sector_cols=self.sector_cols, prune_window_rows=self.prune_window_rows, prune_window_cols=self.prune_window_cols, irr_threshold=IRR_THRESHOLD, step=self.step, plant_types=self.PlantType.plant_names, animate=False) self.plant_centers_original = np.copy(self.PlantType.plant_centers) self.plant_centers = np.copy(self.PlantType.plant_centers) self.non_plant_centers_original = np.copy( self.PlantType.non_plant_centers) self.non_plant_centers = np.copy(self.PlantType.non_plant_centers)
def run_simulation(args, run): start_time = time.time() # Set up params based on command line arguments and constants in simulator_presets.py preset = PLANT_PRESETS[args.setup] if preset["seed"]: np.random.seed(preset["seed"]) if args.setup == 'csv': plants = preset["plants"](args.csv_path) else: plants = preset["plants"]() daily_water = preset["daily-water"] if "daily-water" in preset else DAILY_WATER irrigation_policy = IRRIGATION_POLICIES[args.irrigator][ "policy"]() if args.irrigator in IRRIGATION_POLICIES else lambda _: None if args.setup == 'csv': plant_types = ['nastursium', 'marigold', 'dill', 'bok-choy', 'calendula', 'radish', 'borage', 'unknown'] else: plant_types = ['basil', 'thyme', 'oregano', 'lavender', 'bok-choy', 'parsley', 'sage', 'rosemary', 'chives', 'cilantro', 'dill', 'fennel', 'marjoram', 'tarragon'] # Initialize the garden garden = Garden(plants, NUM_X_STEPS, NUM_Y_STEPS, STEP, plant_types=plant_types, animate=(args.mode == 'a'), save=(args.mode == 's')) # Run the simulation for NUM_TIMESTEPS steps for i in range(NUM_TIMESTEPS): plants = garden.perform_timestep() print("--- %s seconds ---" % (time.time() - start_time)) # Display either graphs of garden data and the final garden state, or a full animation of garden timesteps if args.mode == 'p': plot_data(garden, NUM_TIMESTEPS) plot_garden(garden) elif args.mode == 'a': garden.show_animation() elif args.mode == 's': garden.save_final_step() # Save final state of garden for visualization path = args.save_path + '/' + ''.join(random.choice(string.ascii_lowercase) for _ in range(4)) + ".p" garden.save_plots(path) # Exports the plant data as a JSON file (for Helios visualization purposes) if args.export: export_results(plants, garden.logger, args.export)
def copy_garden(garden_state, rows, cols, sector_row, sector_col, prune_win_rows, prune_win_cols, step, prune_rate): garden = Garden(garden_state=garden_state, N=rows, M=cols, sector_rows=sector_row, sector_cols=sector_col, prune_window_rows=prune_win_rows, prune_window_cols=prune_win_cols, irr_threshold=IRR_THRESHOLD, step=step, prune_rate=prune_rate, animate=False) return garden
def reset(self): """Method called by the gym environment to reset the simulator.""" self.garden = \ Garden( plants=self.PlantType.get_plant_seeds(self.seed, self.rows, self.cols, self.sector_rows, self.sector_cols, randomize_seed_coords=self.randomize_seed_coords, plant_seed_config_file_path=self.plant_seed_config_file_path), N=self.rows, M=self.cols, sector_rows=self.sector_rows, sector_cols=self.sector_cols, prune_window_rows=self.prune_window_rows, prune_window_cols=self.prune_window_cols, irr_threshold=IRR_THRESHOLD, step=self.step, plant_type=self.PlantType, animate=False) ''' Uncomment line below to load from a garden file. ''' # self.garden, self.PlantType = pickle.load(open("garden_copy.pkl", "rb")) self.plant_centers_original = np.copy(self.PlantType.plant_centers) self.plant_centers = np.copy(self.PlantType.plant_centers) self.non_plant_centers_original = np.copy( self.PlantType.non_plant_centers) self.non_plant_centers = np.copy(self.PlantType.non_plant_centers)
class SimAlphaGardenWrapper(WrapperEnv): def __init__(self, max_time_steps, rows, cols, sector_rows, sector_cols, prune_window_rows, prune_window_cols, seed=None, step=1, dir_path="", randomize_seed_coords=False, plant_seed_config_file_path=None): """AlphaGarden's wrapper for Gym, inheriting basic functions from the WrapperEnv. Args: max_time_steps (int): the number of time steps a simulator runs before resetting. rows (int): Amount rows for the grid modeling the garden (N in paper). cols (int): Amount columns for the grid modeling the garden (M in paper). sector_rows (int): Row size of a sector. sector_cols (int): Column size of a sector. prune_window_rows (int): Row size of pruning window. prune_window_cols (int): Column size of pruning window seed (int): Value for "seeding" numpy's random state generator. NOT SEED OF PLANT. step (int): Distance between adjacent points in grid. dir_path (str): Directory location for saving experiment data. randomize_seed_coords (bool): Flag to randomize seed coordinates for given plant seed config file. plant_seed_config_file_path (str): File path for plant seed configuration. """ self.max_time_steps = max_time_steps super(SimAlphaGardenWrapper, self).__init__(max_time_steps) self.rows = rows self.cols = cols #: int: Number of sectors (representing the area observable to the agent at time t) in garden. self.num_sectors = (rows * cols) / (sector_rows * sector_cols) self.sector_rows = sector_rows self.sector_cols = sector_cols self.prune_window_rows = prune_window_rows self.prune_window_cols = prune_window_cols self.step = step self.randomize_seed_coords = randomize_seed_coords self.plant_seed_config_file_path = plant_seed_config_file_path self.PlantType = PlantType( ) #: :obj:`PlantType`: Available types of Plant objects (modeled). self.seed = seed self.reset() #: Reset simulator. self.curr_action = -1 #: int: Current action selected. 0 = no action, 1 = irrigation, 2 = pruning #: Configuration file parser for reinforcement learning with gym. self.config = configparser.ConfigParser() self.config.read('gym_config/config.ini') #: dict of [int,str]: Amount to water every square in a sector by. self.irr_actions = { 1: MAX_WATER_LEVEL, } self.plant_centers_original = [ ] #: Array of [int,int]: Initial seed locations [row, col]. self.plant_centers = [ ] #: Array of [int,int]: Seed locations [row, col] for sectors. self.non_plant_centers_original = [ ] #: Array of [int,int]: Initial locations without seeds [row, col]. self.non_plant_centers = [ ] #: Array of [int,int]: Locations without seeds [row, col] for sectors. self.centers_to_execute = [ ] #: Array of [int,int]: Locations [row, col] where to perform actions. self.actions_to_execute = [] #: List of int: Actions to perform. self.plant_radii = [ ] #: List of tuples (str, float): Tuple containing plant type it's plant radius. self.plant_heights = [ ] #: List of tuples (str, float): Tuple containing plant type it's plant height. self.dir_path = dir_path def get_state(self, multi=False): """Get state of a random sector defined by all local and global quantities. Args: multi (bool): flag for parallel processing Returns: Sector number and state associated with the sector. """ return self.get_data_collection_state(multi=multi) def get_full_state(self): """Get state of the sector defined by all local and global quantities. Returns: Structured array with water levels (float) and plant growth states (int) for grid points """ return np.dstack((self.garden.get_water_grid_full(), self.garden.get_plant_grid_full())) def get_random_centers(self): """Get plant locations and sampled random coordinates without plants. Note: TODO: One line on why we sample non_plant sectors. Returns: array of plant locations and sampled random coordinates without plants """ np.random.shuffle(self.non_plant_centers) return np.concatenate( (self.plant_centers, self.non_plant_centers[:int(PERCENT_NON_PLANT_CENTERS * NUM_PLANTS)])) def get_center_state(self, center, image=True, eval=False): """Get state of the sector defined by all local and global quantities. Args: center (Array of [int,int]): Location [row, col] of sector center image (bool): flag for image generation eval (bool): flag for evaluation Returns: Image and state associated with the sector if image true, only state otherwise. """ cc_per_plant = self.garden.get_cc_per_plant() # Amount of soil and number of grid points per plant type in which the specific plant type is the highest plant. global_cc_vec = np.append( self.rows * self.cols * self.step - np.sum(cc_per_plant), cc_per_plant) if not image: return global_cc_vec, \ np.dstack((self.garden.get_plant_prob(center), self.garden.get_water_grid(center), self.garden.get_health_grid(center))) cc_img = self.get_canopy_image(center, eval) return cc_img, global_cc_vec, \ np.dstack((self.garden.get_plant_prob(center), self.garden.get_water_grid(center), self.garden.get_health_grid(center))) def get_data_collection_state(self, multi=False): """Get state of a random sector defined by all local and global quantities. Args: multi (bool): flag for parallel processing Returns: Sector coordinate, global canopy cover vector and state associated with the sector. """ np.random.seed(random.randint(0, 99999999)) # TODO: don't need plant_in_bounds anymore. Remove. if len(self.actions_to_execute ) <= self.PlantType.plant_in_bounds and len( self.plant_centers) > 0: np.random.shuffle(self.plant_centers) center_to_sample = self.plant_centers[0] if not multi: self.plant_centers = self.plant_centers[1:] else: np.random.shuffle(self.non_plant_centers) center_to_sample = self.non_plant_centers[0] if not multi: self.non_plant_centers = self.non_plant_centers[1:] # Uncomment to make method for 2 plants deterministic # center_to_sample = (7, 15) # center_to_sample = (57, 57) cc_per_plant = self.garden.get_cc_per_plant() # Amount of soil and number of grid points per plant type in which the specific plant type is the highest plant. global_cc_vec = np.append( self.rows * self.cols * self.step - np.sum(cc_per_plant), cc_per_plant) plant_prob = self.garden.get_plant_prob(center_to_sample) return center_to_sample, global_cc_vec, \ np.dstack((self.garden.get_plant_prob(center_to_sample), self.garden.get_water_grid(center_to_sample), self.garden.get_health_grid(center_to_sample))), \ np.dstack((self.garden.get_plant_prob_full(), self.garden.get_water_grid_full(), self.garden.get_health_grid_full())) def get_canopy_image(self, center, eval): """Get image for canopy cover of the garden and save image to specified directory. Note: Circle sizes vary for the radii of the plant. Each shade of green in the simulated garden represents a different plant type. Stress causes the plants to progressively turn brown. Args: center (Array of [int,int]): Location [row, col] of sector center eval (bool): flag for evaluation. Returns: Directory path of saved scenes if eval is False, canopy image otherwise. """ if not eval: dir_path = self.dir_path self.garden.step = 1 # x_low, y_low, x_high, y_high = self.garden.get_sector_bounds(center) x_low, y_low, x_high, y_high = 0, 0, ROWS - 1, COLS - 1 fig, ax = plt.subplots() ax.set_xlim(y_low, y_high) ax.set_ylim(x_low, x_high) ax.set_aspect('equal') ax.axis('off') shapes = [] for plant in sorted([ plant for plant_type in self.garden.plants for plant in plant_type.values() ], key=lambda x: x.height, reverse=False): if x_low <= plant.row <= x_high and y_low <= plant.col <= y_high: self.plant_heights.append((plant.type, plant.height)) self.plant_radii.append((plant.type, plant.radius)) shape = plt.Circle((plant.col, plant.row) * self.garden.step, plant.radius, color=plant.color) shape_plot = ax.add_artist(shape) shapes.append(shape_plot) plt.gca().invert_yaxis() bbox0 = fig.get_tightbbox(fig.canvas.get_renderer()).padded(0.02) if not eval: r = os.urandom(16) file_path = dir_path + '/' + ''.join('%02x' % ord(chr(x)) for x in r) # file_path = dir_path + 'images/' + ''.join('%02x' % ord(chr(x)) for x in r) plt.savefig(file_path + '_cc.png', bbox_inches=bbox0) plt.close() return file_path else: buf = io.BytesIO() fig.savefig(buf, format="rgba", dpi=100, bbox_inches=bbox0) buf.seek(0) # img = np.reshape(np.frombuffer(buf.getvalue(), dtype=np.uint8), newshape=(235, 499, -1)) img = np.reshape(np.frombuffer(buf.getvalue(), dtype=np.uint8), newshape=(373, 373, -1)) img = img[..., :3] buf.close() plt.close() return img # TODO still needed? def plot_water_map(self, folder_path, water_grid, plants): plt.axis('off') plt.imshow(water_grid[:, :, 0], cmap='Blues', interpolation='nearest') for i in range(plants.shape[2]): p = plants[:, :, i] nonzero = np.nonzero(p) for row, col in zip(nonzero[0], nonzero[1]): plt.plot(col, row, marker='.', markersize=1, color="lime") plt.savefig(folder_path + "_water" + '.svg', bbox_inches='tight', pad_inches=0.02) plt.close() def get_garden_state(self): """Get state of garden defined by all local and global quantities. Returns: Structured array with plant, leaf, radius, water, health quantities for grid points """ return self.garden.get_garden_state() def get_radius_grid(self): """Get grid for plant radius representation. Returns: Structured array for grid of plant radius representation. """ return self.garden.get_radius_grid() def get_curr_action(self): """Get current action selected. Actions are for now 0 = no action, 1 = irrigation, 2 = pruning. Planting action and others may be added in the future. Returns: Current action (int). """ return self.curr_action def reward(self, state): # total_cc = np.sum(self.garden.leaf_grid) # cc_per_plant = [np.sum(self.garden.leaf_grid[:,:,i]) for i in range(self.garden.leaf_grid.shape[2])] # prob = cc_per_plant / total_cc # prob = prob[np.where(prob > 0)] # entropy = -np.sum(prob*np.log(prob)) # water_coef = self.config.getfloat('reward', 'water_coef') # cc_coef = self.config.getfloat('reward', 'cc_coef') # action_sum = self.sector_rows * self.sector_cols # water_used = self.irr_action * action_sum # return (cc_coef * total_cc) + (0 * entropy) + water_coef * np.sum(-1 * water_used/action_sum + 1) # TODO: update reward calculation for new state return 0 def take_action(self, center, action, time_step, eval=False): """Method called by the gym environment to execute an action. Args: center (Array of [int,int]): Location [row, col] of sector center action (int): Action for agent. 0 = no action, 1 = irrigation, 2 = pruning time_step (int): Time step of episode. eval (bool): flag for evaluation. Returns: Sector image (out) and updated environment state if eval is True, only the updated environment state otherwise. """ self.curr_action = action # State and action before performing a time step. cc_per_plant = self.garden.get_cc_per_plant() # Amount of soil and number of grid points per plant type in which the specific plant type is the highest plant. global_cc_vec = np.append( self.rows * self.cols * self.step - np.sum(cc_per_plant), cc_per_plant) # plant_grid = self.garden.get_plant_prob(center) # water_grid = self.garden.get_water_grid(center) # health_grid = self.garden.get_health_grid(center) plant_grid = self.garden.get_plant_prob_full() water_grid = self.garden.get_water_grid_full() health_grid = self.garden.get_health_grid_full() # action_vec = np.zeros(len(self.irr_actions) + 2) # Save canopy image before performing a time step. # if True: # sector_obs_per_day = int(NUM_PLANTS + PERCENT_NON_PLANT_CENTERS * NUM_PLANTS) # if ((time_step // sector_obs_per_day) >= PRUNE_DELAY) and time_step % sector_obs_per_day == 0: if self.curr_action >= 0: out = self.get_canopy_image(center, eval) if not eval: path = out # self.plot_water_map(path, self.garden.get_water_grid_full(), self.garden.get_plant_grid_full()) # action_vec = np.array(action) # np.save(path + '_action', action_vec) np.save(path + '_pr', self.garden.prune_rate) # np.savez(path + '.npz', plants=plant_grid, water=water_grid, global_cc=global_cc_vec, heights=self.plant_heights, radii=self.plant_radii) np.savez(path + '.npz', plants=plant_grid, water=water_grid, health=health_grid, global_cc=global_cc_vec) self.plant_heights = [] self.plant_radii = [] self.centers_to_execute.append(center) self.actions_to_execute.append(self.curr_action) # We want PERCENT_NON_PLANT_CENTERS of samples to come from non plant centers if len(self.actions_to_execute) < self.PlantType.plant_in_bounds + int( PERCENT_NON_PLANT_CENTERS * NUM_PLANTS): if eval: return out, self.get_full_state() return self.get_full_state() # Execute actions only if we have reached the number of actions threshold. self.garden.perform_timestep(sectors=self.centers_to_execute, actions=self.actions_to_execute) self.actions_to_execute = [] self.centers_to_execute = [] self.plant_centers = np.copy(self.plant_centers_original) self.non_plant_centers = np.copy(self.non_plant_centers_original) if eval: return out, self.get_full_state() return self.get_full_state() def take_multiple_actions(self, centers, actions): """ Updates plants at given centers with chosen actions. Args: centers (Array of [int,int]): Location [row, col] of sector center actions (Array of [int]): Actions. 0 = no action, 1 = irrigation, 2 = pruning """ self.garden.perform_timestep(sectors=centers, actions=actions) def reset(self): """Method called by the gym environment to reset the simulator.""" self.garden = \ Garden( plants=self.PlantType.get_plant_seeds(self.seed, self.rows, self.cols, self.sector_rows, self.sector_cols, randomize_seed_coords=self.randomize_seed_coords, plant_seed_config_file_path=self.plant_seed_config_file_path), N=self.rows, M=self.cols, sector_rows=self.sector_rows, sector_cols=self.sector_cols, prune_window_rows=self.prune_window_rows, prune_window_cols=self.prune_window_cols, irr_threshold=IRR_THRESHOLD, step=self.step, plant_type=self.PlantType, animate=False) ''' Uncomment line below to load from a garden file. ''' # self.garden, self.PlantType = pickle.load(open("garden_copy.pkl", "rb")) self.plant_centers_original = np.copy(self.PlantType.plant_centers) self.plant_centers = np.copy(self.PlantType.plant_centers) self.non_plant_centers_original = np.copy( self.PlantType.non_plant_centers) self.non_plant_centers = np.copy(self.PlantType.non_plant_centers) def show_animation(self): """Method called by the environment to display animations.""" self.garden.show_animation() def set_prune_rate(self, prune_rate): """Sets the prune rate in the garden. Args: prune_rate (float) """ self.garden.set_prune_rate(prune_rate) def set_irrigation_amount(self, irrigation_amount): """Sets the irrigation_amount in the garden. Args: irrigation_amount (float) """ self.garden.set_irrigation_amount(irrigation_amount) def get_metrics(self): """Evaluate metrics of garden. Return: Lists of: Garden Coverage, Garden Diversity, Garden's water use, performed actions. """ return self.garden.coverage, self.garden.diversity, self.garden.water_use, \ self.garden.actions, self.garden.mme1, self.garden.mme2 def get_prune_window_greatest_width(self, center): """Get the radius of the tallest (non occluded) plant inside prune window. Args: center (Array of [int,int]): Location [row, col] of sector center. Return: Float, radius of plant. """ return self.garden.get_prune_window_greatest_width(center) def get_simulator_state_copy(self): """Get the current stat of all simulator values to be able to restart at the current state. Return: GardenState object. """ return self.garden.get_simulator_state_copy()
class SimAlphaGardenWrapper(WrapperEnv): def __init__(self, max_time_steps, rows, cols, sector_rows, sector_cols, prune_window_rows, prune_window_cols, seed=None, step=1, dir_path="/"): super(SimAlphaGardenWrapper, self).__init__(max_time_steps) self.rows = rows self.cols = cols self.num_sectors = (rows * cols) / (sector_rows * sector_cols) self.sector_rows = sector_rows self.sector_cols = sector_cols self.prune_window_rows = prune_window_rows self.prune_window_cols = prune_window_cols self.step = step self.PlantType = PlantType() self.seed = seed self.reset() self.curr_action = -1 self.config = configparser.ConfigParser() self.config.read('gym_config/config.ini') # Amount to water every square in a sector by self.irr_actions = { 1: MAX_WATER_LEVEL, } self.plant_centers_original = [] self.plant_centers = [] self.non_plant_centers_original = [] self.non_plant_centers = [] self.centers_to_execute = [] self.actions_to_execute = [] self.plant_radii = [] self.plant_heights = [] self.dir_path = dir_path def get_state(self, multi=False): return self.get_data_collection_state(multi=multi) def get_full_state(self): return np.dstack((self.garden.get_water_grid_full(), self.garden.get_plant_grid_full())) def get_random_centers(self): np.random.shuffle(self.non_plant_centers) return np.concatenate( (self.plant_centers, self.non_plant_centers[:int(PERCENT_NON_PLANT_CENTERS * NUM_PLANTS)])) def get_center_state(self, center, eval=False): cc_per_plant = self.garden.get_cc_per_plant() global_cc_vec = np.append( self.rows * self.cols * self.step - np.sum(cc_per_plant), cc_per_plant) cc_img = self.get_canopy_image(center, eval) return cc_img, global_cc_vec, \ np.dstack((self.garden.get_plant_prob(center), self.garden.get_water_grid(center), self.garden.get_health_grid(center))) ''' Returns sector number and state associated with the sector. ''' def get_data_collection_state(self, multi=False): np.random.seed(random.randint(0, 99999999)) # TODO: don't need plant_in_bounds anymore. Remove. if len(self.actions_to_execute ) <= self.PlantType.plant_in_bounds and len( self.plant_centers) > 0: np.random.shuffle(self.plant_centers) center_to_sample = self.plant_centers[0] if not multi: self.plant_centers = self.plant_centers[1:] else: np.random.shuffle(self.non_plant_centers) center_to_sample = self.non_plant_centers[0] if not multi: self.non_plant_centers = self.non_plant_centers[1:] # center_to_sample = (7, 15) # center_to_sample = (57, 57) cc_per_plant = self.garden.get_cc_per_plant() global_cc_vec = np.append( self.rows * self.cols * self.step - np.sum(cc_per_plant), cc_per_plant) return center_to_sample, global_cc_vec, \ np.dstack((self.garden.get_plant_prob(center_to_sample), self.garden.get_water_grid(center_to_sample), self.garden.get_health_grid(center_to_sample))) def get_canopy_image(self, center, eval): if not eval: dir_path = self.dir_path self.garden.step = 1 x_low, y_low, x_high, y_high = self.garden.get_sector_bounds(center) # x_low, y_low, x_high, y_high = 0, 0, 149, 299 fig, ax = plt.subplots() ax.set_xlim(y_low, y_high) ax.set_ylim(x_low, x_high) ax.set_aspect('equal') ax.set_yticklabels([]) ax.set_xticklabels([]) ax.set_yticks([]) ax.set_xticks([]) ax.axis('off') shapes = [] for plant in sorted([ plant for plant_type in self.garden.plants for plant in plant_type.values() ], key=lambda x: x.height, reverse=False): if x_low <= plant.row <= x_high and y_low <= plant.col <= y_high: self.plant_heights.append((plant.type, plant.height)) self.plant_radii.append((plant.type, plant.radius)) shape = plt.Circle((plant.col, plant.row) * self.garden.step, plant.radius, color=plant.color) shape_plot = ax.add_artist(shape) shapes.append(shape_plot) plt.gca().invert_yaxis() if not eval: r = os.urandom(16) file_path = dir_path + '/' + ''.join('%02x' % ord(chr(x)) for x in r) plt.savefig(file_path + '_cc.png', bbox_inches='tight', pad_inches=0.02) plt.close() return file_path else: ax.margins(0) fig.tight_layout() fig.canvas.draw() image_from_plot = np.frombuffer(fig.canvas.tostring_rgb(), dtype=np.uint8) image_from_plot = image_from_plot.reshape( fig.canvas.get_width_height()[::-1] + (3, )) image_from_plot = image_from_plot[:, 13:-14] resized = cv2.resize(image_from_plot, (499, 391)) cropped = resized[78:-78] plt.close() return cropped def plot_water_map(self, folder_path, water_grid, plants): plt.axis('off') plt.imshow(water_grid[:, :, 0], cmap='Blues', interpolation='nearest') for i in range(plants.shape[2]): p = plants[:, :, i] nonzero = np.nonzero(p) for row, col in zip(nonzero[0], nonzero[1]): plt.plot(col, row, marker='.', markersize=1, color="lime") plt.savefig(folder_path + "_water" + '.svg', bbox_inches='tight', pad_inches=0.02) plt.close() def get_garden_state(self): return self.garden.get_garden_state() def get_radius_grid(self): return self.garden.get_radius_grid() def get_curr_action(self): return self.curr_action def reward(self, state): # total_cc = np.sum(self.garden.leaf_grid) # cc_per_plant = [np.sum(self.garden.leaf_grid[:,:,i]) for i in range(self.garden.leaf_grid.shape[2])] # prob = cc_per_plant / total_cc # prob = prob[np.where(prob > 0)] # entropy = -np.sum(prob*np.log(prob)) # water_coef = self.config.getfloat('reward', 'water_coef') # cc_coef = self.config.getfloat('reward', 'cc_coef') # action_sum = self.sector_rows * self.sector_cols # water_used = self.irr_action * action_sum # return (cc_coef * total_cc) + (0 * entropy) + water_coef * np.sum(-1 * water_used/action_sum + 1) #TODO: update reward calculation for new state return 0 ''' Method called by the gym environment to execute an action. Parameters: action - an integer. 0 = no action, 1 = irrigation, 2 = pruning Returns: state - state of the environment after irrigation ''' def take_action(self, center, action, time_step, eval=False): self.curr_action = action # State and action before performing a time step. cc_per_plant = self.garden.get_cc_per_plant() global_cc_vec = np.append( self.rows * self.cols * self.step - np.sum(cc_per_plant), cc_per_plant) plant_grid = self.garden.get_plant_prob(center) water_grid = self.garden.get_water_grid(center) health_grid = self.garden.get_health_grid(center) # action_vec = np.zeros(len(self.irr_actions) + 2) # Save canopy image before performing a time step. # if True: # if time_step % 100 == 0: if self.curr_action >= 0: out = self.get_canopy_image(center, eval) if not eval: path = out # self.plot_water_map(path, self.garden.get_water_grid_full(), self.garden.get_plant_grid_full()) action_vec = np.array(action) np.save(path + '_action', action_vec) # np.savez(path + '.npz', plants=plant_grid, water=water_grid, global_cc=global_cc_vec, heights=self.plant_heights, radii=self.plant_radii) np.savez(path + '.npz', plants=plant_grid, water=water_grid, health=health_grid, global_cc=global_cc_vec) self.plant_heights = [] self.plant_radii = [] self.centers_to_execute.append(center) self.actions_to_execute.append(self.curr_action) # We want PERCENT_NON_PLANT_CENTERS of samples to come from non plant centers if len(self.actions_to_execute) < self.PlantType.plant_in_bounds + int( PERCENT_NON_PLANT_CENTERS * NUM_PLANTS): if eval: return out, self.get_full_state() return self.get_full_state() # Execute actions only if we have reached the nubmer of actions threshold. self.garden.perform_timestep(sectors=self.centers_to_execute, actions=self.actions_to_execute) self.actions_to_execute = [] self.centers_to_execute = [] self.plant_centers = np.copy(self.plant_centers_original) self.non_plant_centers = np.copy(self.non_plant_centers_original) if eval: return out, self.get_full_state() return self.get_full_state() def take_multiple_actions(self, centers, actions): self.garden.perform_timestep(sectors=centers, actions=actions) ''' Method called by the gym environment to reset the simulator. ''' def reset(self): self.garden = \ Garden( plants=self.PlantType.get_random_plants(self.seed, self.rows, self.cols, self.sector_rows, self.sector_cols), N=self.rows, M=self.cols, sector_rows=self.sector_rows, sector_cols=self.sector_cols, prune_window_rows=self.prune_window_rows, prune_window_cols=self.prune_window_cols, irr_threshold=IRR_THRESHOLD, step=self.step, plant_types=self.PlantType.plant_names, animate=False) self.plant_centers_original = np.copy(self.PlantType.plant_centers) self.plant_centers = np.copy(self.PlantType.plant_centers) self.non_plant_centers_original = np.copy( self.PlantType.non_plant_centers) self.non_plant_centers = np.copy(self.PlantType.non_plant_centers) ''' Method called by the environment to display animations. ''' def show_animation(self): self.garden.show_animation() def get_metrics(self): return self.garden.coverage, self.garden.diversity, self.garden.water_use, self.garden.actions def get_prune_window_greatest_width(self, sector): return self.garden.get_prune_window_greatest_width(sector)