diff --git a/pathfinder/model/dot.py b/pathfinder/model/dot.py index e0ee759..9e6ed6d 100644 --- a/pathfinder/model/dot.py +++ b/pathfinder/model/dot.py @@ -35,3 +35,24 @@ class Dot(): # Will throw problems for irregular points, but that's okay for now. chunked_pts = [pts[i: i + pt_length] for i in range(0, len(pts), pt_length)] return sum(self.v_for_point(pt) for pt in chunked_pts) - self.v + + def jac_pt(self, pt: numpy.ndarray) -> numpy.ndarray: + p = pt[0:3] # hardcoded here because chances + s = pt[3:6] # are we'll only ever work in 3d. + + diff = self.r - s + + p_divs = diff / (numpy.linalg.norm(diff)**3) + + r_divs = -p / (numpy.linalg.norm(diff)**3) + 3 * p.dot(diff) * diff / (numpy.linalg.norm(diff)**5) + + return numpy.append(p_divs, r_divs) + + def jac(self, pts: numpy.ndarray) -> numpy.ndarray: + # 6 because dipole in 3d has 6 degrees of freedom. + pt_length = 6 + # creates numpy.ndarrays in groups of pt_length. + # Will throw problems for irregular points, but that's okay for now. + chunked_pts = [pts[i: i + pt_length] for i in range(0, len(pts), pt_length)] + + return numpy.append([], [self.jac_pt(pt) for pt in chunked_pts]) diff --git a/tests/model/test_dot.py b/tests/model/test_dot.py index 7be9185..858d958 100644 --- a/tests/model/test_dot.py +++ b/tests/model/test_dot.py @@ -23,3 +23,23 @@ def test_dot_v_from_dipole(): numpy.testing.assert_allclose(dot.v_for_point(pt), target, err_msg="v from dipole at a dot was incorrect!") numpy.testing.assert_allclose(dot.cost(pt), cost, err_msg="cost from dipole at a dot was incorrect!") + + +def test_dot_jac(): + # for a dot located at (1, 2, 3) + dot = model.Dot(50, (1, 2, 3)) + + # and dipole located at (4, 7, 11) with p=(8, 9, 10) + pt = numpy.array((8, 9, 10, 4, 7, 11)) + + target_jac_pt = [-0.003092303707812889, -0.005153839513021483, -0.00824614322083437, 0.0058585481811285, 0.01423090787983279, 0.02730483137919137] + + # assume a second dipole at (12, 13, -5), with p = (-1, -2, -3) + second_target = [-0.002054993939616119, -0.002054993939616119, 0.001494541046993541, 5.494636202182148e-6, 0.0001923122670763748, 0.0006923241614749492] + pt2 = numpy.array((-1, -2, -3, 12, 13, -5)) + jac_row_target = target_jac_pt + second_target + pts = numpy.append(pt, pt2) + + assert len(dot.jac_pt(pt)) == 6 + numpy.testing.assert_allclose(dot.jac_pt(pt), target_jac_pt, err_msg="Jac pt doesn't match Mathematica result.") + numpy.testing.assert_allclose(dot.jac(pts), jac_row_target, err_msg="whole row should match")