Diverse frame rate

Agents need not operate at the same frame rate. The BaseAgent allows a user to specify the rate at which an agent should be processed by the environment (which also afects the rate at which actions are generated by a policy).

The BaseAgentParser provides a field frame_rate which will define the rate (in Hertz) that the agent will be processed (e.g. a {frame_rate: 0.5} means that the agent will be processed every other time step and have a period == 2). By default, agent frame rates are rounded to the nearest 20th of a second.

The rate the simulation is run at, is the lcm of all the agents in the system (see multi_agent_env.py for the most up-to-date implementation):

agent_periods = [1 / agent.frame_rate for agent in agents]
sim_period = get_sim_period(agent_periods)
def get_sim_period(agent_periods: typing.List[float], max_agent_rate_hz: int = 20):
    def compute_lcm(values: typing.List[fractions.Fraction]) -> float:
        assert len(values) > 0
        lcm = values[0].denominator
        for v in values:
            lcm = lcm // math.gcd(lcm, v.denominator) * v.denominator
        return 1.0 / lcm

    agent_periods = [fractions.Fraction(p).limit_denominator(max_agent_rate_hz) for p in agent_periods]
    sim_period = compute_lcm(agent_periods)
    return sim_period
>>> get_sim_period(agent_periods=[.3, .5, .4])  # [Fraction(3, 10), Fraction(1, 2), Fraction(2, 5)]
0.1
>>> get_sim_period(agent_periods=[.33, .5, .4])  # [Fraction(1, 3), Fraction(1, 2), Fraction(2, 5)]
0.03333333333333333
>>> get_sim_period(agent_periods=[3, 2, 1]) # [Fraction(3, 1), Fraction(2, 1), Fraction(1, 1)]
1.0