Open GarthSnyder opened 3 years ago
@GarthSnyder thanks for the bug report. I am guessing it would be a good idea to add a try-except block for ZeroDivisionError
on line 616. Line 615 initializes the array to 0.0
, so something like this should be good enough:
try:
matrix_x[q - 1] = float(matrix_y[q - 1]) / float(matrix_u[q - 1][q - 1])
except ZeroDivisionError:
pass
It looks like changing the input degree also fixes the issue.
curve = fit.approximate_curve(points, 4) # tested with 2, 3, 4, 6
Is there a specific reason for setting degree to 5 @GarthSnyder? Usually quadratic or cubic should be enough.
Fixed by #121
Hi I seem to have this issue when fitting a surface. Here is my reproducible snippet.
from geomdl.fitting import approximate_surface
pts = [
(5.29724, 2.39077, -4.18169),
(5.29212, 2.31915, -4.09873),
(5.27242, 2.33416, -4.07725),
(5.28571, 2.2567, -4.03315),
(5.2707, 2.27565, -4.01087),
(5.26706, 2.29867, -3.9834),
(5.2648, 2.25254, -3.93714),
(5.26704, 2.22952, -3.9618),
(5.27635, 2.2083, -3.97793),
(5.32034, 2.61671, -4.44898),
(5.31134, 2.54153, -4.35878),
(5.28584, 2.55673, -4.34687),
(5.3047, 2.46589, -4.27014),
(5.28338, 2.48316, -4.25543),
(5.27473, 2.50775, -4.23376),
(5.27542, 2.4077, -4.16565),
(5.26814, 2.43144, -4.14071),
(5.33211, 2.86337, -4.70251),
(5.26721, 2.35746, -4.0507),
(5.34108, 2.93704, -4.792),
(5.30763, 2.8091, -4.59725),
(5.36651, 2.92402, -4.80067),
(5.32066, 2.88619, -4.68443),
(5.33322, 2.9603, -4.77297),
(5.27569, 2.65359, -4.42211),
(5.2725, 2.57996, -4.32821),
(5.29434, 2.73155, -4.51016),
(5.30647, 2.70779, -4.52962),
(5.29296, 2.63058, -4.43997),
(5.32025, 2.78582, -4.61585),
(5.34551, 2.77244, -4.62424),
(5.33331, 2.69442, -4.53731),
(5.35631, 2.85074, -4.71051),
]
surface_1 = approximate_surface(pts, 3, 11, 3, 3)
surface_2 = approximate_surface(pts, 3, 11, 3, 3, ctrlpts_size_u=4, ctrlpts_size_v=4)
Surface_1 results in a index error.
Stack trace:
Traceback (most recent call last):
File "datagen/seam.py", line 123, in <module>
surface_1 = approximate_surface(pts, 3, 11, 3, 3)
File "/home/neev/.miniconda/envs/csm3.7/lib/python3.7/site-packages/geomdl/fitting.py", line 261, in approximate_surface
matrix_ntnu = linalg.matrix_multiply(matrix_ntu, matrix_nu)
File "/home/neev/.miniconda/envs/csm3.7/lib/python3.7/site-packages/geomdl/linalg.py", line 457, in matrix_multiply
p1 = len(mat1[0])
IndexError: list index out of range
Trying to set the num_ctrl_pts to be 3 seems to have an issue. Surface_2 has this error:
Traceback (most recent call last):
File "datagen/seam.py", line 124, in <module>
surface_2 = approximate_surface(pts, 3, 11, 3, 3, ctrlpts_size_u=4, ctrlpts_size_v=4)
File "/home/neev/.miniconda/envs/csm3.7/lib/python3.7/site-packages/geomdl/fitting.py", line 294, in approximate_surface
x = linalg.backward_substitution(matrix_ntnuu, y)
File "/home/neev/.miniconda/envs/csm3.7/lib/python3.7/site-packages/geomdl/linalg.py", line 616, in backward_substitution
matrix_x[q - 1] = float(matrix_y[q - 1]) / float(matrix_u[q - 1][q - 1])
ZeroDivisionError: float division by zero
According to the code comments, the size of u or v needs to be such that (size_u + 1) > num_ctrl_pts, so in theory, for a 3x11 patch, the 3 pt direction should still work with 3 ctrlpts right?
Environment is: Python 3.7, geomdl 5.3.1, Linux.
@neevparikh, did you try this on the #121 fork? It would be interesting to know whether that fix addresses the 2D case. The fix is in a utility routine, so it might or might not be in the path for surfaces.
Oh okay I thought the #121 was merged in. I'll test and report back
No fix from that sadly. The 2nd surface error changes to the same error as the first one now.
It's been a while since I looked at NURBS math in detail, but I think you may be running afoul of some intrinsic requirements.
I don't think the standard approximation method implemented by NURBS-Python (from The NURBS Book) will extrapolate additional points in a dimension. If you have an input dimension of N, you can't ask for N + M control points as an output in that dimension; it has to be at most N and is N - 1 by default.
Similarly, you need at least degree + 1 control points in a dimension. This is a basic requirement for NURBS curves/surfaces. Otherwise, there's nothing to interpolate - all your control points are active at both the start and end of the interval.
Your input data is 3 x 11, so the highest number of control points you can obtain in the first dimension is 3. And that limits your output degree for that dimension to 2.
Rewriting your example in light of these limits and also using the #121 patch works fine. I'm not sure if it works without the #121 patch. (More specifically, it may run without errors without #121, although I believe the approximation is still potentially biased. You may see ringing in the output surface, but then again you might not. It depends on the input data.)
surface_1 = fit.approximate_surface(pts, 3, 11, 2, 3, ctrlpts_size_u=3)
surface_2 = fit.approximate_surface(pts, 3, 11, 2, 3, ctrlpts_size_u=3, ctrlpts_size_v=4)
Note that you have to specify the number of control points in U explicitly for both cases. Otherwise it defaults to 3 - 1 = 2, which is not sufficient to support a degree-2 NURBS.
Oh I see! I think I'd assumed 3 meant degree 3, but in hindsight that's obviously under determined, because you need n+1 pts to fit an n-dim polynomial. Thanks!
I wonder if the default behavior should warn the user of this edge case? Or maybe the default number of control pts should be max(size_u - 1, degree_u + 1)?
I wonder if the default behavior should warn the user of this edge case? Or maybe the default number of control pts should be max(size_u - 1, degree_u + 1)?
That seems like a good idea, but you should probably file it as a separate issue.
Reproducible crash via division by zero. Should be reproducible from the code below. I took a look at the library code, but the exact issue isn't clear to me. Most likely it's something peculiar about the input, but I'm not sure exactly what.
Looks like
matrix_u[q - 1][q - 1]
is probably zero on line 616 of linalg.py.There are 88 points, and accepting the default number of control points from
fit.approximate_curve
(which I assume is 87) causes the crash, as does any number of control points above this. The points fit just fine if fewer control points are requested.I also notice that rounding all the floating point values avoids the problem, even if you round them to 15 digits. Very strange.
Environment: NURBS-Python 5.3.1, macOS Big Sur. Python 3.7 or 3.8.3. Regular Python is installed via Anaconda, but I'm working on a plugin for Fusion 360 which has its own Python environment; either reproduces the issue.
The error output is: