forge/trinity/smith.py (66 lines of code) (raw):
import ray, pickle
from pdb import set_trace as T
from forge.blade import core, lib
#Wrapper for remote async multi environments (realms)
#Supports both the native and vecenv per-env api
class VecEnvServer:
def __init__(self, config, args):
self.envs = [core.VecEnvRealm.remote(config, args, i)
for i in range(args.nRealm)]
#Reset the environments (only for vecenv api. This only returns
#initial empty buffers to avoid special-case first iteration
#code. Environments are persistent--attempting to reset them
#will result in undefined behavior. Don't do it after setup.
def reset(self):
recvs = [e.reset.remote() for e in self.envs]
return ray.get(recvs)
def step(self, actions):
recvs = ray.get([e.step.remote(pickle.dumps(a)) for e, a in
zip(self.envs, actions)])
recvs = [pickle.loads(e) for e in recvs]
return zip(*recvs)
class NativeServer:
def __init__(self, config, args, trinity):
self.envs = [core.NativeRealm.remote(trinity, config, args, i)
for i in range(args.nRealm)]
def step(self, actions=None):
recvs = [e.step.remote() for e in self.envs]
return ray.get(recvs)
#Use native api (runs full trajectories)
def run(self, swordUpdate=None):
recvs = [e.run.remote(swordUpdate) for e in self.envs]
recvs = ray.get(recvs)
return recvs
def send(self, swordUpdate):
[e.recvSwordUpdate.remote(swordUpdate) for e in self.envs]
#Example base runner class
class Blacksmith:
def __init__(self, config, args):
if args.render:
print('Enabling local test mode for render')
args.ray = 'local'
args.nRealm = 1
lib.ray.init(args.ray)
def render(self):
from forge.embyr.twistedserver import Application
Application(self.env, self.renderStep)
#Example runner using the (slower) vecenv api
#The actual vecenv spec was not designed for
#multiagent, so this is a best-effort facsimile
class VecEnv(Blacksmith):
def __init__(self, config, args, renderStep):
super().__init__(config, args)
self.env = VecEnvServer(config, args)
self.renderStep = renderStep
def step(self, actions):
return self.env.step(actions)
def reset(self):
return self.env.reset()
#Example runner using the (faster) native api
#Use the /forge/trinity/ spec for model code
class Native(Blacksmith):
def __init__(self, config, args, trinity):
super().__init__(config, args)
self.pantheon = trinity.pantheon(config, args)
self.trinity = trinity
self.env = NativeServer(config, args, trinity)
self.env.send(self.pantheon.model)
self.renderStep = self.step
self.idx = 0
#Runs full trajectories on each environment
#With no communication -- all on the env cores.
def run(self):
recvs = self.env.run(self.pantheon.model)
self.pantheon.step(recvs)
self.rayBuffers()
#Only for render -- steps are run per core
def step(self):
self.env.step()
#In early versions of ray, freeing memory was
#an issue. It is possible this has been patched.
def rayBuffers(self):
self.idx += 1
if self.idx % 32 == 0:
lib.ray.clearbuffers()