def losses(self, wghts, step, gargs_batch): """Compute PDE and Lyapunov losses separately.""" xM, xdM, LM = gargs_batch[0] # (Un)batched data wghts_agg = gargs_batch[1] # Previous modes kotd = len(wghts_agg) npts, ndim = np.shape(xM) l_pde=0.0 OUT = self.nnet(wghts, xM, wghts_agg) LU = np.einsum('abi,ai->ab', LM, OUT) UtLU = np.einsum('ai,ai->a', OUT, LU) UUtLU = OUT*UtLU[:,np.newaxis] RHS = LU - UUtLU for jj in range(kotd): OUTjj = self.nnet(wghts_agg[jj], xM, wghts_agg[0:jj]) LUjj = np.einsum('abi,ai->ab', LM, OUTjj) PROJ1 = np.einsum('ai,ai->a', OUTjj, LU) PROJ2 = np.einsum('ai,ai->a', OUT, LUjj) RHS = RHS - OUTjj*(PROJ1[:,np.newaxis] + PROJ2[:,np.newaxis]) for ii in range(npts): dudx = jacobian(self.nnet,1)(wghts, xM[ii,:], wghts_agg) LHS = np.dot(dudx,xdM[ii,:]) l_pde = l_pde + np.sum((LHS-RHS[ii,:])**2)/npts l_lya = -np.sinh(np.mean(UtLU)) # Lyapunov regularization return l_pde, l_lya
def test_first_order_cv(self, tol): """Test variance of a first order CV expectation value""" dev = qml.device('default.gaussian', wires=1) @qml.qnode(dev) def circuit(r, phi): qml.Squeezing(r, 0, wires=0) qml.Rotation(phi, wires=0) return qml.var(qml.X(0)) r = 0.543 phi = -0.654 var = circuit(r, phi) expected = np.exp(2 * r) * np.sin(phi)**2 + np.exp( -2 * r) * np.cos(phi)**2 assert np.allclose(var, expected, atol=tol, rtol=0) # circuit jacobians gradA = circuit.jacobian([r, phi], method='A') gradF = circuit.jacobian([r, phi], method='F') expected = np.array([ 2 * np.exp(2 * r) * np.sin(phi)**2 - 2 * np.exp(-2 * r) * np.cos(phi)**2, 2 * np.sinh(2 * r) * np.sin(2 * phi) ]) assert np.allclose(gradA, expected, atol=tol, rtol=0) assert np.allclose(gradF, expected, atol=tol, rtol=0)
def test_function_overloading(): a = pe.pseudo_Obs(17, 2.9, 'e1') b = pe.pseudo_Obs(4, 0.8, 'e1') fs = [ lambda x: x[0] + x[1], lambda x: x[1] + x[0], lambda x: x[0] - x[1], lambda x: x[1] - x[0], lambda x: x[0] * x[1], lambda x: x[1] * x[0], lambda x: x[0] / x[1], lambda x: x[1] / x[0], lambda x: np.exp(x[0]), lambda x: np.sin(x[0]), lambda x: np.cos(x[0]), lambda x: np.tan(x[0]), lambda x: np.log(x[0]), lambda x: np.sqrt(np.abs(x[0])), lambda x: np.sinh(x[0]), lambda x: np.cosh(x[0]), lambda x: np.tanh(x[0]) ] for i, f in enumerate(fs): t1 = f([a, b]) t2 = pe.derived_observable(f, [a, b]) c = t2 - t1 assert c.is_zero() assert np.log(np.exp(b)) == b assert np.exp(np.log(b)) == b assert np.sqrt(b**2) == b assert np.sqrt(b)**2 == b np.arcsin(1 / b) np.arccos(1 / b) np.arctan(1 / b) np.arctanh(1 / b) np.sinc(1 / b)
def _sinch(x): """ Stably evaluate sinch. Notes ----- The strategy of falling back to a sixth order Taylor expansion was suggested by the Spallation Neutron Source docs which was found on the internet by google search. http://www.ornl.gov/~t6p/resources/xal/javadoc/gov/sns/tools/math/ElementaryFunction.html The details of the cutoff point and the Horner-like evaluation was picked without reference to anything in particular. Note that sinch is not currently implemented in scipy.special, whereas the "engineer's" definition of sinc is implemented. The implementation of sinc involves a scaling factor of pi that distinguishes it from the "mathematician's" version of sinc. """ # If x is small then use sixth order Taylor expansion. # How small is small? I am using the point where the relative error # of the approximation is less than 1e-14. # If x is large then directly evaluate sinh(x) / x. x2 = x * x if abs(x) < 0.0135: return 1 + (x2 / 6.) * (1 + (x2 / 20.) * (1 + (x2 / 42.))) else: return np.sinh(x) / x
def test_cv_gradients_multiple_gate_parameters(self, gaussian_dev, tol): "Tests that gates with multiple free parameters yield correct gradients." par = [0.4, -0.3, -0.7, 0.2] def qf(r0, phi0, r1, phi1): qml.Squeezing(r0, phi0, wires=[0]) qml.Squeezing(r1, phi1, wires=[0]) return qml.expval(qml.NumberOperator(0)) q = qml.QNode(qf, gaussian_dev) grad_F = q.jacobian(par, method='F') grad_A = q.jacobian(par, method='A') grad_A2 = q.jacobian(par, method='A', force_order2=True) # analytic method works for every parameter assert q.grad_method_for_par == {i:'A' for i in range(4)} # the different methods agree assert grad_A == pytest.approx(grad_F, abs=tol) assert grad_A2 == pytest.approx(grad_F, abs=tol) # check against the known analytic formula r0, phi0, r1, phi1 = par dn = np.zeros([4]) dn[0] = np.cosh(2 * r1) * np.sinh(2 * r0) + np.cos(phi0 - phi1) * np.cosh(2 * r0) * np.sinh(2 * r1) dn[1] = -0.5 * np.sin(phi0 - phi1) * np.sinh(2 * r0) * np.sinh(2 * r1) dn[2] = np.cos(phi0 - phi1) * np.cosh(2 * r1) * np.sinh(2 * r0) + np.cosh(2 * r0) * np.sinh(2 * r1) dn[3] = 0.5 * np.sin(phi0 - phi1) * np.sinh(2 * r0) * np.sinh(2 * r1) assert dn[np.newaxis, :] == pytest.approx(grad_F, abs=tol)
def test_cv_gradients_multiple_gate_parameters(self): "Tests that gates with multiple free parameters yield correct gradients." self.logTestName() par = [0.4, -0.3, -0.7, 0.2] def qf(r0, phi0, r1, phi1): qml.Squeezing(r0, phi0, wires=[0]) qml.Squeezing(r1, phi1, wires=[0]) return qml.expval(qml.NumberOperator(0)) q = qml.QNode(qf, self.gaussian_dev) grad_F = q.jacobian(par, method='F') grad_A = q.jacobian(par, method='A') grad_A2 = q.jacobian(par, method='A', force_order2=True) # analytic method works for every parameter self.assertTrue(q.grad_method_for_par == {i:'A' for i in range(4)}) # the different methods agree self.assertAllAlmostEqual(grad_A, grad_F, delta=self.tol) self.assertAllAlmostEqual(grad_A2, grad_F, delta=self.tol) # check against the known analytic formula r0, phi0, r1, phi1 = par dn = np.zeros([4]) dn[0] = np.cosh(2 * r1) * np.sinh(2 * r0) + np.cos(phi0 - phi1) * np.cosh(2 * r0) * np.sinh(2 * r1) dn[1] = -0.5 * np.sin(phi0 - phi1) * np.sinh(2 * r0) * np.sinh(2 * r1) dn[2] = np.cos(phi0 - phi1) * np.cosh(2 * r1) * np.sinh(2 * r0) + np.cosh(2 * r0) * np.sinh(2 * r1) dn[3] = 0.5 * np.sin(phi0 - phi1) * np.sinh(2 * r0) * np.sinh(2 * r1) self.assertAllAlmostEqual(grad_A, dn, delta=self.tol)
def test_number_state_gradient(self, gaussian_dev, tol): "Tests that the automatic gradient of a squeezed state with number state expectation is correct." @qml.qnode(gaussian_dev) def circuit(y): qml.Squeezing(y, 0., wires=[0]) return qml.expval(qml.FockStateProjector(np.array([2, 0]), wires=[0, 1])) grad_fn = autograd.grad(circuit, 0) # (d/dr) |<2|S(r)>|^2 = 0.5 tanh(r)^3 (2 csch(r)^2 - 1) sech(r) for r in sqz_vals[1:]: # formula above is not valid for r=0 autograd_val = grad_fn(r) manualgrad_val = 0.5*np.tanh(r)**3 * (2/(np.sinh(r)**2)-1) / np.cosh(r) assert autograd_val == pytest.approx(manualgrad_val, abs=tol)
def test_number_state_gradient(self): "Tests that the automatic gradient of a squeezed state with number state expectation is correct." self.logTestName() @qml.qnode(self.gaussian_dev) def circuit(y): qml.Squeezing(y, 0., wires=[0]) return qml.expval(qml.NumberState(np.array([2, 0]), wires=[0, 1])) grad_fn = autograd.grad(circuit, 0) # (d/dr) |<2|S(r)>|^2 = 0.5 tanh(r)^3 (2 csch(r)^2 - 1) sech(r) for r in sqz_vals[1:]: # formula above is not valid for r=0 autograd_val = grad_fn(r) manualgrad_val = 0.5*np.tanh(r)**3 * (2/(np.sinh(r)**2)-1) / np.cosh(r) self.assertAlmostEqual(autograd_val, manualgrad_val, delta=self.tol)
def test_man_grad(): a = pe.pseudo_Obs(17, 2.9, 'e1') b = pe.pseudo_Obs(4, 0.8, 'e1') fs = [ lambda x: x[0] + x[1], lambda x: x[1] + x[0], lambda x: x[0] - x[1], lambda x: x[1] - x[0], lambda x: x[0] * x[1], lambda x: x[1] * x[0], lambda x: x[0] / x[1], lambda x: x[1] / x[0], lambda x: np.exp(x[0]), lambda x: np.sin(x[0]), lambda x: np.cos(x[0]), lambda x: np.tan(x[0]), lambda x: np.log(x[0]), lambda x: np.sqrt(x[0]), lambda x: np.sinh(x[0]), lambda x: np.cosh(x[0]), lambda x: np.tanh(x[0]) ] for i, f in enumerate(fs): t1 = f([a, b]) t2 = pe.derived_observable(f, [a, b]) c = t2 - t1 assert c.value == 0.0, str(i) assert np.all(np.abs(c.deltas['e1']) < 1e-14), str(i)
def inv_sa(par, x): return np.sinh((np.arcsinh(x) - par[1]) / par[0])
def test_sinh(): fun = lambda x: 3.0 * np.sinh(x) check_grads(fun)(npr.randn())
anp.reciprocal.defjvp(lambda g, ans, gvs, vs, x: -g / x**2) anp.exp.defjvp(lambda g, ans, gvs, vs, x: ans * g) anp.exp2.defjvp(lambda g, ans, gvs, vs, x: ans * anp.log(2) * g) anp.expm1.defjvp(lambda g, ans, gvs, vs, x: (ans + 1) * g) anp.log.defjvp(lambda g, ans, gvs, vs, x: g / x) anp.log2.defjvp(lambda g, ans, gvs, vs, x: g / x / anp.log(2)) anp.log10.defjvp(lambda g, ans, gvs, vs, x: g / x / anp.log(10)) anp.log1p.defjvp(lambda g, ans, gvs, vs, x: g / (x + 1)) anp.sin.defjvp(lambda g, ans, gvs, vs, x: g * anp.cos(x)) anp.cos.defjvp(lambda g, ans, gvs, vs, x: -g * anp.sin(x)) anp.tan.defjvp(lambda g, ans, gvs, vs, x: g / anp.cos(x)**2) anp.arcsin.defjvp(lambda g, ans, gvs, vs, x: g / anp.sqrt(1 - x**2)) anp.arccos.defjvp(lambda g, ans, gvs, vs, x: -g / anp.sqrt(1 - x**2)) anp.arctan.defjvp(lambda g, ans, gvs, vs, x: g / (1 + x**2)) anp.sinh.defjvp(lambda g, ans, gvs, vs, x: g * anp.cosh(x)) anp.cosh.defjvp(lambda g, ans, gvs, vs, x: g * anp.sinh(x)) anp.tanh.defjvp(lambda g, ans, gvs, vs, x: g / anp.cosh(x)**2) anp.arcsinh.defjvp(lambda g, ans, gvs, vs, x: g / anp.sqrt(x**2 + 1)) anp.arccosh.defjvp(lambda g, ans, gvs, vs, x: g / anp.sqrt(x**2 - 1)) anp.arctanh.defjvp(lambda g, ans, gvs, vs, x: g / (1 - x**2)) anp.rad2deg.defjvp(lambda g, ans, gvs, vs, x: g / anp.pi * 180.0) anp.degrees.defjvp(lambda g, ans, gvs, vs, x: g / anp.pi * 180.0) anp.deg2rad.defjvp(lambda g, ans, gvs, vs, x: g * anp.pi / 180.0) anp.radians.defjvp(lambda g, ans, gvs, vs, x: g * anp.pi / 180.0) anp.square.defjvp(lambda g, ans, gvs, vs, x: g * 2 * x) anp.sqrt.defjvp(lambda g, ans, gvs, vs, x: g * 0.5 * x**-0.5) anp.sinc.defjvp(lambda g, ans, gvs, vs, x: g * (anp.cos( anp.pi * x) * anp.pi * x - anp.sin(anp.pi * x)) / (anp.pi * x**2)) anp.reshape.defjvp(lambda g, ans, gvs, vs, x, shape, order=None: anp.reshape( g, vs.shape, order=order)) anp.roll.defjvp(
def inv_asinh(par, x): return (np.sinh((x - par[0]) / par[1]) * par[3]) + par[2]
def sa(par, y): return np.sinh(par[0] * np.arcsinh(y) + par[1])
def test_sinh(): fun = lambda x : 3.0 * np.sinh(x) check_grads(fun)(npr.randn())
def sal(par, y): return par[0] + par[1] * np.sinh(par[2] * np.arcsinh(y) + par[3])
def test_sinh(): fun = lambda x : 3.0 * np.sinh(x) d_fun = grad(fun) check_grads(fun, npr.randn()) check_grads(d_fun, npr.randn())
def inv_sal(par, x): return np.sinh((np.arcsinh((x - par[0]) / par[1]) - par[3]) / par[2])