def test_missing_s2gates(self, num_pairs, tol): """Test identity S2gates are inserted when some (but not all) S2gates are included.""" prog = sf.Program(2 * num_pairs) U = random_interferometer(num_pairs) assert num_pairs > 3 with prog.context as q: ops.S2gate(SQ_AMPLITUDE) | (q[1], q[num_pairs + 1]) ops.S2gate(SQ_AMPLITUDE) | (q[3], q[num_pairs + 3]) ops.Interferometer(U) | tuple(q[i] for i in range(num_pairs)) ops.Interferometer(U) | tuple(q[i] for i in range(num_pairs, 2 * num_pairs)) ops.MeasureFock() | q expected = sf.Program(2 * num_pairs) with expected.context as q: ops.S2gate(SQ_AMPLITUDE) | (q[1], q[num_pairs + 1]) ops.S2gate(0) | (q[0], q[num_pairs + 0]) ops.S2gate(SQ_AMPLITUDE) | (q[3], q[num_pairs + 3]) ops.S2gate(0) | (q[2], q[num_pairs + 2]) ops.Interferometer(U) | tuple(q[i] for i in range(num_pairs)) ops.Interferometer(U) | tuple(q[i] for i in range(num_pairs, 2 * num_pairs)) ops.MeasureFock() | q res = prog.compile("Xunitary") expected = expected.compile("Xunitary") assert program_equivalence(res, expected, atol=tol)
def test_missing_s2gates(self, chip, tol): """Test identity S2gates are inserted when some (but not all) S2gates are included.""" prog = sf.Program(12) U = random_interferometer(6) with prog.context as q: ops.S2gate(SQ_AMPLITUDE) | (q[1], q[7]) ops.S2gate(SQ_AMPLITUDE) | (q[3], q[9]) ops.S2gate(SQ_AMPLITUDE) | (q[5], q[11]) ops.Interferometer(U) | (q[0], q[1], q[2], q[3], q[4], q[5]) ops.Interferometer(U) | (q[6], q[7], q[8], q[9], q[10], q[11]) ops.MeasureFock() | q expected = sf.Program(12) with expected.context as q: ops.S2gate(0) | (q[0], q[6]) ops.S2gate(SQ_AMPLITUDE) | (q[1], q[7]) ops.S2gate(0) | (q[2], q[8]) ops.S2gate(SQ_AMPLITUDE) | (q[3], q[9]) ops.S2gate(0) | (q[4], q[10]) ops.S2gate(SQ_AMPLITUDE) | (q[5], q[11]) ops.Interferometer(U) | (q[0], q[1], q[2], q[3], q[4], q[5]) ops.Interferometer(U) | (q[6], q[7], q[8], q[9], q[10], q[11]) ops.MeasureFock() | q res = prog.compile(chip.short_name) expected = expected.compile(chip.short_name) assert program_equivalence(res, expected, atol=tol)
def test_no_s2gates(self, num_pairs, tol): """Test identity S2gates are inserted when no S2gates are provided.""" prog = sf.Program(2 * num_pairs) U = random_interferometer(num_pairs) with prog.context as q: ops.Interferometer(U) | tuple(q[i] for i in range(num_pairs)) ops.Interferometer(U) | tuple( q[i] for i in range(num_pairs, 2 * num_pairs)) ops.MeasureFock() | q expected = sf.Program(2 * num_pairs) with expected.context as q: for i in range(num_pairs): ops.S2gate(0) | (q[i], q[i + num_pairs]) ops.Interferometer(U) | tuple(q[i] for i in range(num_pairs)) ops.Interferometer(U) | tuple( q[i] for i in range(num_pairs, 2 * num_pairs)) ops.MeasureFock() | q res = prog.compile(compiler="Xcov") expected = expected.compile(compiler="Xcov") assert program_equivalence(res, expected, atol=tol)
def test_all_samples_batched(self): """Test the case of storing all samples for batches""" batch_size = 2 eng = sf.Engine("tf", backend_options={ "batch_size": batch_size, "cutoff_dim": 6 }) prog = sf.Program(5) with prog.context as q: ops.MeasureFock() | q[2] ops.MeasureFock() | (q[1], q[3]) ops.MeasureFock() | q[2] result = eng.run(prog) assert len(result.all_samples[1]) == 1 assert len(result.all_samples[3]) == 1 assert len(result.all_samples[2]) == 2 assert np.array_equal(result.all_samples[1][0].numpy(), np.array([[0], [0]])) assert np.array_equal(result.all_samples[3][0].numpy(), np.array([[0], [0]])) assert np.array_equal(result.all_samples[2][0].numpy(), np.array([[0], [0]])) assert np.array_equal(result.all_samples[2][1].numpy(), np.array([[0], [0]]))
def test_interferometers(self, tol): """Test interferometers correctly decompose to MZ gates""" prog = sf.Program(4) U = random_interferometer(2) with prog.context as q: ops.S2gate(0.5) | (q[0], q[2]) ops.S2gate(0.5) | (q[1], q[3]) ops.Interferometer(U) | (q[0], q[1]) ops.Interferometer(U) | (q[2], q[3]) ops.MeasureFock() | q res = prog.compile("chip0") expected = sf.Program(4) with expected.context as q: ops.S2gate(0.5, 0) | (q[0], q[2]) ops.S2gate(0.5, 0) | (q[1], q[3]) ops.Interferometer(U, mesh="rectangular_symmetric", drop_identity=False) | (q[0], q[1]) ops.Interferometer(U, mesh="rectangular_symmetric", drop_identity=False) | (q[2], q[3]) ops.MeasureFock() | q expected = expected.compile(DummyCircuit()) assert program_equivalence(res, expected, atol=tol)
def test_interferometers(self, tol): """Test that the compilation correctly decomposes the interferometer using the rectangular_symmetric mesh""" prog = sf.Program(8) U = random_interferometer(4) with prog.context as q: ops.S2gate(SQ_AMPLITUDE, 0) | (q[0], q[4]) ops.S2gate(SQ_AMPLITUDE, 0) | (q[1], q[5]) ops.S2gate(SQ_AMPLITUDE, 0) | (q[2], q[6]) ops.S2gate(SQ_AMPLITUDE, 0) | (q[3], q[7]) ops.Interferometer(U) | (q[0], q[1], q[2], q[3]) ops.Interferometer(U) | (q[4], q[5], q[6], q[7]) ops.MeasureFock() | q res = prog.compile("X8_01") expected = sf.Program(8) with expected.context as q: ops.S2gate(SQ_AMPLITUDE, 0) | (q[0], q[4]) ops.S2gate(SQ_AMPLITUDE, 0) | (q[1], q[5]) ops.S2gate(SQ_AMPLITUDE, 0) | (q[2], q[6]) ops.S2gate(SQ_AMPLITUDE, 0) | (q[3], q[7]) ops.Interferometer(U, mesh="rectangular_symmetric", drop_identity=False) | (q[0], q[1], q[2], q[3]) ops.Interferometer(U, mesh="rectangular_symmetric", drop_identity=False) | (q[4], q[5], q[6], q[7]) ops.MeasureFock() | q expected = expected.compile(DummyCircuit()) assert program_equivalence(res, expected, atol=tol)
def test_no_s2gates(self, num_pairs, tol): """Test identity S2gates are inserted when no S2gates are provided.""" prog = sf.Program(2 * num_pairs) U = random_interferometer(num_pairs) with prog.context as q: ops.Interferometer(U) | tuple(q[i] for i in range(num_pairs)) ops.Interferometer(U) | tuple(q[i] for i in range(num_pairs, 2 * num_pairs)) ops.MeasureFock() | q expected = sf.Program(2 * num_pairs) with expected.context as q: for i in range(num_pairs): ops.S2gate(0) | (q[i], q[i + num_pairs]) ops.Interferometer(U) | tuple(q[i] for i in range(num_pairs)) ops.Interferometer(U) | tuple(q[i] for i in range(num_pairs, 2 * num_pairs)) ops.MeasureFock() | q # with pytest.raises(CircuitError, match="There can be no operations before the S2gates."): res = prog.compile("Xunitary") # with pytest.raises(CircuitError, match="There can be no operations before the S2gates."): expected = expected.compile("Xunitary") assert program_equivalence(res, expected, atol=tol)
def test_no_s2gates(self, tol): """Test identity S2gates are inserted when no S2gates are provided.""" prog = sf.Program(8) U = random_interferometer(4) with prog.context as q: ops.Interferometer(U) | (q[0], q[1], q[2], q[3]) ops.Interferometer(U) | (q[4], q[5], q[6], q[7]) ops.MeasureFock() | q expected = sf.Program(8) with expected.context as q: ops.S2gate(0) | (q[0], q[4]) ops.S2gate(0) | (q[1], q[5]) ops.S2gate(0) | (q[2], q[6]) ops.S2gate(0) | (q[3], q[7]) ops.Interferometer(U) | (q[0], q[1], q[2], q[3]) ops.Interferometer(U) | (q[4], q[5], q[6], q[7]) ops.MeasureFock() | q res = prog.compile("X8_01") expected = expected.compile("X8_01") assert program_equivalence(res, expected, atol=tol)
def test_interferometers(self, chip, tol): """Test that the compilation correctly decomposes the interferometer using the rectangular_symmetric mesh""" prog = sf.Program(12) U = random_interferometer(6) with prog.context as q: ops.S2gate(SQ_AMPLITUDE, 0) | (q[0], q[6]) ops.S2gate(SQ_AMPLITUDE, 0) | (q[1], q[7]) ops.S2gate(SQ_AMPLITUDE, 0) | (q[2], q[8]) ops.S2gate(SQ_AMPLITUDE, 0) | (q[3], q[9]) ops.S2gate(SQ_AMPLITUDE, 0) | (q[4], q[10]) ops.S2gate(SQ_AMPLITUDE, 0) | (q[5], q[11]) ops.Interferometer(U) | (q[0], q[1], q[2], q[3], q[4], q[5]) ops.Interferometer(U) | (q[6], q[7], q[8], q[9], q[10], q[11]) ops.MeasureFock() | q res = prog.compile(chip.short_name) expected = sf.Program(12) with expected.context as q: for i, j in np.arange(12).reshape(2, -1).T: ops.S2gate(SQ_AMPLITUDE, 0) | (q[i], q[j]) ops.Interferometer(U, mesh="rectangular_symmetric", drop_identity=False) | (q[0], q[1], q[2], q[3], q[4], q[5]) ops.Interferometer(U, mesh="rectangular_symmetric", drop_identity=False) | (q[6], q[7], q[8], q[9], q[10], q[11]) ops.MeasureFock() | q expected = expected.compile(DummyCircuit()) assert program_equivalence(res, expected, atol=tol)
def test_measurefock_shots(self, setup_eng): """Tests that passing shots with a program containing MeasureFock returns a result whose entries have the right shapes and values""" shots = 5 expected = np.zeros(dtype=int, shape=(shots,)) # all modes eng, p1 = setup_eng(3) with p1.context as q: ops.MeasureFock() | q samples = eng.run(p1, shots=shots).samples.astype(int) assert samples.shape == (shots, 3) assert all(samples[:, 0] == expected) assert all(samples[:, 1] == expected) assert all(samples[:, 2] == expected) # some modes eng, p2 = setup_eng(3) with p2.context as q: ops.MeasureFock() | (q[0], q[2]) samples = eng.run(p2, shots=shots).samples assert samples.shape == (shots, 3) assert all(samples[:, 0].astype(int) == expected) assert all(s is None for s in samples[:, 1]) assert all(samples[:, 2].astype(int) == expected) # one mode eng, p3 = setup_eng(3) with p3.context as q: ops.MeasureFock() | q[0] samples = eng.run(p3, shots=shots).samples assert samples.shape == (shots, 3) assert all(samples[:, 0].astype(int) == expected) assert all(s is None for s in samples[:, 1]) assert all(s is None for s in samples[:, 2])
def test_all_samples_multi_runs(self, eng): """Test that consequtive engine runs reset the _all_samples attribute by checking the length of the attribute.""" prog = sf.Program(5) with prog.context as q: ops.MeasureFock() | q[2] ops.MeasureFock() | (q[1], q[3]) ops.MeasureX | q[2] result = eng.run(prog) # Check that the number of all the samples equals to the number # of measurements assert result.all_samples[1] == [0] assert result.all_samples[3] == [0] assert [bool(i) for i in result.all_samples[2]] == [0,1] prog = sf.Program(5) with prog.context as q: ops.MeasureFock() | q[0] result = eng.run(prog) # Check that _all_samples contains the same elements and new items were # not appended assert result.all_samples[0] == [0]
def test_interferometers(self, num_pairs, tol): """Test that the compilation correctly decomposes the interferometer using the rectangular_symmetric mesh""" prog = sf.Program(2 * num_pairs) U = random_interferometer(num_pairs) with prog.context as q: for i in range(0, num_pairs): ops.S2gate(SQ_AMPLITUDE) | (q[i], q[i + num_pairs]) ops.Interferometer(U) | tuple(q[i] for i in range(num_pairs)) ops.Interferometer(U) | tuple(q[i] for i in range(num_pairs, 2 * num_pairs)) ops.MeasureFock() | q res = prog.compile("Xunitary") expected = sf.Program(2 * num_pairs) with expected.context as q: for i in range(0, num_pairs): ops.S2gate(SQ_AMPLITUDE) | (q[i], q[i + num_pairs]) ops.Interferometer(U, mesh="rectangular_symmetric", drop_identity=False) | tuple( q[i] for i in range(num_pairs) ) ops.Interferometer(U, mesh="rectangular_symmetric", drop_identity=False) | tuple( q[i] for i in range(num_pairs, 2 * num_pairs) ) ops.MeasureFock() | q expected = expected.compile(DummyCircuit()) # Note that since DummyCircuit() has a hard coded limit of 8 modes we only check for this number assert program_equivalence(res, expected, atol=tol, compare_params=False)
def test_equivalence_different_params(self, compare_params): """Programs with different parameters differ.""" prog_1 = sf.Program(2) prog_2 = sf.Program(2) with prog_1.context as q: ops.Fock(2) | q[0] ops.BSgate() | (q[0], q[1]) ops.MeasureFock() | q[1] with prog_2.context as q: ops.Fock(1) | q[0] # different parameter ops.BSgate() | (q[0], q[1]) ops.MeasureFock() | q[1] if compare_params: # should NOT be equivalent assert not prog_1.equivalence(prog_2, compare_params=compare_params) assert not prog_2.equivalence(prog_1, compare_params=compare_params) else: # should be equivalent assert prog_1.equivalence(prog_2, compare_params=compare_params) assert prog_2.equivalence(prog_1, compare_params=compare_params)
def test_GBS_compile_measure_same_twice(self): """Tests that GBS compilation fails when the same mode is measured more than once.""" prog = sf.Program(3) with prog.context as q: ops.Dgate(1.0) | q[0] ops.MeasureFock() | q[0] ops.MeasureFock() | q with pytest.raises(program.CircuitError, match="Measuring the same mode more than once."): prog.compile('gbs')
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_all_samples_multi_meas_per_mode(self, eng): """Test that samples are stored for the correct modes with multiple measurements on certain modes.""" prog = sf.Program(5) with prog.context as q: ops.MeasureFock() | q[2] ops.MeasureFock() | (q[1], q[3]) ops.MeasureX | q[2] result = eng.run(prog) assert result.all_samples[1] == [0] assert result.all_samples[3] == [0] assert [bool(i) for i in result.all_samples[2]] == [0,1]
def test_keyerror_assert_modes_dict(self): """Check that the correct error is raised when calling `prog.assert_number_of_measurements` with an incorrect device spec modes entry.""" # set maximum number of measurements to 2, and measure 3 in prog below device_dict = { "target": "simulon_gaussian", "modes": { "max": { "pnr": 2, "homodyne": 2, "heterodyne": 2 } }, "layout": "", "gate_parameters": {}, "compiler": ["gaussian"], } device = sf.Device(spec=device_dict) prog = sf.Program(3) with prog.context as q: for reg in q: ops.MeasureFock() | reg match = "Expected keys for the maximum allowed number of PNR" with pytest.raises(KeyError, match=match): prog.assert_modes(device)
def test_generate_code_with_engine(self, engine_kwargs): """Test generating code for a regular program with an engine""" prog = sf.Program(3) eng = sf.Engine(**engine_kwargs) with prog.context as q: ops.Sgate(0.54, 0) | q[0] ops.BSgate(0.45, np.pi / 2) | (q[0], q[2]) ops.Sgate(3 * np.pi / 2, 0) | q[1] ops.BSgate(2 * np.pi, 0.62) | (q[0], q[1]) ops.MeasureFock() | q[0] results = eng.run(prog) code = io.generate_code(prog, eng) code_list = code.split("\n") formatting_str = f"\"{engine_kwargs['backend']}\"" if "backend_options" in engine_kwargs: formatting_str += ( ", backend_options=" f'{{"cutoff_dim": {engine_kwargs["backend_options"]["cutoff_dim"]}}}' ) expected = prog_txt.format(engine_args=formatting_str).split("\n") for i, row in enumerate(code_list): assert row == expected[i]
def runJob(self, eng): num_subsystem = 8 prog = sf.Program(num_subsystem, name="remote_job") U = random_interferometer(4) with prog.context as q: # Initial squeezed states # Allowed values are r=1.0 or r=0.0 ops.S2gate(1.0) | (q[0], q[4]) ops.S2gate(1.0) | (q[1], q[5]) ops.S2gate(1.0) | (q[3], q[7]) # Interferometer on the signal modes (0-3) ops.Interferometer(U) | (q[0], q[1], q[2], q[3]) ops.BSgate(0.543, 0.123) | (q[2], q[0]) ops.Rgate(0.453) | q[1] ops.MZgate(0.65, -0.54) | (q[2], q[3]) # *Same* interferometer on the idler modes (4-7) ops.Interferometer(U) | (q[4], q[5], q[6], q[7]) ops.BSgate(0.543, 0.123) | (q[6], q[4]) ops.Rgate(0.453) | q[5] ops.MZgate(0.65, -0.54) | (q[6], q[7]) ops.MeasureFock() | q eng = eng results =eng.run(prog, shots=10) # state = results.state # measurements = results.samples return results.samples
def test_mz_gate_standard(self, tol): """Test that the Mach-Zehnder gate compiles to give the correct unitary for some specific standard parameters""" prog = sf.Program(8) with prog.context as q: ops.MZgate(np.pi / 2, np.pi) | (q[0], q[1]) ops.MZgate(np.pi, 0) | (q[2], q[3]) ops.MZgate(np.pi / 2, np.pi) | (q[4], q[5]) ops.MZgate(np.pi, 0) | (q[6], q[7]) ops.MeasureFock() | q # compile the program using the X8_01 spec res = prog.compile("X8_01") # remove the Fock measurements res.circuit = res.circuit[:-1] # extract the Gaussian symplectic matrix O = res.compile("gaussian_unitary").circuit[0].op.p[0] # By construction, we know that the symplectic matrix is # passive, and so represents a unitary matrix U = O[:8, :8] + 1j * O[8:, :8] # the constructed program should implement the following # unitary matrix expected = np.array([[0.5 - 0.5j, -0.5 + 0.5j, 0, 0], [0.5 - 0.5j, 0.5 - 0.5j, 0, 0], [0, 0, -1, -0], [0, 0, -0, 1]]) expected = block_diag(expected, expected) assert np.allclose(U, expected, atol=tol)
def test_nothing_happens_and_nothing_crashes(self): """Test that even a program that does nothing compiles correctly""" n_modes = 4 squeezing_amplitudes = [0] * n_modes unitary = np.identity(n_modes) prog = sf.Program(n_modes * 2) with prog.context as q: for i in range(n_modes): ops.S2gate(squeezing_amplitudes[i]) | (q[i], q[i + n_modes]) for qumodes in (q[:n_modes], q[n_modes:]): ops.Interferometer(unitary) | qumodes ops.MeasureFock() | q res = prog.compile(compiler="Xcov") # check that all squeezing is 0 assert all(cmd.op.p[0] == 0 for cmd in res.circuit if isinstance(cmd.op, ops.S2gate)) # check that all phase shifts are 0 assert all(cmd.op.p[0] == 0 for cmd in res.circuit if isinstance(cmd.op, ops.Rgate)) # check that all MZgate angles are pi assert all(cmd.op.p[0] == np.pi for cmd in res.circuit if isinstance(cmd.op, ops.MZgate))
def test_GBS_success(self): """GBS check passes.""" prog = sf.Program(3) with prog.context as q: ops.Sgate(1.0) | q[0] ops.MeasureFock() | q[0] ops.BSgate(1.4, 0.4) | q[1:3] ops.MeasureFock() | q[2] ops.Rgate(-1.0) | q[1] ops.MeasureFock() | q[1] prog = prog.compile('gbs') assert len(prog) == 4 last_cmd = prog.circuit[-1] assert isinstance(last_cmd.op, ops.MeasureFock) assert [x.ind for x in last_cmd.reg] == list(range(3))
def pre_measure(self): ops.MeasureFock() | self.q # pylint: disable=pointless-statement, expression-not-assigned # RemoteEngine.run includes compilation that checks the validity of the # defined Program results = self.eng.run(self.prog, shots=self.shots) self.samples = results.samples
def test_equivalence_different_gates(self, compare_params): """Programs with different circuits differ.""" prog_1 = sf.Program(2) prog_2 = sf.Program(2) with prog_1.context as q: ops.Fock(2) | q[0] ops.BSgate() | (q[0], q[1]) ops.MeasureFock() | q[1] with prog_2.context as q: ops.Fock(2) | q[0] ops.MeasureFock() | q[1] # should NOT be equivalent assert not prog_1.equivalence(prog_2, compare_params=compare_params) assert not prog_2.equivalence(prog_1, compare_params=compare_params)
def prog(): """Program fixture.""" program = Program(8) with program.context as q: ops.Rgate(0.5) | q[0] ops.Rgate(0.5) | q[4] ops.MeasureFock() | q return program
def test_unknown_circuit_spec(self): """Test an unknown compile target.""" prog = sf.Program(3) with prog.context as q: ops.MeasureFock() | q with pytest.raises(ValueError, match="Unknown compiler 'foo'"): new_prog = prog.compile(compiler='foo')
def test_unknown_circuit_spec(self): """Test an unknown compile target.""" prog = sf.Program(3) with prog.context as q: ops.MeasureFock() | q with pytest.raises(ValueError, match="Could not find target 'foo' in the Strawberry Fields circuit database"): new_prog = prog.compile(target='foo')
def test_combining_samples(self, eng): """Check that samples are combined correctly when using multiple measurements""" prog = sf.Program(5) with prog.context as q: ops.MeasureX | q[2] ops.MeasureFock() | (q[1], q[3]) ops.MeasureFock() | q[0] result = eng.run(prog) # check that shape is (shots, measured_modes) assert result.samples.shape == (1, 4) # check that MesureFock measures `0` while MeasureX does NOT measure `0`. correct_samples = [0, 0, 1, 0] assert [bool(i) for i in result.samples[0]] == correct_samples
def test_measuring_same_modes(self, eng): """Check that only the last measurement is returned when measuring the same mode twice""" prog = sf.Program(5) with prog.context as q: ops.MeasureFock() | q[2] ops.MeasureFock() | (q[1], q[3]) ops.MeasureX | q[2] result = eng.run(prog) # check that shape is (shots, measured_modes) assert result.samples.shape == (1, 3) # check that MesureFock measures `0` while MeasureX does NOT measure `0`. correct_samples = [0, 1, 0] assert [bool(i) for i in result.samples[0]] == correct_samples
def test_GBS_compile_ops_after_measure(self): """Tests that GBS compilation fails when there are operations following a Fock measurement.""" prog = sf.Program(2) with prog.context as q: ops.MeasureFock() | q ops.Rgate(1.0) | q[0] with pytest.raises(program.CircuitError, match="Operations following the Fock measurements."): prog.compile('gbs')