File size: 4,021 Bytes
23a59ea
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
import importlib
import warnings
warnings.filterwarnings('ignore')

import gymnasium as gym

from envs.wrappers.vectorized_multitask import make_vectorized_multitask_env
from envs.wrappers.render import Render


# Every domain used to be imported eagerly here, which means one missing
# dependency took down *all* tasks rather than just its own -- painful on any
# partial install, and fatal when deploying (a hosted demo cannot install a
# Vulkan ICD for ManiSkill, but should still serve the other nine domains).
# Each domain is now loaded behind a guard, generalising the pattern that was
# already used for Atari: on failure the name binds to a stub that raises the
# original error, but only if that domain's task is actually requested.
# `AVAILABLE_DOMAINS` lets a caller filter its task list up front instead.
#
# The guard catches Exception, not just ImportError, because these domains fail
# in more ways than a missing module: a native renderer with no GL/Vulkan
# context available typically raises RuntimeError or OSError at import time.
_IMPORT_ERRORS = {}
AVAILABLE_DOMAINS = set()


def _load_domain(module, domains, hint):
	"""Return envs.<module>.make_env, or a stub explaining why it is unavailable."""
	try:
		fn = importlib.import_module(f'envs.{module}').make_env
	except Exception as err:
		_IMPORT_ERRORS[module] = err

		def _unavailable(cfg, _m=module, _h=hint, _e=err):
			raise ImportError(
				f'The {_m} domain is not available in this installation ({_h}). '
				f'Original import error: {type(_e).__name__}: {_e}'
			)
		return _unavailable
	AVAILABLE_DOMAINS.update(domains)
	return fn


make_dm_control_env = _load_domain(
	'dmcontrol', ('dmcontrol', 'dmcontrol-ext'), 'needs dm-control and a headless MuJoCo GL backend, e.g. MUJOCO_GL=egl')
make_maniskill_env = _load_domain(
	'maniskill', ('maniskill',), 'needs mani_skill, and SAPIEN requires a Vulkan ICD')
make_metaworld_env = _load_domain(
	'metaworld', ('metaworld',), 'needs the metaworld fork and a headless MuJoCo GL backend')
make_mujoco_env = _load_domain(
	'mujoco', ('mujoco',), 'needs mujoco and a headless GL backend')
make_box2d_env = _load_domain(
	'box2d', ('box2d',), 'needs box2d-py (built with swig) and pygame')
make_robodesk_env = _load_domain(
	'robodesk', ('robodesk',), 'needs robodesk and a headless MuJoCo GL backend')
make_ogbench_env = _load_domain(
	'ogbench', ('ogbench',), 'needs ogbench and a headless MuJoCo GL backend')
make_pygame_env = _load_domain(
	'pygame', ('pygame',), 'needs pygame; set SDL_VIDEODRIVER=dummy when headless')
make_atari_env = _load_domain(
	'atari', ('atari',), 'needs ale_py>=0.10 for continuous-action Atari')


def unavailable_domains():
	"""Map of domain module -> the exception that stopped it importing."""
	return dict(_IMPORT_ERRORS)


def make_env(cfg):
	"""
	Make an environment for world-model experiments.
	"""
	gym.logger.set_level(40)
	if not cfg.child_env:
		env = make_vectorized_multitask_env(cfg, make_env)
	else:
		env = None
		for fn in [
			make_dm_control_env, make_maniskill_env, make_metaworld_env,
			make_mujoco_env, make_box2d_env, make_robodesk_env,
			make_ogbench_env, make_pygame_env, make_atari_env,
		]:
			try:
				env = fn(cfg)
				break
			except ValueError as e:
				if 'Unknown task' in str(e):
					continue
				else:
					raise e
		if env is None:
			raise ValueError(f'Failed to make environment "{cfg.task}": please verify that dependencies are installed and that the task exists.')
		assert cfg.num_envs == 1 or cfg.get('obs', 'state') == 'state', \
			'Vectorized environments only support state observations.'
		if cfg.save_video and cfg.get('num_demos', 0) > 0:
			env = Render(env, cfg)
		print(f'[Rank {cfg.rank}] Created env for task {cfg.task}')
	try: # Dict
		cfg.obs_shape = {k: v.shape for k, v in env.observation_space.spaces.items()}
	except: # Box
		cfg.obs_shape = {cfg.get('obs', 'state'): env.observation_space.shape}
	cfg.action_dim = env.action_space.shape[0]
	cfg.episode_length = env.max_episode_steps
	return env