@@ -0,0 +1,2814 @@
+#!%5Cusr%5Cbin%5Cenv python3%0A%0A'''%0AClasses and methods for multi-armed bandits%0A'''%0A%0A# Author: C. Franzen%0A%0Aimport numpy as np%0A%0A%0Aclass SoftMax:%0A    '''%0A    The softmax bandit algorithm%0A    '''%0A    def __init__(self, counts=None, values=None, tau_scale=1e3):%0A        self.t = 1%0A        self.tau_scale = tau_scale%0A        if counts is not None:%0A            self.counts = np.array(counts, dtype=int)%0A            self.values = np.array(values, dtype=float)%0A            self.num_arms = len(self.counts)%0A        else:%0A            self.counts = counts%0A            self.values = values%0A            self.num_arms = 0%0A%0A    def get_probs(self):%0A        '''%0A        returns probabilites according to the softmax function%0A        '''%0A        # anneal the tau parameter%0A        tau = self.tau_scale / self.t%0A        z = np.sum(%5Bnp.exp(v / tau) for v in self.values%5D)%0A        probs = np.array(%5Bnp.exp(v / tau) / z for v in self.values%5D)%0A        return probs%0A%0A    def initialize(self, n_arms: int):%0A        '''%0A        initializes a blank bandit%0A        '''%0A        self.counts = np.zeros(n_arms, dtype=int)%0A        self.values = np.ones(n_arms, dtype=float)%0A        self.num_arms = n_arms%0A%0A    def results_report(self, action_labels=None):%0A        print('Softmax Results%5Cn' + '-' * 20)%0A        print('%7B%7D total observations over %7B%7D actions'.format(sum(self.counts),%0A                                                             self.num_arms))%0A        if action_labels is None:%0A            action_labels = %5B'action' + str(i) for i in range(self.num_arms)%5D%0A        price_pt_str = '%5Cnprice point: %7B:%3E19%7D' + ' %7B:%3E8%7D' * (self.num_arms - 1)%0A        print(price_pt_str.format(*action_labels))%0A        prob_str = 'probabilities: %7B:%3E17.4f%7D' + %5C%0A                   ' %7B:%3E8.4f%7D' * (self.num_arms - 1)%0A        print(prob_str.format(*self.get_probs()))%0A        ev_str = 'expected value estimate: %7B:%3E7.4f%7D' + %5C%0A                 ' %7B:%3E8.4f%7D' * (self.num_arms - 1)%0A        print(ev_str.format(*self.values))%0A        obs_str = 'observations: %7B:%3E18%7D' + ' %7B:%3E8%7D' * (self.num_arms - 1)%0A        print(obs_str.format(*self.counts))%0A        best_arm = action_labels%5Bnp.argmax(self.values)%5D%0A        print('BEST ACTION: %7B%7D'.format(best_arm))%0A%0A%0A    def select_arm(self):%0A        '''%0A        selects an arm based upon softmax probabilties%0A        '''%0A        probs = self.get_probs()%0A        self.t += 1%0A        return np.random.choice(np.arange(self.num_arms), p=probs)%0A%0A    def update(self, chosen_arm: int, reward: float):%0A        '''%0A        given a reward for a pulled arm, updates softmax counts and values%0A        '''%0A        self.counts%5Bchosen_arm%5D = self.counts%5Bchosen_arm%5D + 1%0A        n = self.counts%5Bchosen_arm%5D%0A        value = self.values%5Bchosen_arm%5D%0A        new_value = ((n - 1) / n) * value + (1 / n) * reward%0A        self.values%5Bchosen_arm%5D = new_value%0A

add pretty printing for bandit results