Plot non-polynomial multivariate implicit equation - python

I am currently trying to plot the following equation with matplotlib:
-4xy + x² - y² + 4(x³y - xy³) = 0
I do not want to solve this awful equation in order to plot it by simply assigning x = np.linspace(-5,5) and then putting it into my equation.
I am wondering if is possible to plot the solution directly with matplotlib.pyplot? I am able to plot the function well and quickly using Geogebra, so I imagine that this should also be possible using python. However, I have not found any other questions about this. Does anyone have any ideas?

One way to do this is using Sympy's plot_implicit function.
Code:
from sympy import plot_implicit, symbols, Eq
x, y = symbols('x y')
plot_implicit(Eq(-4*x*y+x**2-y**2+4*(x**3-x*y**3), 0),
adaptive=False,
points=1000)
Output:
Under the hood, this uses a mesh-grid to decide whether to plot the function at every point on the graph. This is what Geogebra will be doing under the hood as well. To apply this approach in vanilla matplotlib, we can use a contour plot:
Code:
import numpy as np
import matplotlib.pyplot as plt
# Plot axes in middle
fig, ax = plt.subplots()
ax.spines['left'].set_position('center')
ax.spines['bottom'].set_position('center')
ax.spines['right'].set_color('none')
ax.spines['top'].set_color('none')
ax.xaxis.set_ticks_position('bottom')
ax.yaxis.set_ticks_position('left')
# Set up mesh grid
x = y = np.linspace(-1.0, 1.0, 100)
X, Y = np.meshgrid(x,y)
# Plot contour
ax.contour(X, Y, -4*X*Y+X**2-Y**2+4*(X**3-x*Y**3), [0])
plt.show()
Output:

Related

get_points in sympy plot_implicit

I have been trying of solving an implicit equations using contour plot but I realized it was taking into account the singularities of my equation so I was obtaining wrong graphs. Because of this, I tried sympy.plot_implicit. Fortunately, it works properly but I have not found a proper way to set a label on the graphs produced by sympy.plot_implicit. For this aim, I need to obtain the points used by sympy.plot_implicit for producing its the graphs and use them into matplotlib. This way, I am obtainig correct solutions for my implicit equations and I can easily set labels for the graphs of the solutions. At this moment, I am facing many issues when I try to obtain the point from sympy.plot_implicit, I mean, when I only use sympy.plot there is not any problem but the issues appear when I use sympy.plot_implicit.
I have attached the code, thank you in advance.
Best
import matplotlib.pyplot as plt
import sympy as sp
from sympy.abc import x,y,z
def z(x, y):
return x - y
line = sp.plot(x**2,(x,-1,0), show=False)
#line = sp.plot_implicit(z(x, y), (x, 0, 1), (y, 0, 1), show=False)
x, y = line[0].get_points()
plt.plot(x, y)
plt.show()
You can use the move_sympyplot_to_axes function from Display two Sympy plots as two Matplotlib subplots, adapting one line as explained in its comments.
To get something in the legend, matplotlib's standard approach is to add a label to the specific element. In this case, the implicit area seems to be stored as a polygon inside ax.patches. Such a polygon will be shown as a colored rectangle in the legend. In order to get a line, a custom legend element could be created.
As in this case the solution is represented as a thin polygon and not as a line p1[0].get_points() doesn't work. However, you can extract the polygon's vertices from the matplotlib polygon patch:
import matplotlib.pyplot as plt
from matplotlib.lines import Line2D
import sympy as sp
from sympy.abc import x, y, z
def move_sympyplot_to_axes(p, ax):
backend = p.backend(p)
backend.ax = ax
backend._process_series(backend.parent._series, ax, backend.parent)
backend.ax.spines['right'].set_color('none')
backend.ax.spines['top'].set_color('none')
backend.ax.spines['bottom'].set_position('zero')
plt.close(backend.fig)
def z(x, y):
return (x ** 2 + y ** 2 - 1) ** 3 - x ** 2 * y ** 3
p1 = sp.plot_implicit(z(x, y), (x, -1.5, 1.5), (y, -1.5, 1.5), show=False)
fig, ax = plt.subplots()
move_sympyplot_to_axes(p1, ax)
# ax.patches[0].set_label("my label")
handles = [Line2D([], [], color=ax.patches[0].get_facecolor())]
ax.legend(handles=handles, labels=["my label"], loc='upper left')
vertices = ax.patches[0].get_path().vertices
ax.plot(vertices[:, 0], vertices[:, 1], ls=':', color='gold', lw=10, alpha=0.5, zorder=0)
plt.show()

