# Use stochastic fictitious playΒΆ

One of the learning algorithms implemented in Nashpy is called Stochastic fictitious play, this is implemented as a method on the Game class:

>>> import nashpy as nash
>>> import numpy as np
>>> A = np.array([[3, 1], [0, 2]])
>>> B = np.array([[2, 0], [1, 3]])
>>> game = nash.Game(A, B)

The stochastic_fictitious_play method returns a generator of a given collection of learning steps, comprising of the play counts and the mixed strategy of each player:

>>> np.random.seed(0)
>>> iterations = 500
>>> play_counts_and_distributions = game.stochastic_fictitious_play(iterations=iterations)
>>> for play_counts, distributions in play_counts_and_distributions:
...     row_play_counts, column_play_counts = play_counts
...     row_distributions, column_distributions = distributions
...     print(row_play_counts, column_play_counts, row_distributions, column_distributions)
[0 0] [0 0] None None
[1. 0.] [0. 1.] [9.99953841e-01 4.61594628e-05] [0.501447 0.498553]
...
[498.   1.] [497.   2.] [1.00000000e+00 1.07557011e-13] [9.99999998e-01 2.32299935e-09]
[499.   1.] [498.   2.] [1.00000000e+00 1.17304491e-13] [9.99999998e-01 2.18403537e-09]

Note that this process is stochastic:

>>> np.random.seed(1)
>>> play_counts_and_distributions = game.stochastic_fictitious_play(iterations=iterations)
>>> for play_counts, distributions in play_counts_and_distributions:
...     row_play_counts, column_play_counts = play_counts
...     row_distributions, column_distributions = distributions
...     print(row_play_counts, column_play_counts)
[0 0] [0 0]
[1. 0.] [1. 0.]
...
[499.   0.] [499.   0.]
[500.   0.] [500.   0.]

It is also possible to pass a play_counts variable to give a starting point for the algorithm:

>>> np.random.seed(0)
>>> play_counts = (np.array([0., 500.]), np.array([0., 500.]))
>>> play_counts_and_distributions = game.stochastic_fictitious_play(iterations=iterations, play_counts=play_counts)
>>> for play_counts, distributions in play_counts_and_distributions:
...     row_play_counts, column_play_counts = play_counts
...     row_distributions, column_distributions = distributions
...     print(row_play_counts, column_play_counts)
...
[  0. 500.] [  0. 500.]
[  0. 501.] [  0. 501.]
...
[  0. 999.] [  0. 999.]
[   0. 1000.] [   0. 1000.]

A value of etha and epsilon_bar can be passed. See the Stochastic fictitious play reference section for more information. The default values for etha and epsilon bar are $$10^-1$$ and $$10^-2$$ respectively:

>>> np.random.seed(0)
>>> etha = 10**-2
>>> epsilon_bar = 10**-3
>>> play_counts_and_distributions = game.stochastic_fictitious_play(iterations=iterations, etha=etha, epsilon_bar=epsilon_bar)
>>> for play_counts, distributions in play_counts_and_distributions:
...     row_play_counts, column_play_counts = play_counts
...     row_distributions, column_distributions = distributions
...     print(row_play_counts, column_play_counts)
...
[0 0] [0 0]
[1. 0.] [0. 1.]
...
[498.   1.] [497.   2.]
[499.   1.] [498.   2.]