Chapter 19
20 min read
Section 165 of 353

The Fundamental Theorem for Line Integrals

Vector Calculus

Learning Objectives

By the end of this section, you will be able to:

  1. Identify conservative vector fields and understand the condition Py=Qx\frac{\partial P}{\partial y} = \frac{\partial Q}{\partial x} for path independence
  2. Find potential functions for conservative fields by integrating component functions
  3. Apply the Fundamental Theorem for Line Integrals to evaluate Cfdr=f(B)f(A)\int_C \nabla f \cdot d\mathbf{r} = f(B) - f(A) using only endpoint values
  4. Explain path independence and its connection to conservation laws in physics
  5. Connect these ideas to optimization and understand why gradient descent finds minima regardless of the path taken
Why This Matters: The Fundamental Theorem for Line Integrals is the multivariable generalization of the Fundamental Theorem of Calculus. Just as abf(x)dx=f(b)f(a)\int_a^b f'(x) \, dx = f(b) - f(a) tells us that the integral of a derivative depends only on boundary values, the line integral theorem tells us that the integral of a gradient depends only on endpoints—not on the path between them. This principle underlies conservation of energy in physics and path-independence in optimization.

The Big Picture

In the previous sections, we learned to compute line integrals CFdr\int_C \mathbf{F} \cdot d\mathbf{r} by parametrizing the curve CC and integrating. This works but raises a natural question: does the answer depend on which path we take between two points?

For most vector fields, yes—different paths give different integrals. But for a special class called conservative vector fields, something remarkable happens: the integral depends only on where you start and where you end, not on how you get there. This is called path independence.

The Central Insight: A vector field F\mathbf{F} is conservative if and only if it can be written as the gradient of some scalar function: F=f\mathbf{F} = \nabla f. This scalar function ff is called the potential function. When such a potential exists, the line integral from AA to BB along any path is simply f(B)f(A)f(B) - f(A).

Historical Origins

The connection between path independence and potential functions emerged from the study of gravitational and electrical forces in the 18th and 19th centuries:

  • Pierre-Simon Laplace (1749-1827) and Joseph-Louis Lagrange (1736-1813) developed the concept of gravitational potential, showing that gravitational force is the negative gradient of a potential energy function
  • George Green (1793-1841) introduced the term "potential" and developed Green's theorem, which connects line integrals to area integrals
  • Lord Kelvin (William Thomson, 1824-1907) applied potential theory to electrostatics and thermodynamics, showing how path independence leads to conservation laws

The physical insight was profound: when lifting a rock from one height to another, the work done depends only on the height difference—not on whether you lift it straight up, carry it diagonally, or take a winding path. Gravity is a conservative force.


Conservative Vector Fields

Definition and Examples

A vector field F\mathbf{F} is called conservative (or gradient field) if there exists a scalar function ff such that:

F=f=(fx,fy)\mathbf{F} = \nabla f = \left( \frac{\partial f}{\partial x}, \frac{\partial f}{\partial y} \right)

The function ff is called the potential function (or scalar potential) of F\mathbf{F}.

Vector Field FConservative?Potential Function f
(2x,2y)(2x, 2y)Yesf(x,y)=x2+y2f(x,y) = x^2 + y^2
(y,x)(y, x)Yesf(x,y)=xyf(x,y) = xy
(y,x)(y, -x)NoNone exists
(y,x)(-y, x)NoNone exists (rotational field)
(2xy,x2)(2xy, x^2)Yesf(x,y)=x2yf(x,y) = x^2 y
(excos(y),exsin(y))(e^x \cos(y), -e^x \sin(y))Yesf(x,y)=excos(y)f(x,y) = e^x \cos(y)

Testing for Conservative Fields

How can we tell if a field is conservative without guessing a potential function? For a field F=(P,Q)\mathbf{F} = (P, Q) defined on a simply connected region:

F is conservativePy=Qx\mathbf{F} \text{ is conservative} \quad \Longleftrightarrow \quad \frac{\partial P}{\partial y} = \frac{\partial Q}{\partial x}

Simply Connected: A region is simply connected if it has no "holes." Intuitively, any closed curve in the region can be continuously shrunk to a point without leaving the region. The entire plane is simply connected; the plane minus the origin is not.

Example: Is F=(2xy+3,x22y)\mathbf{F} = (2xy + 3, x^2 - 2y) conservative?

Here P=2xy+3P = 2xy + 3 and Q=x22yQ = x^2 - 2y.

  • Py=2x\frac{\partial P}{\partial y} = 2x
  • Qx=2x\frac{\partial Q}{\partial x} = 2x

Since Py=Qx\frac{\partial P}{\partial y} = \frac{\partial Q}{\partial x}, the field is conservative!

The Importance of Simple Connectivity: The field F=(yx2+y2,xx2+y2)\mathbf{F} = \left( \frac{-y}{x^2+y^2}, \frac{x}{x^2+y^2} \right) satisfies Py=Qx\frac{\partial P}{\partial y} = \frac{\partial Q}{\partial x} everywhere it is defined, yet integrating around a circle centered at the origin gives 2π2\pi, not zero! The problem is that the field is undefined at the origin, creating a "hole" that breaks simple connectivity.

