pythonmaxsympyplot3d

Plotting sympy.Max yields TypeError


I am trying to generate a 3-dimensional plot for a function involving sympy.Max, but I am getting a type-error. Minimum example:

import sympy

u, v = sympy.symbols("u v", positive=True)
f = sympy.Max(0, u*v - 0.5)
my_plot = sympy.plotting.plot3d(f, (u, 0, 1), (v, 0, 1), show=False, legend=True)
my_plot.show()

The code runs fine if I remove the -0.5 for example, so it really seems to be about taking the maximum. Any ideas how to fix this?

Full stack trace:

Traceback (most recent call last):
  File "/Users/marcusrockel/Documents/projects/copulas_in_systemic_risk/venv/lib/python3.9/site-packages/sympy/plotting/experimental_lambdify.py", line 131, in __call__
    results = self.vector_func(*temp_args)
  File "/Users/marcusrockel/Documents/projects/copulas_in_systemic_risk/venv/lib/python3.9/site-packages/sympy/plotting/experimental_lambdify.py", line 272, in __call__
    return self.lambda_func(*args, **kwargs)
  File "<string>", line 1, in <lambda>
  File "<__array_function__ internals>", line 180, in amax
  File "/Users/marcusrockel/Documents/projects/copulas_in_systemic_risk/venv/lib/python3.9/site-packages/numpy/core/fromnumeric.py", line 2793, in amax
    return _wrapreduction(a, np.maximum, 'max', axis, None, out,
  File "/Users/marcusrockel/Documents/projects/copulas_in_systemic_risk/venv/lib/python3.9/site-packages/numpy/core/fromnumeric.py", line 86, in _wrapreduction
    return ufunc.reduce(obj, axis, dtype, out, **passkwargs)
TypeError: only integer scalar arrays can be converted to a scalar index

I am using Python 3.9 and sympy 1.11.1.


Solution

  • That's unfortunate: it is a limitation of the current plotting module, which is using experimental_lambdify which is quite old.

    Two solutions:

    1. you lambdify your expression, and manually plot it with matplotlib.
    from sympy import *
    import numpy as np
    import matplotlib.pyplot as plt
    
    u, v = symbols("u v", positive=True)
    expr = Max(0, u*v - 0.5)
    f = np.vectorize(lambdify([u, v], expr))
    uu, vv = np.mgrid[0:1:50j, 0:1:50j]
    zz = f(uu, vv)
    
    fig = plt.figure()
    ax = fig.add_subplot(projection="3d")
    ax.plot_surface(uu, vv, zz)
    ax.set_xlabel("u")
    ax.set_ylabel("v")
    plt.show()
    

    enter image description here

    1. take a look at this plotting module, which uses lambdify and it is able to plot it:
    from sympy import *
    from spb import *
    u, v = symbols("u v", positive=True)
    expr = Max(0, u*v - 0.5)
    plot3d(expr, (u, 0, 1), (v, 0, 1))