safe_rl/pg/buffer.py (64 lines of code) (raw):

import numpy as np from safe_rl.utils.mpi_tools import mpi_statistics_scalar from safe_rl.pg.utils import combined_shape, \ keys_as_sorted_list, \ values_as_sorted_list, \ discount_cumsum, \ EPS class CPOBuffer: def __init__(self, size, obs_shape, act_shape, pi_info_shapes, gamma=0.99, lam=0.95, cost_gamma=0.99, cost_lam=0.95): self.obs_buf = np.zeros(combined_shape(size, obs_shape), dtype=np.float32) self.act_buf = np.zeros(combined_shape(size, act_shape), dtype=np.float32) self.adv_buf = np.zeros(size, dtype=np.float32) self.rew_buf = np.zeros(size, dtype=np.float32) self.ret_buf = np.zeros(size, dtype=np.float32) self.val_buf = np.zeros(size, dtype=np.float32) self.cadv_buf = np.zeros(size, dtype=np.float32) # cost advantage self.cost_buf = np.zeros(size, dtype=np.float32) # costs self.cret_buf = np.zeros(size, dtype=np.float32) # cost return self.cval_buf = np.zeros(size, dtype=np.float32) # cost value self.logp_buf = np.zeros(size, dtype=np.float32) self.pi_info_bufs = {k: np.zeros([size] + list(v), dtype=np.float32) for k,v in pi_info_shapes.items()} self.sorted_pi_info_keys = keys_as_sorted_list(self.pi_info_bufs) self.gamma, self.lam = gamma, lam self.cost_gamma, self.cost_lam = cost_gamma, cost_lam self.ptr, self.path_start_idx, self.max_size = 0, 0, size def store(self, obs, act, rew, val, cost, cval, logp, pi_info): assert self.ptr < self.max_size # buffer has to have room so you can store self.obs_buf[self.ptr] = obs self.act_buf[self.ptr] = act self.rew_buf[self.ptr] = rew self.val_buf[self.ptr] = val self.cost_buf[self.ptr] = cost self.cval_buf[self.ptr] = cval self.logp_buf[self.ptr] = logp for k in self.sorted_pi_info_keys: self.pi_info_bufs[k][self.ptr] = pi_info[k] self.ptr += 1 def finish_path(self, last_val=0, last_cval=0): path_slice = slice(self.path_start_idx, self.ptr) rews = np.append(self.rew_buf[path_slice], last_val) vals = np.append(self.val_buf[path_slice], last_val) deltas = rews[:-1] + self.gamma * vals[1:] - vals[:-1] self.adv_buf[path_slice] = discount_cumsum(deltas, self.gamma * self.lam) self.ret_buf[path_slice] = discount_cumsum(rews, self.gamma)[:-1] costs = np.append(self.cost_buf[path_slice], last_cval) cvals = np.append(self.cval_buf[path_slice], last_cval) cdeltas = costs[:-1] + self.gamma * cvals[1:] - cvals[:-1] self.cadv_buf[path_slice] = discount_cumsum(cdeltas, self.cost_gamma * self.cost_lam) self.cret_buf[path_slice] = discount_cumsum(costs, self.cost_gamma)[:-1] self.path_start_idx = self.ptr def get(self): assert self.ptr == self.max_size # buffer has to be full before you can get self.ptr, self.path_start_idx = 0, 0 # Advantage normalizing trick for policy gradient adv_mean, adv_std = mpi_statistics_scalar(self.adv_buf) self.adv_buf = (self.adv_buf - adv_mean) / (adv_std + EPS) # Center, but do NOT rescale advantages for cost gradient cadv_mean, _ = mpi_statistics_scalar(self.cadv_buf) self.cadv_buf -= cadv_mean return [self.obs_buf, self.act_buf, self.adv_buf, self.cadv_buf, self.ret_buf, self.cret_buf, self.logp_buf] + values_as_sorted_list(self.pi_info_bufs)