Potential Functions

Finding Potential Functions

Once we know a field is conservative, how do we find its potential function? The method is systematic:

Example: Find the potential for F=(2xy+3,x22y)\mathbf{F} = (2xy + 3, x^2 - 2y)

  1. Step 1: Integrate P=2xy+3P = 2xy + 3 with respect to xx:

    f(x,y)=(2xy+3)dx=x2y+3x+g(y)f(x, y) = \int (2xy + 3) \, dx = x^2 y + 3x + g(y)

    The "constant" of integration g(y)g(y) may depend on yy.
  2. Step 2: Differentiate with respect to yy and set equal to QQ:

    fy=x2+g(y)=x22y\frac{\partial f}{\partial y} = x^2 + g'(y) = x^2 - 2y

  3. Step 3: Solve for g(y)g'(y):

    g(y)=2yg(y)=y2+Cg'(y) = -2y \quad \Rightarrow \quad g(y) = -y^2 + C

  4. Result: f(x,y)=x2y+3xy2+Cf(x, y) = x^2 y + 3x - y^2 + C
Verification: You can always check your answer by computing f\nabla f and verifying it equals F\mathbf{F}. Here: f=(2xy+3,x22y)=F\nabla f = (2xy + 3, x^2 - 2y) = \mathbf{F}

Interactive: Potential Function Explorer

Explore different potential functions and see how the gradient field (shown as arrows) relates to the surface. Notice that gradient arrows always point "uphill" on the potential surface:

Loading 3D visualization...

The Fundamental Theorem

Statement of the Theorem

Now we state the central result that connects conservative fields, potential functions, and line integrals:

The Fundamental Theorem for Line Integrals: Let CC be a smooth curve given by r(t)\mathbf{r}(t), atba \leq t \leq b, and let ff be a differentiable function whose gradient f\nabla f is continuous on CC. Then:
Cfdr=f(r(b))f(r(a))=f(B)f(A)\int_C \nabla f \cdot d\mathbf{r} = f(\mathbf{r}(b)) - f(\mathbf{r}(a)) = f(B) - f(A)
where A=r(a)A = \mathbf{r}(a) is the starting point and B=r(b)B = \mathbf{r}(b) is the ending point.

This is the multivariable analog of the Fundamental Theorem of Calculus. Just as abf(x)dx=f(b)f(a)\int_a^b f'(x) \, dx = f(b) - f(a), the line integral of a gradient depends only on the values at the endpoints.

Proof Sketch

The proof uses the chain rule. If r(t)=(x(t),y(t))\mathbf{r}(t) = (x(t), y(t)), then:

Cfdr=abf(r(t))r(t)dt\int_C \nabla f \cdot d\mathbf{r} = \int_a^b \nabla f(\mathbf{r}(t)) \cdot \mathbf{r}'(t) \, dt

By the chain rule, the integrand is exactly:

fr(t)=fxdxdt+fydydt=ddt[f(r(t))]\nabla f \cdot \mathbf{r}'(t) = \frac{\partial f}{\partial x}\frac{dx}{dt} + \frac{\partial f}{\partial y}\frac{dy}{dt} = \frac{d}{dt}[f(\mathbf{r}(t))]

So the integral becomes:

abddt[f(r(t))]dt=f(r(b))f(r(a))\int_a^b \frac{d}{dt}[f(\mathbf{r}(t))] \, dt = f(\mathbf{r}(b)) - f(\mathbf{r}(a))


Worked Example: The Easy Way vs The Hard Way

Words are persuasive, but a single example does more than a paragraph of theory. We will compute the same line integral two ways: once by parametrizing the curve and grinding through the integral, once by reading the potential at the endpoints. They must agree, and we will see exactly how much labor the Fundamental Theorem saves.

The setup. Let F(x,y)=(2xy+3,  x22y)\mathbf{F}(x,y) = (2xy + 3,\; x^2 - 2y). We already showed this field is conservative with potential f(x,y)=x2y+3xy2f(x,y) = x^2 y + 3x - y^2. Compute CFdr\int_C \mathbf{F} \cdot d\mathbf{r} along the curved path r(t)=(1+t,  3t2)\mathbf{r}(t) = (1+t,\; 3t^2) for 0t10 \leq t \leq 1.

Notice the endpoints: r(0)=(1,0)=A\mathbf{r}(0) = (1, 0) = A and r(1)=(2,3)=B\mathbf{r}(1) = (2, 3) = B.

Expand: step-by-step hand calculation (try it yourself first!)

Method 1 — The hard way (direct parametrization). Substitute the path into the field and integrate.

  1. Velocity vector. Differentiate the path: x(t)=1+tx(t)=1x(t) = 1 + t \Rightarrow x'(t) = 1, and y(t)=3t2y(t)=6ty(t) = 3t^2 \Rightarrow y'(t) = 6t. So r(t)=(1,  6t)\mathbf{r}'(t) = (1,\; 6t).
  2. Field on the path. Substitute x=1+t,  y=3t2x = 1+t,\; y = 3t^2 into F=(P,Q)\mathbf{F} = (P, Q):
    • P=2(1+t)(3t2)+3=6t2+6t3+3P = 2(1+t)(3t^2) + 3 = 6t^2 + 6t^3 + 3
    • Q=(1+t)22(3t2)=1+2t+t26t2=1+2t5t2Q = (1+t)^2 - 2(3t^2) = 1 + 2t + t^2 - 6t^2 = 1 + 2t - 5t^2
  3. Dot product. Form F(r(t))r(t)=P1+Q6t\mathbf{F}(\mathbf{r}(t)) \cdot \mathbf{r}'(t) = P \cdot 1 + Q \cdot 6t:
    (6t2+6t3+3)+6t(1+2t5t2)=3+6t+18t224t3(6t^2 + 6t^3 + 3) + 6t(1 + 2t - 5t^2) = 3 + 6t + 18t^2 - 24t^3

    Sanity check at t=12t = \tfrac{1}{2}: 3+3+4.53=7.53 + 3 + 4.5 - 3 = 7.5. (You can verify this matches the direct substitution.)

  4. Integrate term-by-term.
    01(3+6t+18t224t3)dt=[3t+3t2+6t36t4]01\int_0^1 (3 + 6t + 18t^2 - 24t^3)\, dt = \Big[3t + 3t^2 + 6t^3 - 6t^4\Big]_0^1
    =3+3+66=6= 3 + 3 + 6 - 6 = 6

Method 2 — The easy way (Fundamental Theorem). No path, no parametrization, no integration. Just evaluate the potential.

  1. At the endpoint B=(2,3)B = (2, 3): f(2,3)=(2)2(3)+3(2)(3)2=12+69=9f(2, 3) = (2)^2(3) + 3(2) - (3)^2 = 12 + 6 - 9 = 9.
  2. At the starting point A=(1,0)A = (1, 0): f(1,0)=(1)2(0)+3(1)(0)2=0+30=3f(1, 0) = (1)^2(0) + 3(1) - (0)^2 = 0 + 3 - 0 = 3.
  3. Subtract: CFdr=f(B)f(A)=93=6\int_C \mathbf{F} \cdot d\mathbf{r} = f(B) - f(A) = 9 - 3 = 6. ✓
Both methods give 6. The hard way required differentiating the path, substituting into the field, expanding a cubic polynomial, and antidifferentiating four terms. The easy way required two evaluations of a polynomial. That is the power of the Fundamental Theorem — and it would have worked for any path from AA to BB, no matter how twisted, because the field is conservative.

Try this yourself: Replace the curved path with the straight line r(t)=(1+t,3t)\mathbf{r}(t) = (1+t, 3t) and redo the hard way. The integrand becomes a different polynomial, but you will still land on 66. The endpoints did not change, so the answer did not change.


Path Independence

An immediate consequence of the Fundamental Theorem is that line integrals of conservative fields are path-independent. If F=f\mathbf{F} = \nabla f, then CFdr\int_C \mathbf{F} \cdot d\mathbf{r} has the same value for any curve CC connecting AA to BB.

This is remarkable: whether you take a straight line, a curved arc, or a winding path, you get the same answer! The work done against a conservative force depends only on position change, not on the path taken.

Interactive: Path Independence Demo

Select different paths between the same endpoints and compare their line integral values. For the conservative field, all paths give the same result. For the non-conservative field, different paths give different values:

Loading visualization...

Closed Curves and Circulation

Another key consequence: for a conservative field, the line integral around any closed curve is zero. If CC is a closed curve (starting and ending at the same point AA), then:

Cfdr=f(A)f(A)=0\oint_C \nabla f \cdot d\mathbf{r} = f(A) - f(A) = 0

This gives us an equivalent characterization of conservative fields:

F is conservativeCFdr=0 for all closed curves C\mathbf{F} \text{ is conservative} \quad \Longleftrightarrow \quad \oint_C \mathbf{F} \cdot d\mathbf{r} = 0 \text{ for all closed curves } C

Physical Interpretation: The quantity CFdr\oint_C \mathbf{F} \cdot d\mathbf{r} is called the circulation of F\mathbf{F} around CC. Conservative fields have zero circulation around every closed curve—there is no net "swirl" or rotation in the field.

Real-World Applications

Physics: Work and Energy

In physics, when a force F\mathbf{F} moves an object along a path CC, the work done is W=CFdrW = \int_C \mathbf{F} \cdot d\mathbf{r}.

For a conservative force (like gravity or an ideal spring):

  • The force is the negative gradient of potential energy: F=U\mathbf{F} = -\nabla U
  • Work done: W=CUdr=(U(B)U(A))=U(A)U(B)W = -\int_C \nabla U \cdot d\mathbf{r} = -(U(B) - U(A)) = U(A) - U(B)
  • Work equals the decrease in potential energy—independent of path!

