# Plotting implicitly in Tikz

I am trying to plot

y^{2}x+2x^{3}y^{3}=y+1


in Tikz. According to what I have read this is not possible in Tikz? Is this true. I can not make a MWE in the question(sorry!)

• This answer might be helpful: tex.stackexchange.com/a/312761/27666 . Implicit plotting is discussing in a few different questions. For tikz/pgf, it seems like interfacing with GNUplot is best. SageTeX might also be a good option (see DJP's answer below). – Chris Chudzicki Jan 27 at 16:12

It is not "impossible". Here is a quickly written code that computes the gradient at a given point, moves a bit in an orthogonal direction, and iterates. (The smuggling here is not essential, one could also make \x, \y and so on global, but this would be less clean IMHO.)

\documentclass[tikz,border=3.14mm]{standalone}
% smuggling from https://tex.stackexchange.com/a/470979/121799
\usetikzlibrary{fpu}
\newcounter{smuggle}
\DeclareRobustCommand\smuggleone[1]{%
\stepcounter{smuggle}%
\expandafter\global\expandafter\let\csname smuggle@\arabic{smuggle}\endcsname#1%
\aftergroup\let\aftergroup#1\expandafter\aftergroup\csname smuggle@\arabic{smuggle}\endcsname
}
\DeclareRobustCommand\smuggle[2][1]{%
\smuggleone{#2}%
\ifnum#1>1
\aftergroup\smuggle\aftergroup[\expandafter\aftergroup\the\numexpr#1-1\aftergroup]\aftergroup#2%
\fi
}
\begin{document}
\begin{tikzpicture}[declare function={f(\x,\y)=\y*\y*\x+2*pow(\x,3)*pow(\y,3)-\y-1;}]
\pgfmathsetmacro{\dx}{0.01}
\pgfmathsetmacro{\x}{0}
\pgfmathsetmacro{\y}{-1}
\xdef\lstX{(\x,\y)}
\foreach \X in {1,...,200}
{\pgfkeys{/pgf/fpu,/pgf/fpu/output format=fixed}
\pgfmathsetmacro{\dfdx}{((f(\x+\dx,\y)-f(\x,\y))/\dx+(f(\x,\y)-f(\x-\dx,\y))/\dx)/2}
\pgfmathsetmacro{\dfdy}{((f(\x,\y+\dx)-f(\x,\y))/\dx+(f(\x,\y)-f(\x,\y-\dx))/\dx)/2}
\pgfmathsetmacro{\myalpha}{atan2(-\dfdx,\dfdy)}
%\typeout{\dfdx,\dfdy}
\pgfmathsetmacro{\x}{\x+\dx*cos(\myalpha)}
\pgfmathsetmacro{\y}{\y+\dx*sin(\myalpha)}
\pgfmathsetmacro{\ftest}{f(\x,\y)}
%\typeout{\x,\y,\ftest}
\xdef\lstX{\lstX (\x,\y)}
\smuggle{\x}
\smuggle{\y}
}
%\typeout{\lstX}
\draw[-latex] (-2,0) -- (2,0);
\draw[-latex] (0,-2) -- (0,2);
\draw plot coordinates {\lstX};
\end{tikzpicture}
\end{document}


If you compare this to a plot done with Mathematica,

you indeed find that indeed one branch is partly redrawn. That is, this works in principle. You could extend the TikZ code to draw the branch completely and so on. Whether it is wise to reinvent the wheel is another question.

My personal bottom-line: it is not impossible but arguably impractical.

• I am just getting started with GeoGebra and this may be helpful? I have seen some approaches using parametrics but it usually requires a trick. – MathScholar Jan 26 at 18:04
• @MathScholar Pgfplots allows you to do implicit plots through gnuplot. One can redo some of these things with TikZ. Yet if you only want to draw these things, I feel you will be better off with pgfplots or perhaps GeoCobra (which I never used myself and which according to my experience does not produce particularly nice codes, but it may well be that I am missing something). The reason why I like the original question is that this may conceivably be helpful when determining the boundary of some 3d shape. – marmot Jan 26 at 21:35

You can do in TikZ with the help of GNUplot (raw gnuplot). I notice that the graph is not so smooth even if I put the option smooth in \draw command, and increase isosample 1000,1000.

% https://www.iacr.org/authors/tikz/
\documentclass[tikz,border=5mm]{standalone}
\begin{document}
\begin{tikzpicture}
% use GNUplot (avaiable in TeXLive)
\draw[->] (-4.5,0) -- (4.5,0) node[below] {$x$};
\draw[->] (0,-4.5) -- (0,4.5) node[left] {$y$};

\draw[blue,thick] plot[raw gnuplot,smooth] function {
f(x,y) = x*y*y+2*(x**3)*(y**3)-y-1;
set xrange [-4:4];
set yrange [-4:4];
set view 0,0;
set isosample 500,500;
set cont base;
set cntrparam levels incre 0,0.1,0;
unset surface;
splot f(x,y);
};
\node[below=5mm,blue] at (current bounding box.south)
{The curve $xy^2+2x^3y^3=y+1$.};
\end{tikzpicture}
\end{document}


• do you have any idea why when I run the program above , I am not getting the blue plot output? Just the axes. My documentclass is article. – MathScholar Jan 27 at 0:34
• @MathScholar: I guess GNUplot has not yet installed in you computer. I use Texlive that already contained GNUplot. – Black Mild Jan 27 at 0:37
• yes I have the most recent MikTex update. I will check. I tried also running by standalone and the same result. – MathScholar Jan 27 at 0:38
• Yes I have it installed but this is not outputting the image. I checked my program files and found gnuplottex.sty with all the documentation. Strange. – MathScholar Jan 27 at 0:52
• @MathScholar you need to run this with shell-escape allowed. Instructions depend on how you run TeX. In command line, pdflatex --shell-escape foo if your file is foo.tex. – user4686 Jan 27 at 17:19

For that polished look, consider using the open source computer algebra system, Sage, which can be integrated with LaTeX via the sagetex package, documented here. Sage can handle implicit functions and more. Sage is not part of the LaTeX distribution, so you need to either download it to your computer or access it through a free Cocalc account. Here's how I would deal with this particular graph:

\documentclass{standalone}
\usepackage[usenames,dvipsnames]{xcolor}
\usepackage{pgfplots}
\usepackage{sagetex}
\usetikzlibrary{backgrounds}
\usetikzlibrary{decorations}
\begin{document}
\begin{sagesilent}
####### SCREEN SETUP #####################
LowerX = -3
UpperX = 3
LowerY = -3
UpperY = 3
step = .01
Scale = 1.0
xscale=1.0
yscale=1.0
#####################TIKZ PICTURE SET UP ###########
output = r""
output += r"\begin{tikzpicture}"
output += r"[line cap=round,line join=round,x=8.75cm,y=8cm]"
output += r"\begin{axis}["
output += r"grid = both,"
#Change "both" to "none" in above line to remove graph paper
output += r"minor tick num=4,"
output += r"every major grid/.style={Red!30, opacity=1.0},"
output += r"every minor grid/.style={ForestGreen!30, opacity=1.0},"
output += r"height= %f\textwidth,"%(yscale)
output += r"width = %f\textwidth,"%(xscale)
output += r"thick,"
output += r"black,"
output += r"axis lines=center,"
#Comment out above line to have graph in a boxed frame (no axes)
output += r"domain=%f:%f,"%(LowerX,UpperX)
output += r"line join=bevel,"
output += r"xmin=%f,xmax=%f,ymin= %f,ymax=%f,"%(LowerX,UpperX,LowerY, UpperY)
#output += r"xticklabels=\empty,"
#output += r"yticklabels=\empty,"
output += r"major tick length=5pt,"
output += r"minor tick length=0pt,"
output += r"major x tick style={black,very thick},"
output += r"major y tick style={black,very thick},"
output += r"minor x tick style={black,thin},"
output += r"minor y tick style={black,thin},"
#output += r"xtick=\empty,"
#output += r"ytick=\empty"
output += r"]"
##############FUNCTIONS#################################
##FUNCTION 1
x,y = var('x,y')
gridDim = 250
xmin = -3
xmax = 3
deltax = (xmax-xmin)/(gridDim-1)
ymin = -3
ymax = 3
deltay = (ymax-ymin)/(gridDim-1)
xvals = []
yvals = []

f(x,y)= y^2*x+2*x^3*y^3-y-1
P1=implicit_plot(f,(x,-3,-1.5),(y,-1,-.2),plot_points=gridDim)
C1 = P1._objects[0]
for i in range(0,gridDim):
for j in range(0,gridDim):
if abs(C1.xy_data_array[i][j])<.02:
xvals += [-3+j*(1.5/(gridDim-1))]
yvals += [-1+i*(.8/(gridDim-1))]

P2=implicit_plot(f,(x,-1.5,.8),(y,-1.6,0),plot_points=gridDim)
C2 = P2._objects[0]
for i in range(0,gridDim):
for j in range(0,gridDim):
if abs(C2.xy_data_array[i][j])<.02:
xvals += [-1.5+j*(2.3/(gridDim-1))]
yvals += [-1.6+i*(1.6/(gridDim-1))]

P3=implicit_plot(f,(x,.4,.8),(y,-3,-1),plot_points=gridDim)
C3 = P3._objects[0]
for i in range(0,gridDim):
for j in range(0,gridDim):
if abs(C3.xy_data_array[i][j])<.02:
xvals += [.4+j*(.4/(gridDim-1))]
yvals += [-3+i*(2/(gridDim-1))]

P4=implicit_plot(f,(x,.2,1),(y,.8,3),plot_points=gridDim)
C4 = P4._objects[0]
for i in range(0,gridDim):
for j in range(0,gridDim):
if abs(C4.xy_data_array[i][j])<.02:
xvals += [.2+j*(.8/(gridDim-1))]
yvals += [.8+i*(2.2/(gridDim-1))]

P5=implicit_plot(f,(x,1,xmax),(y,0,1),plot_points=gridDim)
C5 = P5._objects[0]
for i in range(0,gridDim):
for j in range(0,gridDim):
if abs(C5.xy_data_array[i][j])<.02:
xvals += [1+j*(2/(gridDim-1))]
yvals += [0+i*(1/(gridDim-1))]
output += r"\addplot+[only marks,mark size=.20pt,NavyBlue] coordinates {"
for i in range(0,len(xvals)):
output += r"(%f, %f) "%(xvals[i],yvals[i])
output += r"};"
##### COMMENT OUT A LINE BY STARTING WITH #
output += r"\end{axis}"
output += r"\end{tikzpicture}"
\end{sagesilent}
\sagestr{output}
\end{document}


The output is shown below, running in Cocalc:

Some points to be made about this approach. Imagine the picture is like a TV in that it is made up of pixels/points and Sage is figuring out which pixels need to be turned on (points plotted). Most pixels are off and trying to keep track of the off pixels is too much to handle. If you try just graphing the function in one piece it will look like a scattering of points because there aren't enough points/pixels to make the graph look like two different connected pieces. My way around that is, by knowing ahead of time what the graph should look like, to break the graph into pieces and applying the pixel approach to each piece. The first piece, P1, is in the code as

P1=implicit_plot(f,(x,-3,-1.5),(y,-1,-.2),plot_points=gridDim).


That gridDim is a reasonable number of pixels that can plotted. Notice that the x interval is 1.5 units and the y interval is .8 units. The line

xvals += [-3+j*(1.5/(gridDim-1))]


is using the starting value of x, which is -3 along with the x interval length of 1.5 to calculate the x values to be plotted. Next,

yvals += [-1+i*(.8/(gridDim-1))]


creates a list of y values, where y starts at -1 and has a y interval length of |-1-(-.2)|=.8. The second piece

P2=implicit_plot(f,(x,-1.5,.8),(y,-1.6,0),plot_points=gridDim)


is going to handle points having an x value between -1.5 and .8 and y values between -1.6 and 0. Again, this is to make the curve look smooth instead of a scattering of points. But now the calculation of x values is different. Because x values start at -1.5 and are in an interval of length |-1.5-.8|=2.3 the line is:

xvals += [-1.5+j*(2.3/(gridDim-1))]


I found it took 5 pieces to get a plot that I liked. Since I already had the code, it was around 10 minutes of playing around until I was happy with the result. Note that the code tells you how to turn off the graph paper as well as graph in a frame, rather than axes.

Run with xelatex

\documentclass[pstricks,border=5mm]{standalone}
\usepackage{pst-func}
\begin{document}
\begin{pspicture*}(-5,-5)(5,5)
\psplotImp[algebraic,linecolor=blue,
stepFactor=0.2,linewidth=1.5pt](-5.1,-5.1)(4,4){x*y^2+2*x^3*y^3-y-1}
\psaxes{->}(0,0)(-4.5,-4.5)(4.5,4.5)
\uput*[45](1.5,2){$xy^2+2x^3y^3=y+1$}
\end{pspicture*}
\end{document}