def test_checkpoints(self, backend): """Test history checkpoints work when creating and deleting modes.""" eng, q = sf.Engine(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() eng.run(backend) assert not alice.active assert charlie.active # check that reset_queue() restores the latest RegRef checkpoint # (created by eng.run() above) eng.reset_queue() assert not alice.active assert charlie.active with eng: diana, = ops.New(1) ops.Del | charlie assert not charlie.active assert diana.active eng.reset_queue() assert charlie.active assert not diana.active 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])
def test_create(self, prog): """test creating a mode""" q = prog.register assert prog.num_subsystems == 2 new_q, = ops.New(1) assert new_q.active assert prog.num_subsystems == 3
def test_create_locked(self, prog): """No new modes can be created in a locked Program.""" prog.lock() with pytest.raises( CircuitError, match="The Program is locked, no new subsystems can be created" ): ops.New(1)
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_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_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_subsystems(self, setup_eng, tol): """Check that the backend keeps in sync with the program when creating and deleting modes.""" null = sf.Program(2) # empty program eng, prog = setup_eng(2) # 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.Del | bob D.H | charlie ops.MeasureX | charlie def check_reg(p, expected_n=None): """Compare Program.register with the mode list returned by the backend. They should always be in agreement after Engine.run() and Engine.reset(). """ rr = p.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]) state = eng.run(null) check_reg(null, 2) state = eng.run(prog).state check_reg(prog, 1) # state norm must be invariant if isinstance(eng.backend, BaseFock): assert np.allclose(state.trace(), 1, atol=tol, rtol=0) # check that reset() works eng.reset() # the regrefs are reset as well assert np.all([r.val is None for r in prog.register])
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_create_delete_multiple_modes(self): """test creating and deleting multiple modes""" prog = sf.Program(3) with prog.context as (alice, bob, charlie): edward, frank, grace = ops.New(3) ops.Del | (alice, grace) # register should only return the active subsystems q = prog.register assert len(q) == prog.num_subsystems assert len(q) == 4 # Program.reg_refs contains all the regrefs, active and inactive assert len(prog.reg_refs) == 6
def test_create_delete_multiple_modes(self): """test creating and deleting multiple modes""" eng, (alice, bob, charlie) = sf.Engine(3) with eng: edward, frank, grace = ops.New(3) ops.Del | (alice, grace) # register should only return the active subsystems q = eng.register assert len(q) == eng.num_subsystems assert len(q) == 4 # Engine.reg_refs contains all the regrefs, active and inactive assert len(eng.reg_refs) == 6
def test_parent_program(self): """Continuing one program with another.""" D = ops.Dgate(0.5) prog = sf.Program(3) with prog.context as q: D | q[1] ops.Del | q[0] cont = sf.Program(prog) with cont.context as q: D | q[0] r = ops.New(1) D | r assert cont.can_follow(prog) assert prog.reg_refs == cont.init_reg_refs assert prog.unused_indices == cont.init_unused_indices
def test_create_non_positive_integer(self, prog): """number of new modes must be a positive integer""" with pytest.raises(ValueError, match='is not a positive integer'): ops.New(-2) with pytest.raises(ValueError, match='is not a positive integer'): ops.New(1.5)
def test_create_outside_program_context(self): """New() must be only called inside a Program context.""" with pytest.raises( RuntimeError, match='can only be called inside a Program context'): ops.New()
def test_create(self, eng): """test creating a mode""" q = eng.register new_q, = ops.New(1) assert new_q.active