Calculus of Variations¶

da PowerShell -- jupyter nbconvert --to html notebook.ipynb

Cosa mostra questo script (concettualmente) - moto rettilineo uniforme¶

  • Non risolve equazioni del moto
  • Confronta tante traiettorie ammesse
  • Calcola l’azione per ciascuna

Mostra che:

  • la traiettoria fisica non è scelta “a caso”
  • rende l’azione stazionaria (minimo in questo caso)
  • Questa è l’essenza del calcolo delle variazioni

Confronto

  • Newton: risolve
$$\large m\, \ddot{a}= 0 $$
  • le Variazioni risolvono:
$$\large \delta S = 0 \quad \rightarrow \quad \frac{d}{dt} \left( \frac{\partial L}{\partial \dot{x}} \right)=0 $$

Stesso risultato, filosofia completamente diversa.

In [1]:
import numpy as np
import matplotlib.pyplot as plt

# -----------------------------
# Parametri fisici e temporali
# -----------------------------
m = 1.0          # massa
t0, t1 = 0.0, 1.0
x0, x1 = 0.0, 1.0

N = 500
t = np.linspace(t0, t1, N)
dt = t[1] - t[0]

# ----------------------------------
# Traiettoria fisica (retta)
# ----------------------------------
x_phys = x0 + (x1 - x0) * (t - t0) / (t1 - t0)

# ----------------------------------
# Funzione per calcolare l'azione
# ----------------------------------
def action(x, dt, m=1.0):
    dxdt = np.gradient(x, dt)
    L = 0.5 * m * dxdt**2
    return np.sum(L) * dt

S_phys = action(x_phys, dt, m)

# ----------------------------------
# Generiamo traiettorie perturbate
# ----------------------------------
epsilons = np.linspace(-0.3, 0.3, 41)
actions = []

trajectories = []

for eps in epsilons:
    # perturbazione che NON altera gli estremi
    eta = np.sin(np.pi * t)
    x_var = x_phys + eps * eta
    trajectories.append(x_var)
    actions.append(action(x_var, dt, m))

actions = np.array(actions)

# ----------------------------------
# Grafici
# ----------------------------------
plt.figure(figsize=(5,3))
for x in trajectories[::10]:
    plt.plot(t, x, color="gray", alpha=0.4)

plt.plot(t, x_phys, linewidth=2)
plt.xlabel("t")
plt.ylabel("x(t)")
plt.title("Traiettorie ammesse (retta + variazioni)")
plt.show()

plt.figure(figsize=(5,3))
plt.plot(epsilons, actions, marker="o")
plt.axvline(0)
plt.xlabel("ampiezza variazione ε")
plt.ylabel("Azione S")
plt.title("L'azione è stazionaria sulla traiettoria fisica")
plt.show()

print("Azione sulla traiettoria fisica:", S_phys)
print("Azione minima approssimata:", actions.min())
No description has been provided for this image
No description has been provided for this image
Azione sulla traiettoria fisica: 0.5010020040080161
Azione minima approssimata: 0.5010020040080161

Esempio didattico di Calcolo delle Variazioni¶