This leads to conservation of mechanical energy: the total E=K+UE = K + U (kinetic + potential) remains constant for conservative forces.

Machine Learning: Optimization Landscapes

In machine learning, we minimize a loss function L(θ)L(\theta) over parameters θ\theta. The gradient L\nabla L points toward steeper increase, so we move opposite to it:

θnew=θoldαL\theta_{\text{new}} = \theta_{\text{old}} - \alpha \nabla L

The Fundamental Theorem tells us that the "work" done against the gradient field—the change in loss—depends only on where we start and end. The path through parameter space doesn't matter for the total change in LL:

CLdθ=L(θend)L(θstart)\int_C \nabla L \cdot d\theta = L(\theta_{\text{end}}) - L(\theta_{\text{start}})

Why This Matters for Optimization: If the loss function is smooth and well-behaved, the specific optimization path (SGD vs. Adam vs. gradient descent) affects only the speed of convergence, not the ability to reduce the loss. The endpoint matters, not the journey—at least in principle. Real neural network loss landscapes are complex, but this theorem provides foundational intuition.

Interactive: Conservative Field Explorer

Explore different vector fields and compute line integrals between points you choose. Compare the numerical integral with the theoretical value f(B)f(A)f(B) - f(A) from the potential function:

Loading visualization...

Python Implementation

Here's a complete implementation for testing conservative fields, finding potential differences, and computing line integrals:

Conservative Fields and Line Integrals
🐍python
1Imports

Import NumPy for numerical computations and matplotlib for visualization.

4Conservative Field Test

Tests if a 2D field is conservative by checking if ∂P/∂y = ∂Q/∂x, which is equivalent to the curl being zero. This is the necessary condition for a potential function to exist.

30Numerical Potential Difference

Finds the potential difference f(B) - f(A) by computing the line integral along a straight path. For conservative fields, this equals the line integral along ANY path between the points.

61Curved Path Line Integral

Computes line integrals along arbitrary parametric curves using the formula ∫_C F · dr = ∫ F(r(t)) · r'(t) dt. This allows us to test path independence.

94Example Functions

Defines a conservative field F = (2x, 2y) = ∇(x² + y²) with known potential, and a non-conservative field F = (-y, x) with non-zero curl.

105Conservative Field Test

Verifies that F = (2x, 2y) is conservative (curl ≈ 0) and confirms the Fundamental Theorem by showing that the line integral equals the potential difference.

120Non-Conservative Demonstration

Shows that for the non-conservative field F = (-y, x), different paths give different integral values, proving path dependence and confirming the field is not conservative.

