[SciPy-Dev] Finding a fixed point on the simplex

Erik Brinkman erik.brinkman at gmail.com
Thu Apr 20 16:01:56 EDT 2017


I recently wrote some code
<https://github.com/egtaonline/gameanalysis/blob/master/gameanalysis/fixedpoint.py>
 that uses simplicial subdivision to compute an approximate fixed point for
functions on a simplex. It's guaranteed to always return an approximate
fixed point, but may take exponential time. It's particularly useful for
functions which have fixed points but no basin of attraction, and so won't
converge via iteration (see the code at at the bottom that's looking for a
fixed point in a degenerate case of rock paper scissors).

Does this warrant inclusion into scipy? Generally computing fixed points of
continuous functions over compact sets seems useful to many communities,
however there are a couple of issues I see with blindly putting this in
scipy based off of the fact that a function for computing fixed points
<https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.fixed_point.html>
 already exists.

   - The proposed function works for cases where the existing function
   fails, which is an argument for inclusion.
   - The existing function is in optimize, but the proposed one is not an
   optimization algorithm, it's a search algorithm.
   - The proposed function only works for functions on the simplex. Most
   functions on compact sets should have homotopies that convert them into
   functions on the simplex, but the current fixed_point makes no such
   restriction.
   - The proposed function has different parameters than the current one. A
   few functions in optimize have signatures that take an `options` dictionary
   of parameters, but it would change the signature of fixed_point.
   - The algorithm is slightly more general than computing a fixed point of
   a simplex function. It actually finds fully labeled subsimplices for an
   arbitrary `proper` labeling function. From my very limited understanding,
   there are other labeling functions which produce approximate solutions to
   other problems. However, given this more general function, it's proper
   module seems less certain.

In addition, the code as it stands is pure python, but it is relatively
computationally intensive, and so potentially could be sped up by switching
to a c or cython implementation. Conditioned on thinking that this would
make a valid PR, is it worth looking into implementing it in a lower level
language despite the fact that this makes a large number of calls to a
supplied python function?

Thanks,
Erik

Example
~~~~~~~
import numpy as np
from scipy import optimize
from gameanalysis import fixedpoint


A = np.array([[[1, 0, 0],
               [2, 0, 0],
               [2, 0, 0]],

              [[0, 1, 0],
               [1, 0, 0],
               [1, 1, 0]],

              [[0, 0, 1],
               [1, 0, 1],
               [1, 0, 0]],

              [[0, 2, 0],
               [0, 1, 0],
               [0, 2, 0]],

              [[0, 1, 1],
               [0, 0, 1],
               [0, 1, 0]],

              [[0, 0, 2],
               [0, 0, 2],
               [0, 0, 1]]])

B = np.array([[ 0,  0,  0],
              [-2,  1,  0],
              [ 1,  0, -3],
              [ 0,  0,  0],
              [ 0, -3,  1],
              [ 0,  0,  0]])

FP = np.array([0.3129473 , 0.40598569, 0.28106701])


def fixed_point_func(x):
    x = np.maximum(x, 0)
    x /= x.sum()
    y = np.sum(np.prod(x ** A, -1) * B, 0)
    z = np.maximum(0, y - y.dot(x)) + x
    z /= z.sum()
    return z


def main():
    close = dict(rtol=1e-3, atol=1e-3)
    x0 = np.ones(3) / 3
    # Verify fixed point
    assert np.allclose(FP, fixed_point_func(FP), **close)

    # Use guaranteed method
    fp = fixedpoint.fixed_point(fixed_point_func, x0, tol=1e-8)
    assert np.allclose(FP, fp, **close)

    # Expect error with scipy method
    try:
        optimize.fixed_point(fixed_point_func, x0)
        assert False, "shouldn't get this far"
    except RuntimeError:
        pass  # Expected

    # Expect error with scipy method
    try:
        optimize.fixed_point(fixed_point_func, x0, method='iteration')
        assert False, "shouldn't get this far"
    except RuntimeError:
        pass  # Expected


if __name__ == '__main__':
    main()
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/scipy-dev/attachments/20170420/b21b57ec/attachment.html>


More information about the SciPy-Dev mailing list