def env(self): ps = DummyPhysicalSystem() rg = DummyReferenceGenerator() rf = DummyRewardFunction() vs = DummyVisualization() env = self.test_class(physical_system=ps, reference_generator=rg, reward_function=rf, visualization=vs) return env
def test_limits(self, number_states, state_filter, expected_result): ps = DummyPhysicalSystem(state_length=number_states) rg = DummyReferenceGenerator() rf = DummyRewardFunction() vs = DummyVisualization() env = self.test_class(physical_system=ps, reference_generator=rg, reward_function=rf, visualization=vs, state_filter=state_filter) assert all(env.limits == expected_result)
def env(self): ps = DummyPhysicalSystem() rg = DummyReferenceGenerator() rf = DummyRewardFunction() vs = () cb = DummyCallback() cm = DummyConstraintMonitor(2) env = self.test_class(physical_system=ps, reference_generator=rg, reward_function=rf, visualization=vs, constraints=cm, callbacks=[cb]) return env
class TestRewardFunction: @staticmethod def mock_standard_reward(*_): return 1 @staticmethod def mock_limit_violation_reward(*_): return -1 @staticmethod def mock_reward_function(*_): return 'Reward Function called' @pytest.mark.parametrize( "observed_states", (['dummy_state_0'], ['dummy_state_0', 'dummy_state_1'])) def test_initialization(self, observed_states): RewardFunction(observed_states) @pytest.mark.parametrize( "reward_function, physical_system, reference_generator, expected_observed_states", ((RewardFunction('dummy_state_0'), DummyPhysicalSystem(), DummyReferenceGenerator(), ['dummy_state_0']), (RewardFunction(['dummy_state_0', 'dummy_state_2']), DummyPhysicalSystem(3), DummyReferenceGenerator(), ['dummy_state_0', 'dummy_state_2']), (RewardFunction('all'), DummyPhysicalSystem(3), DummyReferenceGenerator(), ['dummy_state_0', 'dummy_state_1', 'dummy_state_2']), (RewardFunction('currents'), DummyPhysicalSystem( 3, 'i'), DummyReferenceGenerator(), ['i_0', 'i_1', 'i_2']), (RewardFunction(['voltages']), DummyPhysicalSystem( 3, 'u'), DummyReferenceGenerator(), ['u_0', 'u_1', 'u_2']))) def test_set_modules(self, monkeypatch, reward_function, physical_system, reference_generator, expected_observed_states): reward_function.set_modules(physical_system, reference_generator) assert np.all(reward_function._observed_states == np.isin( physical_system.state_names, expected_observed_states)) @pytest.mark.parametrize( "reward_function, physical_system, reference_generator, expected_observed_states", ( (RewardFunction(['currents', 'voltages']), DummyPhysicalSystem(5), DummyReferenceGenerator(), ['i_a', 'i_e', 'u_a']), (RewardFunction(['currents', 'omega']), DummyPhysicalSystem(5), DummyReferenceGenerator(), ['omega', 'i_a', 'i_e']), (RewardFunction(['omega', 'voltages']), DummyPhysicalSystem(5), DummyReferenceGenerator(), ['omega', 'u_a']), (RewardFunction(['currents', 'omega', 'voltages' ]), DummyPhysicalSystem(5), DummyReferenceGenerator(), ['omega', 'i_a', 'i_e', 'u_a']), )) def test_set_modules_combined_observed_states(self, monkeypatch, reward_function, physical_system, reference_generator, expected_observed_states): physical_states = ['i_a', 'i_e', 'u_a', 'omega', 'torque'] physical_system._state_names = physical_states reward_function.set_modules(physical_system, reference_generator) assert np.all(reward_function._observed_states == np.isin( physical_system.state_names, expected_observed_states)) @pytest.mark.parametrize( "physical_system, reference_generator", ((DummyPhysicalSystem(3), DummyReferenceGenerator()), )) @pytest.mark.parametrize("observed_state_idx, violated_state_idx", (([0, 1], [2]), ([0, 1, 2], []), ([2], [1, 2]))) def test_reward(self, monkeypatch, physical_system, reference_generator, observed_state_idx, violated_state_idx): observed_states = list( np.array(physical_system.state_names)[observed_state_idx]) rf = RewardFunction(observed_states) rf.set_modules(physical_system, reference_generator) monkeypatch.setattr(rf, "_reward", self.mock_standard_reward) monkeypatch.setattr(rf, "_limit_violation_reward", self.mock_limit_violation_reward) state = np.ones_like(physical_system.state_names, dtype=float) * 0.5 state[violated_state_idx] = 1.5 reward, done = rf.reward(state, None) if np.any(np.isin(observed_state_idx, violated_state_idx)): assert reward == -1 assert done else: assert reward == 1 assert not done # Test negative limit violations state[violated_state_idx] = -1.5 reward, done = rf.reward(state, None) if np.any(np.isin(observed_state_idx, violated_state_idx)): assert reward == -1 assert done else: assert reward == 1 assert not done def test_call(self, monkeypatch): rf = RewardFunction() monkeypatch.setattr(rf, "reward", self.mock_reward_function) result = rf(1, 2, 3) assert result == 'Reward Function called'
class TestElectricMotorEnvironment: test_class = ElectricMotorEnvironment key = '' @pytest.fixture def env(self): ps = DummyPhysicalSystem() rg = DummyReferenceGenerator() rf = DummyRewardFunction() vs = DummyVisualization() cb = DummyCallback() env = self.test_class(physical_system=ps, reference_generator=rg, reward_function=rf, visualization=vs, callbacks=[cb]) return env def test_make(self): if self.key != '': env = gem.make(self.key) assert type(env) == self.test_class @pytest.mark.parametrize( "physical_system, reference_generator, reward_function, state_filter, visualization, callbacks, kwargs", [ (DummyPhysicalSystem, DummyReferenceGenerator, DummyRewardFunction, None, None, [], {}), (DummyPhysicalSystem(2), DummyReferenceGenerator, DummyRewardFunction(), ['dummy_state_0' ], ConsolePrinter, [DummyCallback()], { 'a': 1, 'b': 2 }), (DummyPhysicalSystem(10), DummyReferenceGenerator(), DummyRewardFunction(observed_states=['dummy_state_0']), [ 'dummy_state_0', 'dummy_state_2' ], None, [DummyCallback(), DummyCallback()], {}), ]) def test_initialization(self, monkeypatch, physical_system, reference_generator, reward_function, state_filter, visualization, callbacks, kwargs): with monkeypatch.context() as m: instantiate_dict.clear() m.setattr(gym_electric_motor.core, "instantiate", mock_instantiate) env = gym_electric_motor.core.ElectricMotorEnvironment( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, visualization=visualization, state_filter=state_filter, callbacks=callbacks, **kwargs) # Assertions that the Keys are passed correctly to the instantiate fct assert physical_system == instantiate_dict[PhysicalSystem]['key'] assert reference_generator == instantiate_dict[ReferenceGenerator][ 'key'] assert reward_function == instantiate_dict[RewardFunction]['key'] # Assertions that the modules that the instantiate functions returns are set correctly to the properties assert env.physical_system == instantiate_dict[PhysicalSystem][ 'instance'] assert env.reference_generator == instantiate_dict[ReferenceGenerator][ 'instance'] assert env.reward_function == instantiate_dict[RewardFunction][ 'instance'] # Assertions for correct spaces assert env.action_space == instantiate_dict[PhysicalSystem][ 'instance'].action_space, 'Wrong action space' if state_filter is None: assert Tuple( (instantiate_dict[PhysicalSystem]['instance'].state_space, instantiate_dict[ReferenceGenerator]['instance'].reference_space)) \ == env.observation_space, 'Wrong observation space' else: state_idxs = np.isin(physical_system.state_names, state_filter) state_space = Box( instantiate_dict[PhysicalSystem] ['instance'].state_space.low[state_idxs], instantiate_dict[PhysicalSystem] ['instance'].state_space.high[state_idxs], ) assert Tuple( (state_space, instantiate_dict[ReferenceGenerator] ['instance'].reference_space )) == env.observation_space, 'Wrong observation space' assert env.reward_range == instantiate_dict[RewardFunction][ 'instance'].reward_range, 'Wrong reward range' # Test Correct passing of kwargs assert physical_system != DummyPhysicalSystem or env.physical_system.kwargs == kwargs assert reference_generator, DummyReferenceGenerator or env.reference_generator.kwargs == kwargs assert reward_function != DummyRewardFunction or env.reward_function.kwargs == kwargs assert visualization != DummyVisualization or env._visualization.kwargs == kwargs for callback in callbacks: assert callback._env == env def test_reset(self, env): ps = env.physical_system rg = env.reference_generator rf = env.reward_function vs = env._visualization cbs = env._callbacks rf.last_state = rf.last_reference = ps.state = rg.get_reference_state = vs.reference_trajectory = None # Initial amount of resets for callback in cbs: assert callback.reset_begin == 0 assert callback.reset_end == 0 state, ref = env.reset() # The corresponding callback functions should've been called for callback in cbs: assert callback.reset_begin == 1 assert callback.reset_end == 1 assert ( state, ref ) in env.observation_space, 'Returned values not in observation space' assert np.all(np.all(state == ps.state) ), 'Returned state is not the physical systems state' assert np.all( ref == rg.reference_observation ), 'Returned reference is not the reference generators reference' assert np.all(state == rg.get_reference_state ), 'Incorrect state passed to the reference generator' assert rf.last_state == state, 'Incorrect state passed to the Reward Function' assert rf.last_reference == rg.reference_array, 'Incorrect Reference passed to the reward function' @pytest.mark.parametrize('action, set_done', [(0, False), (-1, False), (1, False), (2, True)]) def test_step(self, env, action, set_done): ps = env.physical_system rg = env.reference_generator rf = env.reward_function cbs = env._callbacks rf.set_done(set_done) with pytest.raises(Exception): env.step( action ), 'Environment goes through the step without previous reset' env.reset() # Callback's step inital step values for callback in cbs: assert callback.step_begin == 0 assert callback.step_end == 0 (state, reference), reward, done, _ = env.step(action) # Each of callback's step functions were called in step for callback in cbs: assert callback.step_begin == 1 assert callback.step_end == 1 assert np.all( state == ps.state[env.state_filter] ), 'Returned state and Physical Systems state are not equal' assert rg.get_reference_state == ps.state,\ 'State passed to the Reference Generator not equal to Physical System state' assert rg.get_reference_obs_state == ps.state, \ 'State passed to the Reference Generator not equal to Physical System state' assert ps.action == action, 'Action passed to Physical System not equal to selected action' assert reward == -1 if set_done else 1 assert done == set_done # If episode terminated, no further step without reset if set_done: with pytest.raises(Exception): env.step(action) def test_close(self, env): ps = env.physical_system rg = env.reference_generator rf = env.reward_function vs = env._visualization cbs = env._callbacks # Callback's step inital close value for callback in cbs: assert callback.close == 0 env.close() # Callback's close function was called on close for callback in cbs: assert callback.close == 1 assert ps.closed, 'Physical System was not closed' assert rf.closed, 'Reward Function was not closed' assert rg.closed, 'Reference Generator was not closed' assert vs.closed, 'Visualization was not closed' @pytest.mark.parametrize("reference_generator", (DummyReferenceGenerator(), )) def test_reference_generator_change(self, env, reference_generator): env.reset() env.reference_generator = reference_generator assert env.reference_generator == reference_generator, 'Reference Generator was not changed' # Without Reset an Exception has to be thrown with pytest.raises(Exception): env.step(env.action_space.sample( )), 'After Reference Generator change was no reset required' env.reset() # No Exception raised env.step(env.action_space.sample()) @pytest.mark.parametrize("reward_function", (DummyRewardFunction(), )) def test_reward_function_change(self, env, reward_function): env.reset() reward_function.set_modules( physical_system=env.physical_system, reference_generator=env.reference_generator) env.reward_function = reward_function assert env.reward_function == reward_function, 'Reward Function was not changed' # Without Reset an Exception has to be thrown with pytest.raises(Exception): env.step(env.action_space.sample() ), 'After Reward Function change was no reset required' env.reset() # No Exception raised env.step(env.action_space.sample()) @pytest.mark.parametrize( "number_states, state_filter, expected_result", ((1, ['dummy_state_0'], [10]), (3, ['dummy_state_0', 'dummy_state_1', 'dummy_state_2' ], [10, 20, 30]), (3, ['dummy_state_1'], [20]))) def test_limits(self, number_states, state_filter, expected_result): ps = DummyPhysicalSystem(state_length=number_states) rg = DummyReferenceGenerator() rf = DummyRewardFunction() vs = DummyVisualization() env = self.test_class(physical_system=ps, reference_generator=rg, reward_function=rf, visualization=vs, state_filter=state_filter) assert all(env.limits == expected_result)
class TestRewardFunction: @staticmethod def mock_standard_reward(*_): return 1 @staticmethod def mock_limit_violation_reward(*_): return -1 @staticmethod def mock_reward_function(*_): return 'Reward Function called' @pytest.mark.parametrize( "observed_states", (['dummy_state_0'], ['dummy_state_0', 'dummy_state_1'])) def test_initialization(self, observed_states): _ = RewardFunction(observed_states) @pytest.mark.parametrize( "reward_function, physical_system, reference_generator", ( (RewardFunction(['dummy_state_0']), DummyPhysicalSystem(), DummyReferenceGenerator()), (RewardFunction(['dummy_state_0', 'dummy_state_2']), DummyPhysicalSystem(3), DummyReferenceGenerator()), (RewardFunction( ['all']), DummyPhysicalSystem(3), DummyReferenceGenerator()), )) def test_set_modules(self, monkeypatch, reward_function, physical_system, reference_generator): observed_states = list(reward_function._observed_states.keys()) reward_function.set_modules(physical_system, reference_generator) if 'all' in observed_states: observed_states = physical_system.state_names assert np.all(reward_function._observed_states == np.isin( physical_system.state_names, observed_states)) assert np.all(reward_function._limits == physical_system.limits / abs(physical_system.limits)) @pytest.mark.parametrize( "physical_system, reference_generator", ((DummyPhysicalSystem(3), DummyReferenceGenerator()), )) @pytest.mark.parametrize("observed_state_idx, violated_state_idx", (([0, 1], [2]), ([0, 1, 2], []), ([2], [1, 2]))) def test_reward(self, monkeypatch, physical_system, reference_generator, observed_state_idx, violated_state_idx): observed_states = list( np.array(physical_system.state_names)[observed_state_idx]) rf = RewardFunction(observed_states) rf.set_modules(physical_system, reference_generator) monkeypatch.setattr(rf, "_reward", self.mock_standard_reward) monkeypatch.setattr(rf, "_limit_violation_reward", self.mock_limit_violation_reward) state = np.ones_like(physical_system.state_names, dtype=float) * 0.5 state[violated_state_idx] = 1.5 reward, done = rf.reward(state, None) if np.any(np.isin(observed_state_idx, violated_state_idx)): assert reward == -1 assert done else: assert reward == 1 assert not done # Test negative limit violations state[violated_state_idx] = -1.5 reward, done = rf.reward(state, None) if np.any(np.isin(observed_state_idx, violated_state_idx)): assert reward == -1 assert done else: assert reward == 1 assert not done def test_call(self, monkeypatch): rf = RewardFunction() monkeypatch.setattr(rf, "reward", self.mock_reward_function) result = rf(1, 2) assert result == 'Reward Function called'
class TestConstraintMonitor: @pytest.mark.parametrize( ['ps', 'limit_constraints', 'expected_observed_states'], [[ DummyPhysicalSystem(3), ['dummy_state_0', 'dummy_state_2'], ['dummy_state_0', 'dummy_state_2'] ], [DummyPhysicalSystem(1), ['dummy_state_0'], ['dummy_state_0']], [ DummyPhysicalSystem(2), ['all_states'], ['dummy_state_0', 'dummy_state_1'] ]]) def test_limit_constraint_setting(self, ps, limit_constraints, expected_observed_states): cm = ConstraintMonitor(limit_constraints=limit_constraints) cm.set_modules(ps) assert cm.constraints[ 0]._observed_state_names == expected_observed_states @pytest.mark.parametrize( 'constraints', [[lambda state: 0.0, DummyConstraint(), DummyConstraint()]]) @pytest.mark.parametrize('ps', [DummyPhysicalSystem(1)]) def test_additional_constraint_setting(self, ps, constraints): cm = ConstraintMonitor(additional_constraints=constraints) cm.set_modules(ps) assert all(constraint in cm.constraints for constraint in constraints) @pytest.mark.parametrize( 'additional_constraints', [[lambda state: 0.0, DummyConstraint(), DummyConstraint()]]) @pytest.mark.parametrize('limit_constraints', [['all_states'], ['dummy_state_0'], []]) @pytest.mark.parametrize('ps', [DummyPhysicalSystem(1)]) def test_set_modules(self, ps, limit_constraints, additional_constraints): cm = ConstraintMonitor(limit_constraints, additional_constraints) cm.set_modules(ps) assert all(constraint.modules_set for constraint in cm.constraints if isinstance(constraint, DummyConstraint)) assert all(constraint._observed_states is not None for constraint in cm.constraints if isinstance(constraint, LimitConstraint)) @pytest.mark.parametrize(['violations', 'expected_violation_degree'], [ [(0.5, 0.8, 0.0, 1.0), 1.0], [(0.5, 0.8, 0.0), 0.8], [(0.5, ), 0.5], [(0.0, ), 0.0], ]) @pytest.mark.parametrize('ps', [DummyPhysicalSystem(1)]) def test_max_merge_violations(self, ps, violations, expected_violation_degree): cm = ConstraintMonitor(merge_violations='max') cm.set_modules(ps) cm._merge_violations(violations) @pytest.mark.parametrize(['violations', 'expected_violation_degree'], [ [(0.5, 0.8, 0.0, 1.0), 1.0], [(0.5, 0.8, 0.0), 0.9], [(0.5, ), 0.5], [(0.0, ), 0.0], ]) @pytest.mark.parametrize('ps', [DummyPhysicalSystem(1)]) def test_product_merge_violations(self, ps, violations, expected_violation_degree): cm = ConstraintMonitor(merge_violations='product') cm.set_modules(ps) cm._merge_violations(violations) @pytest.mark.parametrize( ['merging_fct', 'violations', 'expected_violation_degree'], [ [lambda *violations: 1.0, (0.5, 0.8, 0.0, 1.0), 1.0], [lambda *violations: 0.756, (0.5, 0.8, 0.0), 0.756], [lambda *violations: 0.123, (0.5, ), 0.123], [lambda *violations: 0.0, (0.0, ), 0.0], ]) @pytest.mark.parametrize('ps', [DummyPhysicalSystem(1)]) def test_callable_merge_violations(self, ps, merging_fct, violations, expected_violation_degree): cm = ConstraintMonitor(merge_violations=merging_fct) cm.set_modules(ps) cm._merge_violations(violations) @pytest.mark.parametrize(['violations', 'expected_violation_degree'], [ [(0.5, 0.8, 0.0, 1.0), 1.0], [(0.5, 0.8, 0.0), 0.756], [(0.5, ), 0.123], [(0.0, ), 0.0], ]) @pytest.mark.parametrize( ['ps', 'state'], [[DummyPhysicalSystem(1), np.array([1.0])]]) def test_check_constraints(self, ps, state, violations, expected_violation_degree): passed_violations = [] def merge_violations(*violation_degrees): passed_violations.append(violation_degrees) return expected_violation_degree constraints = [ DummyConstraint(viol_degree) for viol_degree in violations ] cm = ConstraintMonitor(additional_constraints=constraints, merge_violations=merge_violations) cm.set_modules(ps) degree = cm.check_constraints(state) assert degree == expected_violation_degree assert all( passed == expected for passed, expected in zip(passed_violations[0][0], violations))