156 lines without explanation
1import numpy as np
2import matplotlib.pyplot as plt
3from scipy.integrate import quad_vec
4
5def is_conservative_2d(P, Q, x, y, h=1e-7):
6    """
7    Test if a 2D vector field F = (P, Q) is conservative.
8
9    For F to be conservative: ∂P/∂y = ∂Q/∂x
10
11    This is equivalent to curl(F) = 0 in 2D.
12
13    Args:
14        P, Q: Component functions of the vector field
15        x, y: Point to test
16        h: Step size for numerical differentiation
17
18    Returns:
19        is_conservative: Boolean
20        curl_value: The curl value (should be ~0 if conservative)
21    """
22    # Compute ∂P/∂y numerically
23    dP_dy = (P(x, y + h) - P(x, y - h)) / (2 * h)
24
25    # Compute ∂Q/∂x numerically
26    dQ_dx = (Q(x + h, y) - Q(x - h, y)) / (2 * h)
27
28    # Curl in 2D: ∂Q/∂x - ∂P/∂y
29    curl = dQ_dx - dP_dy
30
31    is_conservative = abs(curl) < 1e-5
32    return is_conservative, curl
33
34def find_potential_numerical(F, start, end, num_points=100):
35    """
36    Find potential difference using line integral.
37
38    For conservative F = ∇f:
39    f(end) - f(start) = ∫_C F · dr
40
41    This works because line integrals are path-independent
42    for conservative fields.
43
44    Args:
45        F: Vector field function F(x, y) -> (Fx, Fy)
46        start: Starting point (x0, y0)
47        end: Ending point (x1, y1)
48        num_points: Number of points for integration
49
50    Returns:
51        potential_difference: f(end) - f(start)
52    """
53    # Parametrize straight line: r(t) = start + t*(end - start)
54    t_values = np.linspace(0, 1, num_points)
55    dt = 1 / (num_points - 1)
56
57    integral = 0
58    for i in range(len(t_values) - 1):
59        t = (t_values[i] + t_values[i+1]) / 2
60
61        # Position on curve
62        x = start[0] + t * (end[0] - start[0])
63        y = start[1] + t * (end[1] - start[1])
64
65        # Velocity (tangent) vector dr/dt
66        dx = end[0] - start[0]
67        dy = end[1] - start[1]
68
69        # Vector field at this point
70        Fx, Fy = F(x, y)
71
72        # F · dr = F · (dr/dt) dt
73        integral += (Fx * dx + Fy * dy) * dt
74
75    return integral
76
77def line_integral_curved(F, parametric_curve, t_start=0, t_end=1, n=200):
78    """
79    Compute line integral along a parametric curve.
80
81    ∫_C F · dr = ∫_a^b F(r(t)) · r'(t) dt
82
83    Args:
84        F: Vector field F(x, y) -> (Fx, Fy)
85        parametric_curve: Function r(t) -> (x(t), y(t))
86        t_start, t_end: Parameter bounds
87        n: Number of integration points
88
89    Returns:
90        integral_value: The line integral
91    """
92    dt = (t_end - t_start) / n
93    integral = 0
94
95    for i in range(n):
96        t = t_start + (i + 0.5) * dt
97
98        # Position
99        x, y = parametric_curve(t)
100
101        # Velocity (numerical derivative)
102        h = dt / 10
103        x1, y1 = parametric_curve(t - h)
104        x2, y2 = parametric_curve(t + h)
105        dx_dt = (x2 - x1) / (2 * h)
106        dy_dt = (y2 - y1) / (2 * h)
107
108        # Field value
109        Fx, Fy = F(x, y)
110
111        # Integrate F · r'(t) dt
112        integral += (Fx * dx_dt + Fy * dy_dt) * dt
113
114    return integral
115
116# Example: Conservative field F = (2x, 2y) = ∇(x² + y²)
117def F_conservative(x, y):
118    return 2*x, 2*y
119
120def potential_f(x, y):
121    return x**2 + y**2
122
123# Example: Non-conservative field F = (-y, x)
124def F_non_conservative(x, y):
125    return -y, x
126
127# Test conservative field
128print("=== Conservative Field F = (2x, 2y) ===")
129is_cons, curl = is_conservative_2d(
130    lambda x, y: 2*x,
131    lambda x, y: 2*y,
132    1.0, 1.0
133)
134print(f"Is conservative: {is_cons}, curl = {curl:.6f}")
135
136# Compute potential difference via line integral
137start, end = (0, 0), (2, 3)
138integral_value = find_potential_numerical(F_conservative, start, end)
139theoretical = potential_f(*end) - potential_f(*start)
140print(f"Line integral from {start} to {end}: {integral_value:.4f}")
141print(f"Theoretical f(B) - f(A): {theoretical:.4f}")
142
143# Test non-conservative field
144print("\n=== Non-Conservative Field F = (-y, x) ===")
145is_cons, curl = is_conservative_2d(
146    lambda x, y: -y,
147    lambda x, y: x,
148    1.0, 1.0
149)
150print(f"Is conservative: {is_cons}, curl = {curl:.6f}")
151
152# Path 1: Straight line
153path1 = lambda t: (2*t, 3*t)
154integral_straight = line_integral_curved(F_non_conservative, path1)
155
156# Path 2: Curved path (arc)
157path2 = lambda t: (2*t, 3*t + 2*np.sin(np.pi*t))
158integral_curved = line_integral_curved(F_non_conservative, path2)
159
160print(f"Straight path integral: {integral_straight:.4f}")
161print(f"Curved path integral: {integral_curved:.4f}")
162print(f"Difference: {abs(integral_straight - integral_curved):.4f}")
163print("→ Different values prove the field is NOT conservative!")

PyTorch: Conservative Fields via Autograd

Plain Python made the mechanics concrete. PyTorch makes them automatic. Every conservative field comes from a scalar potential ff, and PyTorch's autograd engine is, at its core, a machine for computing gradients of scalars. So we can manufacture a conservative field on demand: write down a potential, and let autograd produce F=f\mathbf{F} = \nabla f for us.

The same machinery that trains neural networks — reverse-mode automatic differentiation — is doing a calculus exercise here. The connection is not metaphorical: gradient descent in optimization is integrating a conservative field (the gradient of the loss), and the Fundamental Theorem is the reason the total descent depends only on the start and end loss values.

What we will show below: compute the line integral three ways — once via FTLI (just evaluate the potential at the endpoints), once along the curved path r(t)=(1+t,3t2)\mathbf{r}(t) = (1+t, 3t^2), once along the straight line — and watch all three answers land on 6.06.0.
FTLI in PyTorch: Potential → Field → Line Integral
🐍python
1Import PyTorch

PyTorch's headline feature is its autograd engine: every tensor with requires_grad=True remembers the operations performed on it, so we can ask for the gradient of any scalar output with respect to any input. We will use this to manufacture a conservative field directly from a potential — no partial derivatives by hand.

4Define the scalar potential f(x, y) = x²y + 3x − y²

This is the same potential we worked out by hand earlier. We accept a tensor p of shape (..., 2) so the function can be called on a single point ([2.0, 3.0]) or on a batch of points (shape (n, 2)) without modification. The trailing ... in p[..., 0] means 'all leading dimensions, then index 0' — it makes the function batch-friendly.

