xaratustra
xaratustra

Reputation: 679

python (sympy) implicit function: get values instead of plot?

I am new to sympy but I already get a nice output when I plot the implicit function (actually the formula for Cassini's ovals) using sympy:

from sympy import plot_implicit, symbols, Eq, solve
x, y = symbols('x y')
k=2.7
a=3
eq = Eq((x**2 + y**2)**2-2*a**2*(x**2-y**2), k**4-a**4)
plot_implicit(eq)

Now is it actually possible to somehow get the x and y values corresponding to the plot? or alternatively solve the implicit equation without plotting at all?

thanks! :-)

Upvotes: 10

Views: 5200

Answers (3)

Eric Barth
Eric Barth

Reputation: 1

In a recent update, it appears that plot_implicit() no longer has an attribute get_points. That has changed to get_data.

Upvotes: 0

Gustavo Mirapalheta
Gustavo Mirapalheta

Reputation: 997

You can do this, even with interval math, if you try getting the mid point of each interval. Starting from your code, and slightly change it, by saving the plot_implicit object in a variable called g we have:

from sympy import plot_implicit, symbols, Eq, solve
x, y = symbols('x y')
k=2.7
a=3
eq = Eq((x**2 + y**2)**2-2*a**2*(x**2-y**2), k**4-a**4)
g = plot_implicit(eq)

Now let's save in a variable named ptos the intervals that were used to draw the plot.

ptos = g[0].get_points()[0]

This way ptos[0][0] will be the first interval in the x axis and ptos[0][1] will be its pair in the y axis. The intervals have a property called mid which gives the middle point of the interval. So you can suppose that ptos[0][0].mid, ptos[0][1].mid will be a pair x,y "true enough" to be one of our numerical solutions.

This way, a data frame composed of this middle point pairs can be generated with:

intervs = np.array(dtype='object')
meio = lambda x0:x0.mid
px = list(map(meio, intervs[:,0]))
py = list(map(meio, intervs[:,1]))  
import pandas as pd
dados = pd.DataFrame({'x':px, 'y':px})
dados.head()

Which in this example would give us:

           x         y
 0 -1.177733  0.598826
 1 -1.175389  0.596483
 2 -1.175389  0.598826
 3 -1.173045  0.596483
 4 -1.173045  0.598826

This idea of getting the intervals middle points can be used whenever one needs to move from "interval math" to "standard" point level math. Hope this helps. Regards.

Upvotes: 0

gboffi
gboffi

Reputation: 25093

This is an answer addressing your

is it actually possible to somehow get the x and y values corresponding to the plot?

and I say "addressing" because it's not possible to get the x and y values used to draw the curves — because the curves are not drawn using a sequenc of 2D points… more on this later,


TL;DR

pli = plot_implicit(...)
series = pli[0]
data, action = series.get_points()
data = np.array([(x_int.mid, y_int.mid) for x_int, y_int in data])

Let's start with your code

from sympy import plot_implicit, symbols, Eq, solve
x, y = symbols('x y')
k=2.7
a=3
eq = Eq((x**2 + y**2)**2-2*a**2*(x**2-y**2), k**4-a**4)

and plot it, with a twist: we save the Plot object and print it

pli = plot_implicit(eq)
print(pli)

to get

Plot object containing:
[0]: Implicit equation: Eq(-18*x**2 + 18*y**2 + (x**2 + y**2)**2, -27.8559000000000) for x over (-5.0, 5.0) and y over (-5.0, 5.0)

We are interested in this object indexed by 0,

ob = pli[0]
print(dir(ob))

that gives (ellipsis are mine)

['__class__', …, get_points, …, 'var_y']

The name get_points sounds full of promise, doesn't it?

 print(ob.get_points())

that gives (edited for clarity and with a big cut)

([
  [interval(-3.759774, -3.750008), interval(-0.791016, -0.781250)],
  [interval(-3.876961, -3.867195), interval(-0.634768, -0.625003)],
  [interval(-3.837898, -3.828133), interval(-0.693361, -0.683596)],
  [interval(-3.847664, -3.837898), interval(-0.673830, -0.664065)],
  ...
  [interval(3.837895, 3.847661), interval(0.664064, 0.673830)],
  [interval(3.828130, 3.837895), interval(0.683596, 0.693362)],
  [interval(3.867192, 3.876958), interval(0.625001, 0.634766)],
  [interval(3.750005, 3.759770), interval(0.781255, 0.791021)]
  ], 'fill')

What is this? the documentation of plot_implicit has

plot_implicit, by default, uses interval arithmetic to plot functions.

Following the source code of plot_implicit.py and plot,py one realizes that, in this case, the actual plotting (speaking of the matpolotlib backend) is just a line of code

 self.ax.fill(x, y, facecolor=s.line_color, edgecolor='None')

where x and y are constructed from the list of intervals, as returned from .get_points(), as follows

x, y = [], []
for intervals in interval_list:
    intervalx = intervals[0]
    intervaly = intervals[1]
    x.extend([intervalx.start, intervalx.start,
                  intervalx.end, intervalx.end, None])
    y.extend([intervaly.start, intervaly.end,
                  intervaly.end, intervaly.start, None])

so that for each couple of intervals matplotlib is directed to draw a filled rectangle, small enough that the eye sees a continuous line (note the use of None to have disjoint rectangles).

We can conclude that the list of couples of intervals

l_xy_intervals = ((pli[0]).get_points())[0]

represents rectangular areas where the implicit expression you are plotting is "true enough"

Upvotes: 3

Related Questions