# 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.]
```

Note that for some large valued input matrices a numerical error can occur:

```
>>> A = np.array([[113, 65, 112], [141, 93, -56], [120, 73, -76]])
>>> B = np.array([[-113, -65, -112], [-141, -93, 56], [-120, -73, 76]])
>>> game = nash.Game(A, B)
>>> iterations = 500
>>> play_counts_and_distributions = tuple(game.stochastic_fictitious_play(iterations=iterations))
Traceback (most recent call last):
...
ValueError: The matrix with values ranging from -76 to 141...
```

This can usually addressed by an affine scaling of the matrices:

```
>>> A = A / 10
>>> B = B / 10
>>> game = nash.Game(A, B)
>>> play_counts_and_distributions = game.stochastic_fictitious_play(iterations=iterations, etha=etha, epsilon_bar=epsilon_bar)
```