import numpy as np import itertools import functools """ Define our data """ # The Statespace states = np.array(['Bonus', 'Ten', 'Fifty', 'Hundred', 'Five-Hundred']) # Possible sequences of events transitionName = np.array([['BB', 'BT', 'SI'], ['RS', 'RR', 'RI'], ['IS', 'IR', 'II']]) # Probabilities Matrix (transition matrix) transitionMatrix = np.array([[0.2, 0.6, 0.2], [0.1, 0.6, 0.3], [0.2, 0.7, 0.1]]) # Starting state startingState = 'Sleep' # Steps to run stepTime = 1 # End state you want to find probabilites of endState = 'Run' """ Set our parameters """ # Should we seed the results? setSeed = False seedNum = 27 """ Simulation parameters """ # Should we simulate more than once? setSim = False simNum = 100000 # A class that implements the Markov chain to forecast the state/mood: class markov(object): """simulates a markov chain given its states, current state and transition matrix. Parameters: states: list containing all the possible states transitionName: a matrix (nested list in a list) containing a list of the all possible state directions transitionMatrix: a matrix (nested list in a list) containing all the probabilites of moving to each state currentState: a string indicating the starting state days: an integer determining how many days (or times) to simulate""" def __init__(self, states: np.array, transitionName: np.array, transitionMatrix: np.array, currentState: str, days: int): super(markov, self).__init__() self.states = states self.list = list self.transitionName = transitionName self.transitionMatrix = transitionMatrix self.currentState = currentState self.days = days @staticmethod def setSeed(num: int): return np.random.seed(num) @functools.lru_cache(maxsize=128) def forecast(self): print(f'Start state: {self.currentState}') # Shall store the sequence of states taken self.stateList = [self.currentState] i = 0 # To calculate the probability of the stateList self.prob = 1 while i != self.days: if self.currentState == 'Sleep': self.change = np.random.choice(self.transitionName[0], replace=True, p=transitionMatrix[0]) if self.change == 'SS': self.prob = self.prob * 0.2 self.stateList.append('Sleep') pass elif self.change == 'SR': self.prob = self.prob * 0.6 self.currentState = 'Run' self.stateList.append('Run') else: self.prob = self.prob * 0.2 self.currentState = "Icecream" self.stateList.append("Icecream") elif self.currentState == "Run": self.change = np.random.choice(self.transitionName[1], replace=True, p=transitionMatrix[1]) if self.change == "RR": self.prob = self.prob * 0.6 self.stateList.append("Run") pass elif self.change == "RS": self.prob = self.prob * 0.1 self.currentState = "Sleep" self.stateList.append("Sleep") else: self.prob = self.prob * 0.3 self.currentState = "Icecream" self.stateList.append("Icecream") elif self.currentState == "Icecream": self.change = np.random.choice(self.transitionName[2], replace=True, p=transitionMatrix[2]) if self.change == "II": self.prob = self.prob * 0.1 self.stateList.append("Icecream") pass elif self.change == "IS": self.prob = self.prob * 0.2 self.currentState = "Sleep" self.stateList.append("Sleep") else: self.prob = self.prob * 0.7 self.currentState = "Run" self.stateList.append("Run") i += 1 print(f'Possible states: {self.stateList}') print(f'End state after {self.days} steps: {self.currentState}') print(f'Probability of all the possible sequence of states:' f' {self.prob}') return self.stateList def main(*args, **kwargs): try: simNum = kwargs['simNum'] except KeyError: pass sumTotal = 0 # Check validity of transitionMatrix for i in range(len(transitionMatrix)): sumTotal += sum(transitionMatrix[i]) if i != len(states) and i == len(transitionMatrix): raise ValueError('Probabilities should add to 1') # Set the seed so we can repeat with the same results if setSeed: markov.setSeed(seedNum) # Save our simulations: list_state = [] count = 0 # Simulate Multiple Times if setSim: for _ in itertools.repeat(None, simNum + 1): markovChain = markov(states, transitionName, transitionMatrix, startingState, stepTime) list_state.append(markovChain.forecast()) else: for _ in range(1, 2): list_state.append(markov(states, transitionName, transitionMatrix, startingState, stepTime).forecast()) for list in list_state: if(list[-1] == f'{endState!s}'): count += 1 if setSim is False: simNum = 1 print(f'\nThe probability of starting in {startingState} and finishing' f' in {endState} after {stepTime} steps is {(count / simNum):.2%}') if __name__ == '__main__': main()