codexsigned-polynomial-area-and-absolute-area.py
signed-polynomial-area-and-absolute-area.py
"""Compare exact signed and geometric areas for x**2 - 1 on [-2, 2]."""
from fractions import Fraction
from pathlib import Path
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np
def primitive(x):
return x**3 / 3 - x
def main():
endpoints = list(map(Fraction, (-2, -1, 1, 2)))
signed_parts = [primitive(b) - primitive(a) for a, b in zip(endpoints, endpoints[1:])]
assert signed_parts == [Fraction(4, 3), Fraction(-4, 3), Fraction(4, 3)]
assert sum(signed_parts) == Fraction(4, 3)
assert sum(map(abs, signed_parts)) == 4
# Independently integrate each smooth piece by the composite trapezoid rule.
errors = []
for a, b, exact in zip(endpoints, endpoints[1:], signed_parts):
nodes = np.linspace(float(a), float(b), 10001)
error = abs(float(np.trapezoid(nodes**2 - 1, nodes)) - float(exact))
assert error < 2e-8
errors.append(error)
plt.rcParams.update({"font.size": 12, "axes.spines.top": False, "axes.spines.right": False})
fig, axes = plt.subplots(1, 2, figsize=(10.0, 4.4), dpi=120, layout="constrained")
fig.patch.set_facecolor("white")
x = np.linspace(-2, 2, 1201)
y = x**2 - 1
for ax in axes:
ax.set_facecolor("white")
ax.axhline(0, color="#555555", linewidth=1)
ax.axvline(0, color="#dddddd", linewidth=1)
ax.set(xlim=(-2.1, 2.1), ylim=(-1.45, 3.5), xlabel="x (dimensionless)", ylabel="function value")
ax.set_xticks([-2, -1, 0, 1, 2])
ax.grid(axis="y", alpha=.15)
axes[0].plot(x, y, color="#252525", linewidth=2.3)
axes[0].fill_between(x, 0, y, where=y >= 0, interpolate=True, color="#277da8", alpha=.35)
axes[0].fill_between(x, 0, y, where=y <= 0, interpolate=True, color="#d56b35", alpha=.4)
axes[0].set_title(r"Signed: $f(x)=x^2-1$")
axes[0].text(-1.78, .65, r"$+4/3$", ha="center", color="#165b7e", fontsize=14)
axes[0].text(1.78, .65, r"$+4/3$", ha="center", color="#165b7e", fontsize=14)
axes[0].text(0, -.65, r"$-4/3$", ha="center", color="#8c3d1d", fontsize=14)
axes[0].text(0, 2.85, r"$8/3-4/3=4/3$", ha="center", fontsize=15)
axes[1].plot(x, np.abs(y), color="#252525", linewidth=2.3)
axes[1].fill_between(x, 0, np.abs(y), color="#277da8", alpha=.35)
axes[1].set_title(r"Total area: $|f(x)|=|x^2-1|$")
for location, height in [(-1.78, .65), (0, .55), (1.78, .65)]:
axes[1].text(location, height, r"$4/3$", ha="center", color="#165b7e", fontsize=14)
axes[1].text(0, 2.85, r"$8/3+4/3=4$", ha="center", fontsize=15)
fig.savefig(Path.cwd() / (Path(__file__).stem + ".png"), facecolor="white", transparent=False)
plt.close(fig)
print({"signed_parts": list(map(str, signed_parts)), "signed_total": "4/3", "geometric_total": 4,
"trapezoid_absolute_errors": errors})
if __name__ == "__main__":
main()