-
Notifications
You must be signed in to change notification settings - Fork 1
Description
There are a few spots in our constraints, e.g. LineTangentToCircle, LinesEqualLength (under #58), where we check for degenerate cases and then don't really do too much with that.
In some cases, like the python implementation's LineLineAngle constraint, we actually effectively report constraint satisfaction for degenerate cases by virtue of returning zero:
def get_residual(self, variable_values: Mapping[str, float]) -> nb.Vector:
# Get direction vectors for both lines.
p1 = self.line1.p1.get_state(variable_values)
p2 = self.line1.p2.get_state(variable_values)
p3 = self.line2.p1.get_state(variable_values)
p4 = self.line2.p2.get_state(variable_values)
v1 = p2 - p1
v2 = p4 - p3
# Calculate magnitudes.
mag1 = nb.np.linalg.norm(v1)
mag2 = nb.np.linalg.norm(v2)
# Check for zero-length lines.
is_invalid = (mag1 < EPS) | (mag2 < EPS)
# 2D cross product and dot product.
cross_2d = v1[0] * v2[1] - v1[1] * v2[0]
dot_product = nb.np.dot(v1, v2)
# Current angle using atan2.
current_angle = nb.np.atan2(cross_2d, dot_product)
# Compute angle difference.
angle_residual = nb.np.array([current_angle - self.angle])
# Return 0.0 if invalid, otherwise return residual. <----------- here
return nb.np.where(is_invalid, nb.np.array([0.0]), angle_residual)
I think the best idea I have is probably to penalise the solver for degenerate solutions.
We don't want the solver to home in on a degenerate candidate solution then report that as satisfactory if an actual, valid, non-degenerate solution exists. Practically this means adding some residual term for degenerate cases, e.g., where line length is very short... though ideally I think we'd shape this function a little bit.
I've done a bunch of hyberbolic tangent or otherwise sigmoid shaped scaling applied to gated functions in the past, something like that might be a good idea here, though maybe a simple linear shape is better. Either way, we could slap in a residual term that's large for true degeneracy, but that smoothly fades out to zero where we have real and valid candidate solutions.
We might also want some amount of error handling as a user could request something degenerate and we need to be able to tell them that. For example, a user could draw a line