Skip to content

Commit 4c5703a

Browse files
committed
Schola v1.2.0
1 parent d564895 commit 4c5703a

51 files changed

Lines changed: 752 additions & 103 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Docs/Sphinx/examples/example_outlines/pong.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ The Pong environment features two agents playing a collaborative game of pong. T
66
.. csv-table::
77

88
"Num Agents", "2"
9-
"Observation Space", "DictSpace({'Camera_SCS_SceneColorHDR_RTF_RGBA8_R_W16_H16': make_camera_space(64,64,num_channels=1)})"
9+
"Observation Space", "DictSpace({'Camera_SCS_SceneColorHDR_RTF_RGBA8_R_W16_H16': make_camera_space(16,16,num_channels=1)})"
1010
"Action Space", "DictSpace({'Teleport_Y_50,00': DiscreteSpace(3)})"
1111
"Num Vectorized Copies", "2"
1212

Proto/GymConnector.proto

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,16 @@ message TrainingStateUpdate {
3737

3838
message TrainingDefinitionRequest {}
3939

40-
message GymConnectorStartRequest {}
40+
enum AutoResetType {
41+
SAMESTEP = 0; //Default value is SAMESTEP for backwards compatibility
42+
NEXTSTEP = 1;
43+
DISABLED = 2;
44+
}
45+
46+
47+
message GymConnectorStartRequest {
48+
AutoResetType autoreset_type = 1;
49+
}
4150

4251
//this could potentially get an observation at some point
4352
message GymConnectorStartResponse {}

Resources/python/schola/core/env.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,20 @@
1717

1818

1919
T = TypeVar("T")
20+
import sys
21+
22+
if sys.version_info >= (3, 11):
23+
from enum import StrEnum
24+
else:
25+
from backports.strenum import StrEnum
26+
27+
class AutoResetType(StrEnum):
28+
"""
29+
Enum for Auto Reset Types.
30+
"""
31+
DISABLED = "Disabled"
32+
SAME_STEP = "SameStep"
33+
NEXT_STEP = "NextStep"
2034

2135
# A Dictionary, with EnvIds as keys and a Dictionary of AgentIds to some TypeVar as Value.
2236
EnvAgentIdDict = Dict[int,Dict[int,T]]
@@ -33,6 +47,8 @@ class ScholaEnv:
3347
The verbosity level for the environment.
3448
environment_start_timeout : int, default=45
3549
The time to wait for the environment to start in seconds.
50+
auto_reset_type : AutoResetType, default=AutoResetType.SAME_STEP
51+
The type of auto-reset for the environment. See Gymnasium for more details on the different modes. Only Disabled, and SameStep are currently supported.
3652
3753
Attributes
3854
----------
@@ -66,6 +82,7 @@ def __init__(
6682
unreal_connection : UnrealConnection,
6783
verbosity:int=0,
6884
environment_start_timeout:int = 45,
85+
auto_reset_type : AutoResetType = AutoResetType.SAME_STEP,
6986
):
7087

7188
log_level = logging.WARNING
@@ -83,8 +100,14 @@ def __init__(
83100
atexit.register(self.close)
84101
self.gym_stub : gym_grpc.GymServiceStub = self.unreal_connection.connect_stubs(gym_grpc.GymServiceStub)[0]
85102

86-
#Server might be booting up if we have a standalone connection, so we wait for 15 to verify
103+
#Server might be booting up if we have a standalone connection, so we wait for 45 to verify
87104
start_msg = gym_communication.GymConnectorStartRequest()
105+
if(auto_reset_type == AutoResetType.DISABLED):
106+
start_msg.autoreset_type = gym_communication.DISABLED
107+
elif(auto_reset_type == AutoResetType.SAME_STEP):
108+
start_msg.autoreset_type = gym_communication.SAMESTEP
109+
elif(auto_reset_type == AutoResetType.NEXT_STEP):
110+
start_msg.autoreset_type = gym_communication.NEXTSTEP
88111
self.gym_stub.StartGymConnector(start_msg, timeout=environment_start_timeout, wait_for_ready=True)
89112

90113
logging.info("requesting environment definition")

Resources/python/schola/core/spaces/box.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,5 +140,5 @@ def __len__(self) -> int:
140140
return self.low.size
141141

142142
def process_data(self, msg : proto_points.FundamentalPoint) -> np.ndarray:
143-
output = np.asarray(msg.box_point.values).reshape(self.shape)
143+
output = np.asarray(msg.box_point.values,dtype=np.float32).reshape(self.shape)
144144
return output

Resources/python/schola/generated/GymConnector_pb2.py

Lines changed: 16 additions & 14 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Resources/python/schola/generated/GymConnector_pb2.pyi

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,11 @@ from typing import ClassVar as _ClassVar, Mapping as _Mapping, Optional as _Opti
99

1010
CLOSED: CommunicatorStatus
1111
DESCRIPTOR: _descriptor.FileDescriptor
12+
DISABLED: AutoResetType
1213
ERROR: CommunicatorStatus
1314
GOOD: CommunicatorStatus
15+
NEXTSTEP: AutoResetType
16+
SAMESTEP: AutoResetType
1417

1518
class EnvironmentReset(_message.Message):
1619
__slots__ = ["options", "seed"]
@@ -36,8 +39,10 @@ class EnvironmentStateUpdate(_message.Message):
3639
def __init__(self, reset: _Optional[_Union[EnvironmentReset, _Mapping]] = ..., step: _Optional[_Union[_StateUpdates_pb2.EnvironmentStep, _Mapping]] = ...) -> None: ...
3740

3841
class GymConnectorStartRequest(_message.Message):
39-
__slots__ = []
40-
def __init__(self) -> None: ...
42+
__slots__ = ["autoreset_type"]
43+
AUTORESET_TYPE_FIELD_NUMBER: _ClassVar[int]
44+
autoreset_type: AutoResetType
45+
def __init__(self, autoreset_type: _Optional[_Union[AutoResetType, str]] = ...) -> None: ...
4146

4247
class GymConnectorStartResponse(_message.Message):
4348
__slots__ = []
@@ -81,3 +86,6 @@ class TrainingStateUpdate(_message.Message):
8186

8287
class CommunicatorStatus(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
8388
__slots__ = []
89+
90+
class AutoResetType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
91+
__slots__ = []

Resources/python/schola/gym/env.py

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44
Implementation of gym.vector.VectorEnv backed by a Schola Environment.
55
"""
66

7-
from typing import Dict, List, Tuple, TypeVar, Union
7+
from typing import Dict, List, Optional, Tuple, TypeVar, Union
88
from schola.core.unreal_connections import UnrealConnection
9-
from schola.core.env import ScholaEnv
9+
from schola.core.env import AutoResetType, ScholaEnv
1010
from schola.core.error_manager import EnvironmentException
1111
import numpy as np
1212
import gymnasium as gym
@@ -16,6 +16,47 @@
1616

1717
T = TypeVar("T")
1818

19+
class GymEnv(gym.Env):
20+
21+
def __init__(self,
22+
unreal_connection: UnrealConnection,
23+
verbosity: int = 0):
24+
25+
self._env = ScholaEnv(
26+
unreal_connection,
27+
verbosity= verbosity,
28+
auto_reset_type=AutoResetType.DISABLED
29+
)
30+
self.id_manager = IdManager(self._env.ids)
31+
32+
self.observation_space = self._env.get_obs_space(env_id=0, agent_id=0)
33+
self.action_space = self._env.get_action_space(env_id=0, agent_id=0)
34+
try:
35+
assert self.id_manager.num_ids == 1, "GymEnv is designed for single-agent non-vectorized environments only. Please use GymVectorEnv for multi-agent or vectorized environments."
36+
except Exception as e:
37+
self._env.close()
38+
raise e
39+
40+
def close(self) -> None:
41+
"""
42+
Close the environment and release resources.
43+
"""
44+
super().close()
45+
# Close the environment connection
46+
return self._env.close()
47+
48+
def reset(self, seed: Optional[int] = None, options: Optional[Dict[str, str]] = None) -> Tuple[Dict[str, np.ndarray], Dict[int, Dict[str, str]]]:
49+
super().reset(seed=seed, options=options)
50+
obs, nested_infos = self._env.hard_reset(env_ids=[0],seeds=seed,options=options)
51+
return obs[0][0], nested_infos[0][0]
52+
53+
def step(self, action: Dict[str, np.ndarray]) -> Tuple[Dict[str, np.ndarray], float, bool, bool, Dict[str, str]]:
54+
self._env.send_actions({0: {0:action}}) # Send action for the first (and only) environment
55+
observations, rewards, terminateds, truncateds, nested_infos = self._env.poll()
56+
observations, rewards, terminated, truncated,infos = observations[0][0], rewards[0][0], terminateds[0][0], truncateds[0][0], nested_infos[0][0]
57+
return observations, rewards, terminated, truncated, infos
58+
59+
1960
class GymVectorEnv(gym.vector.VectorEnv):
2061
"""
2162
A Gym Vector Environment that wraps a Schola Environment.
@@ -40,7 +81,9 @@ def __init__(
4081
self._env = ScholaEnv(
4182
unreal_connection,
4283
verbosity,
84+
auto_reset_type=AutoResetType.SAME_STEP,
4385
)
86+
4487
self.id_manager = IdManager(self._env.ids)
4588
# we just use the default UID to get the shared definition
4689
single_obs_space = self._env.get_obs_space(*self.id_manager[0])
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Copyright (c) 2025 Advanced Micro Devices, Inc. All Rights Reserved.
2+
3+
from gymnasium.spaces import Dict
4+
from gymnasium import Env, Wrapper
5+
6+
7+
class PopActionWrapper(Wrapper):
8+
"""
9+
A wrapper that pops the action from the environment's action space.
10+
This is useful for environments where the action space is a dictionary
11+
and we want to use only one of the actions.
12+
"""
13+
14+
def __init__(self, env: Env):
15+
super().__init__(env)
16+
assert isinstance(env.action_space, Dict), "Action space must be a Dictionary Space."
17+
# Pop the first action from the action space
18+
self.key, self.action_space = list(env.action_space.spaces.items())[0]
19+
20+
def step(self, action):
21+
"""
22+
Step the environment with the given action.
23+
"""
24+
return self.env.step({self.key: action})

Resources/python/schola/ray/env.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import logging
99

1010
from schola.core.unreal_connections import UnrealConnection
11-
from schola.core.env import ScholaEnv, EnvAgentIdDict
11+
from schola.core.env import AutoResetType, ScholaEnv, EnvAgentIdDict
1212
from schola.core.spaces import (
1313
DictSpace,
1414
)
@@ -72,7 +72,7 @@ def __init__(
7272
):
7373
self.first_poll = True
7474

75-
self._env = ScholaEnv(unreal_connection, verbosity)
75+
self._env = ScholaEnv(unreal_connection, verbosity, auto_reset_type=AutoResetType.SAME_STEP)
7676
self.last_reset_obs = {}
7777
self.last_reset_infos = {}
7878

@@ -225,3 +225,4 @@ def try_reset(
225225

226226
def stop(self) -> None:
227227
self._env.close()
228+

0 commit comments

Comments
 (0)