feat: euler and picard-poly
This commit is contained in:
commit
1a7fd3adf6
68
euler-methods.py
Normal file
68
euler-methods.py
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
import numpy as np
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
from matplotlib.scale import FuncScale
|
||||||
|
|
||||||
|
# f: (t, y) -> R, dy/dt = f
|
||||||
|
def euler(f, a, b, y0, h):
|
||||||
|
xs = [a]
|
||||||
|
ys = [y0]
|
||||||
|
x = a
|
||||||
|
y = y0
|
||||||
|
while x < b:
|
||||||
|
y += h * f(x, y)
|
||||||
|
x += h
|
||||||
|
if abs(y) > threshold:
|
||||||
|
break
|
||||||
|
xs.append(x)
|
||||||
|
ys.append(y)
|
||||||
|
return xs, ys
|
||||||
|
|
||||||
|
def improvedEuler(f, a, b, y0, h):
|
||||||
|
xs = [a]
|
||||||
|
ys = [y0]
|
||||||
|
|
||||||
|
x = a
|
||||||
|
y = y0
|
||||||
|
while x <= b:
|
||||||
|
slope1 = f(x, y)
|
||||||
|
y_predictor = y + h * slope1
|
||||||
|
slope2 = f(x + h, y_predictor)
|
||||||
|
y = y + (h/2) * (slope1 + slope2)
|
||||||
|
x = x + h
|
||||||
|
if abs(y) > threshold:
|
||||||
|
break
|
||||||
|
xs.append(x)
|
||||||
|
ys.append(y)
|
||||||
|
return xs, ys
|
||||||
|
|
||||||
|
f = lambda t, y: t**3 + y**3
|
||||||
|
threshold = 100
|
||||||
|
a = 0
|
||||||
|
b = 2
|
||||||
|
|
||||||
|
lims = [
|
||||||
|
(0, 1.4, 1.8, 1, threshold),
|
||||||
|
(1, 0.4, 0.6, 1, threshold),
|
||||||
|
(-1, 0.4, 0.6, -threshold, -1),
|
||||||
|
]
|
||||||
|
|
||||||
|
y0, xm, xM, ym, yM = lims[0]
|
||||||
|
|
||||||
|
fig, ax= plt.subplots(1, 2)
|
||||||
|
for h in [0.1, 0.01, 0.001, 0.0001]:
|
||||||
|
ax[0].plot(*euler(f, a, b, y0, h), label=f'$\\Delta t$={h}', linewidth=1)
|
||||||
|
T, Y = improvedEuler(f, a, b, y0, h)
|
||||||
|
ax[1].plot(T, Y, label=f'$\\Delta t$={h}', linewidth=1)
|
||||||
|
print(f"Max x: {T[-1]}")
|
||||||
|
|
||||||
|
for axe in ax:
|
||||||
|
axe.legend()
|
||||||
|
axe.set_xlim(xm, xM)
|
||||||
|
axe.set_ylim(ym, yM)
|
||||||
|
#axe.set_yscale(FuncScale(axe, (lambda y: np.log(-y), lambda y: -np.exp(y))))
|
||||||
|
|
||||||
|
ax[0].set_title("Euler Method")
|
||||||
|
ax[1].set_title("Improved Euler Method")
|
||||||
|
|
||||||
|
#fig.suptitle(r"Numerically solving $y'=y^3 + t^3$ starting at (0, 0)")
|
||||||
|
plt.show()
|
222
picard-poly.py
Normal file
222
picard-poly.py
Normal file
@ -0,0 +1,222 @@
|
|||||||
|
import numpy as np
|
||||||
|
from numbers import Number
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
|
||||||
|
class Polynomial:
|
||||||
|
"""
|
||||||
|
Represents a polynomial and supports common mathematical operations.
|
||||||
|
|
||||||
|
The coefficients are stored in an array in order of increasing power,
|
||||||
|
e.g., `[c₀, c₁, c₂]` for the polynomial c₀ + c₁x + c₂x².
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, coeff):
|
||||||
|
"""Initializes the polynomial with a list, tuple, or numpy array of coefficients."""
|
||||||
|
# Trim trailing zeros that don't affect the polynomial's degree
|
||||||
|
self.coefficients = np.trim_zeros(np.asarray(coeff, dtype=float), 'b')
|
||||||
|
if self.coefficients.size == 0:
|
||||||
|
self.coefficients = np.array([0.0])
|
||||||
|
|
||||||
|
|
||||||
|
@property
|
||||||
|
def degree(self) -> int:
|
||||||
|
"""Returns the degree of the polynomial."""
|
||||||
|
# The degree is the number of coefficients minus one.
|
||||||
|
# Returns -1 for the zero polynomial P(x) = 0.
|
||||||
|
if self.coefficients.size == 1 and self.coefficients[0] == 0:
|
||||||
|
return -1
|
||||||
|
return self.coefficients.size - 1
|
||||||
|
|
||||||
|
def evalf(self, x: float) -> float:
|
||||||
|
"""
|
||||||
|
Evaluates the polynomial at a given value x using Horner's method.
|
||||||
|
|
||||||
|
For a polynomial P(x) = c₀ + c₁x + ... + cₙxⁿ, this is computed as
|
||||||
|
c₀ + x(c₁ + x(c₂ + ...)).
|
||||||
|
"""
|
||||||
|
# The user's original implementation was correct.
|
||||||
|
rval = self.coefficients[-1]
|
||||||
|
for c in reversed(self.coefficients[:-1]):
|
||||||
|
rval = rval * x + c
|
||||||
|
return rval
|
||||||
|
|
||||||
|
def integrate(self) -> 'Polynomial':
|
||||||
|
"""
|
||||||
|
Computes the indefinite integral of the polynomial, assuming the
|
||||||
|
constant of integration is zero.
|
||||||
|
"""
|
||||||
|
# Corrected to use np.zeros for safe array initialization.
|
||||||
|
integrated_coeffs = np.zeros(self.coefficients.size + 1)
|
||||||
|
for i, c in enumerate(self.coefficients):
|
||||||
|
integrated_coeffs[i + 1] = c / (i + 1)
|
||||||
|
return Polynomial(integrated_coeffs)
|
||||||
|
|
||||||
|
def integratef(self, a: float, b: float) -> float:
|
||||||
|
"""Computes the definite integral of the polynomial from a to b."""
|
||||||
|
# Corrected to pass the integration bounds to evalf.
|
||||||
|
integral = self.integrate()
|
||||||
|
return integral.evalf(b) - integral.evalf(a)
|
||||||
|
|
||||||
|
# --- Refinements and New Features ---
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
"""Provides a user-friendly string representation of the polynomial."""
|
||||||
|
if self.degree == -1:
|
||||||
|
return "0"
|
||||||
|
|
||||||
|
terms = []
|
||||||
|
# Iterate from the highest degree term to the lowest
|
||||||
|
for i, c in list(enumerate(self.coefficients)):
|
||||||
|
if np.isclose(c, 0) and i != 0:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Sign of the term
|
||||||
|
sign = ""
|
||||||
|
if len(terms) > 0:
|
||||||
|
sign = " + " if c > 0 else " - "
|
||||||
|
elif c < 0:
|
||||||
|
sign = "-"
|
||||||
|
|
||||||
|
c = abs(c)
|
||||||
|
|
||||||
|
# Coefficient string
|
||||||
|
coeff_str = ""
|
||||||
|
if not np.isclose(c, 1) or i == 0:
|
||||||
|
coeff_str = f"{c:g}"
|
||||||
|
|
||||||
|
# Variable and power string
|
||||||
|
power_str = ""
|
||||||
|
if i > 0:
|
||||||
|
power_str = "x"
|
||||||
|
if i > 1:
|
||||||
|
power_str += f"^{i}"
|
||||||
|
|
||||||
|
# Combine coefficient and power
|
||||||
|
term = ""
|
||||||
|
if coeff_str and power_str:
|
||||||
|
term = coeff_str + "*" + power_str
|
||||||
|
else:
|
||||||
|
term = coeff_str or power_str
|
||||||
|
|
||||||
|
terms.append(sign + term)
|
||||||
|
|
||||||
|
return "".join(terms)
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
"""Provides an unambiguous representation of the polynomial."""
|
||||||
|
return f"Polynomial({self.coefficients.tolist()})"
|
||||||
|
|
||||||
|
def __add__(self, other):
|
||||||
|
"""Adds two polynomials or a polynomial and a scalar."""
|
||||||
|
if isinstance(other, Polynomial):
|
||||||
|
n1, n2 = self.coefficients.size, other.coefficients.size
|
||||||
|
if n1 > n2:
|
||||||
|
new_coeffs = self.coefficients.copy()
|
||||||
|
new_coeffs[:n2] += other.coefficients
|
||||||
|
else:
|
||||||
|
new_coeffs = other.coefficients.copy()
|
||||||
|
new_coeffs[:n1] += self.coefficients
|
||||||
|
return Polynomial(new_coeffs)
|
||||||
|
elif isinstance(other, Number):
|
||||||
|
new_coeffs = self.coefficients.copy()
|
||||||
|
new_coeffs[0] += other
|
||||||
|
return Polynomial(new_coeffs)
|
||||||
|
return NotImplemented
|
||||||
|
|
||||||
|
def __sub__(self, other):
|
||||||
|
"""Subtracts two polynomials or a scalar from a polynomial."""
|
||||||
|
if isinstance(other, Polynomial):
|
||||||
|
n1, n2 = self.coefficients.size, other.coefficients.size
|
||||||
|
new_coeffs = np.zeros(max(n1, n2), dtype=float)
|
||||||
|
new_coeffs[:n1] = self.coefficients
|
||||||
|
new_coeffs[:n2] -= other.coefficients
|
||||||
|
return Polynomial(new_coeffs)
|
||||||
|
elif isinstance(other, Number):
|
||||||
|
new_coeffs = self.coefficients.copy()
|
||||||
|
new_coeffs[0] -= other
|
||||||
|
return Polynomial(new_coeffs)
|
||||||
|
return NotImplemented
|
||||||
|
|
||||||
|
def __mul__(self, other):
|
||||||
|
"""Multiplies two polynomials or a polynomial and a scalar."""
|
||||||
|
if isinstance(other, Polynomial):
|
||||||
|
# Use convolution to multiply polynomial coefficients
|
||||||
|
return Polynomial(np.convolve(self.coefficients, other.coefficients))
|
||||||
|
elif isinstance(other, Number):
|
||||||
|
return Polynomial(self.coefficients * other)
|
||||||
|
return NotImplemented
|
||||||
|
|
||||||
|
def __pow__(self, n: int):
|
||||||
|
"""Raises the polynomial to a non-negative integer power."""
|
||||||
|
if not isinstance(n, int) or n < 0:
|
||||||
|
raise ValueError("Exponent must be a non-negative integer.")
|
||||||
|
|
||||||
|
# Use exponentiation by squaring for efficiency (O(log n) multiplications)
|
||||||
|
if n == 0:
|
||||||
|
return Polynomial([1]) # P^0 = 1
|
||||||
|
|
||||||
|
result = Polynomial([1])
|
||||||
|
base = self
|
||||||
|
while n > 0:
|
||||||
|
if n % 2 == 1:
|
||||||
|
result *= base
|
||||||
|
base *= base
|
||||||
|
n //= 2
|
||||||
|
return result
|
||||||
|
|
||||||
|
def __radd__(self, other):
|
||||||
|
return self.__add__(other)
|
||||||
|
|
||||||
|
def __rsub__(self, other):
|
||||||
|
# other - self = -(self - other)
|
||||||
|
res = self.__sub__(other)
|
||||||
|
return res * -1
|
||||||
|
|
||||||
|
def __rmul__(self, other):
|
||||||
|
return self.__mul__(other)
|
||||||
|
|
||||||
|
def __call__(self, t):
|
||||||
|
return self.evalf(t)
|
||||||
|
|
||||||
|
def picardIteration(t0, y0, num_iters = 20):
|
||||||
|
phi = [Polynomial([y0])]
|
||||||
|
for _ in range(num_iters):
|
||||||
|
fty = Polynomial([0, 0, 0, 1]) + phi[-1]**3
|
||||||
|
intg = fty.integrate()
|
||||||
|
intg_result = intg - intg.evalf(t0)
|
||||||
|
phi.append(intg_result + phi[0])
|
||||||
|
return phi
|
||||||
|
|
||||||
|
lims = [
|
||||||
|
(0, 2, 0, 100),
|
||||||
|
(0, 1, 1, 100),
|
||||||
|
(0, 1, -1, 100)
|
||||||
|
]
|
||||||
|
num_iter = 6
|
||||||
|
|
||||||
|
t0, tM, y0, yM = lims[1]
|
||||||
|
tm, ym = min(t0, tM), min(y0, yM)
|
||||||
|
|
||||||
|
picard_result = picardIteration(t0, y0, num_iter)
|
||||||
|
'''
|
||||||
|
for idx, poly in enumerate(picard_result):
|
||||||
|
np_poly = np.vectorize(poly)
|
||||||
|
ts = np.linspace(t0, tM, 1000)
|
||||||
|
ys = np_poly(ts)
|
||||||
|
plt.plot(ts, ys)
|
||||||
|
print(idx, poly.degree, poly)
|
||||||
|
'''
|
||||||
|
poly = picard_result[-1]
|
||||||
|
#np_poly = np.vectorize(poly)
|
||||||
|
#ts = np.linspace(t0, tM, 1000)
|
||||||
|
#ys = np_poly(ts)
|
||||||
|
#plt.plot(ts, ys)
|
||||||
|
#print(poly.degree, poly)
|
||||||
|
print(poly.degree)
|
||||||
|
plt.scatter(list(range(poly.degree + 1)), poly.coefficients, s=0.5)
|
||||||
|
plt.yscale('log')
|
||||||
|
#plt.xlim(t0, tM)
|
||||||
|
#plt.ylim(ym, yM)
|
||||||
|
plt.title(f"coefficients of $\\sum c_n t^n$ at y(0) = {y0} of iterations {num_iter}")
|
||||||
|
plt.ylabel("$c_n$")
|
||||||
|
plt.show()
|
Loading…
x
Reference in New Issue
Block a user