EXECUTION STATE
p (single) = tensor([1.0, 0.0])
x = 1.0
y = 0.0
f(1, 0) = 1²·0 + 3·1 − 0² = 3.0
12⬇ input: endpoint A = (1, 0)

Start of our line integral. Just a plain tensor — no requires_grad needed yet because we only evaluate f here, we do not differentiate through A.

EXECUTION STATE
A = tensor([1.0, 0.0])
13⬇ input: endpoint B = (2, 3)

The other endpoint. The Fundamental Theorem says the line integral of ∇f along ANY path from A to B equals f(B) − f(A). Whether the path is a straight line, a corkscrew, or a single point oscillating between A and B, the answer is the same number.

EXECUTION STATE
B = tensor([2.0, 3.0])
16📚 FTLI shortcut — the entire theorem in one line

Evaluate the potential at both endpoints and subtract. Notice we never wrote down the field F, never parametrized a curve, never integrated. f(B) = 12 + 6 − 9 = 9 and f(A) = 0 + 3 − 0 = 3, so ftli_value = 6.0. This is what makes FTLI such a powerful labor-saver: the moment you find the potential, the integral is trivial.

EXECUTION STATE
potential(B) = tensor(9.0)
potential(A) = tensor(3.0)
ftli_value = tensor(6.0)
17.item() — extract a Python float from a 0-d tensor

A PyTorch tensor with a single value still prints as 'tensor(6.0)'. .item() unwraps it into the plain Python float 6.0 so we can format it with f-strings. Useful for printing and for unit tests; do NOT use inside training loops because it forces a CPU sync.

20📚 conservative_F — turn a potential into a vector field via autograd

Mathematically, F = ∇f. PyTorch's torch.autograd.grad is literally an automatic gradient calculator — give it a scalar and an input tensor, get back the gradient. So we can construct the conservative field on demand, at any point, without ever writing the partials by hand.

EXECUTION STATE
input p (example) = tensor([1.0, 2.0])
expected output = tensor([7.0, -3.0]) (i.e. (2xy+3, x²−2y) at (1,2))
25.clone().detach().requires_grad_(True)

Three little tricks at once: clone() makes a copy so we do not mutate the caller's tensor; detach() severs any prior autograd history so we start fresh; requires_grad_(True) tells PyTorch to track operations on this tensor so we can ask for its gradient later. The trailing underscore means 'modify in place'.

26Forward pass: compute f(p)

PyTorch silently builds a computational graph during this call. Each operation (squaring, multiplying, adding, subtracting) records itself so that grad can later walk backward through them via the chain rule.

EXECUTION STATE
val (at p=(1,2)) = tensor(1.0*2.0 + 3.0 - 4.0) = tensor(1.0)
27📚 torch.autograd.grad(val, p) — the heart of autograd

Computes ∂val/∂p using reverse-mode automatic differentiation. The first argument is the scalar output, the second is the tensor we want gradients with respect to. Returns a tuple — [0] picks the first (and only) gradient. This is the same chain-rule machinery that backpropagation uses in neural networks; here it is doing a calculus exercise.

EXECUTION STATE
grad at (1, 2) = tensor([7.0, -3.0]) — matches ∇f = (2xy+3, x²−2y)
31Sanity check the autograd field against the hand-derived F

Always test that autograd agrees with the closed-form gradient on a known point. We chose (1, 2) because the hand math is easy: 2·1·2 + 3 = 7 and 1² − 2·2 = −3. If PyTorch printed anything other than [7., -3.], something is wrong with the potential or the autograd setup.

36n = 500 — discretization resolution

We will approximate the continuous integral ∫_C F·dr by sampling 500 points along the curve and using the trapezoidal rule. More points → more accurate but slower. With smooth fields and curves, 500 is plenty; you would see ~6 decimal places of agreement with the exact answer.

EXECUTION STATE
n = 500
37📚 torch.linspace(0.0, 1.0, n) — evenly spaced parameter values

Returns a 1-D tensor of 500 numbers starting at 0.0, ending at 1.0, evenly spaced. So t[0]=0.0, t[1]=0.002004, …, t[499]=1.0. These are the t-values where we sample the path r(t).

EXECUTION STATE
t.shape = torch.Size([500])
t[0] = 0.0
t[-1] = 1.0
38x_curve = 1 + t — x-coordinates of the curved path

PyTorch broadcasts the scalar 1.0 across the tensor t, returning a tensor of the same shape with every element incremented by 1. Element-by-element: 1+0=1, 1+0.002=1.002, …, 1+1=2. So x_curve goes from 1 to 2 along the path.

EXECUTION STATE
x_curve[0] = 1.0
x_curve[-1] = 2.0
39y_curve = 3 t² — quadratic rise in y

Element-wise: 3·0²=0, 3·0.002²≈1.2e-5, …, 3·1²=3. So y_curve goes from 0 to 3, but quadratically — slow at the start, accelerating near the end. This is what makes the path 'curved' rather than a straight line.