L’obiettivo è mostrare in modo esplicito il significato del $\textit{calcolo delle variazioni$:

  • tra tutte le traiettorie cinematicamente ammissibili, quella fisica rende $\textbf{stazionaria}$ l’azione.

Problema variazionale

Si consideri una particella libera in una dimensione. Il funzionale azione è definito come: $$\large S[x] = \int_{t_0}^{t_1} L(x,\dot{x}) \, dt, \qquad L = \frac{1}{2} m \dot{x}^2 $$

con condizioni al contorno fissate: $$\large x(t_0) = x_0, \qquad x(t_1) = x_1 $$

Il problema consiste nel determinare quale funzione $x(t)$, tra tutte quelle che soddisfano le condizioni agli estremi, renda stazionaria l’azione.

Traiettoria fisica

Applicando l’equazione di Eulero--Lagrange: $$\large \frac{d}{dt}\left( \frac{\partial L}{\partial \dot{x}} \right)

  • \frac{\partial L}{\partial x} = 0
$$ si ottiene: $$

\large m \ddot{x} = 0 $$

la cui soluzione, compatibile con le condizioni al contorno, è: $$\large x_{\text{fis}}(t) = x_0 + \frac{x_1 - x_0}{t_1 - t_0}(t - t_0) $$

ovvero una traiettoria rettilinea uniforme.

Variazioni della traiettoria

Si considerano traiettorie di prova della forma: $$\large x(t) = x_{\text{fis}}(t) + \varepsilon\,\eta(t) $$

dove la funzione di variazione $\eta(t)$ soddisfa: $$\large \eta(t_0) = \eta(t_1) = 0 $$

in modo da non alterare le condizioni agli estremi. Un esempio tipico è: $$\large \eta(t) = \sin(\pi t) $$

Il parametro $\varepsilon$ controlla l’ampiezza della variazione.

Interpretazione fisica

Calcolando l’azione per ciascuna traiettoria ammessa, si osserva che:

  • l’azione varia al variare della traiettoria;
  • la traiettoria fisica non è scelta arbitrariamente;
  • essa rende l’azione stazionaria (in questo caso, minima).

Questo risultato non dipende da un principio di forza, ma da una proprietà globale del moto.

Significato concettuale

  • Il calcolo delle variazioni non risolve direttamente

le equazioni del moto.

  • Confronta insiemi di traiettorie possibili.
  • Seleziona quella che rende stazionario un funzionale.

Nel contesto della meccanica classica, questo approccio fornisce la base formale del $\textit{principio di minima (o stazionarietà) dell’azione}$, ponendo le fondamenta per le formulazioni lagrangiana e hamiltoniana.

Questo esercizio utilizza $SymPy$ per tradurre in forma computazionale alcuni concetti fondamentali della meccanica lagrangiana e del calcolo delle variazioni.

1. Implementazione delle equazioni di Eulero--Lagrange¶

La funzione $$ \texttt{euler\_lagrange}(L, q, \dot q) $$ costruisce simbolicamente le equazioni di Eulero--Lagrange associate a una lagrangiana $$ L = L(q, \dot q, t). $$

Per ciascuna coordinata generalizzata $q_i(t)$, la funzione calcola: $$ \frac{\partial L}{\partial q_i}, \qquad \frac{\partial L}{\partial \dot q_i}, \qquad \frac{d}{dt}\left( \frac{\partial L}{\partial \dot q_i} \right), $$ e combina questi termini secondo la forma standard: $$ \frac{d}{dt}\left( \frac{\partial L}{\partial \dot q_i} \right)

\frac{\partial L}{\partial q_i} = 0. $$

Il risultato è una lista di equazioni differenziali del moto.

2. Applicazione al caso della particella libera}¶

Si considera una coordinata generalizzata $x(t)$ e una lagrangiana $$ L = \frac{1}{2}\dot x^2, $$ che rappresenta l’energia cinetica di una particella libera di massa unitaria.

Poiché la lagrangiana non dipende esplicitamente da $x$, si ottiene: $$ \frac{\partial L}{\partial x} = 0, \qquad \frac{\partial L}{\partial \dot x} = \dot x. $$

L’equazione di Eulero--Lagrange diventa quindi: $$ \frac{d}{dt}(\dot x) = 0 \;\;\Rightarrow\;\; \ddot x = 0, $$ che descrive un moto rettilineo uniforme.

3. Risoluzione simbolica dell’equazione del moto¶

La funzione $$ \texttt{solve\_euler\_lagrange} $$ tenta di risolvere simbolicamente l’equazione di Eulero--Lagrange rispetto alla derivata di ordine più alto, cioè l’accelerazione.

Nel caso della particella libera, il risultato è: $$ \ddot x = 0, $$ che conferma la coerenza tra formalismo lagrangiano e seconda legge di Newton.

4. Estensione a lagrangiane con derivate di ordine superiore¶

L’ultima parte dell’esercizio introduce una lagrangiana che dipende dalla seconda derivata della funzione incognita: $$ L = \frac{1}{2}\ddot y^2 - \frac{1}{2}\dot y^2. $$

Questo tipo di lagrangiane compare, ad esempio, in teorie elastiche, modelli di campo efficaci o meccanica con rigidità.

Per lagrangiane del tipo $$ L = L(y, \dot y, \ddot y), $$ l’equazione di Eulero--Lagrange generalizzata è: $$ \frac{d^2}{dt^2} \left( \frac{\partial L}{\partial \ddot y} \right)

