def test_matrix(self): dofs = spin.ExponentialMap.random() rot = spin.ExponentialMap(dofs) rot2 = spin.ExponentialMap.from_rotation(rot) rot3 = ExponentialMap(dofs) print(make_title('Cython (from dofs)')) print(rot) print(make_title('Cython (from matrix)')) print(rot2) print(make_title('Python (from dofs)')) print(rot3) axis, angle = rot.axis_angle r, theta, phi = csb.polar3d(axis) axisangle = spin.AxisAngle([theta, phi, angle]) rot4 = spin.Rotation(csb.rotation_matrix(axis, -angle)) self.assertTrue(spin.distance(rot, rot2) < self.tol) self.assertTrue(spin.distance(rot, rot4) < self.tol) self.assertTrue(spin.distance(rot3, rot4) < self.tol) self.assertTrue(spin.distance(rot2, axisangle) < self.tol)
def test_gradients(self): print(make_title('testing gradients')) A = np.random.standard_normal((3,3)) func = [spin.NearestRotation(A, trafo=rot()) for rot in \ (spin.EulerAngles, spin.ExponentialMap, spin.AxisAngle, spin.Quaternion)] func+= [spin.NearestUnitQuaternion(A), spin.NearestQuaternion(A)] out = '{0:>14}: err={1:.3e}, cc={2:.2f}' grads = [] for f in func: x = f.trafo.dofs a = f.gradient(x) b = opt.approx_fprime(x, f, 1e-7) err, cc = compare_grad(a,b) print(out.format(f.trafo.name, err, cc)) self.assertAlmostEqual(err, 0., delta=1e-5) self.assertAlmostEqual(cc, 100., delta=1e-2) grads.append(a)
def test_gradients2(self): """ Compare gradients of both implementations using quaternions """ print(make_title('gradient of quaternion-based implementations')) out = '{0:>20}: err={1:.3e}, cc={2:.2f}' A = np.random.standard_normal((3,3)) f = spin.NearestRotation(A, trafo=spin.Quaternion()) g = spin.NearestQuaternion(A) h = spin.NearestUnitQuaternion(A) q = f.trafo.random() if False else np.random.standard_normal(4) a = f.gradient(q) b = g.gradient(q) c = h.gradient(q) print('f(q)={0:.2e}, g(q)={1:.2e}, h(q)={3:.2e}, norm={2:.2e}'.format( f(q), g(q), np.dot(q,q), h(q))) print(np.corrcoef(a,b)[0,1]) print('{0:>22}: {1}'.format(f.__class__.__name__, np.round(a,3))) print('{0:>22}: {1}'.format(g.__class__.__name__, np.round(b,3))) print('{0:>22}: {1}'.format(h.__class__.__name__, np.round(c,3))) self.assertAlmostEqual(np.linalg.norm(a-b), 0., delta=1e-5)
def test_lsq(self): rotation, score = self.lsq['svd'].optimum() rmsd_ = [ np.sqrt(score / len(self.coords[0])), self.lsq['svd'].rmsd(rotation.matrix), rmsd(*self.coords) ] lsq_ = [0.5 * score, self.lsq['svd'](rotation.matrix)] for name in ('euler', 'axisangle', 'expmap', 'axisangle'): dofs = self.lsq[name].trafo.from_rotation(rotation).dofs lsq_.append(self.lsq[name](dofs)) rmsd_ = np.round(rmsd_, 5) lsq_ = np.round(lsq_, 2) print(make_title('checking LSQ optimization using SVD')) print('RMSD: {0}'.format(rmsd_)) print(' LSQ: {0}'.format(lsq_)) tol = 1e-10 self.assertTrue(np.all(np.fabs(rmsd_ - rmsd_[0]) < tol)) self.assertTrue(np.all(np.fabs(lsq_ - lsq_[0]) < tol)) self.assertAlmostEqual(spin.distance( fit(*self.coords)[0], rotation.matrix), 0., delta=tol)
def test_angles(self): print(make_title('angular distributions')) n = int(1e5) nbins = n // 100 tol = 1e-2 kw_hist = dict(bins=nbins) if sys.version_info[0] == 2: kw_hist['normed'] = True else: kw_hist['density'] = True for angle in (spin.Azimuth, spin.Polar, spin.RotationAngle): x = angle.random(n) p, bins = np.histogram(x, **kw_hist) q = angle.prob(0.5 * (bins[1:] + bins[:-1])) mse = np.mean((q - p)**2) print('{0:>15}: MSE={1:.3e}'.format(angle.__name__, mse)) min_, max_ = angle.axis(2) self.assertTrue(mse < tol) self.assertTrue(min_ <= np.min(x)) self.assertTrue(max_ >= np.max(x))
def test_opt(self): print(make_title('test optimization of least-squares residual')) out = '{0:>14}: #steps={1:3d}, RMSD: {5:.2f}->{2:.2f}, ' + \ 'accuracy: {3:.3e} (rot), {4:.3e} (trans)' start = spin.random_rotation(), np.random.standard_normal(3) * 10 R, t = fit(*self.coords) rot = [] trans = [] rmsds = [] for trafo in self.trafos[1:]: trafo.matrix_vector = start f = spin.LeastSquares(*self.coords, trafo=trafo) x = trafo.dofs.copy() y = opt.fmin_bfgs(f, x, f.gradient, disp=False) rot.append(spin.distance(R, trafo.rotation)) trans.append(np.linalg.norm(t - trafo.translation.vector)) rmsds.append(np.sqrt(2 * f(y) / len(self.coords[0]))) print( out.format(trafo.rotation.name, len(f.values), rmsds[-1], rot[-1], trans[-1], np.sqrt(2 * f(x) / len(self.coords[0])))) self.assertAlmostEqual(rot[-1], 0., delta=1e-5) self.assertAlmostEqual(trans[-1], 0., delta=1e-5) self.assertAlmostEqual(np.std(rmsds), 0., delta=1e-5)
def test_optimizers(self): """ Test various optimizers for finding the optimal rotation in Euler parameterization """ optimizers = OrderedDict() optimizers['nedler-mead'] = opt.fmin optimizers['powell'] = opt.fmin_powell optimizers['bfgs'] = opt.fmin_bfgs print(make_title('Testing different optimizers')) rotation, _ = self.lsq['svd'].optimum() lsq_opt = self.lsq['svd'](rotation.matrix) output = '{0:11s} : min.score={1:.2f}, dist={2:.3e}, nsteps={3:d}' for name, lsq in self.lsq.items(): if name == 'svd': continue print(make_title(lsq.trafo.name)) start = lsq.trafo.random() results = OrderedDict() for method, run in optimizers.items(): lsq.values = [] args = [lsq, start.copy() ] + ([lsq.gradient] if method == 'bfgs' else []) best = run(*args, disp=False) results[method] = np.array(lsq.values) summary = lsq(best), spin.distance(rotation, lsq.trafo), len(lsq.values) print(output.format(*((method, ) + summary))) fig, ax = plt.subplots(1, 1, figsize=(10, 6)) fig.suptitle(lsq.trafo.name) for method, values in results.items(): ax.plot(values, lw=5, alpha=0.7, label=method) ax.axhline(lsq_opt, lw=3, ls='--', color='r') ax.legend()
def test_skew_matrix(self): a, b = np.random.standard_normal((2, 3)) A = spin.rotation.skew_matrix(a) x = np.dot(A, b) y = np.cross(a, b) print(make_title('action of skew matrix vs cross product')) print(' Skew matrix: {0}'.format(np.round(x, 5))) print('Cross product: {0}'.format(np.round(y, 5))) for i in range(len(x)): self.assertAlmostEqual(x[i], y[i], delta=1e-10)
def test_params(self): rigid = self.trafos[0] print(make_title('comparing matrix and vector')) out = '{0:14s} : distance rotation={1:.3e}, translation={2:.3e}' for other in self.trafos[1:]: dist_rot = spin.distance(rigid.rotation, other.rotation) dist_trans = np.linalg.norm(rigid.translation.vector - other.translation.vector) print(out.format(other.rotation.name, dist_rot, dist_trans)) self.assertAlmostEqual(dist_rot, 0., delta=1e-10) self.assertAlmostEqual(dist_trans, 0., delta=1e-10)
def test_gradient(self): """ Numerical vs analytical gradient """ print(make_title('Checking gradient')) out = '{0:>15}: rel.error={1:.2e}, corr={2:.2f}%' for name, lsq in self.lsq.items(): if name == 'svd': continue dofs = lsq.trafo.random() grad = lsq.gradient(dofs) num = opt.approx_fprime(dofs, lsq, 1e-7) err, cc = compare_grad(grad, num) print(out.format(lsq.trafo.name, err, cc)) self.assertAlmostEqual(cc, 100, delta=1e-2) self.assertAlmostEqual(err, 0., delta=1e-5)
def test_grad(self): print(make_title('checking gradient')) pose = spin.random_rotation(), np.random.standard_normal(3) * 10 out = '{0:>14}: corr={1:.1f}, rel.error={2:.3e}' for trafo in self.trafos[1:]: trafo.matrix_vector = pose f = spin.LeastSquares(*self.coords, trafo=trafo) x = trafo.dofs.copy() a = f.gradient(x) b = opt.approx_fprime(x, f, 1e-8) err, cc = compare_grad(a, b) print(out.format(trafo.rotation.name, cc, err)) self.assertAlmostEqual(cc, 100., delta=1e-2) self.assertAlmostEqual(err, 0., delta=1e-5)
def test_rmsd(self): R, t = fit(*self.coords) out = '{0:.2f} ({1})' print(make_title('RMSD')) print(out.format(rmsd(*self.coords), 'SVD')) rmsds = [] for trafo in self.trafos[1:]: trafo.matrix_vector = R, t r = np.mean(np.sum((self.coords[0] - trafo(self.coords[1]))**2, 1))**0.5 print(out.format(r, trafo.rotation.name)) rmsds.append(r) self.assertAlmostEqual(np.std(rmsds), 0., delta=1e-10)
def test_opt(self): """ Constrained optimization to determine the best unit quaternion """ coords = load_coords(['1ake', '4ake']) A = np.dot(coords[0].T,coords[1]) R = fit(*coords)[0] func = spin.NearestUnitQuaternion(A) q_opt = func.optimum().dofs q_opt2 = spin.NearestRotation(A, spin.Quaternion()).optimum().dofs ## constrained optimization constraint = [{'type': 'eq', 'fun': lambda q : np.dot(q,q) - 1}] best = -1e308, None for n_trials in range(10): q_start = spin.Quaternion.random() result = opt.minimize(lambda q: -func(q), q_start, constraints=constraint) q_best = result['x'] * np.sign(result['x'][0]) if abs(constraint[0]['fun'](q_best)) < 1e-10 and func(q_best) > best[0]: best = func(q_best), q_best _, q_best = best print(make_title('finding nearest rotation matrix / unit quaternion')) print(np.round(q_opt, 5)) print(np.round(q_best, 5)) print(np.round(q_opt2, 5)) tol = 1e-5 self.assertTrue(np.linalg.norm(q_opt - q_best) < tol) self.assertTrue(np.linalg.norm(q_opt - q_opt2) < tol)