EXECUTION STATE
y_curve[0] = 0.0
y_curve[-1] = 3.0
42📚 torch.stack([ones_like(t), 6*t], dim=-1) — assemble r'(t)

We need a tensor of tangent vectors, one per sample. r'(t) = (1, 6t), so the x-component is the constant 1 (a tensor of ones the same shape as t) and the y-component is 6t. torch.stack with dim=-1 joins them along a new last axis, producing shape (500, 2): row i is [1, 6·t[i]].

EXECUTION STATE
drdt_curve.shape = torch.Size([500, 2])
drdt_curve[0] = tensor([1.0, 0.0]) (since 6·0 = 0)
drdt_curve[-1] = tensor([1.0, 6.0]) (since 6·1 = 6)
45points_curve = stack([x_curve, y_curve]) — sampled curve points

Same stacking trick, but for the path itself. Row i is the 2-D point (x_curve[i], y_curve[i]). This will feed into conservative_F to evaluate the field at every sample.

EXECUTION STATE
points_curve.shape = torch.Size([500, 2])
points_curve[0] = tensor([1.0, 0.0]) = A
points_curve[-1] = tensor([2.0, 3.0]) = B
46List comprehension over points — sample F along the curve

We call conservative_F once per sampled point and stack the results. The reason for the per-point loop: conservative_F uses .clone().detach().requires_grad_(True), which is a per-tensor operation that does not vectorize for free over an extra leading batch dim. There is a vectorized version using torch.func.grad or vmap, but the loop is clearer for pedagogy.

EXECUTION STATE
F_curve.shape = torch.Size([500, 2])
F_curve[0] = F(1, 0) = tensor([3.0, 1.0])
F_curve[-1] = F(2, 3) = tensor([15.0, -2.0])
49(F_curve * drdt_curve).sum(dim=-1) — pointwise dot products

Element-wise multiply gives a (500, 2) tensor whose rows are (F_x·dx/dt, F_y·dy/dt). Summing along dim=-1 collapses each row to its dot product F · r', leaving a 1-D tensor of length 500 — the integrand sampled at every t.

EXECUTION STATE
integrand_curve.shape = torch.Size([500])
integrand_curve[0] = F(1,0)·(1,0) = 3·1 + 1·0 = 3.0
52📚 torch.trapz(integrand, t) — trapezoidal integration

Approximates ∫₀¹ integrand(t) dt using the trapezoidal rule on irregularly- or regularly-spaced samples. Internally: sum 0.5·(y_i + y_{i+1})·(t_{i+1} − t_i). With n=500 and a smooth integrand we get ~6 decimal places of accuracy.

EXECUTION STATE
integral_curve = tensor(6.0000)
56Now a DIFFERENT path: the straight line from A to B

Same A, same B, but a totally different curve in between: x_line goes 1→2 linearly, y_line goes 0→3 linearly. The Fundamental Theorem guarantees this must give the same answer as the curved path. We will check.

57y_line = 3 t — linear ramp instead of quadratic

Same endpoints as y_curve (0 and 3) but a straight ramp. Notice how a single character change (t → t**2) is the difference between a curved and a straight path. The whole point of the theorem is that this change is invisible to the line integral when the field is conservative.

61drdt_line = (1, 3) — constant tangent

For the straight line r(t)=(1+t, 3t), the tangent r'(t)=(1, 3) is constant — no t dependence at all. Compare to the curved path where r'(t)=(1, 6t) varied with t.

62integral_line — the SAME number, computed a totally different way

If everything works, integral_line ≈ 6.0000 just like integral_curve. Different integrand, different geometry, same answer — that IS path independence, made visible.

EXECUTION STATE
integral_line = tensor(6.0000)
66📚 torch.allclose — numerical equality check with tolerance

Returns True iff every element of the two tensors agree to within atol (absolute tolerance) and rtol (relative tolerance, default 1e-5). We use atol=1e-2 because the trapezoidal rule introduces tiny discretization error. The two integrals will not match to machine precision but they will agree well within a hundredth.

68⬆ return: the three-way agreement IS the Fundamental Theorem

All three numbers — ftli_value, integral_curve, integral_line — equal 6.0 (up to trapezoid error). The first was computed by evaluating a potential at two points. The other two were computed by integrating along two different curves. The fact that they all agree is not a coincidence; it is exactly what the Fundamental Theorem for Line Integrals predicts whenever F = ∇f.

