예제 #1
0
 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
예제 #2
0
 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)
예제 #3
0
 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
예제 #4
0
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'
예제 #5
0
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)
예제 #6
0
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'
예제 #7
0
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))