python piecewise linear interpolation

I'm trying to create a piecewise linear interpolation routine and I'm pretty new to all of this so I'm very uncertain of what needs to be done.
I've generate a set of data points in 3D which gives variation in all 3 directions. I want to interpolate between these data points and plot in 3D.
The current data set is much smaller than the final one will be. Linear interpolation is important.
here's the current code
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import scipy.interpolate as interp
x = np.linspace(-1.3,1.3,10)
y1 = np.linspace(.5,0.,5)
y2 = np.linspace(0.,.5,5)
y = np.hstack((y1,y2))
z1 = np.linspace(.1,0.,5)
z2 = np.linspace(0.,.1,5)
z = np.hstack((z1,z2))
data = np.dstack([x,y,z])
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
f = interp.interp2d(x, y, z, kind='linear')
xnew = np.linspace(-1.3,1.3,100)
y1new = np.linspace(.5,0.,50)
y2new = np.linspace(0.,.5,50)
ynew = np.hstack((y1new,y2new))
znew = f(xnew,ynew)
ax.plot(x,y,znew, 'b-')
ax.scatter(x,y,z,'ro')
plt.show()
As I said, dataset is just to add variation. The real set will be much bigger but have less variation. I don't really understand the interpolation tool and the scipy documentation isn't very clear
would appreciate suggestions
2D ok. Please help with 3D
What I'm trying to do is build something that takes data points for deflections of a beam an interpolates between the data points. I wanted to to this in 3D and get a 3D plot showing the deflection along the x-axis in both y and z directions at the same time. As a stop gap measure I've used the below code to individually show deflection in y dir and z dir. Note, the data set is randomly generated for the moment. Some choices might look strange at the mo, but that's to sorta stick to the kinda range the final data set will use. The code below works for a 2D system so may be helpful to someone. I'd still really appreciate if someone could help me do this in 3D.
import numpy as np
import matplotlib.pyplot as plt
from scipy.interpolate import CubicSpline
u=10
x = np.linspace(-1.3,1.3,u) #regular x-data
y = np.random.random_sample(u)/4 #random y data
z = np.random.random_sample(u)/10 # random zdata
ynone = np.ones(u)*0.1 #no deflection dataset
znone = np.ones(u)*0.05
xspace = np.linspace(-1.3, 1.3, u*100)
ydefl = CubicSpline(x, y) #creating cubinc spline function for original data
zdefl = CubicSpline(x, z)
plt.subplot(2, 1, 1)
plt.plot(x, ynone, '-',label='y - no deflection')
plt.plot(x, y, 'go',label='y-deflection data')
plt.plot(xspace, ydefl(xspace), label='spline') #plot xspace vs spline function of xspace
plt.title('X [m]s')
plt.ylabel('Y [m]')
plt.legend(loc='best', ncol=3)
plt.subplot(2, 1, 2)
plt.plot(x, znone, '-',label='z - no deflection')
plt.plot(x, z, 'go',label='z-deflection data')
plt.plot(xspace, zdefl(xspace),label='spline')
plt.xlabel('X [m]')
plt.ylabel('Z [m]')
plt.legend(loc='best', ncol=3)
plt.show()

Python plot of a piecewise defined surface

I am trying to make a 3d plot of a surface that is defined in different ways for different regions. As an example, take f(x,y) that is defined as 1 if x > y and as x^2 if x <= y.
I defined f with logical operators, and tried to plot it with the "plot_surface" function, evaluating it in a grid. Unfortunately, I got an error saying that "the truth value of an array with more than one element is ambiguous".
Do you know any way of solving this?
Taking from the link posted by Serenity you need to define f using np.piecewise
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import axes3d, Axes3D
num_steps = 500
x_arr = np.linspace(0,100, num_steps)
y_arr = np.linspace(0,100, num_steps)
def zfunc(x, y):
return np.piecewise(x, [x>y, x<=y], [lambda x: 1, lambda x: x**2])
x,y = np.meshgrid(x_arr, y_arr)
z =zfunc(x,y)
fig=plt.figure()
ax=fig.add_subplot(1,1,1,projection='3d')
ax.set_xlabel('X axis')
ax.set_ylabel('Y axis')
ax.plot_surface(x,y,z,cmap='viridis') #cmap to make it easier to see
ax.view_init(30, 70)
plt.show()
Giving you this plot:

Contour plotting complex numbers and conjugates

I am trying to make a contour plot in python with complex numbers (i am using matplotlib, pylab).
I am working with sharp bounds on harmonic polynomials, but specifically right now I am trying to plot:
Re(z(bar) - e^(z))= 0
Im(z(bar) - e^z) = 0
and plot them over each other in a contour in order to find their zeros to determine how many solutions there are to the equation z(bar) = e^(z).
Does anyone have experience in contour plotting, specifically with complex numbers?
import numpy as np
from matplotlib import pyplot as plt
x = np.r_[0:10:30j]
y = np.r_[0:10:20j]
X, Y = np.meshgrid(x, y)
Z = X*np.exp(1j*Y) # some arbitrary complex data
def plotit(z, title):
plt.figure()
cs = plt.contour(X,Y,z) # contour() accepts complex values
plt.clabel(cs, inline=1, fontsize=10) # add labels to contours
plt.title(title)
plt.savefig(title+'.png')
plotit(Z, 'real')
plotit(Z.real, 'explicit real')
plotit(Z.imag, 'imaginary')
plt.show()
EDIT: Above is my code, and note that for Z, I need to plot both real and imaginary parts of (x- iy) - e^(x+iy)=0. The current Z that is there is simply arbitrary. It is giving me an error for not having a 2D array when I try to plug mine in.
I don't know how you are plotting since you didn't post any code, but in general I advise moving away from using pylab or the pyplot interface to matplotlib, using the direct object methods is much more robust and just as simple. Here is an example of plotting contours of two sets of data on the same plot.
import numpy as np
import matplotlib.pyplot as plt
# making fake data
x = np.linspace(0, 2)
y = np.linspace(0, 2)
c = x[:,np.newaxis] * y
c2 = np.flipud(c)
# plot
fig, ax = plt.subplots(1, 1)
cont1 = ax.contour(x, y, c, colors='b')
cont2 = ax.contour(x, y, c2, colors='r')
cont1.clabel()
cont2.clabel()
plt.show()
For tom10, here is the plot this code produces. Note that setting colors to a single color makes distinguishing the two plots much easier.

Plotting implicit equations in 3d

