Visualizing stationary paths of a functional
Contents
Visualizing stationary paths of a functional#
Last revised: 02-Feb-2019 by Dick Furnstahl [furnstahl.1@osu.edu]
Consider the functional
\(\begin{align} S = \int_{x_1}^{x_2} f[y(x), y'(x), x] \, dx \end{align}\)
with \(y_1 = y(x_1)\) and \(y_2 = y(x_2)\) fixed. We denote by \(y^*(x)\) the path that minimizes \(S\) (or, more generally, makes it stationary). Then we consider the class of candidate paths \(y(x)\) given by
\(\begin{align} y(x) = y^*(x) + \alpha \eta(x) \end{align}\)
where \(\eta(x)\) is some function that vanishes at the endpoints: \(\eta(x_1) = \eta(x_2) = 0\). We can derive the Euler-Lagrange equations by minimizing \(S(\alpha)\) with respect to \(\alpha\).
Here we visualize this problem by considering a particular \(S\), choosing among some possible \(\eta(x)\) definitions, and seeing how \(S\) is minimized with respect to \(\alpha\). We will also allow for an incorrect determination of \(y^*(x)\), in which case we expect that the minimum alpha will give us a reasonable reproduction of the true \(y^*(x)\). The variation of \(\alpha\) and the choice of functions will be made using widgets from ipywidgets
.
Looking at a plot of the functional evaluation versus \(\alpha\)#
[We’ll use %matplotlib notebook
so that we can modify figures without redrawing them.]
%matplotlib notebook
import numpy as np
import matplotlib.pyplot as plt
import ipywidgets as widgets
from IPython.display import display
Functional from Taylor problem 6.9#
This problem states: “Find the equation of the path from the origin \(O\) to the point \(P(1,1)\) in the \(xy\) plane that makes the integral \(\int_O^P (y'^2 + y y' + y^2)\) stationary. The answer from solving the Euler-Lagrange equation is \(y^*(x) = \sinh(x)/\sinh(1)\).
def y_star(x):
"""Path that minimizes the functional in Taylor problem 6.9."""
return np.sinh(x) / np.sinh(1.)
delta_x = 0.001
x_pts = np.arange(0., 1., delta_x)
fig = plt.figure(figsize=(6,3),
num='Visualizing stationary paths of a functional')
ax1 = fig.add_subplot(1,2,1)
ax2 = fig.add_subplot(1,2,2)
def setup_figure():
ax1.set_title('Show paths')
ax1.plot(x_pts, y_star(x_pts), color='black', lw=2)
ax1.set_xlabel('x')
ax1.set_ylabel('y(x)')
ax2.set_title('Evaluate functional')
ax2.set_xlabel(r'$\alpha$')
ax2.set_ylabel('functional')
ax2.set_xlim(-0.4, 0.4)
ax2.set_ylim(1.5, 3.)
#ax2.axvline(0., color='black', alpha=0.3)
ax2.axhline(evaluate_functional(x_pts, y_star(x_pts)),
color='black', alpha=0.3)
fig.tight_layout()
def evaluate_functional(x_pts, y_pts):
"""Given arrays of x and y points, evaluate the functional from 6.9."""
# The numpy gradient function takes the derivative of an array y_pts
# that is a function of points x in x_pts.
y_deriv_pts = np.gradient(y_pts, x_pts)
f = y_deriv_pts**2 + y_pts * y_deriv_pts + y_pts**2
# Use the numpy trapezoid rule (trapz) to do the integral over f.
return np.trapz(f, x_pts)
def make_path(alpha, ax1_passed, ax2_passed,
base_function='exact', eta_function='sine'):
"""Given a base function, which may be the exact y^*(x) or a guess that
is not correct, generate and plot the path corresponding to adding
alpha*eta(x) to the base function, with eta(x) chosen among some
functions that vanish at the endpoints in x.
"""
# map x_pts to zero to 1 (it may already be there)
x_mapped_pts = (x_pts - x_pts[0]) / (x_pts[-1] - x_pts[0])
# Choices for the base function
if (base_function == 'exact'):
base = lambda x : y_star(x)
elif (base_function == 'guess 1'):
base = lambda x : np.sinh(2.*x) / np.sinh(2.)
elif (base_function == 'guess 2'):
base = lambda x : x**3
if (eta_function == 'sine'):
eta = lambda x : np.sin(np.pi * x)
elif (eta_function == 'parabola'):
eta = lambda x : 4. * x * (1. - x)
y_new_pts = base(x_pts) + alpha * eta(x_mapped_pts)
ax1_passed.plot(x_pts, y_new_pts, color='red', lw=1)
ax2_passed.plot(alpha, evaluate_functional(x_pts, y_new_pts), '.',
color='red')
def reset_graph(event):
ax1.clear()
ax2.clear()
setup_figure()
button = widgets.Button(
description='reset graph'
)
button.on_click(reset_graph)
widgets.interact(make_path,
alpha=widgets.FloatSlider(min=-1., max=1., step=.05,
value=0.0, description=r'$\alpha$',
continuous_update=False),
ax1_passed=widgets.fixed(ax1),
ax2_passed=widgets.fixed(ax2),
base_function=widgets.Dropdown(options=['exact', 'guess 1',
'guess 2'],
value='exact',
description='base function'),
eta_function=widgets.Dropdown(options=['sine', 'parabola'],
value='sine',
description=r'$\eta(x)$')
)
setup_figure()
button