InterpolatePy 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- interpolatepy/__init__.py +8 -0
- interpolatepy/b_spline.py +737 -0
- interpolatepy/b_spline_approx.py +544 -0
- interpolatepy/b_spline_cubic.py +444 -0
- interpolatepy/b_spline_interpolate.py +515 -0
- interpolatepy/b_spline_smooth.py +639 -0
- interpolatepy/c_s_smoot_search.py +177 -0
- interpolatepy/c_s_smoothing.py +611 -0
- interpolatepy/c_s_with_acc1.py +643 -0
- interpolatepy/c_s_with_acc2.py +494 -0
- interpolatepy/cubic_spline.py +486 -0
- interpolatepy/double_s.py +580 -0
- interpolatepy/frenet_frame.py +245 -0
- interpolatepy/linear.py +107 -0
- interpolatepy/polynomials.py +451 -0
- interpolatepy/simple_paths.py +281 -0
- interpolatepy/trapezoidal.py +613 -0
- interpolatepy/tridiagonal_inv.py +96 -0
- interpolatepy/version.py +5 -0
- interpolatepy-1.0.0.dist-info/METADATA +415 -0
- interpolatepy-1.0.0.dist-info/RECORD +24 -0
- interpolatepy-1.0.0.dist-info/WHEEL +5 -0
- interpolatepy-1.0.0.dist-info/licenses/LICENSE +21 -0
- interpolatepy-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
from interpolatepy.c_s_smoothing import CubicSmoothingSpline
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
# Constants to replace magic values
|
|
9
|
+
EPSILON = 1e-6 # Convergence tolerance
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass
|
|
13
|
+
class SplineConfig:
|
|
14
|
+
"""Configuration parameters for smoothing spline calculation."""
|
|
15
|
+
|
|
16
|
+
weights: list[float] | np.ndarray | None = None
|
|
17
|
+
v0: float = 0.0
|
|
18
|
+
vn: float = 0.0
|
|
19
|
+
max_iterations: int = 50
|
|
20
|
+
debug: bool = False
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def smoothing_spline_with_tolerance(
|
|
24
|
+
t_points: np.ndarray,
|
|
25
|
+
q_points: np.ndarray,
|
|
26
|
+
tolerance: float,
|
|
27
|
+
config: SplineConfig,
|
|
28
|
+
) -> tuple[CubicSmoothingSpline, float, float, int]:
|
|
29
|
+
"""Find a cubic smoothing spline with a maximum approximation error smaller than
|
|
30
|
+
a given tolerance using binary search on the μ parameter.
|
|
31
|
+
|
|
32
|
+
This implements the algorithm for "Smoothing spline with prescribed tolerance".
|
|
33
|
+
|
|
34
|
+
Parameters
|
|
35
|
+
----------
|
|
36
|
+
t_points : np.ndarray
|
|
37
|
+
Time points [t₀, t₁, t₂, ..., tₙ]
|
|
38
|
+
q_points : np.ndarray
|
|
39
|
+
Position points [q₀, q₁, q₂, ..., qₙ]
|
|
40
|
+
tolerance : float
|
|
41
|
+
Maximum allowed approximation error δ between original and smoothed points
|
|
42
|
+
config : SplineConfig
|
|
43
|
+
Configuration object with optional parameters:
|
|
44
|
+
- weights: Individual point weights [w₀, w₁, ..., wₙ] (None = equal weights)
|
|
45
|
+
- v0: Initial velocity constraint at t₀
|
|
46
|
+
- vn: Final velocity constraint at tₙ
|
|
47
|
+
- max_iterations: Maximum number of iterations for the binary search
|
|
48
|
+
- debug: Whether to print debug information
|
|
49
|
+
|
|
50
|
+
Returns
|
|
51
|
+
-------
|
|
52
|
+
spline : CubicSmoothingSpline
|
|
53
|
+
The final CubicSmoothingSpline object
|
|
54
|
+
mu : float
|
|
55
|
+
The found value of μ parameter
|
|
56
|
+
e_max : float
|
|
57
|
+
The maximum approximation error achieved
|
|
58
|
+
iterations : int
|
|
59
|
+
Number of iterations performed
|
|
60
|
+
|
|
61
|
+
Examples
|
|
62
|
+
--------
|
|
63
|
+
>>> import numpy as np
|
|
64
|
+
>>> from interpolatepy.c_s_smoothing import CubicSmoothingSpline
|
|
65
|
+
>>> # Create sample data
|
|
66
|
+
>>> t = np.linspace(0, 10, 100)
|
|
67
|
+
>>> q = np.sin(t) + 0.1 * np.random.randn(100)
|
|
68
|
+
>>> config = SplineConfig(max_iterations=20)
|
|
69
|
+
>>> # Find spline with tolerance of 0.05
|
|
70
|
+
>>> spline, mu, error, iterations = smoothing_spline_with_tolerance(t, q, 0.05, config)
|
|
71
|
+
>>> print(f"Found spline with μ={mu:.6f}, error={error:.6f} in {iterations} iterations")
|
|
72
|
+
|
|
73
|
+
Notes
|
|
74
|
+
-----
|
|
75
|
+
The algorithm uses binary search to find the optimal μ parameter value that
|
|
76
|
+
produces a smoothing spline with maximum error below the specified tolerance.
|
|
77
|
+
The parameter μ controls the trade-off between smoothness and accuracy, with
|
|
78
|
+
values closer to 0 producing smoother curves and values closer to 1 producing
|
|
79
|
+
more accurate but less smooth curves.
|
|
80
|
+
"""
|
|
81
|
+
|
|
82
|
+
# Initialize the search range for μ
|
|
83
|
+
# Note: CubicSmoothingSpline requires 0 < μ ≤ 1, so we start with a small positive value
|
|
84
|
+
lower_bound = EPSILON # lower_bound(0) = small positive number (close to 0)
|
|
85
|
+
upper_bound = 1.0 # upper_bound(0) = 1
|
|
86
|
+
|
|
87
|
+
if config.debug:
|
|
88
|
+
print(f"Starting binary search with tolerance δ={tolerance}")
|
|
89
|
+
print(f"Initial lower_bound={lower_bound}, upper_bound={upper_bound}")
|
|
90
|
+
|
|
91
|
+
# Create initial (fallback) spline with μ=1.0 (most accurate)
|
|
92
|
+
# This ensures we always have a valid spline to return
|
|
93
|
+
default_spline = CubicSmoothingSpline(
|
|
94
|
+
t_points, q_points, mu=1.0, weights=config.weights, v0=config.v0, vn=config.vn, debug=False
|
|
95
|
+
)
|
|
96
|
+
default_error = np.max(np.abs(default_spline.q - default_spline.s))
|
|
97
|
+
|
|
98
|
+
# Keep track of the best solution found so far
|
|
99
|
+
best_spline = default_spline
|
|
100
|
+
best_mu = 1.0
|
|
101
|
+
best_error = default_error
|
|
102
|
+
|
|
103
|
+
# Iteration loop
|
|
104
|
+
for i in range(config.max_iterations):
|
|
105
|
+
# Step 1: Assume μ(i) = (lower_bound(i) + upper_bound(i))/2
|
|
106
|
+
mu = (lower_bound + upper_bound) / 2
|
|
107
|
+
|
|
108
|
+
if config.debug:
|
|
109
|
+
print(f"\nIteration {i + 1}: μ={mu}")
|
|
110
|
+
|
|
111
|
+
# Step 2: Compute spline and maximum error
|
|
112
|
+
try:
|
|
113
|
+
# Create spline with current μ
|
|
114
|
+
spline = CubicSmoothingSpline(
|
|
115
|
+
t_points,
|
|
116
|
+
q_points,
|
|
117
|
+
mu=mu,
|
|
118
|
+
weights=config.weights,
|
|
119
|
+
v0=config.v0,
|
|
120
|
+
vn=config.vn,
|
|
121
|
+
debug=False,
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
# Calculate maximum error e_max(i)
|
|
125
|
+
e_max = np.max(np.abs(spline.q - spline.s))
|
|
126
|
+
|
|
127
|
+
if config.debug:
|
|
128
|
+
print(f" Maximum error e_max({i})={e_max}")
|
|
129
|
+
|
|
130
|
+
# Update best solution if better
|
|
131
|
+
if e_max < best_error:
|
|
132
|
+
best_spline = spline
|
|
133
|
+
best_mu = mu
|
|
134
|
+
best_error = e_max
|
|
135
|
+
|
|
136
|
+
if config.debug:
|
|
137
|
+
print(f" New best solution: μ={mu}, error={e_max}")
|
|
138
|
+
|
|
139
|
+
# Step 3: Update lower_bound and upper_bound according to e_max
|
|
140
|
+
if e_max > tolerance:
|
|
141
|
+
# Error is too large, need more accuracy, increase μ
|
|
142
|
+
lower_bound_new = mu
|
|
143
|
+
upper_bound_new = upper_bound
|
|
144
|
+
if config.debug:
|
|
145
|
+
print(f" Error > tolerance, updating lower_bound({i + 1})={lower_bound_new}")
|
|
146
|
+
else:
|
|
147
|
+
# Error is acceptable, can try more smoothing
|
|
148
|
+
upper_bound_new = mu
|
|
149
|
+
lower_bound_new = lower_bound
|
|
150
|
+
if config.debug:
|
|
151
|
+
print(f" Error ≤ tolerance, updating upper_bound({i + 1})={upper_bound_new}")
|
|
152
|
+
|
|
153
|
+
# Update lower_bound and upper_bound for next iteration
|
|
154
|
+
lower_bound = lower_bound_new
|
|
155
|
+
upper_bound = upper_bound_new
|
|
156
|
+
|
|
157
|
+
# Check for convergence or solution
|
|
158
|
+
if abs(e_max - tolerance) < EPSILON or (
|
|
159
|
+
e_max < tolerance and upper_bound - lower_bound < EPSILON
|
|
160
|
+
):
|
|
161
|
+
if config.debug:
|
|
162
|
+
print(f"\nConverged to solution with error {e_max} after {i + 1} iterations")
|
|
163
|
+
return spline, mu, e_max, i + 1
|
|
164
|
+
|
|
165
|
+
except ValueError as e:
|
|
166
|
+
# Handle potential errors with invalid μ values
|
|
167
|
+
if config.debug:
|
|
168
|
+
print(f" Error with μ={mu}: {e}")
|
|
169
|
+
# If μ caused an error, try a value closer to 1 (more accuracy)
|
|
170
|
+
lower_bound = mu
|
|
171
|
+
|
|
172
|
+
if config.debug:
|
|
173
|
+
print(f"\nReached maximum iterations ({config.max_iterations})")
|
|
174
|
+
print(f"Best solution found: μ={best_mu}, error={best_error}")
|
|
175
|
+
|
|
176
|
+
# Return the best solution found - this is guaranteed to be non-None now
|
|
177
|
+
return best_spline, best_mu, best_error, config.max_iterations
|