\frac{d}{dt} \left( \frac{\partial L}{\partial \dot y} \right)

\frac{\partial L}{\partial y} = 0. $$

La funzione $higher\_order\_euler\_lagrange$ implementa esattamente questa formula e restituisce l’equazione differenziale del moto associata alla lagrangiana data.

5. Significato complessivo¶

Nel complesso, l’esercizio mostra come:

  • il formalismo variazionale possa essere automatizzato simbolicamente;
  • le equazioni di Eulero--Lagrange emergano direttamente dalla struttura della lagrangiana;
  • il metodo si estenda naturalmente a teorie con derivate di ordine superiore;
  • la meccanica classica possa essere vista come un problema di estremizzazione funzionale.

L’esercizio costituisce quindi un ponte concreto tra teoria analitica e implementazione computazionale.

Symbolic Approach¶

In [3]:
import sympy as sp 

def euler_lagrange(L, vars, var_derivatives):
    """
    Derive the Euler-Lagrange equation for a given Lagrangian L.
        :param L: Lagrangian function.
        :param vars: List of generalized coordinates.
        :param var_derivatives: List of derivatives of the generalized
         coordinates.
    :return: 
        Euler—Lagrange equations.
    """
    euler_eqs = []
    for i in range(len(vars)):
        # Calculate partial derivatives
        dL_dvar = sp.diff(L, vars[i])
        dL_dvar_prime = sp.diff(L, var_derivatives[i])
        # Calculate total derivative
        d_dt_dL_dvar_prime = sp.diff(dL_dvar_prime, t)
        # Euler-Lagrange equation
        euler_eqs.append(d_dt_dL_dvar_prime - dL_dvar)
    return euler_eqs 

def solve_euler_lagrange(euler_eq, vars, var_derivatives):
    """
    Solve the Euler-Lagrange equation symbolically.
        :param euler_eq: Euler-Lagrange equation.
        :param vars: List of generalized coordinates.
        :param var_derivatives: List of derivatives of the generalized
         coordinates.
    :return: 
        Solutions of the Euler-Lagrange equation.
    """
    solutions = sp.solve(euler_eq, var_derivatives[-1] ) # Solve the last derivative
    return solutions 

# Define symbols
t = sp.symbols('t')
x = sp.Function('x')(t)
v = sp.diff(x, t) # Define Lagrangian for a free particle
L = (1/2) * v**2 # Simplified example where L = T = (l/2)mv^2, m=1
# Derive Euler—Lagrange equation
euler_eqs = euler_lagrange(L, [x], [v])
print("Euler-Lagrange Equations:", euler_eqs)
# Solve Euler-Lagrange equation
solution = solve_euler_lagrange(euler_eqs [0], [x], [sp.diff(x, t, t)])
print("Solutions", solution)
# Higher-order variations (e.g., y'')
y = sp.Function('y')(t)
y_prime = sp.diff(y, t)
y_double_prime = sp.diff(y_prime, t)

def higher_order_euler_lagrange(L, y, y_prime, y_double_prime):
    """
    Calculate higher order Eulei—Lagrange equation.
        :param L: Lagrangian function with higher order derivatives.
        :param y: Original function.
        :param y_prime: First derivative.
        :param y_double_prime: Second derivative.
    :retum: 
        Higher order Euler-Lagrange equation.
    """
    dL_dy = sp.diff(L, y)
    dL_dy_prime = sp.diff(L, y_prime)
    dL_dy_double_prime = sp.diff(L, y_double_prime)
    d_dt_dL_dy_prime = sp.diff(dL_dy_prime, t)
    d_dt_dL_dy_double_prime = sp.diff(dL_dy_double_prime, t, 2)
    higher_order_eq = d_dt_dL_dy_double_prime - d_dt_dL_dy_prime + dL_dy
    return higher_order_eq

# Example higher-order Lagrangian
L_higher = y_double_prime**2 / 2 - y_prime**2 / 2
higher_order_eq = higher_order_euler_lagrange(L_higher, y, y_prime, y_double_prime)
print("Higher Order Euler-Lagrange Equation:", higher_order_eq)
Euler-Lagrange Equations: [1.0*Derivative(x(t), (t, 2))]
Solutions [0.0]
Higher Order Euler-Lagrange Equation: Derivative(y(t), (t, 2)) + Derivative(y(t), (t, 4))
In [ ]: