def test_decomposition_interferometer_with_zero(self, tol): """Test that an graph is correctly decomposed when the interferometer has one zero somewhere in the unitary matrix, which is the case for the adjacency matrix below""" n = 6 prog = sf.Program(n) A = np.array( [ [0, 1, 0, 0, 1, 1], [1, 0, 1, 0, 1, 1], [0, 1, 0, 1, 1, 0], [0, 0, 1, 0, 1, 0], [1, 1, 1, 1, 0, 1], [1, 1, 0, 0, 1, 0], ] ) _, U = dec.graph_embed(A) assert not np.allclose(U, np.identity(n)) G = ops.GraphEmbed(A) cmds = G.decompose(prog.register) last_op = cmds[-1].op param_val = last_op.p[0] assert isinstance(last_op, ops.Interferometer) assert last_op.ns == n assert np.allclose(param_val, U, atol=tol, rtol=0)
def test_decomposition(self, tol): """Test that an graph is correctly decomposed""" n = 3 prog = sf.Program(n) A = np.random.random([n, n]) + 1j * np.random.random([n, n]) A += A.T A -= np.trace(A) * np.identity(n) / n sq, U = dec.graph_embed(A) G = ops.GraphEmbed(A) cmds = G.decompose(prog.register) assert np.all(sq == G.sq) assert np.all(U == G.U) S = np.identity(2 * n) # calculating the resulting decomposed symplectic for cmd in cmds: # all operations should be BSgates, Rgates, or Sgates assert isinstance( cmd.op, (ops.Interferometer, ops.BSgate, ops.Rgate, ops.Sgate) ) # build up the symplectic transform modes = [i.ind for i in cmd.reg] if isinstance(cmd.op, ops.Sgate): S = _squeezing(cmd.op.p[0], cmd.op.p[1], modes, n) @ S if isinstance(cmd.op, ops.Rgate): S = _rotation(cmd.op.p[0], modes, n) @ S if isinstance(cmd.op, ops.BSgate): S = _beamsplitter(cmd.op.p[0], cmd.op.p[1], modes, n) @ S if isinstance(cmd.op, ops.Interferometer): U1 = cmd.op.p[0] S_U = np.vstack( [np.hstack([U1.real, -U1.imag]), np.hstack([U1.imag, U1.real])] ) S = S_U @ S # the resulting covariance state cov = S @ S.T # calculate Hamilton's A matrix: A = X.(I-Q^{-1})* A_res = np.real_if_close(Amat(cov)) # The bottom right corner of A_res should be identical to A, # up to some constant scaling factor. Check if the ratio # of all elements is one ratio = np.real_if_close(A_res[n:, n:] / A) ratio /= ratio[0, 0] assert np.allclose(ratio, np.ones([n, n]), atol=tol, rtol=0)
def test_mean_photon(self, tol): """Test that the mean photon number is correct in graph_embed""" num_modes = 6 A = np.random.random([num_modes, num_modes]) + 1j * np.random.random([num_modes, num_modes]) A += A.T n_mean = 10.0 / num_modes sc, _ = dec.graph_embed(A, mean_photon_per_mode=n_mean) n_mean_calc = np.mean(np.sinh(sc) ** 2) assert np.allclose(n_mean, n_mean_calc, atol=tol, rtol=0)
def test_mean_photon(self, tol): """This test verifies that the maximum amount of squeezing used to encode the graph is indeed capped by the parameter max_mean_photon""" max_mean_photon = 2 A = np.random.random([6, 6]) + 1j * np.random.random([6, 6]) A += A.T sc, _ = dec.graph_embed(A, max_mean_photon=max_mean_photon) res_mean_photon = np.sinh(np.max(np.abs(sc)))**2 assert np.allclose(res_mean_photon, max_mean_photon, atol=tol, rtol=0)
def test_takagi_fixed_random_symm(self): """This test verifies that the maximum amount of squeezing used to encode the graph is indeed capped by the parameter max_mean_photon""" self.logTestName() error = np.empty(nsamples) max_mean_photon = 2 for i in range(nsamples): X = random_degenerate_symmetric() sc, U = dec.graph_embed(X, max_mean_photon=max_mean_photon) error[i] = np.sinh(np.max(np.abs(sc)))**2 - max_mean_photon self.assertAlmostEqual(error.mean(), 0, delta=self.tol)
def test_make_traceless(self, monkeypatch, tol): """Test that A is properly made traceless""" A = np.random.random([6, 6]) + 1j * np.random.random([6, 6]) A += A.T assert not np.allclose(np.trace(A), 0, atol=tol, rtol=0) with monkeypatch.context() as m: # monkeypatch the takagi function to simply return A, # so that we can inspect it and make sure it is now traceless m.setattr(dec, "takagi", lambda A, tol: (np.ones([6]), A)) _, A_out = dec.graph_embed(A, make_traceless=True) assert np.allclose(np.trace(A_out), 0, atol=tol, rtol=0)
def test_decomposition(self, hbar, tol): """Test that an graph is correctly decomposed""" n = 3 prog = sf.Program(n) A = np.random.random([n, n]) + 1j * np.random.random([n, n]) A += A.T A -= np.trace(A) * np.identity(n) / 3 sq, U = dec.graph_embed(A) G = ops.GraphEmbed(A) cmds = G.decompose(prog.register) assert np.all(sq == G.sq) assert np.all(U == G.U) S = np.identity(2 * n) # calculating the resulting decomposed symplectic for cmd in cmds: # all operations should be BSgates, Rgates, or Sgates assert isinstance( cmd.op, (ops.Interferometer, ops.BSgate, ops.Rgate, ops.Sgate)) # build up the symplectic transform modes = [i.ind for i in cmd.reg] if isinstance(cmd.op, ops.Sgate): S = _squeezing(cmd.op.p[0].x, cmd.op.p[1].x, modes, n) @ S if isinstance(cmd.op, ops.Rgate): S = _rotation(cmd.op.p[0].x, modes, n) @ S if isinstance(cmd.op, ops.BSgate): S = _beamsplitter(cmd.op.p[0].x, cmd.op.p[1].x, modes, n) @ S if isinstance(cmd.op, ops.Interferometer): U1 = cmd.op.p[0].x S_U = np.vstack([ np.hstack([U1.real, -U1.imag]), np.hstack([U1.imag, U1.real]) ]) S = S_U @ S # the resulting covariance state cov = S @ S.T # calculate Hamilton's A matrix: A = X.(I-Q^{-1})* I = np.identity(n) O = np.zeros_like(I) X = np.block([[O, I], [I, O]]) x = cov[:n, :n] xp = cov[:n, n:] p = cov[n:, n:] aidaj = (x + p + 1j * (xp - xp.T) - 2 * I) / 4 aiaj = (x - p + 1j * (xp + xp.T)) / 4 Q = np.block([[aidaj, aiaj.conj()], [aiaj, aidaj.conj()] ]) + np.identity(2 * n) A_res = X @ (np.identity(2 * n) - np.linalg.inv(Q)).conj() # The bottom right corner of A_res should be identical to A, # up to some constant scaling factor. Check if the ratio # of all elements is one ratio = np.real_if_close(A_res[n:, n:] / A) ratio /= ratio[0, 0] assert np.allclose(ratio, np.ones([n, n]), atol=tol, rtol=0)
def test_symmetric_validation(self): """Test that the graph_embed decomposition raises exception if not symmetric""" A = np.random.random([5, 5]) + 1j * np.random.random([5, 5]) with pytest.raises(ValueError, match="matrix is not symmetric"): dec.graph_embed(A)