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)