<?xml version="1.0" encoding="UTF-8"?>

<rss version="2.0"
     xmlns:content="http://purl.org/rss/1.0/modules/content/"
     xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
     xmlns:atom="http://www.w3.org/2005/Atom"
     xmlns:dc="http://purl.org/dc/elements/1.1/"
     xmlns:wfw="http://wellformedweb.org/CommentAPI/"
     >
  <channel>
    <atom:link href="http://kitchingroup.cheme.cmu.edu/blog/feed/index.xml" rel="self" type="application/rss+xml" />
    <title>The Kitchin Research Group</title>
    <link>https://kitchingroup.cheme.cmu.edu/blog</link>
    <description>Chemical Engineering at Carnegie Mellon University</description>
    <pubDate>Sat, 01 Nov 2025 13:47:46 GMT</pubDate>
    <generator>Blogofile</generator>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>1</sy:updateFrequency>
    
    <item>
      <title>Using autograd to plot implicit functions</title>
      <link>https://kitchingroup.cheme.cmu.edu/blog/2019/10/02/Using-autograd-to-plot-implicit-functions</link>
      <pubDate>Wed, 02 Oct 2019 21:30:46 EDT</pubDate>
      <category><![CDATA[implicit-function]]></category>
      <category><![CDATA[autograd]]></category>
      <category><![CDATA[nonlinear-algebra]]></category>
      <guid isPermaLink="false">wHgJ4s626IZnowZ4a9K6z-zL8p0=</guid>
      <description>Using autograd to plot implicit functions</description>
      <content:encoded><![CDATA[


&lt;p&gt;
Consider the solution to these equations (adapted from &lt;a href="https://www.mathworks.com/help/optim/ug/fsolve.html"&gt;https://www.mathworks.com/help/optim/ug/fsolve.html&lt;/a&gt;):
&lt;/p&gt;

&lt;p&gt;
\(e^{-e^{-(x_1 + x_2)}} = x_2 (1 + x_1^2)\)
&lt;/p&gt;

&lt;p&gt;
and
&lt;/p&gt;

&lt;p&gt;
\(x_1 \cos(x_2) + x_2 \sin(x_1) = 1/2\)
&lt;/p&gt;

&lt;p&gt;
It is not clear how many solutions there are to this set of equations, or what you should guess for the initial guess. Usually, the best way to see where a solution might be is to plot the equations and see where they intersect. These equations are implicit though, and it is not easy to plot them because we cannot solve for \(x_2\) in terms of \(x_1\) in either case. Here we explore a strategy to get plots so we can see where solutions could be.
&lt;/p&gt;

&lt;p&gt;
The idea is that we find one solution to each equation independently. Then, we derive a differential equation for each equation so we can integrate it to find the curve that is defined by the implicit function.  First, we find a solution for each equation. We guess a value for \(x_2\) and then find the value of \(x_1\) that solves each equation independently.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-ipython"&gt;&lt;span style="color: #0000FF;"&gt;import&lt;/span&gt; autograd.numpy &lt;span style="color: #0000FF;"&gt;as&lt;/span&gt; np
&lt;span style="color: #0000FF;"&gt;from&lt;/span&gt; scipy.optimize &lt;span style="color: #0000FF;"&gt;import&lt;/span&gt; fsolve

&lt;span style="color: #0000FF;"&gt;def&lt;/span&gt; &lt;span style="color: #006699;"&gt;f1&lt;/span&gt;(x1, x2):
&lt;span style="color: #9B9B9B; background-color: #EDEDED;"&gt; &lt;/span&gt;   &lt;span style="color: #0000FF;"&gt;return&lt;/span&gt; np.exp(-np.exp(-(x1 + x2))) - x2 * (1 + x1**2)

&lt;span style="color: #0000FF;"&gt;def&lt;/span&gt; &lt;span style="color: #006699;"&gt;f2&lt;/span&gt;(x1, x2):
&lt;span style="color: #9B9B9B; background-color: #EDEDED;"&gt; &lt;/span&gt;   &lt;span style="color: #0000FF;"&gt;return&lt;/span&gt; x1 * np.cos(x2) + x2 * np.sin(x1) - 0.5

&lt;span style="color: #BA36A5;"&gt;x2_1&lt;/span&gt; = 0.6
x1_1, = fsolve(f1, 0, args=(x2_1,))
&lt;span style="color: #0000FF;"&gt;print&lt;/span&gt;(&lt;span style="color: #008000;"&gt;'f1: '&lt;/span&gt;, x1_1, x2_1)

&lt;span style="color: #BA36A5;"&gt;x2_2&lt;/span&gt; = 1.0
x1_2, = fsolve(f2, 0 ,args=(x2_2,))
&lt;span style="color: #0000FF;"&gt;print&lt;/span&gt;(&lt;span style="color: #008000;"&gt;'f2: '&lt;/span&gt;, x1_2, x2_2)
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
f1:  0.08638978040861575 0.6
f2:  0.32842406163614396 1.0
&lt;/p&gt;

&lt;p&gt;
Next, we need a differential equation that is \(dx_2/dx_1\). If we had that, we could just integrate it from one of the starting points above, and get the curve we want. The functions are implicit, so we have to use the implicit derivative, which for the first equation is \(dx_2/dx_1 = -df1/dx_1 / df1/dx_2\). We will get these gradients from autograd. Then, we just integrate the solution. Here we do this for the first equation.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-ipython"&gt;&lt;span style="color: #0000FF;"&gt;from&lt;/span&gt; scipy.integrate &lt;span style="color: #0000FF;"&gt;import&lt;/span&gt; solve_ivp
&lt;span style="color: #0000FF;"&gt;from&lt;/span&gt; autograd &lt;span style="color: #0000FF;"&gt;import&lt;/span&gt; grad

&lt;span style="color: #BA36A5;"&gt;df1dx1&lt;/span&gt; = grad(f1, 0)
&lt;span style="color: #BA36A5;"&gt;df1dx2&lt;/span&gt; = grad(f1, 1)

&lt;span style="color: #0000FF;"&gt;def&lt;/span&gt; &lt;span style="color: #006699;"&gt;dx2dx1_1&lt;/span&gt;(x1, x2):
&lt;span style="color: #9B9B9B; background-color: #EDEDED;"&gt; &lt;/span&gt;   &lt;span style="color: #0000FF;"&gt;return&lt;/span&gt; -df1dx1(x1, x2) / df1dx2(x1, x2)

&lt;span style="color: #BA36A5;"&gt;x1_span&lt;/span&gt; = (x1_1, 1)
&lt;span style="color: #BA36A5;"&gt;x2_0&lt;/span&gt; = (x2_1, )
&lt;span style="color: #BA36A5;"&gt;sol1&lt;/span&gt; = solve_ivp(dx2dx1_1, x1_span, x2_0, max_step=0.1)
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
And then, we do it for the second equation.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-ipython"&gt;&lt;span style="color: #BA36A5;"&gt;df2dx1&lt;/span&gt; = grad(f2, 0)
&lt;span style="color: #BA36A5;"&gt;df2dx2&lt;/span&gt; = grad(f2, 1)

&lt;span style="color: #0000FF;"&gt;def&lt;/span&gt; &lt;span style="color: #006699;"&gt;dx2dx1_2&lt;/span&gt;(x1, x2):
&lt;span style="color: #9B9B9B; background-color: #EDEDED;"&gt; &lt;/span&gt;   &lt;span style="color: #0000FF;"&gt;return&lt;/span&gt; -df2dx1(x1, x2) / df2dx2(x1, x2)

&lt;span style="color: #BA36A5;"&gt;x1_span&lt;/span&gt; = (x1_2, 1)
&lt;span style="color: #BA36A5;"&gt;x2_0&lt;/span&gt; = (x2_2, )
&lt;span style="color: #BA36A5;"&gt;sol2&lt;/span&gt; = solve_ivp(dx2dx1_2, x1_span, x2_0, max_step=0.1)
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Finally, we plot the two solutions.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-ipython"&gt;%matplotlib inline
&lt;span style="color: #0000FF;"&gt;import&lt;/span&gt; matplotlib.pyplot &lt;span style="color: #0000FF;"&gt;as&lt;/span&gt; plt
plt.plot(sol1.t, sol1.y.T)
plt.plot(sol2.t, sol2.y.T)
plt.xlabel(&lt;span style="color: #008000;"&gt;'$x_1$'&lt;/span&gt;)
plt.ylabel(&lt;span style="color: #008000;"&gt;'$x_2$'&lt;/span&gt;)
plt.legend([&lt;span style="color: #008000;"&gt;'f1'&lt;/span&gt;, &lt;span style="color: #008000;"&gt;'f2'&lt;/span&gt;])
&lt;/pre&gt;
&lt;/div&gt;

&lt;pre class="example"&gt;
&amp;lt;Figure size 432x288 with 1 Axes&amp;gt;
&lt;/pre&gt;


&lt;p&gt;
&lt;figure&gt;&lt;img src="/media/b770a79094dc5fa34b51ebeed23401d697cc0f01.png"&gt;&lt;/figure&gt; 
&lt;/p&gt;

&lt;p&gt;
You can see now that in this range, there is only one intersection, i.e. one solution, and it is near \(x_1=0.4, x_2=0.6\). We can finally use that as an initial guess to find the only solution in this region, with confidence we are not missing any solutions.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;
&lt;pre class="src src-ipython"&gt;&lt;span style="color: #0000FF;"&gt;def&lt;/span&gt; &lt;span style="color: #006699;"&gt;objective&lt;/span&gt;(X):
&lt;span style="color: #9B9B9B; background-color: #EDEDED;"&gt; &lt;/span&gt;   &lt;span style="color: #BA36A5;"&gt;x1&lt;/span&gt;, &lt;span style="color: #BA36A5;"&gt;x2&lt;/span&gt; = X
&lt;span style="color: #9B9B9B; background-color: #EDEDED;"&gt; &lt;/span&gt;   &lt;span style="color: #0000FF;"&gt;return&lt;/span&gt; [f1(x1, x2), f2(x1, x2)]

fsolve(objective, [0.4, 0.6])
&lt;/pre&gt;
&lt;/div&gt;

&lt;pre class="example"&gt;
array([0.35324662, 0.60608174])
&lt;/pre&gt;

&lt;p&gt;
That is the same solution as reported at the Matlab site. Another use of autograd for the win here.
&lt;/p&gt;
&lt;p&gt;Copyright (C) 2019 by John Kitchin. See the &lt;a href="/copying.html"&gt;License&lt;/a&gt; for information about copying.&lt;p&gt;
&lt;p&gt;&lt;a href="/org/2019/10/02/Using-autograd-to-plot-implicit-functions.org"&gt;org-mode source&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Org-mode version = 9.2.3&lt;/p&gt;]]></content:encoded>
    </item>
  </channel>
</rss>