41 lines without explanation
1import torch
2
3# ── 1. Define the SCALAR potential f(x, y) = x²y + 3x − y² ──────────────
4def potential(p):
5    """
6    p has shape (..., 2) with p[..., 0] = x, p[..., 1] = y.
7    Returns a scalar (or tensor of scalars) f(x, y).
8    """
9    x, y = p[..., 0], p[..., 1]
10    return x**2 * y + 3*x - y**2
11
12# Endpoints A = (1, 0) and B = (2, 3).
13A = torch.tensor([1.0, 0.0])
14B = torch.tensor([2.0, 3.0])
15
16# ── 2. FTLI — the "easy way" in three lines ─────────────────────────────
17ftli_value = potential(B) - potential(A)
18print(f"f(B) - f(A) = {ftli_value.item():.4f}")  # 6.0000
19
20# ── 3. Build the CONSERVATIVE field F = ∇f using autograd ───────────────
21def conservative_F(p):
22    """
23    Return the gradient of f at point p. Because F = ∇f, this IS the
24    conservative vector field — no hand-derivation of partials needed.
25    """
26    p = p.clone().detach().requires_grad_(True)
27    val = potential(p)
28    grad = torch.autograd.grad(val, p)[0]
29    return grad
30
31# Sanity check at (1, 2): ∂f/∂x = 2xy+3 = 7,  ∂f/∂y = x²−2y = −3.
32print("F(1, 2) =", conservative_F(torch.tensor([1.0, 2.0])))
33# tensor([ 7., -3.])
34
35# ── 4. Direct line integral along a CURVED path r(t) = (1+t, 3t²) ───────
36n = 500
37t = torch.linspace(0.0, 1.0, n)
38x_curve = 1.0 + t
39y_curve = 3.0 * t**2
40
41# Tangent r'(t) = (1, 6t).
42drdt_curve = torch.stack([torch.ones_like(t), 6.0 * t], dim=-1)
43
44# Sample F along the path one point at a time.
45points_curve = torch.stack([x_curve, y_curve], dim=-1)
46F_curve = torch.stack([conservative_F(p) for p in points_curve])
47
48# Integrand: F(r(t)) · r'(t)
49integrand_curve = (F_curve * drdt_curve).sum(dim=-1)
50
51# Trapezoidal rule on [0, 1]
52integral_curve = torch.trapz(integrand_curve, t)
53print(f"Curved path ∫ ∇f·dr = {integral_curve.item():.4f}")  # ≈6.0000
54
55# ── 5. Same endpoints, different path: STRAIGHT line r(t) = (1+t, 3t) ───
56x_line = 1.0 + t
57y_line = 3.0 * t
58points_line = torch.stack([x_line, y_line], dim=-1)
59F_line = torch.stack([conservative_F(p) for p in points_line])
60drdt_line = torch.stack([torch.ones_like(t), 3.0 * torch.ones_like(t)], dim=-1)
61integral_line = torch.trapz((F_line * drdt_line).sum(dim=-1), t)
62print(f"Straight path ∫ ∇f·dr = {integral_line.item():.4f}")  # ≈6.0000
63
64# ── 6. All three numbers must agree — that IS path independence ─────────
65print("\nPath independence ✓" if torch.allclose(
66    integral_curve, ftli_value, atol=1e-2
67) else "MISMATCH ✗")
Connection to deep learning. When you train a neural network with gradient descent, the trajectory of parameters in weight space is exactly a path through a vector field — the gradient of the loss. Because that field is the gradient of a scalar (the loss itself), it is conservative by construction. The Fundamental Theorem then tells you that the change in loss between two points in weight space depends only on those two points, not on the optimization path. Different optimizers (SGD, Adam, RMSProp) take different paths but cannot improve beyond what the loss landscape allows.

Common Pitfalls

PitfallIssueSolution
Forgetting simple connectivityTesting Py=Qx\frac{\partial P}{\partial y} = \frac{\partial Q}{\partial x} on a non-simply connected domainCheck that the domain has no holes; if it does, the test may fail
Wrong order of subtractionComputing f(A)f(B)f(A) - f(B) instead of f(B)f(A)f(B) - f(A)Remember: endpoint value minus starting point value
Non-unit vectorsUsing direction vectors that aren't normalizedFor directional derivatives, normalize; for line integrals, the parametrization handles length
Assuming all fields are conservativeApplying FTLI to non-conservative fieldsAlways test Py=Qx\frac{\partial P}{\partial y} = \frac{\partial Q}{\partial x} before assuming path independence
Forgetting the constantFinding ff without the arbitrary constant CCPotential functions are unique only up to a constant; f+Cf + C works equally well

Test Your Understanding


Summary

The Fundamental Theorem for Line Integrals is one of the most important results in vector calculus. Here are the key takeaways:

  1. Conservative Fields: A field F\mathbf{F} is conservative if F=f\mathbf{F} = \nabla f for some potential function ff. Test: Py=Qx\frac{\partial P}{\partial y} = \frac{\partial Q}{\partial x}.
  2. The Fundamental Theorem: Cfdr=f(B)f(A)\int_C \nabla f \cdot d\mathbf{r} = f(B) - f(A). The line integral depends only on endpoints, not on the path.
  3. Path Independence: For conservative fields, all paths between two points give the same integral value.
  4. Closed Curves: The circulation around any closed curve is zero for conservative fields.
  5. Physical Meaning: Conservative forces conserve mechanical energy; work done depends only on position change.
Looking Ahead: In the next section, we'll study Green's Theorem, which connects line integrals around closed curves to double integrals over the enclosed region. This provides another way to test whether fields are conservative and leads to powerful computational techniques.
Loading comments...