I'd like to plot implicit equation F(x,y,z) = 0 in 3D. Is it possible in Matplotlib?
You can trick matplotlib into plotting implicit equations in 3D. Just make a one-level contour plot of the equation for each z value within the desired limits. You can repeat the process along the y and z axes as well for a more solid-looking shape.
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
import numpy as np
def plot_implicit(fn, bbox=(-2.5,2.5)):
''' create a plot of an implicit function
fn ...implicit function (plot where fn==0)
bbox ..the x,y,and z limits of plotted interval'''
xmin, xmax, ymin, ymax, zmin, zmax = bbox*3
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
A = np.linspace(xmin, xmax, 100) # resolution of the contour
B = np.linspace(xmin, xmax, 15) # number of slices
A1,A2 = np.meshgrid(A,A) # grid on which the contour is plotted
for z in B: # plot contours in the XY plane
X,Y = A1,A2
Z = fn(X,Y,z)
cset = ax.contour(X, Y, Z+z, [z], zdir='z')
# [z] defines the only level to plot for this contour for this value of z
for y in B: # plot contours in the XZ plane
X,Z = A1,A2
Y = fn(X,y,Z)
cset = ax.contour(X, Y+y, Z, [y], zdir='y')
for x in B: # plot contours in the YZ plane
Y,Z = A1,A2
X = fn(x,Y,Z)
cset = ax.contour(X+x, Y, Z, [x], zdir='x')
# must set plot limits because the contour will likely extend
# way beyond the displayed level. Otherwise matplotlib extends the plot limits
# to encompass all values in the contour.
ax.set_zlim3d(zmin,zmax)
ax.set_xlim3d(xmin,xmax)
ax.set_ylim3d(ymin,ymax)
plt.show()
Here's the plot of the Goursat Tangle:
def goursat_tangle(x,y,z):
a,b,c = 0.0,-5.0,11.8
return x**4+y**4+z**4+a*(x**2+y**2+z**2)**2+b*(x**2+y**2+z**2)+c
plot_implicit(goursat_tangle)
You can make it easier to visualize by adding depth cues with creative colormapping:
Here's how the OP's plot looks:
def hyp_part1(x,y,z):
return -(x**2) - (y**2) + (z**2) - 1
plot_implicit(hyp_part1, bbox=(-100.,100.))
Bonus: You can use python to functionally combine these implicit functions:
def sphere(x,y,z):
return x**2 + y**2 + z**2 - 2.0**2
def translate(fn,x,y,z):
return lambda a,b,c: fn(x-a,y-b,z-c)
def union(*fns):
return lambda x,y,z: np.min(
[fn(x,y,z) for fn in fns], 0)
def intersect(*fns):
return lambda x,y,z: np.max(
[fn(x,y,z) for fn in fns], 0)
def subtract(fn1, fn2):
return intersect(fn1, lambda *args:-fn2(*args))
plot_implicit(union(sphere,translate(sphere, 1.,1.,1.)), (-2.,3.))
Update: I finally have found an easy way to render 3D implicit surface with matplotlib and scikit-image, see my other answer. I left this one for whom is interested in plotting parametric 3D surfaces.
Motivation
Late answer, I just needed to do the same and I found another way to do it at some extent. So I am sharing this another perspective.
This post does not answer: (1) How to plot any implicit function F(x,y,z)=0? But does answer: (2) How to plot parametric surfaces (not all implicit functions, but some of them) using mesh with matplotlib?
#Paul's method has the advantage to be non parametric, therefore we can plot almost anything we want using contour method on each axe, it fully addresses (1). But matplotlib cannot easily build a mesh from this method, so we cannot directly get a surface from it, instead we get plane curves in all directions. This is what motivated my answer, I wanted to address (2).
Rendering mesh
If we are able to parametrize (this may be hard or impossible), with at most 2 parameters, the surface we want to plot then we can plot it with matplotlib.plot_trisurf method.
That is, from an implicit equation F(x,y,z)=0, if we are able to get a parametric system S={x=f(u,v), y=g(u,v), z=h(u,v)} then we can plot it easily with matplotlib without having to resort to contour.
Then, rendering such a 3D surface boils down to:
# Render:
ax = plt.axes(projection='3d')
ax.plot_trisurf(x, y, z, triangles=tri.triangles, cmap='jet', antialiased=True)
Where (x, y, z) are vectors (not meshgrid, see ravel) functionally computed from parameters (u, v) and triangles parameter is a Triangulation derived from (u,v) parameters to shoulder the mesh construction.
Imports
Required imports are:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits import mplot3d
from matplotlib.tri import Triangulation
Some surfaces
Lets parametrize some surfaces...
Sphere
# Parameters:
theta = np.linspace(0, 2*np.pi, 20)
phi = np.linspace(0, np.pi, 20)
theta, phi = np.meshgrid(theta, phi)
rho = 1
# Parametrization:
x = np.ravel(rho*np.cos(theta)*np.sin(phi))
y = np.ravel(rho*np.sin(theta)*np.sin(phi))
z = np.ravel(rho*np.cos(phi))
# Triangulation:
tri = Triangulation(np.ravel(theta), np.ravel(phi))
Cone
theta = np.linspace(0, 2*np.pi, 20)
rho = np.linspace(-2, 2, 20)
theta, rho = np.meshgrid(theta, rho)
x = np.ravel(rho*np.cos(theta))
y = np.ravel(rho*np.sin(theta))
z = np.ravel(rho)
tri = Triangulation(np.ravel(theta), np.ravel(rho))
Torus
a, c = 1, 4
u = np.linspace(0, 2*np.pi, 20)
v = u.copy()
u, v = np.meshgrid(u, v)
x = np.ravel((c + a*np.cos(v))*np.cos(u))
y = np.ravel((c + a*np.cos(v))*np.sin(u))
z = np.ravel(a*np.sin(v))
tri = Triangulation(np.ravel(u), np.ravel(v))
Möbius Strip
u = np.linspace(0, 2*np.pi, 20)
v = np.linspace(-1, 1, 20)
u, v = np.meshgrid(u, v)
x = np.ravel((2 + (v/2)*np.cos(u/2))*np.cos(u))
y = np.ravel((2 + (v/2)*np.cos(u/2))*np.sin(u))
z = np.ravel(v/2*np.sin(u/2))
tri = Triangulation(np.ravel(u), np.ravel(v))
Limitation
Most of the time, Triangulation is required in order to coordinate mesh construction of plot_trisurf method, and this object only accepts two parameters, so we are limited to 2D parametric surfaces. It is unlikely we could represent the Goursat Tangle with this method.
Matplotlib expects a series of points; it will do the plotting if you can figure out how to render your equation.
Referring to Is it possible to plot implicit equations using Matplotlib? Mike Graham's answer suggests using scipy.optimize to numerically explore the implicit function.
There is an interesting gallery at http://xrt.wikidot.com/gallery:implicit showing a variety of raytraced implicit functions - if your equation matches one of these, it might give you a better idea what you are looking at.
Failing that, if you care to share the actual equation, maybe someone can suggest an easier approach.
As far as I know, it is not possible. You have to solve this equation numerically by yourself. Using scipy.optimize is a good idea. The simplest case is that you know the range of the surface that you want to plot, and just make a regular grid in x and y, and try to solve equation F(xi,yi,z)=0 for z, giving a starting point of z. Following is a very dirty code that might help you
from scipy import *
from scipy import optimize
xrange = (0,1)
yrange = (0,1)
density = 100
startz = 1
def F(x,y,z):
return x**2+y**2+z**2-10
x = linspace(xrange[0],xrange[1],density)
y = linspace(yrange[0],yrange[1],density)
points = []
for xi in x:
for yi in y:
g = lambda z:F(xi,yi,z)
res = optimize.fsolve(g, startz, full_output=1)
if res[2] == 1:
zi = res[0]
points.append([xi,yi,zi])
points = array(points)
Actually there is an easy way to plot implicit 3D surface with the scikit-image package. The key is the marching_cubes method.
import numpy as np
from skimage import measure
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import axes3d
Then we compute the function over a 3D meshgrid, in this example we use the goursat_tangle method #Paul defined in its answer:
xl = np.linspace(-3, 3, 50)
X, Y, Z = np.meshgrid(xl, xl, xl)
F = goursat_tangle(X, Y, Z)
The magic is happening here with marching_cubes:
verts, faces, normals, values = measure.marching_cubes(F, 0, spacing=[np.diff(xl)[0]]*3)
verts -= 3
We just need to correct vertices coordinates as they are expressed in Voxel coordinates (hence scaling using spacing switch and the subsequent origin shift).
Finally it is just about rendering the iso-surface using tri_surface:
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot_trisurf(verts[:, 0], verts[:, 1], faces, verts[:, 2], cmap='jet', lw=0)
Which returns:
Have you looked at mplot3d on matplotlib?
Finally, I did it (I updated my matplotlib to 1.0.1).
Here is code:
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
def hyp_part1(x,y,z):
return -(x**2) - (y**2) + (z**2) - 1
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
x_range = np.arange(-100,100,10)
y_range = np.arange(-100,100,10)
X,Y = np.meshgrid(x_range,y_range)
A = np.linspace(-100, 100, 15)
A1,A2 = np.meshgrid(A,A)
for z in A:
X,Y = A1, A2
Z = hyp_part1(X,Y,z)
ax.contour(X, Y, Z+z, [z], zdir='z')
for y in A:
X,Z= A1, A2
Y = hyp_part1(X,y,Z)
ax.contour(X, Y+y, Z, [y], zdir='y')
for x in A:
Y,Z = A1, A2
X = hyp_part1(x,Y,Z)
ax.contour(X+x, Y, Z, [x], zdir='x')
ax.set_zlim3d(-100,100)
ax.set_xlim3d(-100,100)
ax.set_ylim3d(-100,100)
Here is result:
Thank You, Paul!
MathGL (GPL plotting library) can plot it easily. Just create a data mesh with function values f[i,j,k] and use Surf3() function to make isosurface at value f[i,j,k]=0. See this sample.

Categories

Resources