import pytest
import numpy as np
import scirs2
class TestTrapezoid:
def test_trapezoid_constant(self):
y = np.array([2.0, 2.0, 2.0, 2.0, 2.0])
result = scirs2.trapezoid_array_py(y, dx=0.25)
assert abs(result - 2.0) < 1e-10
def test_trapezoid_linear(self):
x = np.linspace(0, 1, 101)
y = x.copy()
result = scirs2.trapezoid_array_py(y, x=x)
assert abs(result - 0.5) < 0.001
def test_trapezoid_quadratic(self):
x = np.linspace(0, 1, 1001)
y = x**2
result = scirs2.trapezoid_array_py(y, x=x)
assert abs(result - 1/3) < 0.001
def test_trapezoid_sine(self):
x = np.linspace(0, np.pi, 1001)
y = np.sin(x)
result = scirs2.trapezoid_array_py(y, x=x)
assert abs(result - 2.0) < 0.001
def test_trapezoid_with_dx(self):
y = np.array([0.0, 1.0, 2.0, 3.0, 4.0])
result = scirs2.trapezoid_array_py(y, dx=0.5)
assert abs(result - 4.0) < 0.001
class TestSimpson:
def test_simpson_constant(self):
y = np.array([3.0, 3.0, 3.0, 3.0, 3.0])
result = scirs2.simpson_array_py(y, dx=0.25)
assert abs(result - 3.0) < 1e-10
def test_simpson_quadratic(self):
x = np.linspace(0, 1, 101)
y = x**2
result = scirs2.simpson_array_py(y, x=x)
assert abs(result - 1/3) < 0.0001
def test_simpson_cubic(self):
x = np.linspace(0, 1, 101)
y = x**3
result = scirs2.simpson_array_py(y, x=x)
assert abs(result - 0.25) < 0.0001
def test_simpson_sine(self):
x = np.linspace(0, np.pi, 101)
y = np.sin(x)
result = scirs2.simpson_array_py(y, x=x)
assert abs(result - 2.0) < 0.0001
def test_simpson_exp(self):
x = np.linspace(0, 1, 101)
y = np.exp(x)
result = scirs2.simpson_array_py(y, x=x)
assert abs(result - (np.e - 1)) < 0.001
class TestCumulativeTrapezoid:
def test_cumulative_linear(self):
x = np.array([0.0, 1.0, 2.0, 3.0, 4.0])
y = x.copy() result = scirs2.cumulative_trapezoid_py(y, x=x)
expected = np.array([0.5, 2.0, 4.5, 8.0])
np.testing.assert_allclose(result, expected, rtol=1e-10)
def test_cumulative_with_initial(self):
y = np.array([0.0, 1.0, 2.0, 3.0])
result = scirs2.cumulative_trapezoid_py(y, dx=1.0, initial=0.0)
assert len(result) == 4
assert abs(result[0]) < 1e-10
def test_cumulative_constant(self):
y = np.array([2.0, 2.0, 2.0, 2.0, 2.0])
result = scirs2.cumulative_trapezoid_py(y, dx=1.0)
expected = np.array([2.0, 4.0, 6.0, 8.0])
np.testing.assert_allclose(result, expected, rtol=1e-10)
class TestRomberg:
def test_romberg_quadratic(self):
x = np.linspace(0, 1, 17) y = x**2
dx = x[1] - x[0]
result = scirs2.romberg_array_py(y, dx=dx)
assert abs(result - 1/3) < 0.001
def test_romberg_sine(self):
x = np.linspace(0, np.pi, 17)
y = np.sin(x)
dx = x[1] - x[0]
result = scirs2.romberg_array_py(y, dx=dx)
assert abs(result - 2.0) < 0.01
class TestEdgeCases:
def test_trapezoid_two_points(self):
y = np.array([0.0, 1.0])
result = scirs2.trapezoid_array_py(y, dx=1.0)
assert abs(result - 0.5) < 1e-10
def test_simpson_three_points(self):
y = np.array([0.0, 1.0, 0.0])
result = scirs2.simpson_array_py(y, dx=1.0)
assert result > 0
def test_irregular_spacing(self):
x = np.array([0.0, 0.1, 0.5, 1.0])
y = x**2
result = scirs2.trapezoid_array_py(y, x=x)
assert abs(result - 1/3) < 0.1
class TestQuad:
def test_quad_quadratic(self):
result = scirs2.quad_py(lambda x: x**2, 0.0, 1.0)
assert result["success"]
assert abs(result["value"] - 1/3) < 1e-6
assert result["error"] < 1e-6
def test_quad_sine(self):
result = scirs2.quad_py(lambda x: np.sin(x), 0.0, np.pi)
assert result["success"]
assert abs(result["value"] - 2.0) < 1e-6
def test_quad_exp(self):
result = scirs2.quad_py(lambda x: np.exp(x), 0.0, 1.0)
assert result["success"]
assert abs(result["value"] - (np.e - 1)) < 1e-6
def test_quad_sqrt(self):
result = scirs2.quad_py(lambda x: np.sqrt(x), 0.01, 1.0)
assert result["success"]
expected = 2.0/3.0 * (1.0**1.5 - 0.01**1.5)
assert abs(result["value"] - expected) < 1e-4
def test_quad_oscillatory(self):
result = scirs2.quad_py(lambda x: np.sin(10*x), 0.0, 2*np.pi)
assert result["success"]
assert abs(result["value"]) < 1e-4
def test_quad_polynomial(self):
result = scirs2.quad_py(lambda x: x**3 + 2*x, 0.0, 2.0)
assert result["success"]
assert abs(result["value"] - 8.0) < 1e-6
class TestSolveIVP:
def test_solve_ivp_exponential_decay(self):
def f(t, y):
return [-y[0]]
result = scirs2.solve_ivp_py(
f,
t_span=(0.0, 2.0),
y0=[1.0],
method="RK45"
)
assert result["success"]
assert len(result["t"]) > 0
y_final = result["y"][0, -1]
assert abs(y_final - np.exp(-2.0)) < 0.01
def test_solve_ivp_harmonic_oscillator(self):
def f(t, y):
return [y[1], -y[0]]
result = scirs2.solve_ivp_py(
f,
t_span=(0.0, 2*np.pi),
y0=[1.0, 0.0], method="RK45"
)
assert result["success"]
y_final = result["y"][0, -1]
assert abs(y_final - 1.0) < 0.1
def test_solve_ivp_linear_growth(self):
def f(t, y):
return [t]
result = scirs2.solve_ivp_py(
f,
t_span=(0.0, 1.0),
y0=[0.0],
method="RK45"
)
assert result["success"]
y_final = result["y"][0, -1]
assert abs(y_final - 0.5) < 0.01
def test_solve_ivp_system(self):
def f(t, y):
alpha, beta, delta, gamma = 1.0, 0.1, 0.075, 1.5
prey, predator = y[0], y[1]
return [
alpha * prey - beta * prey * predator,
delta * prey * predator - gamma * predator
]
result = scirs2.solve_ivp_py(
f,
t_span=(0.0, 10.0),
y0=[10.0, 5.0],
method="RK45"
)
assert result["success"]
assert result["y"].shape[0] == 2 assert np.all(result["y"] >= 0)
def test_solve_ivp_rk23(self):
def f(t, y):
return [-y[0]]
result = scirs2.solve_ivp_py(
f,
t_span=(0.0, 1.0),
y0=[1.0],
method="RK23"
)
assert result["success"]
y_final = result["y"][0, -1]
assert abs(y_final - np.exp(-1.0)) < 0.05
def test_solve_ivp_dop853(self):
def f(t, y):
return [-y[0]]
result = scirs2.solve_ivp_py(
f,
t_span=(0.0, 1.0),
y0=[1.0],
method="DOP853"
)
assert result["success"]
y_final = result["y"][0, -1]
assert abs(y_final - np.exp(-1.0)) < 0.01