def test_convex(self): mb_0 = Metaball(1.0, 2.0, 0.0) mb_1 = Metaball(1.0, 2.0, 2.8) interval = mb_1.lower, mb_0.upper coeffs = mb_0.get_coeffs(False) + mb_1.get_coeffs(True) self._test_result(coeffs, interval, *np_roots(coeffs, interval)) a, b = np_roots(coeffs, interval)[:2] mid = (a + b) / 2 # Only left root. interval = mb_1.lower, mid self._test_result(coeffs, interval, a, np.nan) # Only right root. interval = mid, mb_0.upper self._test_result(coeffs, interval, b, np.nan) # Roots are interval ends. # The root must be picked up either in interval i or i + 1 interval = a, b result = self.get_roots(coeffs, interval) np_result = np_roots(coeffs, interval) interval = b, mb_0.upper result = np.sort(np.concatenate((result, self.get_roots(coeffs, interval)))) np_result = np.sort(np.concatenate((np_result, np_roots(coeffs, interval)))) result = filter_close(result) np_result = filter_close(np_result) np.testing.assert_almost_equal(result, np_result, decimal=self.precision_places)
def test_derivative_stationary(self): """Test the case when there are two roots and f' has stationary points in the interval end points. """ mb = Metaball(1.0, 2.0, 0.0) coeffs = mb.get_coeffs(True) stat_left = self.get_stationary(coeffs, (mb.lower, 0)) stat_right = self.get_stationary(coeffs, (0, mb.upper)) self._test_result(coeffs, (mb.lower, stat_right), -1, 1) self._test_result(coeffs, (stat_left, mb.upper), -1, 1) self._test_result(coeffs, (stat_left, stat_right), -1, 1)
def test_small_on_big_metaball(self): mb_0 = Metaball(0.0, 0.5, 0.51) mb_1 = Metaball(1.5, 100.0, -1.5) # First interval, only big metaball, two roots. coeffs = mb_1.get_coeffs(True) interval = mb_1.lower, mb_0.lower self._test_result(coeffs, interval, *np_roots(coeffs, interval)) # Last interval, nothing in it. interval = mb_0.upper, mb_1.upper self._test_result(coeffs, interval, np.nan, np.nan) # Small and big metaballs, two roots. coeffs = mb_0.get_coeffs(False) + mb_1.get_coeffs(True) interval = mb_0.lower, mb_0.upper self._test_result(coeffs, interval, *np_roots(coeffs, interval))
def test_stationary_far_from_center(self): mb = Metaball(0.1, 2.0, 0.0) # Far left and right beyond the object. # Both roots. coeffs = mb.get_coeffs(True) interval = mb.lower, 1.5 * mb.r self._test_result(coeffs, interval, -mb.r, mb.r) # One root. interval = mb.lower, 0 self._test_result(coeffs, interval, -mb.r, np.nan) # Far right interval = -1.5 * mb.r, mb.upper self._test_result(coeffs, interval, -mb.r, mb.r) # One root. interval = 0, mb.upper self._test_result(coeffs, interval, mb.r, np.nan)
def test_convex(self): mb_0 = Metaball(1.0, 2.0, 0.0) mb_1 = Metaball(1.0, 2.0, 2.8) interval = mb_1.lower, mb_0.upper coeffs = mb_0.get_coeffs(False) + mb_1.get_coeffs(True) self._test_result(coeffs, interval, *np_roots(coeffs, interval)) a, b = np_roots(coeffs, interval)[:2] mid = (a + b) / 2 # Only left root. interval = mb_1.lower, mid self._test_result(coeffs, interval, a, np.nan) # Only right root. interval = mid, mb_0.upper self._test_result(coeffs, interval, b, np.nan) # Roots are interval ends. # The root must be picked up either in interval i or i + 1 interval = a, b result = self.get_roots(coeffs, interval) np_result = np_roots(coeffs, interval) interval = b, mb_0.upper result = np.sort( np.concatenate((result, self.get_roots(coeffs, interval)))) np_result = np.sort( np.concatenate((np_result, np_roots(coeffs, interval)))) result = filter_close(result) np_result = filter_close(np_result) np.testing.assert_almost_equal(result, np_result, decimal=self.precision_places)
def test(offset): mb = Metaball(1.0, 2.0, 0.0) # Root into the stationary point. coeffs = mb.get_coeffs(True) coeffs = coeffs - \ np.array([0, 0, 0, 0, f(coeffs, mb.center) + offset]) # Tip of the quartic is the stationary point. interval = mb.lower, mb.upper self._test_result(coeffs, interval, *np_roots(coeffs, interval)) # Moreover, split the interval in the root interval = mb.lower, 0.0 result = self.get_roots(coeffs, interval) ground_truth = np_roots(coeffs, interval) interval = 0.0, mb.upper result = np.sort( np.concatenate((result, self.get_roots(coeffs, interval)))) ground_truth = np.sort( np.concatenate((ground_truth, np_roots(coeffs, interval)))) np.testing.assert_almost_equal(result, ground_truth, decimal=self.precision_places)
def test(offset): mb = Metaball(1.0, 2.0, 0.0) # Root into the stationary point. coeffs = mb.get_coeffs(True) coeffs = coeffs - \ np.array([0, 0, 0, 0, f(coeffs, mb.center) + offset]) # Tip of the quartic is the stationary point. interval = mb.lower, mb.upper self._test_result(coeffs, interval, *np_roots(coeffs, interval)) # Moreover, split the interval in the root interval = mb.lower, 0.0 result = self.get_roots(coeffs, interval) ground_truth = np_roots(coeffs, interval) interval = 0.0, mb.upper result = np.sort(np.concatenate((result, self.get_roots(coeffs, interval)))) ground_truth = np.sort(np.concatenate((ground_truth, np_roots(coeffs, interval)))) np.testing.assert_almost_equal(result, ground_truth, decimal=self.precision_places)
def test_one_metaball(self): # Regular two roots. for r in np.linspace(self.pixel_size / 4, 1.0 - self.pixel_size, 10): mb = Metaball(r, 1.0, 0.0) coeffs = mb.get_coeffs(True) interval = (mb.lower, mb.upper) self._test_result(coeffs, interval, -r, r) # One root left mb = Metaball(1.0, 2.0, 0.0) coeffs = mb.get_coeffs(True) interval = mb.lower, mb.center - mb.r / 2 self._test_result(coeffs, interval, -1.0, np.nan) # One root right interval = mb.center + mb.r / 2, mb.upper self._test_result(coeffs, interval, 1, np.nan) # Make interval end points stationary. coeffs = mb.get_coeffs(True) interval = (mb.lower, 0) self._test_result(coeffs, interval, -1, np.nan) interval = (0, mb.upper) self._test_result(coeffs, interval, 1, np.nan) # Put interval end points to roots. # In right endpoint. interval = mb.lower, -1.0 self._test_result(coeffs, interval, -1.0) # In left endpoint. interval = -1.0, 0.0 self._test_result(coeffs, interval) # By epsilon too far -> no root expected interval = mb.lower, -1 - self.pixel_size self._test_result(coeffs, interval) interval = 1.0, mb.upper # By epsilon too far -> no root expected interval = 1.0 + self.pixel_size, mb.upper self._test_result(coeffs, interval, np.nan, np.nan) # Left end point is a root. coeffs = coeffs - np.array([0, 0, 0, 0, f(coeffs, mb.center)]) interval = 0.0, mb.upper self._test_result(coeffs, interval, 0.0) # Right end point is a root. interval = mb.lower, 0.0 self._test_result(coeffs, interval, 0.0) # No roots coeffs = mb.get_coeffs(True) coeffs = coeffs - np.array([0, 0, 0, 0, f(coeffs, mb.center) + 1]) interval = mb.lower, mb.upper self._test_result(coeffs, interval, np.nan, np.nan) # No roots on the left from the stationary point coeffs = mb.get_coeffs(True) interval = mb.lower, (mb.lower - mb.r) / 2 self._test_result(coeffs, interval, np.nan, np.nan) # No roots on the right from the stationary point coeffs = mb.get_coeffs(True) interval = mb.upper - (mb.upper - mb.r) / 2, mb.upper self._test_result(coeffs, interval, np.nan, np.nan) # No roots because the metaball does not reach y = 1. mb = Metaball(0.0, 0.5, 0.0) coeffs = mb.get_coeffs(True) interval = mb.lower, mb.upper self._test_result(coeffs, interval, np.nan, np.nan)
def test_thickness(self): def test(coeffs, roots, expected_thickness, expected_previous, previous=np.nan, last_derivative_sgn=-2, expected_last_derivative_sgn=None): thickness_res, previous_res, last_derivative_sgn_res = \ self.get_thickness_addition(coeffs, roots, previous, last_derivative_sgn) np.testing.assert_almost_equal(expected_thickness, thickness_res, decimal=self.precision_places) np.testing.assert_almost_equal(expected_previous, previous_res, decimal=self.precision_places) if expected_last_derivative_sgn is not None: np.testing.assert_almost_equal(expected_last_derivative_sgn, last_derivative_sgn_res) mb_0 = Metaball(1.0, 2.0, 0.0) coeffs = mb_0.get_coeffs(True) # Simple cases, no previous, no last accounted value. roots = 4 * [np.nan] test(coeffs, roots, 0.0, np.nan, np.nan) roots_base = [-2.5, -1, 1, 2.5, np.nan] for i in range(1, 5): roots[:i] = roots_base[:i] if i % 2 == 0: # Roots are coupled. test(coeffs, roots + [np.nan], i / 2 * 1.5, np.nan) else: # Previous is created. test(coeffs, roots + [np.nan], (i - 1) / 2 * 1.5, roots[i - 1], expected_last_derivative_sgn=sgn( f(derivative(coeffs), roots[i]))) # Previous is not nan. roots = [-1, 1, 1.5, np.nan, np.nan] previous = -3 test(coeffs, roots, 2, roots[1], previous=previous, last_derivative_sgn=f(derivative(coeffs), previous)) roots = [-1, 1, 2.5, 4, np.nan] previous = -3 test(coeffs, roots, 3.5, np.nan, previous=previous) # The leftmost root was coupled before, just skip the current # first root. roots = [1, 2, np.nan, np.nan, np.nan] previous = np.nan last_accounted = 1 - self.pixel_size / 2 last_derivative_sgn = sgn(f(derivative(coeffs), last_accounted)) test(coeffs, roots, 0, roots[1], previous, last_derivative_sgn) # The leftmost root existed before, but was not coupled. # Take the root into account. roots = [1, 2, np.nan, np.nan, np.nan] previous = 1 - self.pixel_size / 2 last_derivative_sgn = sgn(f(derivative(coeffs), previous)) test(coeffs, roots, 1, np.nan, previous, last_derivative_sgn) # Extremum in the middle of the new roots, but the surrounding # roots lie on the opposite sides, thus take them into account. roots = [-2.1, -self.pixel_size / 4, self.pixel_size / 4, 2.1, np.nan] test(coeffs, roots, roots[1] - roots[0] + roots[3] - roots[2], np.nan) # Make the middle roots to be on the same slope of the extremum, # thus do not take them both into account. # First root is not coupled with anything, thus must be coupled # with one of them. roots = [-1, self.pixel_size / 4, self.pixel_size / 2, 2.5, np.nan] test(coeffs, roots, roots[1] - roots[0], roots[-2]) # Create some previous value in order for the first root to be # coupled with some previous, then one of the two roots is saved # as previous. roots = [-1, self.pixel_size / 4, self.pixel_size / 2, np.nan, np.nan] previous = -2.5 test(coeffs, roots, roots[0] - previous, roots[1], previous=previous) # Multiple roots. roots = [1, 1, 1, 1, np.nan] test(coeffs, roots, 0, 1, expected_last_derivative_sgn=sgn(f(derivative(coeffs), 1))) # Multiple roots with previous. previous = 1 last_derivative_sgn = sgn(f(derivative(coeffs), previous)) test(coeffs, roots, 0, previous, expected_last_derivative_sgn=last_derivative_sgn) # Left multiple roots. roots = [-1, -1, 1.5, 1.7, np.nan] test(coeffs, roots, roots[2] - roots[0], np.nan) # Right multiple roots. roots = [-1, 1, 2.5, 2.5, np.nan] test(coeffs, roots, roots[1] - roots[0], roots[-2]) # Stationary points as roots which are not extrema. roots = [-2, 2, np.nan, np.nan, np.nan] test(coeffs, roots, 4, np.nan, np.nan) # More than polynomial degree roots. roots = [-2.5, -1, 1, 2.5, 2.5] test(coeffs, roots, 3, np.nan)
def test_thickness(self): def test(coeffs, roots, expected_thickness, expected_previous, previous=np.nan, last_derivative_sgn=-2, expected_last_derivative_sgn=None): thickness_res, previous_res, last_derivative_sgn_res = \ self.get_thickness_addition(coeffs, roots, previous, last_derivative_sgn) np.testing.assert_almost_equal(expected_thickness, thickness_res, decimal=self.precision_places) np.testing.assert_almost_equal(expected_previous, previous_res, decimal=self.precision_places) if expected_last_derivative_sgn is not None: np.testing.assert_almost_equal(expected_last_derivative_sgn, last_derivative_sgn_res) mb_0 = Metaball(1.0, 2.0, 0.0) coeffs = mb_0.get_coeffs(True) # Simple cases, no previous, no last accounted value. roots = 4 * [np.nan] test(coeffs, roots, 0.0, np.nan, np.nan) roots_base = [-2.5, -1, 1, 2.5, np.nan] for i in range(1, 5): roots[:i] = roots_base[:i] if i % 2 == 0: # Roots are coupled. test(coeffs, roots + [np.nan], i / 2 * 1.5, np.nan) else: # Previous is created. test(coeffs, roots + [np.nan], (i - 1) / 2 * 1.5, roots[i - 1], expected_last_derivative_sgn=sgn(f(derivative(coeffs), roots[i]))) # Previous is not nan. roots = [-1, 1, 1.5, np.nan, np.nan] previous = -3 test(coeffs, roots, 2, roots[1], previous=previous, last_derivative_sgn=f(derivative(coeffs), previous)) roots = [-1, 1, 2.5, 4, np.nan] previous = -3 test(coeffs, roots, 3.5, np.nan, previous=previous) # The leftmost root was coupled before, just skip the current # first root. roots = [1, 2, np.nan, np.nan, np.nan] previous = np.nan last_accounted = 1 - self.pixel_size / 2 last_derivative_sgn = sgn(f(derivative(coeffs), last_accounted)) test(coeffs, roots, 0, roots[1], previous, last_derivative_sgn) # The leftmost root existed before, but was not coupled. # Take the root into account. roots = [1, 2, np.nan, np.nan, np.nan] previous = 1 - self.pixel_size / 2 last_derivative_sgn = sgn(f(derivative(coeffs), previous)) test(coeffs, roots, 1, np.nan, previous, last_derivative_sgn) # Extremum in the middle of the new roots, but the surrounding # roots lie on the opposite sides, thus take them into account. roots = [-2.1, -self.pixel_size / 4, self.pixel_size / 4, 2.1, np.nan] test(coeffs, roots, roots[1] - roots[0] + roots[3] - roots[2], np.nan) # Make the middle roots to be on the same slope of the extremum, # thus do not take them both into account. # First root is not coupled with anything, thus must be coupled # with one of them. roots = [-1, self.pixel_size / 4, self.pixel_size / 2, 2.5, np.nan] test(coeffs, roots, roots[1] - roots[0], roots[-2]) # Create some previous value in order for the first root to be # coupled with some previous, then one of the two roots is saved # as previous. roots = [-1, self.pixel_size / 4, self.pixel_size / 2, np.nan, np.nan] previous = -2.5 test(coeffs, roots, roots[0] - previous, roots[1], previous=previous) # Multiple roots. roots = [1, 1, 1, 1, np.nan] test(coeffs, roots, 0, 1, expected_last_derivative_sgn=sgn(f(derivative(coeffs), 1))) # Multiple roots with previous. previous = 1 last_derivative_sgn = sgn(f(derivative(coeffs), previous)) test(coeffs, roots, 0, previous, expected_last_derivative_sgn=last_derivative_sgn) # Left multiple roots. roots = [-1, -1, 1.5, 1.7, np.nan] test(coeffs, roots, roots[2] - roots[0], np.nan) # Right multiple roots. roots = [-1, 1, 2.5, 2.5, np.nan] test(coeffs, roots, roots[1] - roots[0], roots[-2]) # Stationary points as roots which are not extrema. roots = [-2, 2, np.nan, np.nan, np.nan] test(coeffs, roots, 4, np.nan, np.nan) # More than polynomial degree roots. roots = [-2.5, -1, 1, 2.5, 2.5] test(coeffs, roots, 3, np.nan)
def test_one_metaball(self): # Regular two roots. for r in np.linspace(self.pixel_size / 4, 1.0 - self.pixel_size, 10): mb = Metaball(r, 1.0, 0.0) coeffs = mb.get_coeffs(True) interval = (mb.lower, mb.upper) self._test_result(coeffs, interval, -r, r) # One root left mb = Metaball(1.0, 2.0, 0.0) coeffs = mb.get_coeffs(True) interval = mb.lower, mb.center - mb.r / 2 self._test_result(coeffs, interval, -1.0, np.nan) # One root right interval = mb.center + mb.r / 2, mb.upper self._test_result(coeffs, interval, 1, np.nan) # Make interval end points stationary. coeffs = mb.get_coeffs(True) interval = (mb.lower, 0) self._test_result(coeffs, interval, -1, np.nan) interval = (0, mb.upper) self._test_result(coeffs, interval, 1, np.nan) # Put interval end points to roots. # In right endpoint. interval = mb.lower, -1.0 self._test_result(coeffs, interval, -1.0) # In left endpoint. interval = -1.0, 0.0 self._test_result(coeffs, interval) # By epsilon too far -> no root expected interval = mb.lower, - 1 - self.pixel_size self._test_result(coeffs, interval) interval = 1.0, mb.upper # By epsilon too far -> no root expected interval = 1.0 + self.pixel_size, mb.upper self._test_result(coeffs, interval, np.nan, np.nan) # Left end point is a root. coeffs = coeffs - np.array([0, 0, 0, 0, f(coeffs, mb.center)]) interval = 0.0, mb.upper self._test_result(coeffs, interval, 0.0) # Right end point is a root. interval = mb.lower, 0.0 self._test_result(coeffs, interval, 0.0) # No roots coeffs = mb.get_coeffs(True) coeffs = coeffs - np.array([0, 0, 0, 0, f(coeffs, mb.center) + 1]) interval = mb.lower, mb.upper self._test_result(coeffs, interval, np.nan, np.nan) # No roots on the left from the stationary point coeffs = mb.get_coeffs(True) interval = mb.lower, (mb.lower - mb.r) / 2 self._test_result(coeffs, interval, np.nan, np.nan) # No roots on the right from the stationary point coeffs = mb.get_coeffs(True) interval = mb.upper - (mb.upper - mb.r) / 2, mb.upper self._test_result(coeffs, interval, np.nan, np.nan) # No roots because the metaball does not reach y = 1. mb = Metaball(0.0, 0.5, 0.0) coeffs = mb.get_coeffs(True) interval = mb.lower, mb.upper self._test_result(coeffs, interval, np.nan, np.nan)