From 75a6f326e2579f99c27ca84a7971d4f9629cf0e7 Mon Sep 17 00:00:00 2001 From: Tue Herlau <tuhe@dtu.dk> Date: Thu, 27 Mar 2025 18:10:15 +0100 Subject: [PATCH] Week8 --- .gitignore | 26 +- irlc/ex08/__init__.py | 2 + irlc/ex08/bandit_example.py | 27 ++ irlc/ex08/bandits.py | 213 ++++++++++++++ irlc/ex08/gradient_agent.py | 48 +++ irlc/ex08/grand_bandit_race.py | 78 +++++ irlc/ex08/nonstationary.py | 62 ++++ irlc/ex08/simple_agents.py | 57 ++++ irlc/ex08/ucb_agent.py | 45 +++ irlc/lectures/lec08/__init__.py | 1 + irlc/lectures/lec08/demo_bandit.py | 23 ++ irlc/lectures/lec08/demo_bandit_ucb.py | 26 ++ irlc/tests/tests_week05.py | 32 +- irlc/tests/tests_week08.py | 278 ++++++++++++++++++ irlc/tests/unitgrade_data/BanditQuestion.pkl | Bin 0 -> 96256 bytes .../BrachistochroneConstrainedQuestion.pkl | Bin 7242 -> 0 bytes .../BrachistochroneQuestion.pkl | Bin 7242 -> 0 bytes .../unitgrade_data/CartpoleCostQuestion.pkl | Bin 7242 -> 5447 bytes .../unitgrade_data/CartpoleTimeQuestion.pkl | Bin 7242 -> 5447 bytes .../unitgrade_data/DirectAgentPendulum.pkl | Bin 231 -> 231 bytes irlc/tests/unitgrade_data/DirectMethods.pkl | Bin 1459 -> 1459 bytes .../unitgrade_data/DirectSolverQuestion.pkl | Bin 7242 -> 5447 bytes .../Exam5InventoryEvaluation.pkl | Bin 217 -> 217 bytes irlc/tests/unitgrade_data/Exam6Toy2d.pkl | Bin 283 -> 283 bytes .../ExamQuestion7FlowersStore.pkl | Bin 182 -> 182 bytes .../unitgrade_data/GradientBanditQuestion.pkl | Bin 0 -> 96256 bytes .../unitgrade_data/ILQRAgentQuestion.pkl | Bin 326 -> 326 bytes .../unitgrade_data/ILQRPendulumQuestion.pkl | Bin 298 -> 298 bytes .../NonstatiotnaryAgentQuestion.pkl | Bin 0 -> 96256 bytes .../tests/unitgrade_data/PendulumQuestion.pkl | Bin 7242 -> 5447 bytes .../unitgrade_data/Problem1BobsFriend.pkl | Bin 170 -> 170 bytes .../Problem1DiscreteKuromoto.pkl | Bin 570 -> 570 bytes .../tests/unitgrade_data/Problem1Kuramoto.pkl | Bin 3014 -> 3014 bytes .../unitgrade_data/Problem2BobsPolicy.pkl | Bin 368 -> 368 bytes .../Problem2DeterministicDP.pkl | Bin 161 -> 161 bytes .../Problem2DeterministicInventory.pkl | Bin 128 -> 128 bytes .../Problem3InventoryInventoryEnvironment.pkl | Bin 323 -> 323 bytes irlc/tests/unitgrade_data/Problem3LQR.pkl | Bin 2025 -> 2025 bytes irlc/tests/unitgrade_data/Problem3PID.pkl | Bin 334 -> 334 bytes .../unitgrade_data/Problem3StochasticDP.pkl | Bin 345 -> 345 bytes irlc/tests/unitgrade_data/Problem4DPAgent.pkl | Bin 121 -> 121 bytes .../unitgrade_data/Problem4InventoryTrain.pkl | Bin 242 -> 242 bytes .../tests/unitgrade_data/Problem4LQRAgent.pkl | Bin 443 -> 443 bytes .../tests/unitgrade_data/Problem4PIDAgent.pkl | Bin 4673 -> 4673 bytes .../Problem5PacmanHardcoded.pkl | Bin 125 -> 125 bytes .../unitgrade_data/Problem5_6_Boeing.pkl | Bin 4219 -> 4219 bytes .../Problem6ChessTournament.pkl | Bin 197 -> 197 bytes irlc/tests/unitgrade_data/Problem7PIDCar.pkl | Bin 419 -> 419 bytes .../unitgrade_data/Problem7_8_PidLQR.pkl | Bin 415 -> 415 bytes irlc/tests/unitgrade_data/RendevouzItem.pkl | Bin 603 -> 603 bytes .../tests/unitgrade_data/UCBAgentQuestion.pkl | Bin 0 -> 96256 bytes irlc/utils/bandit_graphics_environment.py | 7 +- solutions/ex07/ilqr_TODO_1.py | 2 + solutions/ex07/ilqr_TODO_10.py | 1 + solutions/ex07/ilqr_TODO_11.py | 4 + solutions/ex07/ilqr_TODO_12.py | 4 + solutions/ex07/ilqr_TODO_13.py | 2 + solutions/ex07/ilqr_TODO_14.py | 3 + solutions/ex07/ilqr_TODO_15.py | 1 + solutions/ex07/ilqr_TODO_16.py | 1 + solutions/ex07/ilqr_TODO_2.py | 2 + solutions/ex07/ilqr_TODO_3.py | 1 + solutions/ex07/ilqr_TODO_4.py | 1 + solutions/ex07/ilqr_TODO_5.py | 2 + solutions/ex07/ilqr_TODO_6.py | 2 + solutions/ex07/ilqr_TODO_7.py | 1 + solutions/ex07/ilqr_TODO_8.py | 1 + solutions/ex07/ilqr_TODO_9.py | 1 + solutions/ex07/ilqr_agent_TODO_1.py | 1 + solutions/ex07/ilqr_pendulum_TODO_1.py | 1 + solutions/ex07/linearization_agent_TODO_1.py | 4 + solutions/ex07/linearization_agent_TODO_2.py | 1 + solutions/ex07/linearization_agent_TODO_3.py | 1 + solutions/ex08/bandits_TODO_1.py | 2 + solutions/ex08/gradient_agent_TODO_1.py | 9 + solutions/ex08/grand_bandit_race_TODO_1.py | 1 + solutions/ex08/grand_bandit_race_TODO_2.py | 5 + solutions/ex08/grand_bandit_race_TODO_3.py | 1 + solutions/ex08/grand_bandit_race_TODO_4.py | 1 + solutions/ex08/grand_bandit_race_TODO_5.py | 1 + solutions/ex08/grand_bandit_race_TODO_6.py | 1 + solutions/ex08/grand_bandit_race_TODO_7.py | 1 + solutions/ex08/grand_bandit_race_TODO_8.py | 1 + solutions/ex08/nonstationary_TODO_1.py | 2 + solutions/ex08/nonstationary_TODO_2.py | 2 + solutions/ex08/nonstationary_TODO_3.py | 1 + solutions/ex08/nonstationary_TODO_4.py | 4 + solutions/ex08/nonstationary_TODO_5.py | 1 + solutions/ex08/simple_agents_TODO_1.py | 2 + solutions/ex08/simple_agents_TODO_2.py | 1 + solutions/ex08/simple_agents_TODO_3.py | 2 + solutions/ex08/ucb_agent_TODO_1.py | 2 + solutions/ex08/ucb_agent_TODO_2.py | 3 + solutions/ex08/ucb_agent_TODO_3.py | 1 + 94 files changed, 976 insertions(+), 30 deletions(-) create mode 100644 irlc/ex08/__init__.py create mode 100644 irlc/ex08/bandit_example.py create mode 100644 irlc/ex08/bandits.py create mode 100644 irlc/ex08/gradient_agent.py create mode 100644 irlc/ex08/grand_bandit_race.py create mode 100644 irlc/ex08/nonstationary.py create mode 100644 irlc/ex08/simple_agents.py create mode 100644 irlc/ex08/ucb_agent.py create mode 100644 irlc/lectures/lec08/__init__.py create mode 100644 irlc/lectures/lec08/demo_bandit.py create mode 100644 irlc/lectures/lec08/demo_bandit_ucb.py create mode 100644 irlc/tests/tests_week08.py create mode 100644 irlc/tests/unitgrade_data/BanditQuestion.pkl delete mode 100644 irlc/tests/unitgrade_data/BrachistochroneConstrainedQuestion.pkl delete mode 100644 irlc/tests/unitgrade_data/BrachistochroneQuestion.pkl create mode 100644 irlc/tests/unitgrade_data/GradientBanditQuestion.pkl create mode 100644 irlc/tests/unitgrade_data/NonstatiotnaryAgentQuestion.pkl create mode 100644 irlc/tests/unitgrade_data/UCBAgentQuestion.pkl create mode 100644 solutions/ex07/ilqr_TODO_1.py create mode 100644 solutions/ex07/ilqr_TODO_10.py create mode 100644 solutions/ex07/ilqr_TODO_11.py create mode 100644 solutions/ex07/ilqr_TODO_12.py create mode 100644 solutions/ex07/ilqr_TODO_13.py create mode 100644 solutions/ex07/ilqr_TODO_14.py create mode 100644 solutions/ex07/ilqr_TODO_15.py create mode 100644 solutions/ex07/ilqr_TODO_16.py create mode 100644 solutions/ex07/ilqr_TODO_2.py create mode 100644 solutions/ex07/ilqr_TODO_3.py create mode 100644 solutions/ex07/ilqr_TODO_4.py create mode 100644 solutions/ex07/ilqr_TODO_5.py create mode 100644 solutions/ex07/ilqr_TODO_6.py create mode 100644 solutions/ex07/ilqr_TODO_7.py create mode 100644 solutions/ex07/ilqr_TODO_8.py create mode 100644 solutions/ex07/ilqr_TODO_9.py create mode 100644 solutions/ex07/ilqr_agent_TODO_1.py create mode 100644 solutions/ex07/ilqr_pendulum_TODO_1.py create mode 100644 solutions/ex07/linearization_agent_TODO_1.py create mode 100644 solutions/ex07/linearization_agent_TODO_2.py create mode 100644 solutions/ex07/linearization_agent_TODO_3.py create mode 100644 solutions/ex08/bandits_TODO_1.py create mode 100644 solutions/ex08/gradient_agent_TODO_1.py create mode 100644 solutions/ex08/grand_bandit_race_TODO_1.py create mode 100644 solutions/ex08/grand_bandit_race_TODO_2.py create mode 100644 solutions/ex08/grand_bandit_race_TODO_3.py create mode 100644 solutions/ex08/grand_bandit_race_TODO_4.py create mode 100644 solutions/ex08/grand_bandit_race_TODO_5.py create mode 100644 solutions/ex08/grand_bandit_race_TODO_6.py create mode 100644 solutions/ex08/grand_bandit_race_TODO_7.py create mode 100644 solutions/ex08/grand_bandit_race_TODO_8.py create mode 100644 solutions/ex08/nonstationary_TODO_1.py create mode 100644 solutions/ex08/nonstationary_TODO_2.py create mode 100644 solutions/ex08/nonstationary_TODO_3.py create mode 100644 solutions/ex08/nonstationary_TODO_4.py create mode 100644 solutions/ex08/nonstationary_TODO_5.py create mode 100644 solutions/ex08/simple_agents_TODO_1.py create mode 100644 solutions/ex08/simple_agents_TODO_2.py create mode 100644 solutions/ex08/simple_agents_TODO_3.py create mode 100644 solutions/ex08/ucb_agent_TODO_1.py create mode 100644 solutions/ex08/ucb_agent_TODO_2.py create mode 100644 solutions/ex08/ucb_agent_TODO_3.py diff --git a/.gitignore b/.gitignore index 1e25f17..1ef69d9 100644 --- a/.gitignore +++ b/.gitignore @@ -4,11 +4,11 @@ exam_tabular_examples #solutions/ex01 #solutions/ex02 #solutions/ex03 -solutions/ex04 -solutions/ex05 -solutions/ex06 -solutions/ex07 -solutions/ex08 +#solutions/ex04 +#solutions/ex05 +#solutions/ex06 +#solutions/ex07 +#solutions/ex08 solutions/ex09 solutions/ex10 solutions/ex11 @@ -31,10 +31,10 @@ solutions/ex13 #irlc/tests/tests_week02.py #irlc/tests/tests_week03.py #irlc/tests/tests_week04.py -irlc/tests/tests_week05.py -irlc/tests/tests_week06.py -irlc/tests/tests_week07.py -irlc/tests/tests_week08.py +#irlc/tests/tests_week05.py +#irlc/tests/tests_week06.py +#irlc/tests/tests_week07.py +#irlc/tests/tests_week08.py irlc/tests/tests_week09.py irlc/tests/tests_week10.py irlc/tests/tests_week11.py @@ -68,10 +68,10 @@ irlc/exam/exam20*/solution # irlc/lectures/lec02 #irlc/lectures/lec03 #irlc/lectures/lec04 -irlc/lectures/lec05 -irlc/lectures/lec06 -irlc/lectures/lec07 -irlc/lectures/lec08 +#irlc/lectures/lec05 +#irlc/lectures/lec06 +#irlc/lectures/lec07 +#irlc/lectures/lec08 irlc/lectures/lec09 irlc/lectures/lec10 irlc/lectures/lec11 diff --git a/irlc/ex08/__init__.py b/irlc/ex08/__init__.py new file mode 100644 index 0000000..2851411 --- /dev/null +++ b/irlc/ex08/__init__.py @@ -0,0 +1,2 @@ +# This file may not be shared/redistributed without permission. Please read copyright notice in the git repo. If this file contains other copyright notices disregard this text. +"""This directory contains the exercises for week 8.""" diff --git a/irlc/ex08/bandit_example.py b/irlc/ex08/bandit_example.py new file mode 100644 index 0000000..2eb8ccc --- /dev/null +++ b/irlc/ex08/bandit_example.py @@ -0,0 +1,27 @@ +# This file may not be shared/redistributed without permission. Please read copyright notice in the git repo. If this file contains other copyright notices disregard this text. +import numpy as np +import matplotlib.pyplot as plt + +if __name__ == "__main__": + from irlc import Agent, train, savepdf + from irlc.ex08.bandits import StationaryBandit + bandit = StationaryBandit(k=10) # A 10-armed bandit + agent = Agent(bandit) # Recall the agent takes random actions + _, trajectories = train(bandit, agent, return_trajectory=True, num_episodes=1, max_steps=500) + plt.plot(trajectories[0].reward) + plt.xlabel("Time step") + plt.ylabel("Reward per time step") + savepdf("dumbitA") + plt.show() + + agent = Agent(bandit) # Recall the agent takes random actions + for i in range(10): + _, trajectories = train(bandit, agent, return_trajectory=True, num_episodes=1, max_steps=500) + regret = np.asarray([r['gab'] for r in trajectories[0].env_info[1:]]) + cum_regret = np.cumsum(regret) + plt.plot(cum_regret, label=f"Episode {i}") + plt.legend() + plt.xlabel("Time step") + plt.ylabel("Accumulated Regret") + savepdf("dumbitB") + plt.show() diff --git a/irlc/ex08/bandits.py b/irlc/ex08/bandits.py new file mode 100644 index 0000000..5df7412 --- /dev/null +++ b/irlc/ex08/bandits.py @@ -0,0 +1,213 @@ +# This file may not be shared/redistributed without permission. Please read copyright notice in the git repo. If this file contains other copyright notices disregard this text. +""" + +References: + [SB18] Richard S. Sutton and Andrew G. Barto. Reinforcement Learning: An Introduction. The MIT Press, second edition, 2018. (Freely available online). +""" +import numpy as np +import matplotlib.pyplot as plt +from gymnasium import Env +from gymnasium.spaces import Discrete +from irlc import train +from tqdm import tqdm +import sys +from irlc import cache_read, cache_write, cache_exists + +class BanditEnvironment(Env): + r""" + A helper class for defining bandit problems similar to e.g. the 10-armed testbed discsused in (SB18). + We are going to implement the bandit problems as greatly simplfied gym environments, as this will allow us to + implement the bandit agents as the familiar ``Agent``. I hope this way of doing it will make it clearer that bandits + are in fact a sort of reinforcement learning method. + + The following code shows an example of how to use a bandit environment: + + .. runblock:: pycon + + >>> from irlc.ex08.bandits import StationaryBandit + >>> env = StationaryBandit(k=10) # 10-armed testbed. + >>> env.reset() # Reset env.q_star + >>> s, r, _, _, info = env.step(3) + >>> print(f"The reward we got from taking arm a=3 was {r=}") + + """ + def __init__(self, k : int): + r""" + Initialize a bandit problem. The observation space is given a dummy value since bandit problems of the sort + (SB18) discuss don't have observations. + + :param k: The number of arms. + """ + super().__init__() + self.observation_space = Discrete(1) # Dummy observation space with a single observation. + self.action_space = Discrete(k) # The arms labelled 0,1,...,k-1. + self.k = k # Number of arms + + def reset(self): + r""" + Use this function to reset the all internal parameters of the environment and get ready for a new episode. + In the (SB18) 10-armed bandit testbed, this would involve resetting the expected return + + .. math:: + q^*_a + + The function must return a dummy state and info dictionary to agree with the gym ``Env`` class, but their values are + irrelevant + + :return: + - s - a state, for instance 0 + - info - the info dictionary, for instance {} + """ + raise NotImplementedError("Implement the reset method") + + def bandit_step(self, a): + r"""This helper function simplify the definition of the environments ``step``-function. + + Given an action :math:`r`, this function computes the reward obtained by taking that action :math:`r_t` + and the gab. This is defined as the expected reward we miss out on by taking the potentially suboptimal action :math:`a` + and is defined as: + + .. math:: + \Delta = \max_{a'} q^*_{a'} - q_a + + Once implemented, the reward and regret enters into the ``step`` function as follows: + + .. runblock:: pycon + + >>> from irlc.ex08.bandits import StationaryBandit + >>> env = StationaryBandit(k=4) # 4-armed testbed. + >>> env.reset() # Reset all parameters. + >>> _, r, _, _, info = env.step(2) # Take action a=2 + >>> print(f"Reward from a=2 was {r=}, the gab was {info['gab']=}") + + :param a: The current action we take + :return: + - r - The reward we thereby incur + - gab - The regret incurred by taking this action (0 for an optimal action) + """ + reward = 0 # Compute the reward associated with arm a + gab = 0 # Compute the gab, by comparing to the optimal arms reward. + return reward, gab + + def step(self, action): + r"""You do not have to edit this function. + In a bandit environment, the step function is simplified greatly since there are no + states to keep track on. It should simply return the reward incurred by the action ``a`` + and (for convenience) also returns the gab in the ``info``-dictionary. + + :param action: The current action we take :math:`a_t` + :return: + - next_state - This is always ``None`` + - reward - The reward obtained by taking the given action. In (SB18) this is defined as :math:`r_t` + - terminated - Always ``False``. Bandit problems don't terminate. + - truncated - Always ``False`` + - info - For convenience, this includes the gab (used by the plotting methods) + + """ + reward, gab = self.bandit_step(action) + info = {'gab': gab} + return None, reward, False, False, info + +class StationaryBandit(BanditEnvironment): + r"""Implement the 'stationary bandit environment' which is described in (SB18, Section 2.3) + and used as a running example throughout the chapter. + + We will implement a version with a constant mean offset (q_star_mean), so that + + q* = x + q_star_mean, x ~ Normal(0,1) + + q_star_mean can just be considered to be zero at first. + """ + def __init__(self, k, q_star_mean=0): + super().__init__(k) + self.q_star_mean = q_star_mean + + def reset(self): + """ Set q^*_k = N(0,1) + mean_value. The mean_value is 0 in most examples. I.e., implement the 10-armed testbed environment. """ + self.q_star = np.random.randn(self.k) + self.q_star_mean + self.optimal_action = np.argmax(self.q_star) # Optimal action is the one with the largest q^*-value. + return 0, {} # The reset method in a gym Env must return a (dummy) state and a dictionary. + + def bandit_step(self, a): + """ Return the reward/gab for action a for the simple bandit. Use self.q_star (see reset-function above). + To implement it, implement the reward (see the description of the 10-armed testbed for more information. + How is it computed from q^*_k?) and also compute the gab. + + As a small hint, since we are computing the gab, it will in fact be the difference between the + value of q^* corresponding to the current arm, and the q^* value for the optimal arm. + Remember it is 0 if the optimal action is selected. + """ + # TODO: 2 lines missing. + raise NotImplementedError("Insert your solution and remove this error.") + # Actual logic goes here. Use self.q_star[a] to get mean reward and np.random.randn() to generate random numbers. + return reward, gab + + def __str__(self): + return f"{type(self).__name__}_{self.q_star_mean}" + +""" +Helper function for running a bunch of bandit experiments and plotting the results. + +The function will run the agents in 'agents' (a list of bandit agents) +on the bandit environment 'bandit' and plot the result. + +Each agent will be evaluated for num_episodes episodes, and one episode consist of 'steps' steps. +However, to speed things up you can use cache, and the bandit will not be evaluated for more than +'max_episodes' over all cache runs. + +""" +def eval_and_plot(bandit, agents, num_episodes=2000, max_episodes=2000, steps=1000, labels=None, use_cache=True): + if labels is None: + labels = [str(agent) for agent in agents] + + f, axs = plt.subplots(nrows=3, ncols=1) + f.set_size_inches(10,7) + (ax1, ax2, ax3) = axs + for i,agent in enumerate(agents): + rw, oa, regret, num_episodes = run_agent(bandit, agent, episodes=num_episodes, max_episodes=max_episodes, steps=steps, use_cache=use_cache) + ax1.plot(rw, label=labels[i]) + ax2.plot(oa, label=labels[i]) + ax3.plot(regret, label=labels[i]) + + for ax in axs: + ax.grid() + ax.set_xlabel("Steps") + + ax1.set_ylabel("Average Reward") + ax2.set_ylabel("% optimal action") + ax3.set_ylabel("Regret $L_t$") + ax3.legend() + f.suptitle(f"Evaluated on {str(bandit)} for {num_episodes} episodes") + +def run_agent(env, agent, episodes=2000, max_episodes=2000, steps=1000, use_cache=False, verbose=True): + """ + Helper function. most of the work involves the cache; the actual training is done by 'train'. + """ + C_regrets_cum_sum, C_oas_sum, C_rewards_sum, C_n_episodes = 0, 0, 0, 0 + if use_cache: + cache = f"cache/{str(env)}_{str(agent)}_{steps}.pkl" + if cache_exists(cache): + print("> Reading from cache", cache) + C_regrets_cum_sum, C_oas_sum, C_rewards_sum, C_n_episodes = cache_read(cache) + + regrets = [] + rewards = [] + cruns = max(0, min(episodes, max_episodes - C_n_episodes)) # Missing runs. + for _ in tqdm(range(cruns), file=sys.stdout, desc=str(agent),disable=not verbose): + stats, traj = train(env, agent, max_steps=steps, verbose=False, return_trajectory=True) + regret = np.asarray([r['gab'] for r in traj[0].env_info[1:]]) + regrets.append(regret) + rewards.append(traj[0].reward) + + regrets_cum_sum = C_regrets_cum_sum + oas_sum = C_oas_sum + rewards_sum = C_rewards_sum + episodes = C_n_episodes + if len(regrets) > 0: + regrets_cum_sum += np.cumsum(np.sum(np.stack(regrets), axis=0)) + oas_sum += np.sum(np.stack(regrets) == 0, axis=0) + rewards_sum += np.sum(np.stack(rewards), axis=0) + episodes += cruns + if use_cache and cruns > 0: + cache_write((regrets_cum_sum, oas_sum, rewards_sum, episodes), cache, protocol=4) + return rewards_sum/episodes, oas_sum/episodes, regrets_cum_sum/episodes, episodes diff --git a/irlc/ex08/gradient_agent.py b/irlc/ex08/gradient_agent.py new file mode 100644 index 0000000..34b296b --- /dev/null +++ b/irlc/ex08/gradient_agent.py @@ -0,0 +1,48 @@ +# This file may not be shared/redistributed without permission. Please read copyright notice in the git repo. If this file contains other copyright notices disregard this text. +from irlc import savepdf +import numpy as np +import matplotlib.pyplot as plt +from irlc.ex08.bandits import eval_and_plot, StationaryBandit +from irlc import Agent + +class GradientAgent(Agent): + def __init__(self, env, alpha=None, use_baseline=True): + self.k = env.action_space.n + self.alpha = alpha + self.baseline=use_baseline + self.H = np.zeros((self.k,)) + super().__init__(env) + + def Pa(self): + """ This helper method returns the probability distribution P(A=a) of chosing the + arm a as a vector + """ + pi_a = np.exp(self.H) + return pi_a / np.sum(pi_a) + + def pi(self, s, t, info_s=None): + if t == 0: + self.R_bar = 0 # average reward baseline + self.H *= 0 # Reset H to all-zeros. + self.t = t # Sore the current time step. + return np.random.choice( self.k, p=self.Pa() ) + + def train(self, s, a, r, sp, done=False, info_s=None, info_sp=None): + # TODO: 9 lines missing. + raise NotImplementedError("Implement function body") + + def __str__(self): + return f"{type(self).__name__}_{self.alpha}_{'baseline' if self.baseline else 'no_baseline'}" + +if __name__ == "__main__": + baseline_bandit = StationaryBandit(k=10, q_star_mean=4) + alphas = [0.1, 0.4] + agents = [GradientAgent(baseline_bandit, alpha=alpha, use_baseline=False) for alpha in alphas] + agents += [GradientAgent(baseline_bandit, alpha=alpha, use_baseline=True) for alpha in alphas] + + labels = [f'Gradient Bandit alpha={alpha}' for alpha in alphas ] + labels += [f'With baseline: Gradient Bandit alpha={alpha}' for alpha in alphas ] + use_cache = False + eval_and_plot(baseline_bandit, agents, max_episodes=2000, num_episodes=100, labels=labels, use_cache=use_cache) + savepdf("gradient_baseline") + plt.show() diff --git a/irlc/ex08/grand_bandit_race.py b/irlc/ex08/grand_bandit_race.py new file mode 100644 index 0000000..ad466aa --- /dev/null +++ b/irlc/ex08/grand_bandit_race.py @@ -0,0 +1,78 @@ +# This file may not be shared/redistributed without permission. Please read copyright notice in the git repo. If this file contains other copyright notices disregard this text. +import matplotlib.pyplot as plt +from irlc.ex08.simple_agents import BasicAgent +from irlc.ex08.bandits import StationaryBandit, eval_and_plot +from irlc.ex08.nonstationary import MovingAverageAgent, NonstationaryBandit +from irlc.ex08.gradient_agent import GradientAgent +from irlc.ex08.ucb_agent import UCBAgent +from irlc import savepdf +import time + +if __name__ == "__main__": + print("Ladies and gentlemen. It is time for the graaand bandit race") + def intro(bandit, agents): + print("We are live from the beautiful surroundings where they will compete in:") + print(bandit) + print("Who will win? who will have the most regret? we are about to find out") + print("in a minute after a brief word from our sponsors") + time.sleep(1) + print("And we are back. Let us introduce todays contestants:") + for a in agents: + print(a) + print("And they are off!") + epsilon = 0.1 + alpha = 0.1 + c = 2 + # TODO: 1 lines missing. + raise NotImplementedError("Define the bandit here: bandit1 = ...") + # TODO: 5 lines missing. + raise NotImplementedError("define agents list here") + labels = ["Basic", "Moving avg.", "gradient", "Gradient+baseline", "UCB"] + ''' + Stationary, no offset. Vanilla setting. + ''' + intro(bandit1, agents) + # TODO: 1 lines missing. + raise NotImplementedError("Call eval_and_plot here") + plt.suptitle("Stationary bandit (no offset)") + savepdf("grand_race_1") + plt.show() + ''' + Stationary, but with offset + ''' + print("Whew what a race. Let's get ready to next round:") + # TODO: 1 lines missing. + raise NotImplementedError("Define bandit2 = ... here") + intro(bandit2, agents) + # TODO: 1 lines missing. + raise NotImplementedError("Call eval_and_plot here") + plt.suptitle("Stationary bandit (with offset)") + savepdf("grand_race_2") + plt.show() + ''' + Long (nonstationary) simulations + ''' + print("Whew what a race. Let's get ready to next round which will be a long one.") + # TODO: 1 lines missing. + raise NotImplementedError("define bandit3 here") + intro(bandit3, agents) + # TODO: 1 lines missing. + raise NotImplementedError("call eval_and_plot here") + plt.suptitle("Non-stationary bandit (no offset)") + savepdf("grand_race_3") + plt.show() + + ''' + Stationary, no offset, long run. Exclude stupid bandits. + ''' + agents2 = [] + agents2 += [GradientAgent(bandit1, alpha=alpha, use_baseline=False)] + agents2 += [GradientAgent(bandit1, alpha=alpha, use_baseline=True)] + agents2 += [UCBAgent(bandit1, c=2)] + labels = ["Gradient", "Gradient+baseline", "UCB"] + intro(bandit1, agents2) + # TODO: 1 lines missing. + raise NotImplementedError("Call eval_and_plot here") + plt.suptitle("Stationary bandit (no offset)") + savepdf("grand_race_4") + plt.show() diff --git a/irlc/ex08/nonstationary.py b/irlc/ex08/nonstationary.py new file mode 100644 index 0000000..546c5ec --- /dev/null +++ b/irlc/ex08/nonstationary.py @@ -0,0 +1,62 @@ +# This file may not be shared/redistributed without permission. Please read copyright notice in the git repo. If this file contains other copyright notices disregard this text. +""" + +References: + [SB18] Richard S. Sutton and Andrew G. Barto. Reinforcement Learning: An Introduction. The MIT Press, second edition, 2018. (Freely available online). +""" +import numpy as np +import matplotlib.pyplot as plt +from irlc.ex08.simple_agents import BasicAgent +from irlc.ex08.bandits import StationaryBandit, eval_and_plot +from irlc import savepdf + +class NonstationaryBandit(StationaryBandit): + def __init__(self, k, q_star_mean=0, reward_change_std=0.01): + self.reward_change_std = reward_change_std + super().__init__(k, q_star_mean) + + def bandit_step(self, a): + r""" Implement the non-stationary bandit environment (as described in (SB18)). + Hint: use reward_change_std * np.random.randn() to generate a single random number with the given std. + then add one to each coordinate. Remember you have to compute the regret as well, see StationaryBandit for ideas. + (remember the optimal arm will change when you add noise to q_star) """ + # TODO: 2 lines missing. + raise NotImplementedError("Implement function body") + return super().bandit_step(a) + + def __str__(self): + return f"{type(self).__name__}_{self.q_star_mean}_{self.reward_change_std}" + + +class MovingAverageAgent(BasicAgent): + r""" + The simple bandit from (SB18, Section 2.4), but with moving average alpha + as described in (SB18, Eqn. (2.3)) + """ + def __init__(self, env, epsilon, alpha): + # TODO: 2 lines missing. + raise NotImplementedError("Implement function body") + + def train(self, s, a, r, sp, done=False, info_s=None, info_sp=None): + # TODO: 1 lines missing. + raise NotImplementedError("Implement function body") + + def __str__(self): + return f"{type(self).__name__}_{self.epsilon}_{self.alpha}" + + +if __name__ == "__main__": + plt.figure(figsize=(10, 10)) + epsilon = 0.1 + alphas = [0.15, 0.1, 0.05] + + # TODO: 4 lines missing. + raise NotImplementedError("Insert your solution and remove this error.") + + labels = [f"Basic agent, epsilon={epsilon}"] + # TODO: 1 lines missing. + raise NotImplementedError("Insert your solution and remove this error.") + use_cache = False # Set this to True to use cache (after code works!) + eval_and_plot(bandit, agents, steps=10000, num_episodes=200, labels=labels, use_cache=use_cache) + savepdf("nonstationary_bandits") + plt.show() diff --git a/irlc/ex08/simple_agents.py b/irlc/ex08/simple_agents.py new file mode 100644 index 0000000..18bdca1 --- /dev/null +++ b/irlc/ex08/simple_agents.py @@ -0,0 +1,57 @@ +# This file may not be shared/redistributed without permission. Please read copyright notice in the git repo. If this file contains other copyright notices disregard this text. +""" + +References: + [SB18] Richard S. Sutton and Andrew G. Barto. Reinforcement Learning: An Introduction. The MIT Press, second edition, 2018. (Freely available online). +""" +import numpy as np +import matplotlib.pyplot as plt +from irlc.ex08.bandits import StationaryBandit, eval_and_plot +from irlc import Agent +from irlc import savepdf + +class BasicAgent(Agent): + r""" + Simple bandit as described on (SB18, Section 2.4). + """ + def __init__(self, env, epsilon): + super().__init__(env) + self.k = env.action_space.n + self.epsilon = epsilon + + def pi(self, s, t, info=None): + """ Since this is a bandit, s=None and can be ignored, while t refers to the time step in the current episode """ + if t == 0: + # At step 0 of episode. Re-initialize data structure. + # TODO: 2 lines missing. + raise NotImplementedError("Insert your solution and remove this error.") + # compute action here + # TODO: 1 lines missing. + raise NotImplementedError("Insert your solution and remove this error.") + + def train(self, s, a, r, sp, done=False, info_s=None, info_sp=None): + """ Since this is a bandit, done, s, sp, info_s, info_sp can all be ignored. + From the input arguments you should only use a + """ + # TODO: 2 lines missing. + raise NotImplementedError("Implement function body") + + def __str__(self): + return f"BasicAgent_{self.epsilon}" + +if __name__ == "__main__": + N = 100000 + S = [np.max( np.random.randn(10) ) for _ in range(100000) ] + print( np.mean(S), np.std(S)/np.sqrt(N) ) + + use_cache = False # Set this to True to use cache (after code works!) + from irlc.utils.timer import Timer + timer = Timer(start=True) + R = 100 + steps = 1000 + env = StationaryBandit(k=10) + agents = [BasicAgent(env, epsilon=.1), BasicAgent(env, epsilon=.01), BasicAgent(env, epsilon=0) ] + eval_and_plot(env, agents, num_episodes=100, steps=1000, max_episodes=150, use_cache=use_cache) + savepdf("bandit_epsilon") + plt.show() + print(timer.display()) diff --git a/irlc/ex08/ucb_agent.py b/irlc/ex08/ucb_agent.py new file mode 100644 index 0000000..5c805ea --- /dev/null +++ b/irlc/ex08/ucb_agent.py @@ -0,0 +1,45 @@ +# This file may not be shared/redistributed without permission. Please read copyright notice in the git repo. If this file contains other copyright notices disregard this text. +""" + +References: + [SB18] Richard S. Sutton and Andrew G. Barto. Reinforcement Learning: An Introduction. The MIT Press, second edition, 2018. (Freely available online). +""" +import numpy as np +import matplotlib.pyplot as plt +from irlc.ex08.simple_agents import BasicAgent +from irlc import savepdf +from irlc import Agent + +class UCBAgent(Agent): + def __init__(self, env, c=2): + self.c = c + super().__init__(env) + + def train(self, s, a, r, sp, done=False, info_s=None, info_sp=None): + # TODO: 2 lines missing. + raise NotImplementedError("Train agent here") + + def pi(self, s, k, info=None): + if k == 0: + """ Initialize the agent""" + # TODO: 3 lines missing. + raise NotImplementedError("Reset agent (i.e., make it ready to learn in a new episode with a new optimal action)") + # TODO: 1 lines missing. + raise NotImplementedError("Compute (and return) optimal action") + + def __str__(self): + return f"{type(self).__name__}_{self.c}" + +from irlc.ex08.bandits import StationaryBandit, eval_and_plot +if __name__ == "__main__": + r"""Reproduce (SB18, Fig. 2.4) comparing UCB agent to epsilon greedy """ + runs, use_cache = 100, False + c = 2 + eps = 0.1 + + steps = 1000 + env = StationaryBandit(k=10) + agents = [UCBAgent(env,c=c), BasicAgent(env, epsilon=eps)] + eval_and_plot(bandit=env, agents=agents, num_episodes=runs, steps=steps, max_episodes=2000, use_cache=use_cache) + savepdf("UCB_agent") + plt.show() diff --git a/irlc/lectures/lec08/__init__.py b/irlc/lectures/lec08/__init__.py new file mode 100644 index 0000000..a56057c --- /dev/null +++ b/irlc/lectures/lec08/__init__.py @@ -0,0 +1 @@ +# This file may not be shared/redistributed without permission. Please read copyright notice in the git repo. If this file contains other copyright notices disregard this text. diff --git a/irlc/lectures/lec08/demo_bandit.py b/irlc/lectures/lec08/demo_bandit.py new file mode 100644 index 0000000..e8b4762 --- /dev/null +++ b/irlc/lectures/lec08/demo_bandit.py @@ -0,0 +1,23 @@ +# This file may not be shared/redistributed without permission. Please read copyright notice in the git repo. If this file contains other copyright notices disregard this text. +from irlc.utils.bandit_graphics_environment import GraphicalBandit +import time +from irlc import train +from irlc.ex08.simple_agents import BasicAgent +from irlc import interactive + +def bandit_eps(autoplay=False): + env = GraphicalBandit(10, render_mode='human',frames_per_second=30) + env.reset() + agent = BasicAgent(env, epsilon=0.1) + agent.method = 'Epsilon-greedy' + env, agent = interactive(env, agent, autoplay=autoplay) + + t0 = time.time() + n = 3000 + stats, _ = train(env, agent, max_steps=n, num_episodes=10, return_trajectory=False, verbose=False) + tpf = (time.time()-t0)/ n + print("tpf", tpf, 'fps', 1/tpf) + env.close() + +if __name__ == "__main__": + bandit_eps() diff --git a/irlc/lectures/lec08/demo_bandit_ucb.py b/irlc/lectures/lec08/demo_bandit_ucb.py new file mode 100644 index 0000000..440c976 --- /dev/null +++ b/irlc/lectures/lec08/demo_bandit_ucb.py @@ -0,0 +1,26 @@ +# This file may not be shared/redistributed without permission. Please read copyright notice in the git repo. If this file contains other copyright notices disregard this text. +from irlc.utils.bandit_graphics_environment import GraphicalBandit +from irlc import interactive, train +# import numpy as np +import time + +def bandit_ucb(autoplay=False): + env = GraphicalBandit(10, render_mode='human', frames_per_second=30) + env.reset() + #env.viewer.show_q_star = True + #env.viewer.show_q_ucb = True + from irlc.ex08.ucb_agent import UCBAgent + agent = UCBAgent(env, c=1) + agent.method = 'UCB' + + env, agent = interactive(env, agent, autoplay=autoplay) + t0 = time.time() + n = 500 + stats, _ = train(env, agent, max_steps=n, num_episodes=10, return_trajectory=False, verbose=False) + tpf = (time.time() - t0) / n + print("tpf", tpf, 'fps', 1 / tpf) + env.close() + + +if __name__ == "__main__": + bandit_ucb() diff --git a/irlc/tests/tests_week05.py b/irlc/tests/tests_week05.py index 4a7f813..e863e8f 100644 --- a/irlc/tests/tests_week05.py +++ b/irlc/tests/tests_week05.py @@ -79,20 +79,20 @@ class CartpoleCostQuestion(DirectSolverQuestion): from irlc.ex05.direct_cartpole_kelly import compute_solutions return compute_solutions()[1] -class BrachistochroneQuestion(DirectSolverQuestion): - """ Brachistochrone (unconstrained) """ - - @classmethod - def compute_solution(cls): - from irlc.ex05.direct_brachistochrone import compute_constrained_solutions - return compute_constrained_solutions()[1] - -class BrachistochroneConstrainedQuestion(DirectSolverQuestion): - """ Brachistochrone (constrained) """ - @classmethod - def compute_solution(cls): - from irlc.ex05.direct_brachistochrone import compute_constrained_solutions - return compute_constrained_solutions()[1] +# class BrachistochroneQuestion(DirectSolverQuestion): +# """ Brachistochrone (unconstrained) """ +# +# @classmethod +# def compute_solution(cls): +# from irlc.ex05.direct_brachistochrone import compute_constrained_solutions +# return compute_constrained_solutions()[1] +# +# class BrachistochroneConstrainedQuestion(DirectSolverQuestion): +# """ Brachistochrone (constrained) """ +# @classmethod +# def compute_solution(cls): +# from irlc.ex05.direct_brachistochrone import compute_constrained_solutions +# return compute_constrained_solutions()[1] class Week05Tests(Report): title = "Tests for week 05" @@ -105,8 +105,8 @@ class Week05Tests(Report): (DirectAgentPendulum, 10), # ok (CartpoleTimeQuestion, 5), # ok (CartpoleCostQuestion, 5), # ok - (BrachistochroneQuestion, 5), # ok - (BrachistochroneConstrainedQuestion, 10), # ok + # (BrachistochroneQuestion, 5), # ok + # (BrachistochroneConstrainedQuestion, 10), # ok ] if __name__ == '__main__': diff --git a/irlc/tests/tests_week08.py b/irlc/tests/tests_week08.py new file mode 100644 index 0000000..340d69c --- /dev/null +++ b/irlc/tests/tests_week08.py @@ -0,0 +1,278 @@ +# This file may not be shared/redistributed without permission. Please read copyright notice in the git repo. If this file contains other copyright notices disregard this text. +from unitgrade import UTestCase, Report, cache +import numpy as np +from irlc import train + + +def train_recording(env, agent, trajectories): + for t in trajectories: + env.reset() + for k in range(len(t.action)): + s = t.state[k] + r = t.reward[k] + a = t.action[k] + sp = t.state[k+1] + agent.pi(s,k) + agent.train(s, a, r, sp, done=k == len(t.action)-1) + + +class BanditQuestion(UTestCase): + """ Value (Q) function estimate """ + tol = 1e-2 # tie-breaking in the gradient bandit is ill-defined. + # testfun = QPrintItem.assertL2 + + # def setUpClass(cls) -> None: + # from irlc.ex08.simple_agents import BasicAgent + # from irlc.ex08.bandits import StationaryBandit + # env = StationaryBandit(k=10, ) + # agent = BasicAgent(env, epsilon=0.1) + # _, cls.trajectories = train(env, agent, return_trajectory=True, num_episodes=1, max_steps=100) + # cls.Q = agent.Q + # cls.env = env + # cls.agent = agent + + def get_env_agent(self): + from irlc.ex08.simple_agents import BasicAgent + from irlc.ex08.bandits import StationaryBandit + env = StationaryBandit(k=10) + agent = BasicAgent(env, epsilon=0.1) + return env, agent + + @cache + def get_trajectories(self): + env, agent = self.get_env_agent() + _, trajectories = train(env, agent, return_trajectory=True, num_episodes=1, max_steps=100) + return trajectories + + # def precompute_payload(self): + # env, agent = self.get_env_agent() + # _, trajectories = train(env, agent, return_trajectory=True, num_episodes=1, max_steps=100) + # return trajectories, agent.Q + + + def test_agent(self): + trajectories = self.get_trajectories() + env, agent = self.get_env_agent() + train_recording(env, agent, trajectories) + self.assertL2(agent.Q, tol=1e-5) + # return agent.Q + # self.Q = Q + # self.question.agent = agent + # return agent.Q + + # testfun = QPrintItem.assertL2 + + def test_action_distributin(self): + T = 10000 + tol = 1 / np.sqrt(T) * 5 + trajectories = self.get_trajectories() + env, agent = self.get_env_agent() + train_recording(env, agent, trajectories) + # for k in self._cache.keys(): print(k) + + from collections import Counter + counts = Counter([agent.pi(None, k) for k in range(T)]) + distrib = [counts[k] / T for k in range(env.k)] + self.assertL2(np.asarray(distrib), tol=tol) + + + # def process_output(self, res, txt, numbers): + # return res + + # def process_output(self, res, txt, numbers): + # return res + # + # def test(self, computed, expected): + # super().test(computed, self.Q) + +# class BanditQuestion(QPrintItem): +# # tol = 1e-6 +# tol = 1e-2 # tie-breaking in the gradient bandit is ill-defined. +# title = "Value (Q) function estimate" +# testfun = QPrintItem.assertL2 +# +# def get_env_agent(self): +# from irlc.ex08.simple_agents import BasicAgent +# from irlc.ex08.bandits import StationaryBandit +# env = StationaryBandit(k=10, ) +# agent = BasicAgent(env, epsilon=0.1) +# return env, agent +# +# def precompute_payload(self): +# env, agent = self.get_env_agent() +# _, trajectories = train(env, agent, return_trajectory=True, num_episodes=1, max_steps=100) +# return trajectories, agent.Q +# +# def compute_answer_print(self): +# trajectories, Q = self.precomputed_payload() +# env, agent = self.get_env_agent() +# train_recording(env, agent, trajectories) +# self.Q = Q +# self.question.agent = agent +# return agent.Q +# +# def process_output(self, res, txt, numbers): +# return res +# +# def test(self, computed, expected): +# super().test(computed, self.Q) +# +# class BanditItemActionDistribution(QPrintItem): +# # Assumes setup has already been done. +# title = "Action distribution test" +# T = 10000 +# tol = 1/np.sqrt(T)*5 +# testfun = QPrintItem.assertL2 +# +# def compute_answer_print(self): +# # print("In agent print code") +# from collections import Counter +# counts = Counter( [self.question.agent.pi(None, k) for k in range(self.T)] ) +# distrib = [counts[k] / self.T for k in range(self.question.agent.env.k)] +# return np.asarray(distrib) +# +# def process_output(self, res, txt, numbers): +# return res +# +# class BanditQuestion(QuestionGroup): +# title = "Simple bandits" +# class SimpleBanditItem(BanditItem): +# #title = "Value function estimate" +# def get_env_agent(self): +# from irlc.ex08.simple_agents import BasicAgent +# from irlc.ex08.bandits import StationaryBandit +# env = StationaryBandit(k=10, ) +# agent = BasicAgent(env, epsilon=0.1) +# return env, agent +# class SimpleBanditActionDistribution(BanditItemActionDistribution): +# pass + + + +class GradientBanditQuestion(BanditQuestion): + """ Gradient agent """ + # class SimpleBanditItem(BanditItem): + # title = "Simple agent question" + def get_env_agent(self): + from irlc.ex08.bandits import StationaryBandit + from irlc.ex08.gradient_agent import GradientAgent + env = StationaryBandit(k=10) + agent = GradientAgent(env, alpha=0.05) + return env, agent + + # def precompute_payload(self): + # env, agent = self.get_env_agent() + # _, trajectories = train(env, agent, return_trajectory=True, num_episodes=1, max_steps=100) + # return trajectories + + def test_agent(self): + trajectories = self.get_trajectories() + env, agent = self.get_env_agent() + train_recording(env, agent, trajectories) + self.assertL2(agent.H, tol=1e-5) + + + # def test(self, computed, expected): + # self.testfun(computed, self.H) + # + # class SimpleBanditActionDistribution(BanditItemActionDistribution): + # pass + + +# class GradientBanditQuestion(QuestionGroup): +# title = "Gradient agent" +# class SimpleBanditItem(BanditItem): +# # title = "Simple agent question" +# def get_env_agent(self): +# from irlc.ex08.bandits import StationaryBandit +# from irlc.ex08.gradient_agent import GradientAgent +# env = StationaryBandit(k=10) +# agent = GradientAgent(env, alpha=0.05) +# return env, agent +# +# def precompute_payload(self): +# env, agent = self.get_env_agent() +# _, trajectories = train(env, agent, return_trajectory=True, num_episodes=1, max_steps=100) +# return trajectories, agent.H +# +# def compute_answer_print(self): +# trajectories, H = self.precomputed_payload() +# env, agent = self.get_env_agent() +# train_recording(env, agent, trajectories) +# self.H = H +# self.question.agent = agent +# return agent.H +# +# def test(self, computed, expected): +# self.testfun(computed, self.H) +# +# class SimpleBanditActionDistribution(BanditItemActionDistribution): +# pass + + + +class UCBAgentQuestion(BanditQuestion): + """ UCB agent """ + # class UCBAgentItem(BanditItem): + def get_env_agent(self): + from irlc.ex08.bandits import StationaryBandit + from irlc.ex08.ucb_agent import UCBAgent + env = StationaryBandit(k=10) + agent = UCBAgent(env) + return env, agent + + # class UCBAgentActionDistribution(BanditItemActionDistribution): + # pass + + +# class UCBAgentQuestion(QuestionGroup): +# title = "UCB agent" +# class UCBAgentItem(BanditItem): +# def get_env_agent(self): +# from irlc.ex08.bandits import StationaryBandit +# from irlc.ex08.ucb_agent import UCBAgent +# env = StationaryBandit(k=10) +# agent = UCBAgent(env) +# return env, agent +# +# class UCBAgentActionDistribution(BanditItemActionDistribution): +# pass + +# class NonstatiotnaryAgentQuestion(QuestionGroup): +# title = "Nonstationary bandit environment" +# class NonstationaryItem(BanditItem): +# def get_env_agent(self): +# epsilon = 0.1 +# from irlc.ex08.nonstationary import NonstationaryBandit, MovingAverageAgent +# bandit = NonstationaryBandit(k=10) +# agent = MovingAverageAgent(bandit, epsilon=epsilon, alpha=0.15) +# return bandit, agent +# +# class NonstationaryActionDistribution(BanditItemActionDistribution): +# pass + +class NonstatiotnaryAgentQuestion(BanditQuestion): + """ UCB agent """ + # class UCBAgentItem(BanditItem): + def get_env_agent(self): + epsilon = 0.1 + from irlc.ex08.nonstationary import NonstationaryBandit, MovingAverageAgent + bandit = NonstationaryBandit(k=10) + agent = MovingAverageAgent(bandit, epsilon=epsilon, alpha=0.15) + return bandit, agent + +import irlc +class Week08Tests(Report): + title = "Tests for week 08" + pack_imports = [irlc] + individual_imports = [] + questions = [ + (BanditQuestion, 10), + (GradientBanditQuestion, 10), + (UCBAgentQuestion, 5), + (NonstatiotnaryAgentQuestion, 5) + ] + +if __name__ == '__main__': + from unitgrade import evaluate_report_student + evaluate_report_student(Week08Tests()) diff --git a/irlc/tests/unitgrade_data/BanditQuestion.pkl b/irlc/tests/unitgrade_data/BanditQuestion.pkl new file mode 100644 index 0000000000000000000000000000000000000000..55e379fe474d7a967700bb9c83202905b8ebcbfa GIT binary patch literal 96256 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 Y&6Hnd6SU<Gv?LS+zKP;m;5qdF0RFck>;M1& literal 0 HcmV?d00001 diff --git a/irlc/tests/unitgrade_data/BrachistochroneConstrainedQuestion.pkl b/irlc/tests/unitgrade_data/BrachistochroneConstrainedQuestion.pkl deleted file mode 100644 index 1d1b594d8ccdde8336eb3cd174e105beaf5eaf6f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7242 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 L*tZ%XseS$p(!Obt diff --git a/irlc/tests/unitgrade_data/BrachistochroneQuestion.pkl b/irlc/tests/unitgrade_data/BrachistochroneQuestion.pkl deleted file mode 100644 index 1d1b594d8ccdde8336eb3cd174e105beaf5eaf6f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7242 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 L*tZ%XseS$p(!Obt diff --git a/irlc/tests/unitgrade_data/CartpoleCostQuestion.pkl b/irlc/tests/unitgrade_data/CartpoleCostQuestion.pkl index 1d1b594d8ccdde8336eb3cd174e105beaf5eaf6f..21e4c24c13dd49d445c4efe18438fe4a0b360513 100644 GIT binary patch delta 186 zcmX?Qaa@bFfn}<V=tNd4rj2_hI$AKEm{>K7NsM8#7~@VR-UpK}GAbzuXPzx!U|`5F z%3y1olEKk7rFIIG&C)i--R{U_VI~t!nFBy2AVrf8vdL}EV>-voBq%W1j!jL$m|?mE zNX8dQJA@6^er$3Jn+cN~!{%LVk2#nG9VXA`S7VV{c5uPull;<*$0k4JH(`<v*eore N%*rWn0c=UB9sr<$ITio_ delta 1913 zcmeH|dq`7p6vuzN>a_6HX0={&r_LlT%%rC2oXdQa(YC^*P|8PLOEyshvBz#{qi{ub zuOU`$p;*#`%Cx7ES*bxaU-Y2N%tv5Z)JmzSe&?rw{nbA~|Mb`QoX<Jup5OWHcUaS$ zdvXWXz9eF1u|p_zyQ?@-F6b=IFQWoRxG91wl=LyzD)6HH^M>3JLQD`NH1dp-k>A0O z+4e?0M$yTMso)7R2B$z0*C6z)(NPcVD0+lb2z}|8wM9V?Suw7B)F$QaT%j<Snk}ih zDtzTcdHHc>H(&dp#x;pti`xvr6RyuKm<p8C%y9R1m>LY{K*%&zmAxUA$;ipor!v`k z9j6w0Bze@D0-;U8xHh9UY12k3A?)&Nh~<b8Gt-thz3psGcD>pY!5eukpEH&LMG-!E zq>3qkp$xN@+u_n_`&5@Hg4cG5kA-6^tctr;k2N4~SKtk-+jp8z0(Wk{&<4DlYL@}G z4kz>ie-?4>3-IU{{o%k(*Q(wFk6+gI1Gr?+tOTyh$#@N1z1O)9a|22{_{tjs&3zp& zAYk#?(E<flv%d@Qo7B@=z}X{3t-ujV_zr5z_xl9AH?|5vUPo!QAL#wK2Vw<in@j}$ zx3E*>K)S;P-oRy%s>ztM(#_FPH*kMM=PsNM0pH#sXDF~WZjobEno?e>FlUD+RxAL1 zK5%~`XkJ#R7WjfejSBm0)(MAASS^|J^%~6Ai)QVK1m9IEPREMA>1x%O(`V9~;07$3 zn1ocU%4ShhtOEB(wmMS?fChQ*I;?D_p>B}^>tyQyOvL&NF2!L*J)4$#VUC_ue<hO@ z_t4~Eg;59WftT)W{R}s=I$i`5BFm#6<VM{qT&%#mYb%C8*WY{B1vl62X!p<nfPHO# z1ByZ$pF)7Xe&{aowa@xNAM3wNf(EBoUbKO~+SyJ54QwSca@AQH>I3k?vNGT|8yrhk z;e57E+#Q5{<+iA^YRC^W<^!h}-+vC=^6?XAfB?Jlj#Q2V?0&}p5A37lOP)T!k7lNX zV6GgjolbtUjg6JS(MH#5(iqKktqI3|W%jaxdOnv=frl=|@0mPwT7pCPC3^k7Sm{gj zx}4nX4H>%hG!3T}ijrK8@3!DXrhMF%joR=Xg%!R+!hhPba<Ye{xsQCT=>L}e-?D$b TWodtV%gV=X8T>&Zv~}K38EAHT diff --git a/irlc/tests/unitgrade_data/CartpoleTimeQuestion.pkl b/irlc/tests/unitgrade_data/CartpoleTimeQuestion.pkl index 1d1b594d8ccdde8336eb3cd174e105beaf5eaf6f..21e4c24c13dd49d445c4efe18438fe4a0b360513 100644 GIT binary patch delta 186 zcmX?Qaa@bFfn}<V=tNd4rj2_hI$AKEm{>K7NsM8#7~@VR-UpK}GAbzuXPzx!U|`5F z%3y1olEKk7rFIIG&C)i--R{U_VI~t!nFBy2AVrf8vdL}EV>-voBq%W1j!jL$m|?mE zNX8dQJA@6^er$3Jn+cN~!{%LVk2#nG9VXA`S7VV{c5uPull;<*$0k4JH(`<v*eore N%*rWn0c=UB9sr<$ITio_ delta 1913 zcmeH|dq`7p6vuzN>a_6HX0={&r_LlT%%rC2oXdQa(YC^*P|8PLOEyshvBz#{qi{ub zuOU`$p;*#`%Cx7ES*bxaU-Y2N%tv5Z)JmzSe&?rw{nbA~|Mb`QoX<Jup5OWHcUaS$ zdvXWXz9eF1u|p_zyQ?@-F6b=IFQWoRxG91wl=LyzD)6HH^M>3JLQD`NH1dp-k>A0O z+4e?0M$yTMso)7R2B$z0*C6z)(NPcVD0+lb2z}|8wM9V?Suw7B)F$QaT%j<Snk}ih zDtzTcdHHc>H(&dp#x;pti`xvr6RyuKm<p8C%y9R1m>LY{K*%&zmAxUA$;ipor!v`k z9j6w0Bze@D0-;U8xHh9UY12k3A?)&Nh~<b8Gt-thz3psGcD>pY!5eukpEH&LMG-!E zq>3qkp$xN@+u_n_`&5@Hg4cG5kA-6^tctr;k2N4~SKtk-+jp8z0(Wk{&<4DlYL@}G z4kz>ie-?4>3-IU{{o%k(*Q(wFk6+gI1Gr?+tOTyh$#@N1z1O)9a|22{_{tjs&3zp& zAYk#?(E<flv%d@Qo7B@=z}X{3t-ujV_zr5z_xl9AH?|5vUPo!QAL#wK2Vw<in@j}$ zx3E*>K)S;P-oRy%s>ztM(#_FPH*kMM=PsNM0pH#sXDF~WZjobEno?e>FlUD+RxAL1 zK5%~`XkJ#R7WjfejSBm0)(MAASS^|J^%~6Ai)QVK1m9IEPREMA>1x%O(`V9~;07$3 zn1ocU%4ShhtOEB(wmMS?fChQ*I;?D_p>B}^>tyQyOvL&NF2!L*J)4$#VUC_ue<hO@ z_t4~Eg;59WftT)W{R}s=I$i`5BFm#6<VM{qT&%#mYb%C8*WY{B1vl62X!p<nfPHO# z1ByZ$pF)7Xe&{aowa@xNAM3wNf(EBoUbKO~+SyJ54QwSca@AQH>I3k?vNGT|8yrhk z;e57E+#Q5{<+iA^YRC^W<^!h}-+vC=^6?XAfB?Jlj#Q2V?0&}p5A37lOP)T!k7lNX zV6GgjolbtUjg6JS(MH#5(iqKktqI3|W%jaxdOnv=frl=|@0mPwT7pCPC3^k7Sm{gj zx}4nX4H>%hG!3T}ijrK8@3!DXrhMF%joR=Xg%!R+!hhPba<Ye{xsQCT=>L}e-?D$b TWodtV%gV=X8T>&Zv~}K38EAHT diff --git a/irlc/tests/unitgrade_data/DirectAgentPendulum.pkl b/irlc/tests/unitgrade_data/DirectAgentPendulum.pkl index e9d2ed475214b22bc52f1cc0dfc8c04c71d9a2b9..8bcfd04385b49acb537aa90a6c1906443c00c348 100644 GIT binary patch delta 23 fcmaFP_?&TqEwkPChKUaIIE7xWQea?UDAfZ1aAXKJ delta 23 fcmaFP_?&TqEwer6f{70EIMtIr3otM+l<ENhVrB<0 diff --git a/irlc/tests/unitgrade_data/DirectMethods.pkl b/irlc/tests/unitgrade_data/DirectMethods.pkl index f81ab2560bf4752a237712f1df94fc8ae01ac0ce..1872c37be157b1d23e330e90fb98df324bc707a7 100644 GIT binary patch delta 47 zcmdnYy_tK01<S6#Qy3=NY+&L@*!b=cBa=A8=1ol1j7&TUoAp_$896x>7(k#@4*-r@ B4SoOs delta 47 zcmdnYy_tK01<Rv<;tdmRHZaKvY<zc!kxBi>=1ol1j7+8moAp_$899wUFn~a*9ssUA B4{87a diff --git a/irlc/tests/unitgrade_data/DirectSolverQuestion.pkl b/irlc/tests/unitgrade_data/DirectSolverQuestion.pkl index 1d1b594d8ccdde8336eb3cd174e105beaf5eaf6f..21e4c24c13dd49d445c4efe18438fe4a0b360513 100644 GIT binary patch delta 186 zcmX?Qaa@bFfn}<V=tNd4rj2_hI$AKEm{>K7NsM8#7~@VR-UpK}GAbzuXPzx!U|`5F z%3y1olEKk7rFIIG&C)i--R{U_VI~t!nFBy2AVrf8vdL}EV>-voBq%W1j!jL$m|?mE zNX8dQJA@6^er$3Jn+cN~!{%LVk2#nG9VXA`S7VV{c5uPull;<*$0k4JH(`<v*eore N%*rWn0c=UB9sr<$ITio_ delta 1913 zcmeH|dq`7p6vuzN>a_6HX0={&r_LlT%%rC2oXdQa(YC^*P|8PLOEyshvBz#{qi{ub zuOU`$p;*#`%Cx7ES*bxaU-Y2N%tv5Z)JmzSe&?rw{nbA~|Mb`QoX<Jup5OWHcUaS$ zdvXWXz9eF1u|p_zyQ?@-F6b=IFQWoRxG91wl=LyzD)6HH^M>3JLQD`NH1dp-k>A0O z+4e?0M$yTMso)7R2B$z0*C6z)(NPcVD0+lb2z}|8wM9V?Suw7B)F$QaT%j<Snk}ih zDtzTcdHHc>H(&dp#x;pti`xvr6RyuKm<p8C%y9R1m>LY{K*%&zmAxUA$;ipor!v`k z9j6w0Bze@D0-;U8xHh9UY12k3A?)&Nh~<b8Gt-thz3psGcD>pY!5eukpEH&LMG-!E zq>3qkp$xN@+u_n_`&5@Hg4cG5kA-6^tctr;k2N4~SKtk-+jp8z0(Wk{&<4DlYL@}G z4kz>ie-?4>3-IU{{o%k(*Q(wFk6+gI1Gr?+tOTyh$#@N1z1O)9a|22{_{tjs&3zp& zAYk#?(E<flv%d@Qo7B@=z}X{3t-ujV_zr5z_xl9AH?|5vUPo!QAL#wK2Vw<in@j}$ zx3E*>K)S;P-oRy%s>ztM(#_FPH*kMM=PsNM0pH#sXDF~WZjobEno?e>FlUD+RxAL1 zK5%~`XkJ#R7WjfejSBm0)(MAASS^|J^%~6Ai)QVK1m9IEPREMA>1x%O(`V9~;07$3 zn1ocU%4ShhtOEB(wmMS?fChQ*I;?D_p>B}^>tyQyOvL&NF2!L*J)4$#VUC_ue<hO@ z_t4~Eg;59WftT)W{R}s=I$i`5BFm#6<VM{qT&%#mYb%C8*WY{B1vl62X!p<nfPHO# z1ByZ$pF)7Xe&{aowa@xNAM3wNf(EBoUbKO~+SyJ54QwSca@AQH>I3k?vNGT|8yrhk z;e57E+#Q5{<+iA^YRC^W<^!h}-+vC=^6?XAfB?Jlj#Q2V?0&}p5A37lOP)T!k7lNX zV6GgjolbtUjg6JS(MH#5(iqKktqI3|W%jaxdOnv=frl=|@0mPwT7pCPC3^k7Sm{gj zx}4nX4H>%hG!3T}ijrK8@3!DXrhMF%joR=Xg%!R+!hhPba<Ye{xsQCT=>L}e-?D$b TWodtV%gV=X8T>&Zv~}K38EAHT diff --git a/irlc/tests/unitgrade_data/Exam5InventoryEvaluation.pkl b/irlc/tests/unitgrade_data/Exam5InventoryEvaluation.pkl index a511917ab3f43109f1d3fe37b025f2fde713339f..288459bca52e824a5d9dabdcb4cf10e164f64114 100644 GIT binary patch delta 30 jcmcb~c$0C06SLh9hl#Ep%(4?0CQfkR<Vauufl@sHoQViS delta 30 jcmcb~c$0C06SF<ffr+jj%nd#c6DK%uDm`ETfl@sHopuP( diff --git a/irlc/tests/unitgrade_data/Exam6Toy2d.pkl b/irlc/tests/unitgrade_data/Exam6Toy2d.pkl index f927b5ae7578002c5a8840c91705f5d4c7d806f0..06341fef90fd2beed50cccac023bdd729b480a91 100644 GIT binary patch delta 29 kcmbQuG@EIH3A5ez0~5_>GfliT@roViglh#1Kv1d&0I%%|>;M1& delta 29 jcmbQuG@EIH3A4RI#6<JiOsg9vUa{j`kygO~1f_ZakZK7Y diff --git a/irlc/tests/unitgrade_data/ExamQuestion7FlowersStore.pkl b/irlc/tests/unitgrade_data/ExamQuestion7FlowersStore.pkl index 6555641d17ccd50bdc906dab13481cdad59254cc..7de7875d690be1fc4143070c2139bd34f61288ae 100644 GIT binary patch delta 30 jcmdnSxQ%gwGqc_20~6iMm`x5aOsv=A<XOM~0;PHYqGt%u delta 30 jcmdnSxQ%gwGqXKs!$dbT=0?>A6YKRj)e0Cupi~b4kcS8( diff --git a/irlc/tests/unitgrade_data/GradientBanditQuestion.pkl b/irlc/tests/unitgrade_data/GradientBanditQuestion.pkl new file mode 100644 index 0000000000000000000000000000000000000000..55e379fe474d7a967700bb9c83202905b8ebcbfa GIT binary patch literal 96256 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 Y&6Hnd6SU<Gv?LS+zKP;m;5qdF0RFck>;M1& literal 0 HcmV?d00001 diff --git a/irlc/tests/unitgrade_data/ILQRAgentQuestion.pkl b/irlc/tests/unitgrade_data/ILQRAgentQuestion.pkl index 4af21ecb688c99771e1897bc53ecbae1bc667b8f..94b38667b6a59b2bdd827e9569ad5bce677cc91e 100644 GIT binary patch delta 104 zcmV-u0GI#90>%Q6I|DxVz>z+#T6hoxPw>epK!n{Bb^ag+z}^qx<2uMGK#YJigX1R$ zzy{cO-)(<^KV+`~A-V^3zdeD3o3RRYKT`u>+hvS_zm#;8VsmH#Xa<IqXat6oM?c9l K*Z=?k0Cg^91uLWg delta 104 zcmV-u0GI#90>%Q6I|DxpAdx<<T6_=#Pw>epKz!X3b^ag+z~c|$<2uMGK!boZgX1R$ zz@XT8-)(<^KjN|hA-V^3zlnx~o3RRYKl%e;+hvS_zm#;8VsmH#Xa<IqXat6oM?c^@ Kvj6}90Cg@e2rL}{ diff --git a/irlc/tests/unitgrade_data/ILQRPendulumQuestion.pkl b/irlc/tests/unitgrade_data/ILQRPendulumQuestion.pkl index 1019b5db1c0a0ad9b3e14545aaf80055652fcb66..af4efa1cc7fc8336bfab2d97317419f4573a58da 100644 GIT binary patch delta 64 zcmZ3*w2Enh9kbn!0}~y$8$E7V`Nh`0e801ZlI6Ys@9j_7-WRI>7Gpo9WJ*$T24e<W U+msBJwkhuRH_k0$0D@9I0KJAAs{jB1 delta 64 zcmV-G0KfmL0;&R#J_A1qppikhGK+wz^gTa!ze5xvIo|*8KN3OT5`XqwKa_NoVsmH# WXa<IqXat6oM?dUeWPktw0Cg@{1sQk% diff --git a/irlc/tests/unitgrade_data/NonstatiotnaryAgentQuestion.pkl b/irlc/tests/unitgrade_data/NonstatiotnaryAgentQuestion.pkl new file mode 100644 index 0000000000000000000000000000000000000000..55e379fe474d7a967700bb9c83202905b8ebcbfa GIT binary patch literal 96256 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 Y&6Hnd6SU<Gv?LS+zKP;m;5qdF0RFck>;M1& literal 0 HcmV?d00001 diff --git a/irlc/tests/unitgrade_data/PendulumQuestion.pkl b/irlc/tests/unitgrade_data/PendulumQuestion.pkl index 1d1b594d8ccdde8336eb3cd174e105beaf5eaf6f..21e4c24c13dd49d445c4efe18438fe4a0b360513 100644 GIT binary patch delta 186 zcmX?Qaa@bFfn}<V=tNd4rj2_hI$AKEm{>K7NsM8#7~@VR-UpK}GAbzuXPzx!U|`5F z%3y1olEKk7rFIIG&C)i--R{U_VI~t!nFBy2AVrf8vdL}EV>-voBq%W1j!jL$m|?mE zNX8dQJA@6^er$3Jn+cN~!{%LVk2#nG9VXA`S7VV{c5uPull;<*$0k4JH(`<v*eore N%*rWn0c=UB9sr<$ITio_ delta 1913 zcmeH|dq`7p6vuzN>a_6HX0={&r_LlT%%rC2oXdQa(YC^*P|8PLOEyshvBz#{qi{ub zuOU`$p;*#`%Cx7ES*bxaU-Y2N%tv5Z)JmzSe&?rw{nbA~|Mb`QoX<Jup5OWHcUaS$ zdvXWXz9eF1u|p_zyQ?@-F6b=IFQWoRxG91wl=LyzD)6HH^M>3JLQD`NH1dp-k>A0O z+4e?0M$yTMso)7R2B$z0*C6z)(NPcVD0+lb2z}|8wM9V?Suw7B)F$QaT%j<Snk}ih zDtzTcdHHc>H(&dp#x;pti`xvr6RyuKm<p8C%y9R1m>LY{K*%&zmAxUA$;ipor!v`k z9j6w0Bze@D0-;U8xHh9UY12k3A?)&Nh~<b8Gt-thz3psGcD>pY!5eukpEH&LMG-!E zq>3qkp$xN@+u_n_`&5@Hg4cG5kA-6^tctr;k2N4~SKtk-+jp8z0(Wk{&<4DlYL@}G z4kz>ie-?4>3-IU{{o%k(*Q(wFk6+gI1Gr?+tOTyh$#@N1z1O)9a|22{_{tjs&3zp& zAYk#?(E<flv%d@Qo7B@=z}X{3t-ujV_zr5z_xl9AH?|5vUPo!QAL#wK2Vw<in@j}$ zx3E*>K)S;P-oRy%s>ztM(#_FPH*kMM=PsNM0pH#sXDF~WZjobEno?e>FlUD+RxAL1 zK5%~`XkJ#R7WjfejSBm0)(MAASS^|J^%~6Ai)QVK1m9IEPREMA>1x%O(`V9~;07$3 zn1ocU%4ShhtOEB(wmMS?fChQ*I;?D_p>B}^>tyQyOvL&NF2!L*J)4$#VUC_ue<hO@ z_t4~Eg;59WftT)W{R}s=I$i`5BFm#6<VM{qT&%#mYb%C8*WY{B1vl62X!p<nfPHO# z1ByZ$pF)7Xe&{aowa@xNAM3wNf(EBoUbKO~+SyJ54QwSca@AQH>I3k?vNGT|8yrhk z;e57E+#Q5{<+iA^YRC^W<^!h}-+vC=^6?XAfB?Jlj#Q2V?0&}p5A37lOP)T!k7lNX zV6GgjolbtUjg6JS(MH#5(iqKktqI3|W%jaxdOnv=frl=|@0mPwT7pCPC3^k7Sm{gj zx}4nX4H>%hG!3T}ijrK8@3!DXrhMF%joR=Xg%!R+!hhPba<Ye{xsQCT=>L}e-?D$b TWodtV%gV=X8T>&Zv~}K38EAHT diff --git a/irlc/tests/unitgrade_data/Problem1BobsFriend.pkl b/irlc/tests/unitgrade_data/Problem1BobsFriend.pkl index cb3e05e507f46a771194aa47e9d478a38ea6dc4a..0a911216fa96ee726261d5fd6122f47c63b7becd 100644 GIT binary patch delta 29 icmZ3*xQcOtEt5dPM0*ovyI&3y%d|P4H!y%esU84`>Ihl@ delta 29 icmZ3*xQcOtEt6QlM0*ovdnSd6W!jt{4={j0sU84&KnH^W diff --git a/irlc/tests/unitgrade_data/Problem1DiscreteKuromoto.pkl b/irlc/tests/unitgrade_data/Problem1DiscreteKuromoto.pkl index 5884cbbaec323589db6f77d93e93aab42b835c82..6174c0b3159b23350a66f8510986566388f9a9e9 100644 GIT binary patch delta 44 ycmdnRvWsPc6SLjlhKa7dOxAlQUfIrMoie$X(T_Q=fnoAJ#$e8PAqEgA)dK)}s}5)Y delta 44 zcmdnRvWsPc6SKXD!9>?yrihA(SGF^SGEVMg^keSuOPG9*F_^P-eFFm!l<ENhQNa#7 diff --git a/irlc/tests/unitgrade_data/Problem1Kuramoto.pkl b/irlc/tests/unitgrade_data/Problem1Kuramoto.pkl index ef1b528ea3cb8460738583d5cf526ff19f05dc78..5da65912b9c77917947555ed5b62336969918a99 100644 GIT binary patch delta 37 scmX>meoTCV6|>!MhKaVlOi`i}&unM1y|cNM(SnQf_}nZ8ASl%X014a-u>b%7 delta 37 tcmX>meoTCV6|=oS!$jL&rd+v+XSOp1Z`j<*Xu-w#(xIh+fq|h^4*&p+46FbE diff --git a/irlc/tests/unitgrade_data/Problem2BobsPolicy.pkl b/irlc/tests/unitgrade_data/Problem2BobsPolicy.pkl index 25f36de2f85c245c47e3f76ccfd13411d7dbb190..5d994baa391da54fd3a6e1c1a369b72a9df5f17a 100644 GIT binary patch delta 59 zcmeys^nq!D4YS=>hl%z^%s&n=Ok6EDagrEwnXtpe)h5h`rv^<{W0af3sLZ_h`~@J> Ok@L{MI0hgn)dK*fycGBV delta 59 zcmeys^nq!D4YNIW!$f-{=6?qmCa#v7I7y7Tr{TfG)h5havpgoNG0IJ1RAxS<x&X*@ N<UHYN#{dMSdH|BV6SDvS diff --git a/irlc/tests/unitgrade_data/Problem2DeterministicDP.pkl b/irlc/tests/unitgrade_data/Problem2DeterministicDP.pkl index c6a6d86c444884d85398ab5d68b4a8f2d731b17c..4029b85e80a9ebbf315924351ada7ba445fcb24a 100644 GIT binary patch delta 23 ccmZ3;xR7yzBeUIChlwsBoW=_nK%i6)09S(sGynhq delta 23 ccmZ3;xR7yzBeOlnf{896oZb=)AW*6Y085AkH~;_u diff --git a/irlc/tests/unitgrade_data/Problem2DeterministicInventory.pkl b/irlc/tests/unitgrade_data/Problem2DeterministicInventory.pkl index d7b664c9873a3f229a3ada6b5c0794429402f080..547769c9bb40f7e2f9e061a3d24943b7bf016ea1 100644 GIT binary patch delta 22 acmZo*Y+#(=$;6N_(OZY}Edv;o>Hz>iBLv?7 delta 22 acmZo*Y+#(=$t1jCqPGs`4+bzO)dK)V0|iY0 diff --git a/irlc/tests/unitgrade_data/Problem3InventoryInventoryEnvironment.pkl b/irlc/tests/unitgrade_data/Problem3InventoryInventoryEnvironment.pkl index ed6643cb8af26e6368d19072746fd41ae4c60ab0..f8b966396874d03b37f527e8166a7431bd63ce66 100644 GIT binary patch delta 56 zcmX@ibeL&^KeOHM0~3SVWhcI^u{VtqJTRqgO3;+z48{z$wka7bZByLs7b!_h{N}>B M-1h_n5R~cx0Gy!}od5s; delta 56 zcmX@ibeL&^KeN5ygNZ@yvZoiiZu|UK;J}o&DM3?;GZ-`2+NNZ%v`ulhKiZWr@tX_h MX-Pf?ASl%X0Jnq|X8-^I diff --git a/irlc/tests/unitgrade_data/Problem3LQR.pkl b/irlc/tests/unitgrade_data/Problem3LQR.pkl index 604d5c20678f95431aae0b2c24d839ff98a641a0..cd8f6f6cd8072c224d9de2763d5585bdba4a6d80 100644 GIT binary patch delta 633 zcmaFK|B`=#8I#qOi56Ql54i8_wr6KJkhJTc=driE2YCGBOiZ$W>^Ho%;)Ivy!Tn2k zLtbxrz~``JvM8gWW=XJI>r8<+_U96I>&^M|dcWT+E1T|5Z|palHU5vSdA;9xx0X_m z=_C8Ulk*uZ6)Vp2zuR))qW$ma%K>}YzS(Qb8_L>jmpj0}V$Go+Q=jhVpL~+hk<n{1 zH<JOAcE@B}CVNi7Do56HoFDCHO>Sk<<^0pLX=g*#qx~9_cLRCPTJ~2or$`@IJ^3S( zskHft;%i&~GdN5snUYkR!I)v*HYJ0lZHl|STE%2XW@kr^b(R(D4js0ik{K%)aY4l4 zgZ5I+u=GE0e!0iI^efpy2TB-iH6AOmAArfj_y;B*Wj1uo`%*TsT8P^rw{ITnM%BOj zwR+~x7n}QeKa3yr<IiP*#lP&AiS%Y)Qo3XhlZWxsCY!SuI*OQ9NUn9^IB@#bEbrW; zU;BL<cUsT-#qI#(w{ahtc5%^>{W=ZbPd|>lWDk>v@tr2GWN~C<n*5qYUXW??YL!VB z-|jCu@!M}_@^gEY$<nM{Oj;h3m$TXndhgAvWn%xczvo}sm51!_?e|Uo&Z;ZOkP^&P zcW{mU5B4{TM>agOe>T~aO;@mD(M#>ZY0M6OKlt|DWn*!0n_LVOt(rf-@t~;Sf$r7! z{(Vg2a_F7Bku8i#<HBT4c3o|=M?aU8C;zpd(l#Y%iZ^4%h74(E4(6a86Hgd$J3s`P ZAcE$T6WK)=>nE47XK|XkFn~a*9srjy7()O6 delta 633 zcmaFK|B`=#8I%8_i56Ql+uV0{+p{wqXx#PB^VnP71Lpp5CMMZG_KV(Kal*^<;QnpA zA+NVQ;Bz=IS(MRGGa*>6b*8`@`=bfF_2&F}z2AG5l}-1jH};igjsIh7UhlWst)<js z`pEwA<a|a;#qhKI@3tJcXwMgOIbbi_H+yY)Ls^^catD4dUvuck)TjHcC!b_=Wb~fQ z&1AsjG-a|alRf9FN=MdnoFDCLC$}=`a^C6Lw6me=(SE_nyMesBE&D5)Q=|{fpZt-@ zRNC}J@wKi0862jROi3!uV9YRYo07rOHpSiEs$;Svv$JEsO3R9MhYs7X$cz<?xFF)7 zrL&YXEd39hpXV_z{YtjbfkH-GjmJvt2Vn9r{({LznGGEczLZU@7UFgY?3>5BQT6YB z)t<TY#pZtA590^?_;Xoc@h|%sBE8v{lrGuB<YD~m$>uDEjwYrRl51T!4xGF-%R4ve z*M7&woz}B{u{*%{G2BO{U0ifzKXb$P(~l!B*~8>veCf$6SsWP|C%<Nq7i8SLT4mD3 zxBH7u{Px?K{M=q)vNUTKlk<$p%USIOMfc{_GO_>JpYX5j%0u?|_M0YuXVn#K$PH$y zJGjRF0Q(!oBO9LC|CnsbrYl&q=%seyG-e08AAI}nvavW=O)dtCR?eT_cu-XEK<Dav z|30R1IrL24$QH)rzG5;byRP<`hd-B;C;zpd(l#Y%iZ^4%h74(Ej>CaFCY~_hc7O;n ZK?Fr6C$ft$Hcl>M&*JpIzyJcJdH^`l7zqFX diff --git a/irlc/tests/unitgrade_data/Problem3PID.pkl b/irlc/tests/unitgrade_data/Problem3PID.pkl index 535051b828a3939a46f439310b29198a5e080a0a..252cfd024c97e5da728820dacd87ab9910607247 100644 GIT binary patch delta 42 wcmX@dbdG6)DYM;=0~0OmnM4*$taN0OX_&ajl}R*UGBcwSr-%Rp2$bpp05!e~r~m)} delta 42 vcmX@dbdG6)DYHHIgNYXQOd1{&D;=419VYH^Wzsd6%*^P-sXK!K1WNS)6lMzo diff --git a/irlc/tests/unitgrade_data/Problem3StochasticDP.pkl b/irlc/tests/unitgrade_data/Problem3StochasticDP.pkl index 04713f394a37f31128be4aead3269f8c3c2e4695..0e1fc83741cb9bd0877d29de2b3828b78bdd5b01 100644 GIT binary patch delta 29 icmcb~bdza<9kbon0}~x{n1lo-u3yOMRK)-SrFsCPG6^OC delta 29 jcmcb~bdza<9kacH!9>R#CZ&Xl>lbn+@HH?1L8%@9jSUD? diff --git a/irlc/tests/unitgrade_data/Problem4DPAgent.pkl b/irlc/tests/unitgrade_data/Problem4DPAgent.pkl index 54dc9c584545f20c53ce6eab91442e358d24233e..178368d13873f75c43be9a31cb3dbdb10d5fef36 100644 GIT binary patch delta 20 Zcmb=doM6dh_jRJRDW}i@1`sIK0{})41#kcW delta 20 Zcmb=doM6dh&pFZBlv8m60|=Dr0RT8Y1Y`gJ diff --git a/irlc/tests/unitgrade_data/Problem4InventoryTrain.pkl b/irlc/tests/unitgrade_data/Problem4InventoryTrain.pkl index 2e0efe223d9500337a95c76732a6fcfc3cbb2872..22065591b65be79d935c05472a7603be0e00bcdb 100644 GIT binary patch delta 49 zcmeyw_=$0X1GC+ahKbHS%C+{Ual1SP4@_yB5;UbagE51xZAu19+Z1>E-jxg>P^t$2 D^Q95< delta 49 zcmeyw_=$0X1GBx<go(~Q${7>i*4Ud09+=WLC1^@<24e<W+msBJwkhuRt5@G(0D@9I E0PF`6q5uE@ diff --git a/irlc/tests/unitgrade_data/Problem4LQRAgent.pkl b/irlc/tests/unitgrade_data/Problem4LQRAgent.pkl index b7f0a6a6d9def299660158f65d0b85af24d92699..42b50d8f321a365c574de2e27cc5dead749dbee4 100644 GIT binary patch delta 65 zcmdnZyqkG~6|>#f0~2jOF*i&}m}n<4*^5z~F>P`Yqq<VYVgLEUwGa1CDVdT~n!%W% V-ZmwJrEQA4eQ{d@0}zzz0RTt57z_Xa delta 65 zcmdnZyqkG~6|+4@!bICo%#+F;CfZ3%_F`0L%${7tsIHW9*nhro?Zf?3N~R>0W-w-` Tw@t}lX`AA1-}#gQ1WNS)_G}i5 diff --git a/irlc/tests/unitgrade_data/Problem4PIDAgent.pkl b/irlc/tests/unitgrade_data/Problem4PIDAgent.pkl index 70a85f4bcf6fc78c263690ba8b0491e7799bf59c..14b3e4b4c95270f0c2953a2cc41a66833ba99d7f 100644 GIT binary patch delta 55 zcmX@8a!_T06|>#fhK;s6S(umQI!w;tl-w-D8qUJJv}VHQ9L@|NW246A>%569oJ$iv JFaSZR9soN<68HcB delta 55 zcmX@8a!_T06|+6ZgN?R3S(uM+W15`9DY;pQHJpWc&$$zub2u|tm=6}-*nFKgk%e>b L<R=V3P^t$2LsAq3 diff --git a/irlc/tests/unitgrade_data/Problem5PacmanHardcoded.pkl b/irlc/tests/unitgrade_data/Problem5PacmanHardcoded.pkl index 3968cbf7284d8d716e19ed9972ad53d4d3172bf7..33dfa81f677fd061a0a39b2c51757d929785cd80 100644 GIT binary patch delta 21 bcmb=eo#4o9_nl#)ivefmn+66TDAfZ1QiukM delta 21 bcmb=eo#4o9&+RbL#elPJbprzsl<ENhM)?J^ diff --git a/irlc/tests/unitgrade_data/Problem5_6_Boeing.pkl b/irlc/tests/unitgrade_data/Problem5_6_Boeing.pkl index 4650e0a6d3fa65b094ffb89356fae69744d7793a..b61782009434e3024f670821a02eff567ea7220c 100644 GIT binary patch delta 3311 zcmWlYc|28JAI05!Wu}27Q`}RcK^Y>%*)CoQ4eF^RB^fF-7)zcGsboy15K%NrluJ?h zZIVg~=bDEMC9^VDQty5Czu&d@XMNUMdsbyuCDw>#G825&q=MlW&tj}(h{vyHq=?2& zj9*yCyar|Q9~gUCFN;rKB^MVMGZBLoZ0Qsq%sNDwW}Y2B+doK-o9$Zjd<a8c`7eFW zom-$I+=u)955R!NAD!DpKS{M5am3z3L#_EPS?7uZsBDX7NlH<mr@^iB^kyPOafKRI zNe37#tXn&NrWAB>ntXTbRXF&h!mK1p7+r|5o+`KOg<k5EUEkpfm>4jMR*=r5p!3jl zjJlo#@|yhn;n?nB;4tkSsm1TW#5`BIcKv;5r0ni+H!VOa9bF0SlVe~deZ<L={Q(R* zg7k!ip8!Ykhk)q%MJUX3##v%?3ZxRPMUpqvLV&OI0&A^ju<TdZ&HX>6QCQ$}4Qu8v zuycHuKYqLpE_{#gT0TmZ!t|=)jLT_@5o2^~&I83iz=_GYC1ps1{u!?T3DH*&B)E0z zui_GvXlA5-ScZWkT%VluN~R&k@nrjhXO-a9IOW~?uN*qAn!oC`C<_mL<JH<<p`kuE zyQ1J^6&$hung6M5DH8qoE&WTcATF9x;)t)LVN`F+PfJSaBgloM2uBXdBemJI({?w7 za8CNvzn_ls9Xh_+q1{*uayqlwN^=V6PESHa{{|7fy6U1!*5O*H%wXMI7DmH%w}G1L zKNOK&vUz*U4N+Xe^W`u&A7MpT>S8Qk59aD;-J3g=ko5+;PHwXpmj75;(Bk+W_8P7E zLFGj@K>lg`=nPjGeU-T1XWuQ3i<a1xoY8#?!3!UaH8eCr#On#~KLN{-riO({|5FJp zE`s|aKUF}Ve&Fc7=_b%i61DAKtbzhtGAFY3OX3ZA3dhuZ%0MNdCn;s98T2^U;zN$8 zpeEOL_r_XD9H$j5_eAOi)Feq#I>Lo5pqLTcb&{)shF&ska%812+WJWOsY4-H<e$%3 zva=P&42M4J{8B+$Vm>+IT2eU5PB-4AEf*fZ^g87?t#EKdb%?p4Dta8(u;(-(g+&kb zJ=+a8f%yLJ^J?ifSgd&a_aZ-4B=EJM<I9*NwzQ>QB^pLR-e>Ai#fU*0I9=<W7RpgY ziLtR$@tKl1)~_{E_E8X7GN(*On6$x_8$;WJ+f|Xi@n79(9Z4Kux{TAkHib-^_s2eT zc^k-BP4+d3vQf~`#-zx637lfFK!W3*O{$E%mx=q<3PF%$;H1Mw>0JV5Z{;Phr-IOp zI7SXRN^#!bzmnAo$5j@6ceQ7uRm;8){Pq{e+K%$WYP$I(*UIM4Uh`JqB5L3IAU1j^ znDUDAP7LobdoH-q?g_bOfotlsh8Eb@wV<An#zvVqVw=ZeF??v%6-~0IfW&PRHai_! zz;x+SCAnfY%00c{*~9@+-0`||v~+|^QguI~m6MyB;f0pTy@xzDdVB0r(d<1Dyv9&f z=m%9ux=jm26>M(?neD$jCWqMQNw7*8IW3Ia;(M*UZWod^J=aDqRyKjWlhs}}lR(F| zzB87!62^_zNn1{M6q0cdcXXv~YJ%@uMwT3wC6Ewfq34M#A)Gy1aQ<;8mm+cBnKSDO z8^OJ@huf`ApgGkCkDG*qu;*%e_uzak8LH*QQqpb&qvH06*9HU{-nVz`tnWO$ZKreD zIne_0twr$c2)6;&UT^8qwj$78S~Y&%h#>wo7rorD<uPe~zErTuqyg-9hsay+A<%J@ z)9Z6e5Z?)Ra$gil<&*ilHio>ct%pT}J@3}I5GdLIN@y@c5FhlP^ZmZ_5xL&;TBxx{ zJ;ZFB*FSNBKvAW=9yaMLJaTC6e{NI``32cm=S$VYho?73ivtOC<sJ9@HFFl;9_4cS z>iv6U5TP8d_nZd?J(hEc7YJl>&QEu8nu&)L0-D>YuG{3bpzPS^CwZV<Bl9~gnn3fN zD*n_KFmYU}f0U?J7P+TYtc|&j2j*YoOS=;ZB>So0Zb%>#zpW}U($C5y8Jpv0bEjy~ zNeU<Y(h1}(dT-Kl3lp1MOs8hfrjcRN8txVqG(6mxX5^MlpvjK;#SUyH)_&}XsRiQ6 zq?*6FJxZlvo6~l;@&^P`ii=VlXE3qD{{7~KGx6l=M;kOH0%-8-==4j>Cs19a@L#ij z25wOt4jM6yCA0QxN=xsf;nVJ!nt?(B2~(Bs@%0S+lWk4itqv#Szu66!acKA_E;HtB z34y+~JRDzF$-pa(uTtSwFGERl-t_~~>uD&EZOk+P0&%yT*A0BZz<(?}*oVV|$cEgK z!6`NksW-TTX0He&`r1O~YY_tn-42ZrN%tmQY%fSfE~O#U*VuKWl0c4EZ54lW8QA9a zDiNQBhsmVl$J|DwX&C#JYHU<RAo{m)l|BFdgU^)i<MM7TawyR9L(M`OQluLvv_24M z(~<K2{1OH>UHo2B*wg`zQuF_vSwzE7Zr|yK8Ul5`4rEl8G4Mj|14Rp4Ji%y6MBiu$ z4f|~*Z8T^CwMkX}@Osa{*GD_=>}vA^Nm1c*A<8sFG@D*qUr(UGHo^XgCI<GW4zw;v zHux7-jlB8(Qk{mSDTP}`8~71&?%RrdW#D64ELnTo^RQb<xp8Cz4X5(t^j(_>barOq zVdFFdca8^jJF+gp*oTR4(*L6&{-02_<Yoe`O}<(DRg{S@o4p*1co7Z_ff2bn_B1rP zlum@V5a_<XnC9|jOiW3HY%9(@5e?fMQ<|+$(9m3UJHxn@K<_hmc%IZ_V!f*q1B^?t zpn?_Kw_m0qto0w4B0kR<+9a92jfqE(Dpizjii4SX<&|pLG`zH`em%qIPk)MqxE*8S z2M$8=veogR_qciO^>P}hTQ@T1wGrr$uW|qJOH2xfcP_cOQza2P7lq0l{7l2S^UmwO z@E@=;4YM}LV&c56hfCM6lc4HLfD%K92gTr|kj&?2#RMEyU?!d^x0)7yngrRAgPnaE zJn#@&<EQqYfk)aM9_>tgyoS5uXju|WP72A}Zsmc(o}#A-`~=E`LZ=mGC?;Ng<@xPX zs!0$V{>;U|n+H3h!f-!7!iWo*CzsA=;kIrOg^Ps=;GrWm5}d#TL)-FK>U<vAucod@ zu<(z~tKNz`#DR0{4$HCv9%#uH8anVRI`B-qB43MzuO^!9%=i=y9j*^!oIdiPuB@v1 zQX_#LJNGskQU)x{HVOPPn0WzmcJt<<dw8Id^-lW<e*+G#doTXbXW`3V*S*^icnWIk z{WQ!+c~BweE%SvxqW1=amBMRS_?N}ql?L)hNc=`X!1fytCW{NZ*nB=KC$h~;nuVQ5 z#+x{8VPs5$SB~Xp9ys=nX8Q67v|MooK`9M0v8N_yOOj6#Il(nMn*W9eKK_MoKJiza zx8iWKAeV`+-<m5f*?yD!TYtuRFog#NC)9tt^7)~4$bHFCCeHqSRN;;BJ<{lQY_Rnn z9+Zi{?vdp4M<V9_`}iZ&*t?`Y<WC;ikkO>MYL14$jT*hpbp+}!DWOIt;~024eW3J_ z1(yuI5%YDJKQfN$(O+-)f+A(}BojdfzE=`Idwu8`dBfk?PT@x#sEZAsYUT5Dx=G%( z4gy%I<=K3y?<M)sqOt7c<~oQMk$&}`fnMA3FRQfv!ohwk=KiEIa+5;tm2UyHFqdKI z;>hQx%BiI?MLu(|VbH<o)jt*F(i_I}2SsaOik!+R=L=S(@7+><ZWfvy98x3I-;h_N zs;SiWYDhM?1G@YO`%dh8T4(nY&UReC-^i*W`>H=(jB=|2r9b^~_xOD3cI>J1W7Ci| zph)_)Rg)EloYc^}Z{W14*X#m*MK7os``lZOlTfNAw?yey4H<B-HZeT20`Aum9S8V= zEWNXTjjxY^pu=cY^YS{<CQ{p8cvl&0_6F||KHs`pzt8O82&@n5N~=9YlkCh&vj>sS zL1ykwksF^U-3s5G&*_7fyuJ$$gL!0}VKgszx&U%igTK%64N~Q~R{PYycR`lUi(vP_ zdUAb>fQ8KPT~NH^<DtnHWGP##+Kksjh=QwB+~x){PLewmz>9)`R&D!XegzGc(tGkQ z0T{HsRm$saAlDSIofof6AoGk;dso!*zu~5%W1d<3YCU4B(rg<^J>MNS^eS>l72lY1 zA3qQ}XhMJ4;TRT8&b#}<Pvl%98JS${R@YoaCio^e`y62$vAC3QMDS9AC0W@2TY!K3 GH2)7-aE74( delta 3311 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 BdH(<a diff --git a/irlc/tests/unitgrade_data/Problem6ChessTournament.pkl b/irlc/tests/unitgrade_data/Problem6ChessTournament.pkl index e7da887928def8fb94b9e7e3d034e373928ce32e..354e3485c6913c4ed2b0e90c1416d05becf63c1c 100644 GIT binary patch delta 22 ccmX@gc$9I16BC2NM3)NA>)QJnfS^<l08lvwI{*Lx delta 22 dcmX@gc$9I16B8H1M3)NA4`Tcc3=9mVdH_v$1^EB~ diff --git a/irlc/tests/unitgrade_data/Problem7PIDCar.pkl b/irlc/tests/unitgrade_data/Problem7PIDCar.pkl index e22d2e24d83e87606e137a33987ca83d3c67a210..2ff576403f28ebc1f96c87a40defa18f2263737b 100644 GIT binary patch delta 42 vcmZ3?yqI}{B@3U7(t?S$af}xy&Iw^U!Z7huFw+5s$?lB7oCg@dpi~b4D5MO1 delta 45 ycmZ3?yqI}{C5y&S>5PfCag1*!eiWU!(Vppf!^BU)OphBTyE6uJK4k!dQau2hmk?3_ diff --git a/irlc/tests/unitgrade_data/Problem7_8_PidLQR.pkl b/irlc/tests/unitgrade_data/Problem7_8_PidLQR.pkl index d455290c459b3de4a4a31f3905f4f131de9a2b78..c0103b3e977fa2b98a34cf16e69b4168cf7d8d53 100644 GIT binary patch delta 69 zcmbQwJfC@jHM8BnhKY9D8Glavrq1|tvK*rXbJGll$(D?IVn3Ufd0hIqz<x^Gl%Ofz Zj2Su^QqCMdC)YAca#p`MzyJiLdH~1X8Z7_- delta 69 zcmbQwJfC@jHM2dp!bH36jK3#-Q)m1=S&mVHc|OO7$(D?IV!xY~d0hIqz<x^Gl%Ofz Zj2Su^QqCN|Cf71ba!&MYU;u(rJphLo7?uD4 diff --git a/irlc/tests/unitgrade_data/RendevouzItem.pkl b/irlc/tests/unitgrade_data/RendevouzItem.pkl index 91c3ae562ba9fce0668664f1fa58979cd070b2a7..2ea308be8ae3ae254027640d548e0f9972c8cfe6 100644 GIT binary patch delta 185 zcmcc3a+_s>IkVj#hKW}He7N^tne^Y3{lEeJtxxLDYC0^84A*F~WIrG@@%w^J@0;ys zMFd48iZ<-$3Io&jZGB+z{ZmS&Bo${cX0WwQ$zW-l;%>j&y=8I>V{Oze>FFM2=QJH+ zb02fYn6V$I$qwB;9cXZ9?SAiTK!c6z`#$wCwAh>St$$mf+pzyUvu~fAUW2{rx!1{W T|2M-8R)rb7Od^8;2uk$;Ph?P< delta 185 zcmcc3a+_s>IkP?QgNauEeE9ZXne^Y3{lG!}txxLDYC0^74A*F~WIs?e;roJ3@0;y^ zMFd48iZ<*&5(cL2_4~l$`=^vlNh;1@%wTJqlEKn8#ohk$yStNH7;B?$OHcPGJE!T8 zmiw49#*F<yO?K$+=|F>{YxjF!0~%~m-}kAHp~c>hZ~fZ>-G==Kn0@=?^cw8d&%I85 U`@b1(uqw>pgYTRefS^<l0As3Cy8r+H diff --git a/irlc/tests/unitgrade_data/UCBAgentQuestion.pkl b/irlc/tests/unitgrade_data/UCBAgentQuestion.pkl new file mode 100644 index 0000000000000000000000000000000000000000..55e379fe474d7a967700bb9c83202905b8ebcbfa GIT binary patch literal 96256 zcmeHQ30zLw*M1unN)gJC3{47&XyQ#tlOa>c&|GgM&59@?MF=573ME1+ib5rs%1|mQ zA|WCThRP7%c}wnf?>W!8Zg+71-|7A?+3VSBuf6u#d!ON1XZ!PSXR&x#+@BR;3L&Dp zR?fDL9#cK-+&vtZIERIFbhq=E>7wUk<?bF97#70s;kek2_{CssvrbDT4o^r&4+3mq z<zVOR!G#DCP&+s7uPee#SRp-}Jr}$947IRX;$}B=v8R)Vqm`SRl}}hmXA3twn<dWf z9&VmC9%0+ULIhfnVIduyZCfGRi27^8^mwep)`d+AQ!wE%>9{tGBN`%L>*3>K$F0I^ zuO1d0)GFl*;n5D8JbAM57viVo4-=M0n02t8X(y5HY8s30X87-Ikgr;slX1qpaEOJ= ztBf!9tDaa`*RyrxSI^&8RFLuIiLm5OvyT}q9V7mjD4rIw@?{l!i$;&BfgA3zXPc?Y z-oBm54kOgMb9gv{+}`TgSlMvw!a~Fx>^v+y+^iPb*?25*bF_003lX;WbSD0>#EJO5 zqlKZBJIBs;x}7UwxZ<j?$twr|hbZ&1a`LnbTQ9V{Pnz<AJ4dFt2ZmX%BfK|1EToI0 zo0H8@J8zZYLtEV&(s4#x3qFJzh52C$91)Hf*G-(RU!A1L=^4W7_$$9~dXsrYsI8aW z-jFC!3)T~AjSChFKzP5Z0T4cV2tPeUfF2@959vS;X>$zdkubfMj`WaD^pMW<5D|Ka zC_SVLJw%KiB2EwKN)PEq50RjUbf<^(pojFNhe*;xr05~N=pnu7A${l}ed!_6^pJk^ z5E*(%e|pFOddNU}$RK)%EImYy9wJW<QJ{w?(nAK*LzL(t%Jh&S^pK(SkYV%?6?({U zdWb4LWCT5ABt1lp9x{p^GMXNuP7l$bhiKA6wCEw)^pG+1kg@a-Ha$d#9x{#|qDv3a zqlb*Ahv?HoCeT9+=plym5F>hsF+IeD9x{<0GKn5CnI1BQ9%4!lnMx0tMh}@z51By^ znMn^Zqle6*hs>sj%%O*v(?jOcL*~&#=F>wK&_gWfA(r$ID|(1EJ;a6{V*4wI9dXlV z!Xj?z{`MbkOPo$2g6=j}PF8Nj{U%3EukG%LMckoI3tOl{-2ZV)zuhT*S4s$5e!j<k zyC3`x%apcy@;g76@PG60cje@zCcG5oLgeNDwNjG~|E$F(!r$8acE|s7$E*p@H;eyB zys&-*-F~czfXP4HV*71oG_<btCVbyK@gJEB-|YP(GvSw+{s+)MqWNdQw(R4-e@xtq zc)}|%DezT<@Y@WEhn-x=A`J=kO}BY60i@T$dwtaS-y<y(#ZC-cG3ZsxvvZQC&D@4$ z*`G&NDmEa<(H$iTCh6}GWZ%FW+sm)8bbz_Fy(2PR-D;5*4^^Sqtw|3vfR@R5i{3^F zd`4QXmTlLZq;wTQI`GX*iu2|{=2{*Yw)fG<Y^3Gk-RwFCxsM1kxXW?1RL^n**<7k0 zu<A%Pf?V$1Q@ZKeCj@DGn*v<-r93``r@0AfnbtVuQ)PWKf_P;;);pY#p8*<jno{d9 zC-NPFw7uK`TCN;2@Y<O09@%oh>fGQ9R?si~yee*tSqvegWb*d!JPC(!iAK}*NQV!| z7E$#fn=XXDMvwr`i$zNJ`BBsbJUg7}xb+RvB4!)fr#Yh@LE2uS0q=%beICxqhMlg{ zK7G$;<Wtm;;}m<@sD3bZ8_RbMet$;*b^3m1-u@pK*Pw=^UfRrU=7sA^-xs>JMkxhI z%MOFAc&%H-2x68vI{F4L^h>!|NP6jjQyIX!-r@;eUoTHb4b?jt+_iUyOOX1kt=^8K z;S7+owe;Vg376*6xr5~n@WDAf^2)kZIrr+2xwk_7Gx8=uEuSY=+P|B718Grp?lQH( z2+pGo6;aitRdB_udNL+y-@3P`q4Zsr)4Deaqo~VHXYnq|e31c$vF+6~fT-Bpx}|BK zL|Qf<D!Z*I1t;tsVYzkPp2BJWVs+ozV*zlaukYhc4UWBu{36gi$1c(t+Ts&C=!Rbv z93vOkfj2&mf(@yR^Ywhr;zI+V8+`uxpa*bHk9V7!QGD_l>QPG0++~{;Um%E#_U%1q zhCD=L^x*9F5jSe!RC~6F_tlZTji{jm`-)6fPK2?$(<Z5(L=-Rb%l^jnUHdEH7&UDV zY1Vz6hk7J^LS}iHFtnxGKKb>&l_f~ayv6qijn{&Us%tmFIY+yEL|Q_vqtdesZX<}3 z^raU!oF5@b_uXsU3^%|XcA>mZZlxKF-C485`F&mBFiKVx-|cn09NF@^(yx5X#5-)T zGQ2+XI>c;68G@V{v~uUDx-!&IdjIWu>tjzNNZj^_Wf5OskLsjV-k!b$FNygZ4^Dk^ z1700w4c5K0y}KZaU5z6W;Ve;jLBN}@DZ4otu5~J1cKct-g42F|@ZQ=_#&G)_>g9QH z;W-#DhwQf>(x2J71aa@zRuJr|5IzSh>#xsPd+7-k=9=Xjq`zdhJWX|95w>t7!HH?T zza*r4z57`5ogi*%xKF5lCmpv*Zlzikp`KLw^)YSho&N9YGSXlB`QP$}$uPegG4rWL zzdcs9CpH2qI*zi=e?k#;?Xl{AukN+lPfjBC#3PXZ&>R%Znj+;<ORQX^He>1k-U#9~ zwmtpDYkPY%|9y2AZ{K4Sv4Xe{AdQXxVvqhlUoo10hCTHE+j`ZWsAGKfXCv0mBWzWe zRcn3#ho8umU{BlCG7?0tM9<b-2^(&5g@vsnk^JCj?djp@Ost_Cq1KFv-@P4a$$B7O zkeF_s?lEZRmumKYhm$Ydlh?AH5AvK7=(>h29~GSNsA?_yO4^x?f^~uHum(Yk@QG{L zX-1uSmDUBapD$Eu*jcukeeeCsh37n0vqy@L(mp?pz_sQvEWAn(=ZW@ZIQ=<;S~C+k z@?`l4oS_s62i#nNZ?VRt6MRd=;QFP_F%<XbT*$W<s?@Gy(g}Y5{*voDCY^xXFHAZC zxnBl+U8cZn97qpg(h11D%cK*K`-Mp-AomNCPC)J#CY^xXFHAZCxnG!c0&>4F=>+6{ z`S;QZzR@AS-7az;9)2q&Pa|MTQIf@X-uYDuz<1?8;$ahkA3^`K*k93*|9t;`p)&tz z9sg{v+us_M-+q-a@SQJA1l!*xxdz&#if>Ot{&!XelV1{WzW4GkwDa%E$vwxE{-LBZ z#*=?|w`hMvfz*QMocqVO^tp`qLP?v-4{+)s+mm(MbdHk%WGder*I7Ze29huxyqCQ1 zJqA+qV&aD+-&KTKDjs+(UKIp6T@z$K`DC&o1M1i;B@=sT$OAuEG5GGj{*W)E8Zpni z))O|=eekU$p-4z?D%UFSa-=uRVaS!2(Vw*jvayP9c)9cDX0^Oe_}$wJMGiN&Lp!U; zYsX_q+U*uN`{Dp&NZ69;ZM62|%0s9j<!wbf<3~Z(bgvNK(hMHRuuKmPKjbNQ9>uPQ zVDzY`vCok$v8i`CBhSKySQqTpcgcQ?w4Ax1rx3&oXMp7_jq+>ukR}*$vhiVWWytm% zytm}+b#XC7s`KA>ZlapSYUCHmEAvycXTy;`-1BYJ#p$p|lA`8ohfIcC{o7_<%@uBt zOFR7fUDla2NX<*}8LHWt5Bg=@5SORDBRe3!JmToDG4O-4vB!eQ^M<*dkroSC{tl=8 zq3ik?Xk0&g1TG$*SG(<T$LmX`*Y&uqTO-jh-hK1%qG=4IL}#A&PJKQ9E{ff{Ly=3n z$;0$AdDjp2@^x?q42fB-ebNCAWBKOWMdym)7|qtI6qHJUdq9Yw<{`s@Ur>)!=42Xd zC^?NF&$Yy*<3_?ID56tpPWgPelLQv{r$3W}wDor@?zx}zfm>FS*7%Yo2hSjLKQ@h# z@~wvr8T0PC5z`Ac6e8hPtHj?8g?E+eqEHbLI7t?oITfbVK}b#c8fVtbHyIRfaJZXI zpW~GoQ^vwwO+n0Xe{Y^E$S)^78ulk%fqoe#-{0;@9&aYFMeOLTQQeooO*oV@>a2c7 z39{wggvV0i*WoIasMkToaW6F2xw_`9x)w~ZG_3sClsgPI<S=W5WZFj9qe{8%{m)s$ z@E&ShDVX;Vt}n`IOC{DN!<5lbt15q&dCACi(?q1p#^zTdh~^%@x#pALW$1+XY{e6! z;Fk5ke8%hOWO&hb-SJ}eSPSUg>!0+p=KH~1p!?ZJ!k1XzLp=(R8ZtiI0Is_>L&l!0 z)P%VmKErz&`9;G;b?~~9EGr*4(wrhwi6AAI{xRe61NZm-Fe}5<;k|QX72Nm^Nshmh zx&!VcIy~h$`4(^wm{{vHW~1Q)<lXR)IDPFd1nHTaufbh{=Ix4^{%Bx6f<)w440)>$ z*Safv&JVij0e6y&5sTCU6QM1Gd)(FOmE4+v)|OiEo7@4v#4h|Y_bHg1-#w~jvIE-a z0i>+gpVL>th8h}|iij!r58FITSVF|1lnC*ExZQti6eNQx`o%8!=d*17t#<z{KmS|p z{#$<bFt#w8AwlfbKh-=k8zHs-OJfy<Rqc`fTjTq4wwpBQa5u*kBH8gzj6NQPpR>P= zRezRw{@a{T#*_bBo@~o3Xk+ZRBo_qcT5`uZq=$i<m8~Oj2*YoVaNzI~+<q={>^mJU zkhRd6_+~u!@B;dbgY?3RJeM&Q?1F~XBi@wNWr(f`wdbTi%{VN&QmiQcNyaHR0e)rQ z;*8w1`!82_YGj-1Xy3^Dl)%=VJ8SKSto#g((M|5wNu})B@7Kt=FMrf>JLLXL`%K2c zFNZd8+3d^#2IPDt<^Th7zc2?Fko$!>z<}H@%mD`EeqjzUAomM%fC0H*m;(&R{lXkz zK<*dj00VNrFb5cr`-M5c;IBE`fcv(K_~>{C6Co3R;(tEk1i=m_!bA!2A#&nB-j?5a zTBNKNlrVu{5mF&S!u^|{!0;0P;_472N`#2Nxqq?<oFMT(4<Y4h=4usg(a+bSQ-F{N z6EXp!On~@}zeO8Y5BE2ILMrrCU#l%#je>-fTPoO6liM1%Ew0`UghT)^Eyr}R4~fl( zUqhR>rK;zMgsKei%5qw6e7|}7A;}}EU-;76jYpA|d3HM&ug--WgU1c>n|1aIAlAgW z7u}k!YqAi;Ks$1dtQa4HXo@&IIT`}r8Fwr^9UV8|73z^+rPt{7L6F6lq$U0M^KRlv zIQ0wR55^-k?)$;l?Lz$W>)qvGr%Q8ZEE>HLzP}#x#w^)wG9*?^IcnPXMC2#bX$|SC z1C5m-_d+YJ)3Hrcnvj;+-6D_k8AD=)=dk3xA<h-Zb!mqeh_4EO435zr{-r9TA@?H3 z>B6QDHqg6;1H99=X+YQAoi1C|yd4Gu=P<8!VB|^U7tXX<>Rz!>OY?E*F%Nq`M1DC@ zvrTY!K4kpVMk)?j&+`G<@-BM+4u^U;j~=D(v(breL|W7yjSjRvRe>PS`SpzzdO;SS ze{uTGFt-%cX@$MA`MS}NBRPt_EHCo#ZKQ=G={3Y_X##@e#iXU>6`Vj2TjQIuuRlTt zNA$95Z`RjB_H<_7=#)ekNOiS7D{2%j`XvLbFGt0jEi~$3?CM_*Nhz2D*=nKNu5)Hw z;YW*V{kXE{=k(!m0P(lRL>wLs%`J^FQjIHxolXl$S{*v13=M!yV!@$!F}U%)QAkbx z-1jANouU3wHy`WHsM9MRhbqenLf573DDvpuPY`KY+PM3XunAnH5--H<8ZiM<7aK0j zyA>Zpd<c?yv#FUU<m<!-hcV>1v%k|3_(}E8$F-_BK2VG6<2fM{k|04z-Bj0Ze*aF$ zFX7g*o1W-EEi&pQoLcYWsMCc*7Wisy%R?g_^k$2&jx3}u*2%At=vN6>DVYzW3w*c1 zX)pX`)KK{#Nc1y5xc{x|Kxl4S#AWmPZEzD_P-8Q;a{fcqX|0Bry2^*%BS?q-b1y`E zffpzC_i85vNWeWnX1!}o*6J&$p#Y10w@#_T3xdJU8>|jW!$~4@ZDi>+4|rWSd7k`B zseO=tSA1vYsC5ep*kB%Yb&Rb(F&<uSy-EJK;=pc5U5t7!*RN_1`~rNp`?dRuAHZ); zPaYYx>f%vIqfDOMd+5bFc%k$rQ%u$G5)8~BMGK{l0u5*w`!>F<xfl$YmbYK;cAe1x z_oK<I_n*%;L6Rz`aN@GLQc1{`$?VBiVzXgLZT9=HKHe1i<!Y$s>P&Cgkgjk3PHj1O zr5Y|f;;mPOkPbJb$eETcFJOs3$hjPX5uG$UMG-7xQl^K>sTn%shEcJA)Xy2(nGPk| z{u6bl8+#lbdy>ffA+5`w79?fwBT6B~Ht+10eDPulv4oLo#@@hElV`(Mr;Ac~14|#D zlsOt_+>xS3?XemAsy!o!dxS>;w~PCWdxWjRBiJ6R+S4wcDL6K;6!!>w1CL<B0@f1G zX5zMobRJ=EV5@Mu?a_?u;<&>7#dWb1k06eX_IMJ{GAwP+`i$%1c5%dFDINvfE*>M? zF77X`i><=3fukL_i{lE{ZI4yBE*?Q_GoHWLS6DOl29~y`U0fIU7h8q<i#1~@j%Pf} zux9Ly_UtLxW-P_+;!*fs+RC*2g{j!Wv|Oyv0%|F3qhQj0w0T0xo906ARQ9{n()G=D zr`cB&HMi#aonfohs_PrLrLwzOT`W+tOJ#p}^)gdqU>ZAH_qgK&*EIGSoq&of;b++g z^tlSou=g!c%oV$m)^fYrN}+5sfniZHEJ`7Q(%bSw@ic?WE@fDh<Zs&<7A3h~7#1bD zUl<l8xnCF-CAnW17A3h~7#1bDUl<l8xnCF-CAnW17A3h~7#8JU$fD$O9k~ogE{BrK zdE_Vl)yk&i@*lZeN-jf^`#YEK*x@UJk0=*zVKs8Gf-V0G6Mu7=kz6cS%1`{x)AH|D z_9a(y2cm>4=OVbR@wTuc`H9wBS(#i`B$t25{hRwAm(ST+!sS-BGCTzc1ea^c)yK7g z`zJqOQFg7=TfKM&JehWxovY58DELxyu#x4ojJ1%I71k%`NZJ#~xT=mAbFFbdq+amv zVm*mIMV$9aO#|^4m;CI@euCbf+En*&h_r(}%G+KOA2$Hf9Zstr?XH&xJAJ)0B0|b8 z8`)xC^JUMgQSc?WeW>QnfEs9v>Xe(2agD^gJ!-!wxP%=Wp!XEnlIJ|2vTJ!gg1GJz ztQB^FFXZRTrA#%JC_q{&o;ZY0cn`S@Z;O*9i^Cu##dW0kgS`)7Fx=w1E}`)Px{j}Z z-@0ZE_*%cqy&$#e&mfD^EqI7Vy&CM1{L;$;K4p-%r8z&wUFihzDx7*4#SBWO*YWm1 z%pxmAk*8Jxko~3PK6-QY3CP9lYVbr=U$m4BY`Gj#(<@G>4vn-<Q|4Bl!f>SJrKRh$ zBbcFQzO2ANvO6RcdWBW>oy!B~k<RB8FGEx+k-59=x9@KdhI4wFKcD#?#S5rMAusGT z3#A~bytiDokA)(nyp239tniTsa)O2@sy%tJ9nOGt+xZeK)<Blirw2ln9x9NDHn~)N zJzp#g%uQ2ohgQe5X1h~-54Uq^v~%S8GBg0`kGpAviatk>*=9*TE>0q7l6(mB(s$5< z(>~~4PC;J*7~XXcB?LznoJS)as&AkDdQJm^T$S9jKX(XZE*ics)p9q1fm!z<F~{U1 zv}J{LcDeXWI7UKCH@%zSUWmMF$aeHx?F>0&-hoNAAuHh?&~vkCseK~kCuP1q|45S! zS4@LZ1Ez262iKQN>Z2<B*pS*KwtH>2xoe?cRCc`n{OrX&G>qj#4!<!O0J%~s+k~bD zB|&o?OT(5t<t;=FCB+1}G}P1}Nac9rp6_ekA&6+sdn;enX9!}YVjS<&?R5rN;Byx3 zDD@SC*HUf{i%z{b1cN~$u7AngmvF6no1?o=`8~n5qTcv;DhK%FzJ{yRM<JEBMjs!b z9?9Lh-)9X@33_cDua;#AuRQvR?S9zoeit=#p>9G`^Ktl9-FbaJWX)oUqfUR$o*pqj z4UQ52-1j4vyoUp@uy25)YTi|3?y1DRI*vtfjC{Ljb@v?G`Yw)Qf%nn~nR;{&<nHEG z>^@n&Iv?3m`FQ3Dt2~1CLmdoa%@18u-64~H)Ur1X*EQkg)~cZK*_w(UkuBm^l()T& zg|l(-+tWwN$3uQ~M9y(ztpjjBDxEcY`2}ypqAZxBS~)F=*lS4HP*_?T-*ocL8LF?A zaUurCY9H-N!3@Mw+^+qGEfdY35qDaoHh+D4$NDo;+#_t&leyz`%VrRvL24D&jBOsG zm_0F2lGu(&HAg$FG3m(to-U~r`wH7^>Dzat2#Z*=NHw>|<~(I5$!pplD54Jg3fqjW z!cyEWj#%txtQn6l9)<SAB-V`mjO*h5;=0(AICAj#Vn5?{agT7j*k(L}xGruN+l<@A zt4e#?#c_qL!tLVm#Zo-_Sc<K}b@5!lBZ%9@>lGf~zc1==kFYmzY+%iQpA`EF`x#r+ z9&g~-Xpbjxf7>I){l%Wd{l!u|3RsF)6<ilfzqk2UEXp>rVhfQnMLC=c3=x~Dr#sGp z_+;f&Pdj%H$0g2Tzxr5ZW{a+ck&=J(W0hiZH%p7VHn0bZ80F40;>#@8%8|S^cPIPY zu}g|q1D<D0iITdbye>aOQmpU8n8`)#z6TCXyP}rKj!6qPY~1!VV{j+calxFz3`^xs zM~;5^+;T7E{!9A|&61&6GBit)#~oa5Y4?`NO5`<?Kd9hB7@8$HO@X0VlKX|BS(5vO zp;?mqg`ruJ`-P!dlKX|BS(5vOp;?mqg`ruJ`-P!d{&h6VZwGRJy_{yXyv6udN`ACC zZ`-vu_l?IlEFWnpKWQm(N=D1i_pJ$%mUbX56(TJaCN2G1_?y0w_k!Fq@=|hp$^AgC zf!sUfefyDKkXuIH8o6cUt&x|Kx7L2W<Re5r#^kM$_n6#X^47@p{zy!cW67h0+`m6k zN*+7pe)ti+43>B*sHWHE-ruYxjCX!^?~u=9@^K?yAIP!fs|GoiDgDDy0@5aU<)S^s zWFddL?SVYS!$IzOx!E1hbQokrFnY+JA95k#Sz|X|db%G$gyyB{ckL95SWJ5t#e}A9 zeU7w*+?Ky?G$9W`A|*R_&YcSBQiJpl=ylo&iE+W_#WNPmHX?J2(>o*_Y=+-=oS(3D zz^EhTNQ<xdB#)`x;1>v2T1l13uu4#;d!H^cP_=`88C+T(V_*iq9e97UQol64$H-jk zl3oROtKqkg?X~@7i#hO^?7(H`IzC+pPezWD8(+A6=zV01opnvqUJb||U6j6~C`b_w zz~v5Uo!4wALf-XFNUSry3W;$E(~1x9eau8!Qbi|Bh!Dv}J*u60Uty7Q8Pbw>Z%C}1 zD`f4P$~uT7JcCIF$A&(<lv)nyn0qG$Tzl&TQ=z)1aGrdad>fhDd9!^?_Ln5oX;#vv z#1M68?gg>OhWaj$Z{LOYhNW!-)RGx8e3DQx^ls<#cMe+)xQluuajEa~>HJlwN5W%m z*2{i?U#{o9Vr_pj2_|>d7>7D3UWAiGN4c}g{Omo*mQ1Bj(m8YCJUXS+Ggac*8>Hot zpW~QI2jL7jva;Wf$VTErmDJnBv`c9#WjPR%o~jb}<`kslH!OLjzIHbx)0aC7o(Vs9 z4h^G<<8$+dh-awNDnr*v1<!^X-^Y#jE$@dzLUMCgy|Qz|AyNBO-t*#;P?*p2Tr2W< z<`(Fe<Q=Abvv}bc=?LvT9_R;c*|h4kdZ`c0LvWKi)^*bYIFAZDIxiTm39mfDii--X z`oS3>`C`KG1EXO=)fV-OOV8bed%))YR)Vjd!8`;>%{qCZnQ$28MTO$WS-=E5Lso=y z{q9RB>eecXAK2>92~Coe3#JpT?!ivK8u6%f!4fzFu9{mHnybM9u&+<KcYFqnI@c6V z+SYzBq(Z`<D|XZQf;z4InD>J3LU?gv8$7m4%K8#C8=G!*NmE-|jT)*lNcK**feRUb zyz89n9pFw9D0r%_%e!3Ekk~OE?O}P)To*&hL}Rgd)T24GN6a4L3Az7bM?XA{K6w&p zu@~g2+3!P8pQ+c~gpAFR$8+E{ro8+6&0Y3F?`|3xC}^}Fj&yzB_Xkb7!UUPC6PA`+ zR285e^%39nN@DmO)T1!%?#nMff)n<H$BhkFCwE3u&9iKnmF8Qx!#WRb?!dbc_NZam z@Ijf4aFXZ<DmRW9(|Te3XFfhH;pily97gp~<kLZlw(g&)==@4wVv4Kh5s4q9r29+a zY{!YiiTpNFY2#A28E>44<W5rQb{mmp+f|5;lS((qA8vYNLG{t=@fGoY>|4aTO<MP9 zsc-DuJw)ORsr0*t1Ma^)SQ~o-_XtaIf3csjW;_bmSMBiz?h&>M_Xt~srP!*!uU#BD zI3}@G*ps-w*ecv^d*&dni=!QnZ+oP8E?_^mr(LEl@mRR^1!k|`Jlg-=3e5P5@fATg z{S$ll-xoP}oy6_p^%>X2t2eI8NPoV0^fhhbTjoPc&V>1amZ56NM`%l?p#AuC>*&_8 z+eW#)e{=8uT2mt0OklDin5+mUD}u?2AP!pQQRHy*Jo<cHWGJ2$aUp%_Axu^TIonc> z-gWZy5CwXOB0Xd<Jw%BfqD&7NLJwiGBFN)~$%-KN3zHQ=?w1MlQ_X-LVn`1$qK6pM zLrmx)6X_w7=pjs21bMtLSrO!ZVX`8atO%htdUDHSs&APY<cxW8?2nXwUBrIFg(hdV zlj|jC+Wv^W<ji*RycKc|Fgb(wN9-lnORnKZN`J(YKce?Xus_nbAHn{J4gW0mN8;h{ zvg}7@0J#n1bMt5D8@avYrR2FY<k)|<lziR!XKi3wYt1M4f8z&`6|w$sM$9uK__5T` z0ryL6USuH#l7f}zC4&b^2olE8GUJ~JzcV|-$TcQS<}%U}I&{xxA?*wVITKn@<se^y zAg=Ldof}`luRb6C^kU4|Jjnc1(~ceNcN@~ix9>5PeLf0OOxJja-RXZFHgv*QX~)Po z2+@-&UZc}gkwNh+lbe<jq-9poG4dMn%fr<swp*Pc3zcV|qkGS0$RCy4+FZ%Ha30yh z8rnNox%dXMrC5E%g2veg$spUSm$q(G+$Xu*t{JmGDd{DCLM<xDvx3!4;HRi>%gXNk zG^>=X#kPj3SKBUHa2Y~QS&lyXavG$&i}<R}jI@O8*;(VoVg@XQbkr)z2-dvykUg8H z8ky&_9Oe*7<{0RyG{Q^)s~OrV_clX<Z9R|2E}bus96qvgoOq8zut#EBrF$;y+WI|Z zk{1=+k)Ao`uFm`%$mHFy!DKn_5XgoUyZ3f$xDf1-cR=ziK~*@?I_33U_uPh)J4ts# zZ|mKV)EYW@N}pxAkW(2SdDlf>1ZD~pW=6&e=EE=ZpDxerq*@ARKzM=S@e+MFM*IDS zjj$Zuj3$Y}OpS!g=5UN=TPz8keE~Lf>0`2tzAc34jjtOVC5hujI`*cICR{o@xmcy@ z+rsaX%jm}nnx?Nq>q~sk;jvXYFc|FjE$dM338}F&jx8FtVi!DXz`Q8m$*L!uum*ac z9sPR3c<Eg`>9IpR?6m#W@D$|<Fr>U}Zssnt>3~?p?+o&rPiRB7weR{=QJy)_Tu~9D zWUCUG@WIkG>7F(k9)dA%!@A*z8{sJ=6SDKBcCCgX^(iFp;>tU4PS=*iyy<!c>_L zO`hxxfbqhz(JYP<*oUGn^nr(IRQDZdHfByPPT0K`o>(v?Q0ODa3<kpm&+vk{MKBNJ zqV~ju#gWjv5_=CxIVQtkkSTHMJ4yylSn+55rZ3+Qqpl*-Q*lQmyh_=0#^v%x5qPcB zH*?}!IcK;{jI@mN(oTn)aEN7>;BaZUV#*KkpKF&68@i*kd9dL!7*a3iO!(llwHum^ zVWyYn$uz+w=<=(&lNZI|0Nl(EXIXB5$K!1Lvihxa5$w^(yB*BF;4|vBy{=MWzk^u| zUB;ONcN_pua!~WU>9w^EE*?(Z>RoxHyCBy|WOn@WCL3;_ah+D$d9Q=6Yl=#)x0(W{ zn%qSDriGJQw@>o)i07LUdO5ifu|P^H92Ddh5UxPXLsF@3mGo5cw?y6wskDp!>_<z) zh+!j@df07uOHQDw>(zAYu6PaAc@=6K90T?zQl(Z5@9I}jZce57yJu3rBiKq@czXS@ zEa6g8n{nOtNbx9OPvUm5uiDdJJc2lK@c81mYEOT0yI6C3r0p5O_O#ocy6y2*d+N5w z=AW}}d*b=u>QQ@E25d84>;8+r!u`e4_Uu+z^S>p<`@+9v71J*9W|T=Y`iHOU{<^69 zReD5Q7D7vgM3jUAw~>(Elb1NVdsq?YNq9J0x%to?Frh$DYB_>|VI10Qiq4ak&$!X~ z(eJV;B%Zu2xNtR_ZTh}b|4qgh*#-^6!sS22{|;xhxrg4Dw2R-$TeB&?wa=tdFsT$w zDh0_K3g&<b^3QHCsT9<Hk^Q>gP`tV2x{gVuAg8V}sTAaXVNxl`{lcVDko$#6r6BhU zlS)DE7bca0+%HTj#Xpux@!d`Ow}b_z^bdb1oADW|<%J>Rho8p}CIZZ8{8gi2!e{dJ z(eV!+K0%&5L|)3Qw<PPW3BQRD6UTp59RG+XL7D?6e&K>HE|3&Fe*9CNORugYE}c}M z)|VB#AfMXbGSN$*d@~~L`InxOFkI=6Aa%h;V?>)DAX}1__xqBu3{tLHmpgPxN?U~* zI;^K`(zg!skz5@mQ+ZrpAY01$lFjzZKz5+5N5|*k&pse6`aV~UhH15a7nbDVDwqCk zFn8wDY5I4OmeZ`EGd8?~6t}wdouwTd>XDX-!R2m;ZFn*%p2~4;xi;bKhYJCaK=|JF z?lh&P1g(ku@gG-9akZSIV%_Bo0C6)oSMekk^4Ao!gYU=$=AecW<V<(y+=1CDmW`HH ze6^6cy)WR|*(b#i(lzL=`m{?$s7KxJCmsyH0OyhQ0~6(2D)6JyEQ2#f!XF$^LmncA zw(0KBFU2ExV<zcdLRu7>4_@i3aUVgX&DB4d+%H8CUk~ANT78QVg!Os)rUVuYOwQ~_ z85ek9zSbL^GF6?0PuU=(Jd1dTMr#%$NaoW5^=-9j$XxF;iMFw)>k%YL&8f=gQxR&Y zq_ofLc`0QG5_QQt);=ZzHPqSAW%%(Jc>0E!3TvH3K0K@9#dy|%9g@(ym5uT<yz)wr zxs_9ED_pE$>VlYDC%;n;aFsgeHEG=L<<ND~Yn+#4bt5R3)a%Q<<{<yB&hV?=GPd5w z{63~2E$b{abXVF!a(&#Fivhd&VA`F-uG<llKS3t6pvLw&57xkq??L*GS^f*)BzZhy zmj2Vz(7T(gzI3125uR7n^<K5|hSM;oPBh!Wv)4Fi%gqhf&p!2rD`wrhWy>y}huM=7 zRd1zajuxRApdz_fwYpO|f+%QZiCAyEgGPFQ1&@1`KRgX;dN=hB16Z&}IlERDMc;yu z!>&V5t*g3?%sriV=0Mi*8U)$#TJqIC9yrpKTS_u|hrvy_pM<&uZwb7_dDO)*YJDio z*APlBte>;yB=W9xncXoS6*vIp3g;%L_`(Ah(weN47S+SOaYn-Q+*gaBUoK90=;GB4 zURL-n9V0T>6+$G;3OVbZ!d=aSwQ^>C3QWdm3Xx17s0z~sh8<Wgmg@%>4>K{3+*P6t zXaJ-yjuw&SgFUJ~X+Ogy?KO%zNu@DrjZfjFpK0yYF%v3aj;B}lyTsa|Fm>UPm0I<~ z(eQk$uw2g8X)`mCxwFh4^I7KBBFMJ8zWdT8@m5~;K_}%iyntNmoYZ&DW|%TIMYrqP zhJCGhmE_qC*Pk77?a_f)J4kt7a%-lDE}lXq4ZmhQey$glG%VPTBYuWT8r{^`rzV$5 zsxm4iwf-)Zv|OqA{4GmLY0`#!uY{?jMHb>sZW+YQ6KQ|7mldig3Q^U~uj-@hSVvXY zZP1<0H%h3av!g50eY2>f(S45D-rq|l9V+eCby7Z+)XbId&Vemd(g@4UJo{Z#(x!$@ zUfPqXq-O^h24118JNsSk+~8+ab#rc-%^MR(B|TK@&2#(;m2}V}m(qr#RMO{{Z|}Wx zm`eI!l)lPXH!A6<`vpe?r&37|yqx6`Y)2*SBPf$wxt&USxU`S)cvmXvZFQSWH-4(P zl55t~wdS6cNg9)UABRa6&Zm-&G#AizkEW7d-M8pLfE|^z^vp^fNj8<#a@CB2z;{$V z>X=vF+qZ^Fni?;n7k!6HT721Pn&T*{b}x?2im9ATC6!1}uPKY8l3sOtl5}!Bl~nNB zhUjJ5RMG%Wr#1E?sib8Wm#vWzppvSNj&QQnqLM~!kQH2aiz;U-YU$<ymVK$}YK-49 z(&`44Cm&?-I^KOvC7tZ1I&MrQm9%bJ$%Hz}b&@^!f{9%(Dy#T2F4*ikL8W<cqPXzK z9aPe5xz7@EC}+3TQH4t>`>E<`_Ffcppq5Iy?ZmSInRltAm-B0eF3h2nhKw?i;802X zE#eewT2o19edr;*zbA3EN;-dMOqx6PS_)NN>xo|8_g$!@{u6iQdDv1({Zb1z@6@Ae zSO1!*PPG}8bY1Cti>^9UnvV!rdOfQkE@(-6^rru<Zc~M*>dFa}T&}!NRd>qT7n(~F zsib3TmE*2-r_%hmx-hK!G%Bf>-PT*xW2vO3A9j~|bI<c5?NNGg=Bi$XR8kg?lf0@T zmDJkdNU!O$sif8Z2YJGlP)P&&chBL|qFNcgd*SoDOw3?$(ScnlcjqVaMh4R<<7!&M zlvq#7tDt~~dnAqcsOEm7%cJJuJ*lMHZzdG@QLdtDMb2Vl-%@!aP)qRe*y~i%YgO;N z87`%gT8@aC!#9db`Y@_r#>atFQn6!W-_&)clIn=Ayn2Ch^w*}X&<prL70)xgHlH+i zpwj&Kx^&IHP%7z;tcf3!liH+Tvm3r;1+=6#m{zug$U<&uTh0S*y22Rc!rhg9U$Jin z-F{HsX*Ju+wYNz55Px=|lATn=>~-wt3zZsnmaS$lmxwobv}_G~qrxkZbd_Lsno(z7 zrFDU9t%p-K?sN`d-+RAu;W>}hE%$%!-<iN<EHD`hOvVDqOEo5Af&9@qld(YU7batY zoc_ri{y^>*wXau=6mO}xQ;o@3Aa@;;u|Vz@CS!rzFHFV)xnG!!1#-VI84G`1#)1jo zH?AT%Tb8`^yGJVDG<<gufeG)o^6x&xZo*Gq{xg*RiUa$-&A)JCzbpS4Y-Vg|W$l^> z{?|P5GtY#-%a_06_cJr8m50yx;Xmbvzb}G+l}YrcV*+FoB-t%Z+nfSfL}#Ci?c>pf zyeJO8aMr*AKEyFk>372E2|r}+yc^%G_!!Ou=v=q+u(TBM+@AcI4wr^5cq{1G=s?J> z>ajn`_{iB#$lN8N-ImI2fFD2|`gw^`=r~9b+B3gmZQUxUMc$}ry81YnP4Kd+AXUl= zey{cV>zB$|{UIbu(CV0M(r4tlKD%>=O%jK+V&8QSwW1Sikrszi*X1AUVe-TH(4PE- z0hLIL@7jnn`^_I@fRXM$UNmYxAEa@e{IGjt@K{JplS%F&?X=|#vL!DmY_a2n`v{VD z@ARYfM_(g|SY5+;jnR-i=vMY{SwSNV@1XHF%@6E?;XUBGQ}2OwkB}`_civ35+W}Jr z((~Rgc5j67GRRqt6RiqAf^FF&Q*>!0JS(L-M?Bu8zAOWbw50t~yH`sfWmSCckxmv; zZ~*d7^|2I4hOE|5zra&HS3!pDot&3CH=`k`@{VQF!k!}_*KyCXe4d5em+{mK*~s(| z$%)G#4ZU#NMU&(nke;u=+xX_GI$VO*8yRoO=YxTH^@#W*v0O;c7uFq_aQ_$!EvloX zhsQnFg|-|t%{<?C7=)xn#JE(4z>%Kl-<kiA5lj_`C^C9srUwV0)^bfm;sR)kwQr9y z{STEWcC+H18Y~_J&6VzBmOaJ<a;ZZV-}m)vI)@taE;dlq@P>1GVPv6vegHht#on*B z=A#}AsW_QsYYpUJy!2o_J~eG1?6iHJ%L{35xHK0x^@|y>xEf888HWANx=es+3%4h# z4n1E2wM?)YDIIee_Nc<V_)_E~I0K@$U#wrX{yrKbfB)Ka)uG2xk3O5J#q8_>3FLL5 z>t}5jxPr7a3Dpj;PJzR?r6{v}LvP42ztdMFVOAs@MoVjFr=?s52lYahs(Z3DZ!^5a zSuJ9ty&x8*Eew<Q4$D6R8``<`bMd~zkjy?mR-j}T&P$uOb`_rj{to|A`KE1ZG0>K* zE53?3+3-Ti>BH5XX64Yk69){~zJCE+Uu4veyyU-q4h_Ki@qP5mvf-8L<V#mP4pzf` zTD>MS`f1|T3^39?MsFBwB@OQ;)DQQ&{D=?cG49LXG-bwAXv^z_HJ{eqhKt7uDf3s& zs_^=yhg`{OzUX??BZ);;UXf)mq;$>K%-b-%1Pwse_B{p0K~M{8VU|E9-^&&)<k<v^ z4-DKXGLpFRBW2kskGnmyNtQ|)=<{*c<E~Uv^EWn&B`vAWO6az!ca3T#aXCra?spG; zf5)TqH?w+JKWRNhUAM9KyG=1AHdu1ana}s=E#6I0SEnpBR<vRVh1Amb^&n1hG=(&3 zzt*${=XeUK|C(hsu5S~lq$l{6uM(w_>fF0%d$G?6in=RO&N%eXrs{9`C*`rLj~%6` zTjbN(Yr#9Jx~z(U{A%G;b^Z6H@yTW$q0r3gVtA}Gi>hvBLHxE0qp5rq>g(oM<C#LC zS*54G{9(s5D(QH|jjT;)DWv}V+jbiFJVPNh{VX_0cV;Su)Y4_Zqt^qdtjd!TJXKju zRk!?Lk&3rBRl8zaXGR|PCvG7~BUVPMarII@Dyb!3oQCI?WQw{`QBv}%hmt6yE)G)L zG|Nv>NE70o4+(6h>Tgt^*^RfXDBI05EWYHQL7}<4Pt>lLg~as=X@7O>uJ~_0N)_$? zV>eALTTdmGnI~~`!|)gi%~6+oWF4PEIe)coTDo?k)V!%e?8YWaY2xeiQyx>zUzd?y zZkMyDW|_ZV;Edk-RGR%C#3|i4Pc>8gBRgI2=08T!-_V|-8FeyLR<WKPS@%GIDz3^k z&)%+3h@;S~^Ukzh%;7MF)L(a(4JU(Y?(1lzEpf}BjGQ-fx(2PJ?C)^5fN367YmQpt zlrO7zsCFD3wf^R#tM*aY%(`$#XVxLAJw->AZ|$XLTPW)K2RfGDliy4w^$mD=a0gYp zmV#=|*7;O^E?;eFn4(J6U&|o9+q%|Nb^R4mQ!l^XOJS9a&lo=Q5UQCX^R#=a#iCe> zx|S85Gp}bKq>z@MFX-`L>wXHU%-|wh9RaGkQET<;%O$AlW_ElS<tj@h^`CI=$cBSd zd$)hd3Pm4vDsN<NtFu}*iAw74m*p|{`XP!QMQMqZIxe7ERb*BL=NMH}`MF%)cd_kI zDruDWC-wAURP!j|K~SXZQL6RX#dEk|RVmeuQx|b(bi_)kUDCyF+U+VWDygaS0iUz7 zRQqpcza3jg<x^Q@8hi9<^@7tBp42I*F<iHqYGruBQF(eakt#c;Zb-gY(j6+zbv80N zoBC3D!}R52-A;Uo6jrf<OG5@KQcBy%d!&1r%)5ruRSi@#wA@gp+Z07AssDy;ek<m+ Y&6Hnd6SU<Gv?LS+zKP;m;5qdF0RFck>;M1& literal 0 HcmV?d00001 diff --git a/irlc/utils/bandit_graphics_environment.py b/irlc/utils/bandit_graphics_environment.py index 391f0ea..a050ed3 100644 --- a/irlc/utils/bandit_graphics_environment.py +++ b/irlc/utils/bandit_graphics_environment.py @@ -50,7 +50,12 @@ class GraphicalBandit(BinaryBandit): def reset(self): s, info = super().reset() + if hasattr(self, 'agent'): + if hasattr(self.agent, 'Q'): del self.agent.Q + if hasattr(self.agent, 'N'): del self.agent.N + self.render() + return s, info def step(self, action): @@ -217,7 +222,7 @@ class BanditViewer: reward = self.last_reward action = self.last_action self.ghost.set_direction(self.ghost.rand_eyes()) # Random eyes. - if reward is not None: + if reward is not None and action is not None: if reward <= 0: self.ghost.kill() else: diff --git a/solutions/ex07/ilqr_TODO_1.py b/solutions/ex07/ilqr_TODO_1.py new file mode 100644 index 0000000..c9ada2c --- /dev/null +++ b/solutions/ex07/ilqr_TODO_1.py @@ -0,0 +1,2 @@ + l, L = [np.zeros((m,))]*N, [np.zeros((m,n))]*N + x_bar, u_bar = forward_pass(model, x_bar, u_bar, L=L, l=l) \ No newline at end of file diff --git a/solutions/ex07/ilqr_TODO_10.py b/solutions/ex07/ilqr_TODO_10.py new file mode 100644 index 0000000..9742318 --- /dev/null +++ b/solutions/ex07/ilqr_TODO_10.py @@ -0,0 +1 @@ + Delta, mu = max(1.0, Delta) * Delta_0, max(mu_min, mu * Delta) # Increase \ No newline at end of file diff --git a/solutions/ex07/ilqr_TODO_11.py b/solutions/ex07/ilqr_TODO_11.py new file mode 100644 index 0000000..dafc65d --- /dev/null +++ b/solutions/ex07/ilqr_TODO_11.py @@ -0,0 +1,4 @@ + R = c_uu + H = c_ux + q, qN = c_x[:-1], c_x[-1] + r = c_u \ No newline at end of file diff --git a/solutions/ex07/ilqr_TODO_12.py b/solutions/ex07/ilqr_TODO_12.py new file mode 100644 index 0000000..b5823c6 --- /dev/null +++ b/solutions/ex07/ilqr_TODO_12.py @@ -0,0 +1,4 @@ + # fs = [(v[1],v[2]) for v in [model.f(x, u, k, compute_jacobian=True) for k, (x, u) in enumerate(zip(x_bar[:-1], u_bar))]] + fs = [model.f_jacobian(x, u, k) for k, (x, u) in enumerate(zip(x_bar[:-1], u_bar))] + + A, B = zip(*fs) \ No newline at end of file diff --git a/solutions/ex07/ilqr_TODO_13.py b/solutions/ex07/ilqr_TODO_13.py new file mode 100644 index 0000000..41ceba5 --- /dev/null +++ b/solutions/ex07/ilqr_TODO_13.py @@ -0,0 +1,2 @@ + gs = [model.cost.c(x, u, i, compute_gradients=True) for i, (x, u) in enumerate(zip(x_bar[:-1], u_bar))] + c, c_x, c_u, c_xx, c_ux, c_uu = zip(*gs) \ No newline at end of file diff --git a/solutions/ex07/ilqr_TODO_14.py b/solutions/ex07/ilqr_TODO_14.py new file mode 100644 index 0000000..8c92508 --- /dev/null +++ b/solutions/ex07/ilqr_TODO_14.py @@ -0,0 +1,3 @@ + c = c + (cN,) + c_x = c_x + (c_xN,) + c_xx = c_xx + (c_xxN,) \ No newline at end of file diff --git a/solutions/ex07/ilqr_TODO_15.py b/solutions/ex07/ilqr_TODO_15.py new file mode 100644 index 0000000..73a0fa4 --- /dev/null +++ b/solutions/ex07/ilqr_TODO_15.py @@ -0,0 +1 @@ + u_star[i] = u_bar[i] + alpha * l[i] + L[i] @ (x[i] - x_bar[i]) \ No newline at end of file diff --git a/solutions/ex07/ilqr_TODO_16.py b/solutions/ex07/ilqr_TODO_16.py new file mode 100644 index 0000000..20904f4 --- /dev/null +++ b/solutions/ex07/ilqr_TODO_16.py @@ -0,0 +1 @@ + x[i + 1] = model.f(x[i], u_star[i], i) \ No newline at end of file diff --git a/solutions/ex07/ilqr_TODO_2.py b/solutions/ex07/ilqr_TODO_2.py new file mode 100644 index 0000000..a5ff3ca --- /dev/null +++ b/solutions/ex07/ilqr_TODO_2.py @@ -0,0 +1,2 @@ + A, B, c, c_x, c_u, c_xx, c_ux, c_uu = get_derivatives(model, x_bar, u_bar) + J = sum(c) \ No newline at end of file diff --git a/solutions/ex07/ilqr_TODO_3.py b/solutions/ex07/ilqr_TODO_3.py new file mode 100644 index 0000000..417755d --- /dev/null +++ b/solutions/ex07/ilqr_TODO_3.py @@ -0,0 +1 @@ + L, l = backward_pass(A, B, c_x, c_u, c_xx, c_ux, c_uu, mu) \ No newline at end of file diff --git a/solutions/ex07/ilqr_TODO_4.py b/solutions/ex07/ilqr_TODO_4.py new file mode 100644 index 0000000..6db866f --- /dev/null +++ b/solutions/ex07/ilqr_TODO_4.py @@ -0,0 +1 @@ + x_bar, u_bar = forward_pass(model, x_bar, u_bar, L=L, l=l, alpha=alpha) \ No newline at end of file diff --git a/solutions/ex07/ilqr_TODO_5.py b/solutions/ex07/ilqr_TODO_5.py new file mode 100644 index 0000000..4ead664 --- /dev/null +++ b/solutions/ex07/ilqr_TODO_5.py @@ -0,0 +1,2 @@ + l, L = [np.zeros((m,))] * N, [np.zeros((m, n))] * N + x_bar, u_bar = forward_pass(model, x_bar, u_bar, L=L, l=l) \ No newline at end of file diff --git a/solutions/ex07/ilqr_TODO_6.py b/solutions/ex07/ilqr_TODO_6.py new file mode 100644 index 0000000..2e7b84c --- /dev/null +++ b/solutions/ex07/ilqr_TODO_6.py @@ -0,0 +1,2 @@ + A, B, c, c_x, c_u, c_xx, c_ux, c_uu = get_derivatives(model, x_bar, u_bar) + J_prime = sum(c) \ No newline at end of file diff --git a/solutions/ex07/ilqr_TODO_7.py b/solutions/ex07/ilqr_TODO_7.py new file mode 100644 index 0000000..f23d1c9 --- /dev/null +++ b/solutions/ex07/ilqr_TODO_7.py @@ -0,0 +1 @@ + L, l = backward_pass(A, B, c_x, c_u, c_xx, c_ux, c_uu, mu) \ No newline at end of file diff --git a/solutions/ex07/ilqr_TODO_8.py b/solutions/ex07/ilqr_TODO_8.py new file mode 100644 index 0000000..123b9bb --- /dev/null +++ b/solutions/ex07/ilqr_TODO_8.py @@ -0,0 +1 @@ + J_new = cost_of_trajectory(model, x_hat, u_hat) \ No newline at end of file diff --git a/solutions/ex07/ilqr_TODO_9.py b/solutions/ex07/ilqr_TODO_9.py new file mode 100644 index 0000000..1fe4de6 --- /dev/null +++ b/solutions/ex07/ilqr_TODO_9.py @@ -0,0 +1 @@ + Delta, mu = min(1.0, Delta) / Delta_0, max(0, mu*Delta) \ No newline at end of file diff --git a/solutions/ex07/ilqr_agent_TODO_1.py b/solutions/ex07/ilqr_agent_TODO_1.py new file mode 100644 index 0000000..5632d14 --- /dev/null +++ b/solutions/ex07/ilqr_agent_TODO_1.py @@ -0,0 +1 @@ + u = self.ubar[k] + self.L[k]@ (x-self.xbar[k]) + self.l[k] \ No newline at end of file diff --git a/solutions/ex07/ilqr_pendulum_TODO_1.py b/solutions/ex07/ilqr_pendulum_TODO_1.py new file mode 100644 index 0000000..dee07f7 --- /dev/null +++ b/solutions/ex07/ilqr_pendulum_TODO_1.py @@ -0,0 +1 @@ + xs, us, J_hist, L, l = ilqr(model, N, x0, n_iter=n_iter, use_linesearch=use_linesearch) \ No newline at end of file diff --git a/solutions/ex07/linearization_agent_TODO_1.py b/solutions/ex07/linearization_agent_TODO_1.py new file mode 100644 index 0000000..9a9c162 --- /dev/null +++ b/solutions/ex07/linearization_agent_TODO_1.py @@ -0,0 +1,4 @@ + xp = model.f(xbar, ubar, k=0) + A, B = model.f_jacobian(xbar, ubar, k=0) + + d = xp - A @ xbar - B @ ubar \ No newline at end of file diff --git a/solutions/ex07/linearization_agent_TODO_2.py b/solutions/ex07/linearization_agent_TODO_2.py new file mode 100644 index 0000000..f84fac4 --- /dev/null +++ b/solutions/ex07/linearization_agent_TODO_2.py @@ -0,0 +1 @@ + (self.L, self.l), (V, v, vc) = LQR(A=[A]*N, B=[B]*N, d=[d]*N, Q=[Q]*N, q=[q]*N, R=[self.model.cost.R]*N) \ No newline at end of file diff --git a/solutions/ex07/linearization_agent_TODO_3.py b/solutions/ex07/linearization_agent_TODO_3.py new file mode 100644 index 0000000..797bd93 --- /dev/null +++ b/solutions/ex07/linearization_agent_TODO_3.py @@ -0,0 +1 @@ + u = self.L[0] @ x + self.l[0] \ No newline at end of file diff --git a/solutions/ex08/bandits_TODO_1.py b/solutions/ex08/bandits_TODO_1.py new file mode 100644 index 0000000..e6ee400 --- /dev/null +++ b/solutions/ex08/bandits_TODO_1.py @@ -0,0 +1,2 @@ + reward = self.q_star[a] + np.random.randn() + gab = self.q_star[self.optimal_action] - self.q_star[a] \ No newline at end of file diff --git a/solutions/ex08/gradient_agent_TODO_1.py b/solutions/ex08/gradient_agent_TODO_1.py new file mode 100644 index 0000000..2c166f9 --- /dev/null +++ b/solutions/ex08/gradient_agent_TODO_1.py @@ -0,0 +1,9 @@ + pi_a = self.Pa() + for b in range(self.k): + if b == a: + self.H[b] += self.alpha * (r - self.R_bar) * (1 - pi_a[b]) + else: + self.H[b] -= self.alpha * (r - self.R_bar) * pi_a[b] + + if self.baseline: + self.R_bar = self.R_bar + (self.alpha if self.alpha is not None else 1/(self.t+1)) * (r - self.R_bar) \ No newline at end of file diff --git a/solutions/ex08/grand_bandit_race_TODO_1.py b/solutions/ex08/grand_bandit_race_TODO_1.py new file mode 100644 index 0000000..06e8845 --- /dev/null +++ b/solutions/ex08/grand_bandit_race_TODO_1.py @@ -0,0 +1 @@ + bandit1 = StationaryBandit(k=10) \ No newline at end of file diff --git a/solutions/ex08/grand_bandit_race_TODO_2.py b/solutions/ex08/grand_bandit_race_TODO_2.py new file mode 100644 index 0000000..c9ffb8d --- /dev/null +++ b/solutions/ex08/grand_bandit_race_TODO_2.py @@ -0,0 +1,5 @@ + agents = [BasicAgent(bandit1, epsilon=epsilon)] + agents += [MovingAverageAgent(bandit1, epsilon=epsilon, alpha=alpha)] + agents += [GradientAgent(bandit1, alpha=alpha,use_baseline=False) ] + agents += [GradientAgent(bandit1, alpha=alpha,use_baseline=True) ] + agents += [UCBAgent(bandit1, c=2)] \ No newline at end of file diff --git a/solutions/ex08/grand_bandit_race_TODO_3.py b/solutions/ex08/grand_bandit_race_TODO_3.py new file mode 100644 index 0000000..807579c --- /dev/null +++ b/solutions/ex08/grand_bandit_race_TODO_3.py @@ -0,0 +1 @@ + eval_and_plot(bandit1, agents, max_episodes=2000, labels=labels) \ No newline at end of file diff --git a/solutions/ex08/grand_bandit_race_TODO_4.py b/solutions/ex08/grand_bandit_race_TODO_4.py new file mode 100644 index 0000000..3a9cb82 --- /dev/null +++ b/solutions/ex08/grand_bandit_race_TODO_4.py @@ -0,0 +1 @@ + bandit2 = StationaryBandit(k=10, q_star_mean=4) \ No newline at end of file diff --git a/solutions/ex08/grand_bandit_race_TODO_5.py b/solutions/ex08/grand_bandit_race_TODO_5.py new file mode 100644 index 0000000..1a6cfc7 --- /dev/null +++ b/solutions/ex08/grand_bandit_race_TODO_5.py @@ -0,0 +1 @@ + eval_and_plot(bandit2, agents, max_episodes=2000, labels=labels) \ No newline at end of file diff --git a/solutions/ex08/grand_bandit_race_TODO_6.py b/solutions/ex08/grand_bandit_race_TODO_6.py new file mode 100644 index 0000000..20c9ba0 --- /dev/null +++ b/solutions/ex08/grand_bandit_race_TODO_6.py @@ -0,0 +1 @@ + bandit3 = NonstationaryBandit(k=10) \ No newline at end of file diff --git a/solutions/ex08/grand_bandit_race_TODO_7.py b/solutions/ex08/grand_bandit_race_TODO_7.py new file mode 100644 index 0000000..a2a5676 --- /dev/null +++ b/solutions/ex08/grand_bandit_race_TODO_7.py @@ -0,0 +1 @@ + eval_and_plot(bandit3, agents, max_episodes=2000, steps=10000, labels=labels) \ No newline at end of file diff --git a/solutions/ex08/grand_bandit_race_TODO_8.py b/solutions/ex08/grand_bandit_race_TODO_8.py new file mode 100644 index 0000000..b34bc62 --- /dev/null +++ b/solutions/ex08/grand_bandit_race_TODO_8.py @@ -0,0 +1 @@ + eval_and_plot(bandit1, agents2, steps=10000, labels=labels) \ No newline at end of file diff --git a/solutions/ex08/nonstationary_TODO_1.py b/solutions/ex08/nonstationary_TODO_1.py new file mode 100644 index 0000000..e53da8b --- /dev/null +++ b/solutions/ex08/nonstationary_TODO_1.py @@ -0,0 +1,2 @@ + self.q_star += self.reward_change_std * np.random.randn(self.k) + self.optimal_action = np.argmax(self.q_star) \ No newline at end of file diff --git a/solutions/ex08/nonstationary_TODO_2.py b/solutions/ex08/nonstationary_TODO_2.py new file mode 100644 index 0000000..26dc95b --- /dev/null +++ b/solutions/ex08/nonstationary_TODO_2.py @@ -0,0 +1,2 @@ + self.alpha=alpha + super().__init__(env, epsilon=epsilon) \ No newline at end of file diff --git a/solutions/ex08/nonstationary_TODO_3.py b/solutions/ex08/nonstationary_TODO_3.py new file mode 100644 index 0000000..d45b3a0 --- /dev/null +++ b/solutions/ex08/nonstationary_TODO_3.py @@ -0,0 +1 @@ + self.Q[a] = self.Q[a] + self.alpha * (r-self.Q[a]) \ No newline at end of file diff --git a/solutions/ex08/nonstationary_TODO_4.py b/solutions/ex08/nonstationary_TODO_4.py new file mode 100644 index 0000000..e4ffd80 --- /dev/null +++ b/solutions/ex08/nonstationary_TODO_4.py @@ -0,0 +1,4 @@ + bandit = NonstationaryBandit(k=10) + + agents = [BasicAgent(bandit, epsilon=epsilon)] + agents += [MovingAverageAgent(bandit, epsilon=epsilon, alpha=alpha) for alpha in alphas] \ No newline at end of file diff --git a/solutions/ex08/nonstationary_TODO_5.py b/solutions/ex08/nonstationary_TODO_5.py new file mode 100644 index 0000000..9742313 --- /dev/null +++ b/solutions/ex08/nonstationary_TODO_5.py @@ -0,0 +1 @@ + labels += [f"Mov.avg. agent, epsilon={epsilon}, alpha={alpha}" for alpha in alphas] \ No newline at end of file diff --git a/solutions/ex08/simple_agents_TODO_1.py b/solutions/ex08/simple_agents_TODO_1.py new file mode 100644 index 0000000..5416e07 --- /dev/null +++ b/solutions/ex08/simple_agents_TODO_1.py @@ -0,0 +1,2 @@ + self.Q = np.zeros((self.k,)) + self.N = np.zeros((self.k,)) \ No newline at end of file diff --git a/solutions/ex08/simple_agents_TODO_2.py b/solutions/ex08/simple_agents_TODO_2.py new file mode 100644 index 0000000..d91b393 --- /dev/null +++ b/solutions/ex08/simple_agents_TODO_2.py @@ -0,0 +1 @@ + return np.random.randint(self.k) if np.random.rand() < self.epsilon else np.argmax(self.Q) \ No newline at end of file diff --git a/solutions/ex08/simple_agents_TODO_3.py b/solutions/ex08/simple_agents_TODO_3.py new file mode 100644 index 0000000..df218f0 --- /dev/null +++ b/solutions/ex08/simple_agents_TODO_3.py @@ -0,0 +1,2 @@ + self.N[a] = self.N[a] + 1 + self.Q[a] = self.Q[a] + 1/self.N[a] * (r-self.Q[a]) \ No newline at end of file diff --git a/solutions/ex08/ucb_agent_TODO_1.py b/solutions/ex08/ucb_agent_TODO_1.py new file mode 100644 index 0000000..4812f63 --- /dev/null +++ b/solutions/ex08/ucb_agent_TODO_1.py @@ -0,0 +1,2 @@ + self.N[a] += 1 + self.Q[a] += 1/self.N[a] * (r - self.Q[a]) \ No newline at end of file diff --git a/solutions/ex08/ucb_agent_TODO_2.py b/solutions/ex08/ucb_agent_TODO_2.py new file mode 100644 index 0000000..437563c --- /dev/null +++ b/solutions/ex08/ucb_agent_TODO_2.py @@ -0,0 +1,3 @@ + k = self.env.action_space.n + self.Q = np.zeros((k,)) + self.N = np.zeros((k,)) \ No newline at end of file diff --git a/solutions/ex08/ucb_agent_TODO_3.py b/solutions/ex08/ucb_agent_TODO_3.py new file mode 100644 index 0000000..5925504 --- /dev/null +++ b/solutions/ex08/ucb_agent_TODO_3.py @@ -0,0 +1 @@ + return np.argmax( self.Q + self.c * np.sqrt( np.log(k+1)/(self.N+1e-8) ) ) \ No newline at end of file -- GitLab