def test_nonexistent_index(self, prog): """Acting on a non-existent mode raises an error.""" with prog.context as q: with pytest.raises(IndexError): ops.Dgate(0.5) | q[3] with pytest.raises(program.RegRefError, match="does not exist"): ops.Dgate(0.5) | 3
def test_deleted_index(self, prog): """Test that acting on a deleted mode raises an error""" with prog.context as q: ops.Del | q[0] with pytest.raises(program.RegRefError, match="been deleted"): ops.Dgate(0.5) | 0 with pytest.raises(program.RegRefError, match="been deleted"): ops.Dgate(0.5) | q[0]
def test_nonexistent(self): """Test that acting on a non-existent mode raises an error""" eng, q = sf.Engine(2) with eng: with pytest.raises(IndexError): ops.Dgate(0.5) | q[3] with pytest.raises(engine.RegRefError, match="does not exist"): ops.Dgate(0.5) | 3
def test_print_commands(self, eng, prog): """Program.print and Engine.print_applied return correct strings.""" prog = sf.Program(2) # store the result of the print command in list res res = [] # use a print function that simply appends the operation # name to the results list print_fn = lambda x: res.append(x.__str__()) # prog should now be empty prog.print(print_fn) assert res == [] # define some gates D = ops.Dgate(0.5) BS = ops.BSgate(2 * np.pi, np.pi / 2) R = ops.Rgate(np.pi) with prog.context as q: alice, bob = q D | alice BS | (alice, bob) ops.Del | alice R | bob charlie, = ops.New(1) BS | (bob, charlie) ops.MeasureX | bob ops.Dgate(bob.par).H | charlie ops.Del | bob ops.MeasureX | charlie res = [] prog.print(print_fn) expected = [ "Dgate(0.5, 0) | (q[0])", "BSgate(6.283, 1.571) | (q[0], q[1])", "Del | (q[0])", "Rgate(3.142) | (q[1])", "New(1)", "BSgate(6.283, 1.571) | (q[1], q[2])", "MeasureX | (q[1])", "Dgate(q1, 0).H | (q[2])", "Del | (q[1])", "MeasureX | (q[2])", ] assert res == expected # NOTE optimization can change gate order result = eng.run(prog, compile_options={'optimize': False}) res = [] eng.print_applied(print_fn) assert res == ["Run 0:"] + expected
def test_deleted(self): """Test that acting on a deleted mode raises an error""" eng, q = sf.Engine(2) with eng: ops.Del | q[0] with pytest.raises(engine.RegRefError, match="been deleted"): ops.Dgate(0.5) | 0 with pytest.raises(engine.RegRefError, match="been deleted"): ops.Dgate(0.5) | q[0]
def test_print_commands(self, backend): """Test print_queue and print_applied returns correct strings.""" eng, q = sf.Engine(2) # define some gates D = ops.Dgate(0.5) BS = ops.BSgate(2 * np.pi, np.pi / 2) R = ops.Rgate(np.pi) # get register references alice, bob = q with eng: D | alice BS | (alice, bob) ops.Del | alice R | bob charlie, = ops.New(1) BS | (bob, charlie) ops.MeasureX | bob ops.Dgate(bob).H | charlie ops.Del | bob ops.MeasureX | charlie res = [] print_fn = lambda x: res.append(x.__str__()) eng.print_queue(print_fn) expected = [ "Dgate(0.5, 0) | (q[0])", "BSgate(6.283, 1.571) | (q[0], q[1])", "Del | (q[0])", "Rgate(3.142) | (q[1])", "New(1) ", "BSgate(6.283, 1.571) | (q[1], q[2])", "MeasureX | (q[1])", "Dgate(RR(q[1]), 0).H | (q[2])", "Del | (q[1])", "MeasureX | (q[2])", ] assert res == expected state = eng.run(backend) # queue should now be empty res = [] eng.print_queue(print_fn) assert res == [] # print_applied should now not be empty res = [] eng.print_applied(print_fn) assert res == ["Run 0:"] + expected
def test_GBS_compile_nonconsec_measurefock(self): """Tests that GBS compilation fails when Fock measurements are made with an intervening gate.""" prog = sf.Program(2) with prog.context as q: ops.Dgate(1.0) | q[0] ops.MeasureFock() | q[0] ops.Dgate(-1.0) | q[1] ops.BSgate(-0.5, 2.0) | q # intervening gate ops.MeasureFock() | q[1] with pytest.raises(program.CircuitError, match="The Fock measurements are not consecutive."): prog.compile('gbs')
def test_print_commands(self, eng, prog): """Program.print and Engine.print_applied return correct strings.""" prog = sf.Program(2) res = [] print_fn = lambda x: res.append(x.__str__()) # prog should now be empty prog.print(print_fn) assert res == [] # define some gates D = ops.Dgate(0.5) BS = ops.BSgate(2 * np.pi, np.pi / 2) R = ops.Rgate(np.pi) with prog.context as q: alice, bob = q D | alice BS | (alice, bob) ops.Del | alice R | bob charlie, = ops.New(1) BS | (bob, charlie) ops.MeasureX | bob ops.Dgate(bob).H | charlie ops.Del | bob ops.MeasureX | charlie res = [] prog.print(print_fn) expected = [ "Dgate(0.5, 0) | (q[0])", "BSgate(6.283, 1.571) | (q[0], q[1])", "Del | (q[0])", "Rgate(3.142) | (q[1])", "New(1)", "BSgate(6.283, 1.571) | (q[1], q[2])", "MeasureX | (q[1])", "Dgate(RR(q[1]), 0).H | (q[2])", "Del | (q[1])", "MeasureX | (q[2])", ] assert res == expected state = eng.run( prog, compile=False ) # FIXME optimization can change gate order, however this is not a good way of avoiding it res = [] eng.print_applied(print_fn) assert res == ["Run 0:"] + expected
def test_regref_transform(self, setup_eng): """Test circuit with regref transforms doesn't raise any errors""" eng, prog = setup_eng(2) with prog.context as q: ops.MeasureX | q[0] ops.Sgate(q[0]) | q[1] # symbolic hermitian conjugate together with register reference ops.Dgate(q[0]).H | q[1] ops.Sgate(ops.RR(q[0], lambda x: x ** 2)) | q[1] ops.Dgate(ops.RR(q[0], lambda x: -x)).H | q[1] eng.run(prog)
def test_measured_parameter(self, setup_eng): """Test that a circuit with measured parameters executes successfully.""" eng, prog = setup_eng(2) with prog.context as q: ops.MeasureX | q[0] ops.Sgate(q[0].par) | q[1] # symbolic hermitian conjugate together with register reference ops.Dgate(q[0].par, 0).H | q[1] # algebraic transformation ops.Sgate(q[0].par**2) | q[1] # algebraic transformation and h.c. ops.Dgate(q[0].par, np.pi).H | q[1] eng.run(prog)
def test_invalid_measurement(self, eng, prog): """Cannot use a measurement before it exists.""" with prog.context as q: ops.Dgate(q[0]) | q[1] with pytest.raises(program.CircuitError, match="nonexistent measurement result"): eng.run(prog)
def test_corrupt_index(self, prog): """User messes up the RegRef indices.""" with prog.context as q: q[0].ind = 1 with pytest.raises(program.RegRefError, match="RegRef state has become inconsistent"): ops.Dgate(0.5) | q[0]
def test_free_parameters(setup_eng, tol): """Programs with free parameters.""" eng, prog = setup_eng(1) x = prog.params("x") # free parameter with prog.context as q: ops.Dgate(x) | q ops.Sgate(-1.2 * x * pf.sin(x**2 - 0.1)) | q with pytest.raises(ParameterError, match="Unknown free parameter"): eng.run(prog, args={"foo": 1.0}) with pytest.raises(ParameterError, match="unbound parameter with no default value"): eng.run(prog) # successful run eng.run(prog, args={x: 0.0}) assert np.all(eng.backend.is_vacuum(tol)) eng.reset() # now set a default value for the free parameter x.default = 0.0 eng.run(prog) assert np.all(eng.backend.is_vacuum(tol)) eng.reset() # override the default x.default = 1.0 eng.run(prog, args={x: 0.0}) assert np.all(eng.backend.is_vacuum(tol))
def test_no_return_state(self, eng, prog): """Engine returns None when no state is requested""" with prog.context as q: ops.Dgate(0.34) | q[0] res = eng.run(prog, return_state=False) assert res is None
def test_extract_arbitrary_unitary_one_mode(self, setup_eng, cutoff, tol): """Test that arbitrary unitary extraction works for 1 mode""" S = ops.Sgate(0.4, -1.2) D = ops.Dgate(2, 0.9) K = ops.Kgate(-1.5) # not a state but it doesn't matter initial_state = np.random.rand(cutoff) + 1j * np.random.rand(cutoff) eng_ref, p0 = setup_eng(1) with p0.context as q: ops.Ket(initial_state) | q prog = sf.Program(p0) with prog.context as q: S | q D | q K | q U = utils.extract_unitary(prog, cutoff_dim=cutoff, backend=eng_ref.backend_name) if isinstance(U, tf.Tensor): with tf.Session() as sess: sess.run(tf.global_variables_initializer()) in_state = tf.constant(initial_state.reshape([-1]), dtype=tf.complex64) final_state = sess.run(tf.einsum("ab,b", U, in_state)) else: final_state = U @ initial_state expected_state = eng_ref.run([p0, prog]).ket() assert np.allclose(final_state, expected_state, atol=tol, rtol=0)
def test_apply_history(self, setup_eng, pure): """Tests the reapply history argument works correctly with a backend""" eng, q = setup_eng(2) a = 0.23 r = 0.1 def inspect(): res = [] print_fn = lambda x: res.append(x.__str__()) eng.print_applied(print_fn) return res with eng: ops.Dgate(a) | q[0] ops.Sgate(r) | q[1] state1 = eng.run() expected = [ "Run 0:", "Dgate({}, 0) | (q[0])".format(a), "Sgate({}, 0) | (q[1])".format(r), ] assert inspect() == expected # reset backend, but reapply history eng.backend.reset(pure=pure) state2 = eng.run(apply_history=True) assert inspect() == expected assert state1 == state2 # append more commands to the same backend with eng: ops.Rgate(r) | q[0] state3 = eng.run() expected = [ "Run 0:", "Dgate({}, 0) | (q[0])".format(a), "Sgate({}, 0) | (q[1])".format(r), "Run 1:", "Rgate({}) | (q[0])".format(r), ] assert inspect() == expected assert not state2 == state3 # reset backend, but reapply history eng.backend.reset(pure=pure) state4 = eng.run(apply_history=True) expected = [ "Run 0:", "Dgate({}, 0) | (q[0])".format(a), "Sgate({}, 0) | (q[1])".format(r), "Rgate({}) | (q[0])".format(r), ] assert inspect() == expected assert state3 == state4
def layer(params, q): """CV quantum neural network layer acting on ``N`` modes. Args: params (list[float]): list of length ``2*(max(1, N-1) + N**2 + n)`` containing the number of parameters for the layer q (list[RegRef]): list of Strawberry Fields quantum registers the layer is to be applied to """ N = len(q) M = int(N * (N - 1)) + max(1, N - 1) int1 = params[:M] s = params[M:M + N] int2 = params[M + N:2 * M + N] dr = params[2 * M + N:2 * M + 2 * N] dp = params[2 * M + 2 * N:2 * M + 3 * N] k = params[2 * M + 3 * N:2 * M + 4 * N] # begin layer interferometer(int1, q) for i in range(N): ops.Sgate(s[i]) | q[i] interferometer(int2, q) for i in range(N): ops.Dgate(dr[i], dp[i]) | q[i] ops.Kgate(k[i]) | q[i]
def test_merge_regrefs(): """Test merging two gates with regref parameters.""" prog = Program(2) with prog.context as q: ops.MeasureX | q[0] D = ops.Dgate(q[0]) F = ops.Dgate(q[0], 0.1) # gates that are the inverse of each other merged = D.merge(D.H) assert merged is None # gates that have different parameters with pytest.raises(MergeFailure, match="Don't know how to merge these gates."): F.merge(D.H)
def test_coherent(self, a, setup_eng, hbar, tol): """Test coherent function matches Gaussian backends""" eng, prog = setup_eng(1) with prog.context as q: ops.Dgate(np.abs(a), np.angle(a)) | q[0] state = eng.run(prog).state mu, cov = utils.coherent_state(np.abs(a), np.angle(a), basis="gaussian", hbar=hbar) if eng.backend_name == "gassian": mu_exp, cov_exp = state.reduced_gaussian(0) assert np.allclose(mu, mu_exp, atol=tol, rtol=0) assert np.allclose(cov, cov_exp, atol=tol, rtol=0) elif eng.backend_name == "bosonic": _, mu_exp, cov_exp = state.reduced_bosonic(0) assert np.allclose(np.expand_dims(mu, axis=0), mu_exp, atol=tol, rtol=0) assert np.allclose(np.expand_dims(cov, axis=0), cov_exp, atol=tol, rtol=0)
def test_extract_arbitrary_unitary_one_mode(self, setup_eng, cutoff, tol): """Test that arbitrary unitary extraction works for 1 mode""" S = ops.Sgate(0.4, -1.2) D = ops.Dgate(2, 0.9) K = ops.Kgate(-1.5) # not a state but it doesn't matter initial_state = np.random.rand(cutoff) + 1j * np.random.rand(cutoff) eng_ref, p0 = setup_eng(1) with p0.context as q: ops.Ket(initial_state) | q prog = sf.Program(p0) with prog.context as q: S | q D | q K | q U = utils.extract_unitary(prog, cutoff_dim=cutoff, backend=eng_ref.backend_name) if isinstance(U, tf.Tensor): U = U.numpy() final_state = U @ initial_state expected_state = eng_ref.run([p0, prog]).state.ket() assert np.allclose(final_state, expected_state, atol=tol, rtol=0)
def test_displaced_squeezed(self, setup_eng, hbar, cutoff, bsize, pure, tol): """Test displaced squeezed function matches Fock backends""" eng, prog = setup_eng(1) a = 0.32 + 0.1j r = 0.112 phi = 0.123 with prog.context as q: ops.Sgate(r, phi) | q[0] ops.Dgate(np.abs(a), np.angle(a)) | q[0] state = eng.run(prog).state ket = utils.displaced_squeezed_state(np.abs(a), np.angle(a), r, phi, basis="fock", fock_dim=cutoff, hbar=hbar) if not pure: expected = state.dm() ket = np.tile(np.outer(ket, ket.conj()), (bsize, 1, 1)) else: expected = state.ket() ket = np.tile(ket, (bsize, 1)) assert np.allclose(expected, ket, atol=tol, rtol=0)
def test_program_subroutine(self, setup_eng, tol): """Simple quantum program with a subroutine and references.""" eng, prog = setup_eng(2) # define some gates D = ops.Dgate(0.5) BS = ops.BSgate(0.7 * np.pi, np.pi / 2) R = ops.Rgate(np.pi / 3) def subroutine(a, b): """Subroutine for the quantum program""" R | a BS | (a, b) R.H | a # main program with prog.context as q: # get register references alice, bob = q ops.All(ops.Vacuum()) | (alice, bob) D | alice subroutine(alice, bob) BS | (alice, bob) subroutine(bob, alice) state = eng.run(prog).state # state norm must be invariant if isinstance(eng.backend, BaseFock): assert np.allclose(state.trace(), 1, atol=tol, rtol=0)
def test_apply_gate(self): """Test successful gate application""" eng, _ = sf.Engine(2) with eng: ops.Dgate(0.5) | 0 assert len(eng.cmd_queue) == 1
def test_invalid(self): """cannot referring to a register with a non-integral or RegRef object""" eng, _ = sf.Engine(2) with eng: with pytest.raises(engine.RegRefError, match="using integers and RegRefs"): ops.Dgate(0) | 1.2
def test_wrong_engine(self): """Test that acting on a mode not belonging to the engine raises error""" eng, _ = sf.Engine(2) q_new = engine.RegRef(3) with eng: with pytest.raises(engine.RegRefError, match="Unknown RegRef."): ops.Dgate(0.5) | q_new
def test_checkpoints(self, setup_eng, tol): """Test history checkpoints work when creating and deleting modes.""" eng, q = setup_eng(2) alice, bob = q # define some gates D = ops.Dgate(0.5) BS = ops.BSgate(2 * np.pi, np.pi / 2) R = ops.Rgate(np.pi) with eng: D | alice BS | (alice, bob) ops.Del | alice R | bob charlie, = ops.New(1) BS | (bob, charlie) ops.MeasureX | bob ops.Del | bob D.H | charlie ops.MeasureX | charlie eng.optimize() state = eng.run() # state norm must be invariant if isinstance(eng.backend, BaseFock): assert np.allclose(state.trace(), 1, atol=tol, rtol=0) def check_reg(self, expected_n=None): """Compare Engine.register with the mode list returned by the backend. They should always be in agreement after Engine.run(), Engine.reset_queue() and Engine.reset(). """ rr = eng.register modes = eng.backend.get_modes() # number of elements assert len(rr) == len(modes) if expected_n is not None: assert len(rr) == expected_n # check indices match assert np.all([r.ind for r in rr] == modes) # activity assert np.all([r.active for r in rr]) # check that reset() works check_reg(1) eng.reset() new_reg = eng.register # original number of modes assert len(new_reg) == len(q) # the regrefs are reset as well assert np.all([r.val is None for r in new_reg]) check_reg(2)
def test_wrong_index(self): """This should never happen!""" eng, q = sf.Engine(2) q[0].ind = 1 with eng: with pytest.raises(engine.RegRefError, match="Should never happen!"): ops.Dgate(0.5) | q[0]
def test_no_return_state(backend): """Tests that engine returns None when no state is requested""" eng, q = sf.Engine(2) with eng: ops.Dgate(0.34) | q[0] res = eng.run(backend, return_state=False) assert res is None
def test_GBS_compile_no_fock_meas(self): """Tests that GBS compilation fails when no fock measurements are made.""" prog = sf.Program(2) with prog.context as q: ops.Dgate(1.0) | q[0] ops.Sgate(-0.5) | q[1] with pytest.raises(program.CircuitError, match="GBS circuits must contain Fock measurements."): prog.compile('gbs')
def test_apply_history(self, eng): """Tests the reapply history argument""" a = 0.23 r = 0.1 def inspect(): res = [] print_fn = lambda x: res.append(x.__str__()) eng.print_applied(print_fn) return res p1 = sf.Program(2) with p1.context as q: ops.Dgate(a) | q[1] ops.Sgate(r) | q[1] eng.run(p1) expected = [ "Run 0:", "Dgate({}, 0) | (q[1])".format(a), "Sgate({}, 0) | (q[1])".format(r), ] assert inspect() == expected # run the program again eng.reset() eng.run(p1) assert inspect() == expected # apply more commands to the same backend p2 = sf.Program(2) with p2.context as q: ops.Rgate(r) | q[1] eng.run(p2) expected = [ "Run 0:", "Dgate({}, 0) | (q[1])".format(a), "Sgate({}, 0) | (q[1])".format(r), "Run 1:", "Rgate({}) | (q[1])".format(r), ] assert inspect() == expected # reapply history eng.reset() eng.run([p1, p2]) #expected = [ # "Run 0:", # "Dgate({}, 0) | (q[1])".format(a), # "Sgate({}, 0) | (q[1])".format(r), # "Rgate({}) | (q[1])".format(r), #] assert inspect() == expected