Skip to content

Commit

Permalink
Merge pull request #504 from quantumlib/qsim-repeat-samples
Browse files Browse the repository at this point in the history
Use qsim for repeated sampling
  • Loading branch information
95-martin-orion authored Feb 11, 2022
2 parents 8f7f940 + d3fd8dd commit 15595ec
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 19 deletions.
24 changes: 24 additions & 0 deletions pybind_interface/pybind_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,15 @@ class SimulatorHelper {
return helper.release_state_to_python();
}

static std::vector<uint64_t> sample_final_state(
const py::dict &options, bool is_noisy, uint64_t num_samples) {
auto helper = SimulatorHelper(options, is_noisy);
if (!helper.is_valid || !helper.simulate(0)) {
return {};
}
return helper.sample(num_samples);
}

template <typename StateType>
static std::vector<std::complex<double>> simulate_expectation_values(
const py::dict &options,
Expand Down Expand Up @@ -753,6 +762,11 @@ class SimulatorHelper {
return result;
}

std::vector<uint64_t> sample(uint64_t num_samples) {
StateSpace state_space = factory.CreateStateSpace();
return state_space.Sample(state, num_samples, seed);
}

py::array_t<float> release_state_to_python() {
StateSpace state_space = factory.CreateStateSpace();
state_space.InternalToNormalOrder(state);
Expand Down Expand Up @@ -931,6 +945,16 @@ qtrajectory_simulate_moment_expectation_values(

// Methods for sampling.

std::vector<uint64_t> qsim_sample_final(
const py::dict &options, uint64_t num_samples) {
return SimulatorHelper::sample_final_state(options, false, num_samples);
}

std::vector<uint64_t> qtrajectory_sample_final(
const py::dict &options, uint64_t num_samples) {
return SimulatorHelper::sample_final_state(options, true, num_samples);
}

std::vector<unsigned> qsim_sample(const py::dict &options) {
Circuit<Cirq::GateCirq<float>> circuit;
try {
Expand Down
12 changes: 12 additions & 0 deletions pybind_interface/pybind_main.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ py::array_t<float> qsim_simulate_fullstate(
const py::dict &options, const py::array_t<float> &input_vector);

std::vector<unsigned> qsim_sample(const py::dict &options);
std::vector<uint64_t> qsim_sample_final(
const py::dict &options, uint64_t num_samples);

// Methods for simulating noisy circuits.
std::vector<std::complex<float>> qtrajectory_simulate(const py::dict &options);
Expand All @@ -100,6 +102,8 @@ py::array_t<float> qtrajectory_simulate_fullstate(
const py::dict &options, const py::array_t<float> &input_vector);

std::vector<unsigned> qtrajectory_sample(const py::dict &options);
std::vector<uint64_t> qtrajectory_sample_final(
const py::dict &options, uint64_t num_samples);

// As above, but returning expectation values instead.
std::vector<std::complex<double>> qsim_simulate_expectation_values(
Expand Down Expand Up @@ -200,8 +204,12 @@ std::vector<std::complex<float>> qsimh_simulate(const py::dict &options);
\
/* Methods for returning samples */ \
m.def("qsim_sample", &qsim_sample, "Call the qsim sampler"); \
m.def("qsim_sample_final", &qsim_sample_final, \
"Call the qsim final-state sampler"); \
m.def("qtrajectory_sample", &qtrajectory_sample, \
"Call the qtrajectory sampler"); \
m.def("qtrajectory_sample_final", &qtrajectory_sample_final, \
"Call the qtrajectory final-state sampler"); \
\
using GateCirq = qsim::Cirq::GateCirq<float>; \
using OpString = qsim::OpString<GateCirq>; \
Expand Down Expand Up @@ -400,8 +408,12 @@ std::vector<std::complex<float>> qsimh_simulate(const py::dict &options);
\
/* Methods for returning samples */ \
m.def("qsim_sample", &qsim_sample, "Call the qsim sampler"); \
m.def("qsim_sample_final", &qsim_sample_final, \
"Call the qsim final-state sampler"); \
m.def("qtrajectory_sample", &qtrajectory_sample, \
"Call the qtrajectory sampler"); \
m.def("qtrajectory_sample_final", &qtrajectory_sample_final, \
"Call the qtrajectory final-state sampler"); \
\
using GateCirq = qsim::Cirq::GateCirq<float>; \
using OpString = qsim::OpString<GateCirq>; \
Expand Down
33 changes: 14 additions & 19 deletions qsimcirq/qsim_simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -349,19 +349,7 @@ def _sample_measure_results(
)

noisy = _needs_trajectories(program)
if noisy:
translator_fn_name = "translate_cirq_to_qtrajectory"
sampler_fn = self._sim_module.qtrajectory_sample
else:
translator_fn_name = "translate_cirq_to_qsim"
sampler_fn = self._sim_module.qsim_sample

if (
not noisy
and program.are_all_measurements_terminal()
and repetitions > 1
and num_qubits <= 32 # max length of ndarray.shape
):
if not noisy and program.are_all_measurements_terminal() and repetitions > 1:
# Measurements must be replaced with identity gates to sample properly.
# Simply removing them may omit qubits from the circuit.
for i in range(len(program.moments)):
Expand All @@ -378,12 +366,12 @@ def _sample_measure_results(
cirq.QubitOrder.DEFAULT,
)
options["s"] = self.get_seed()
final_state = self._sim_module.qsim_simulate_fullstate(options, 0)
full_results = cirq.sample_state_vector(
final_state.view(np.complex64),
range(num_qubits),
repetitions=repetitions,
seed=self._prng,
raw_results = self._sim_module.qsim_sample_final(options, repetitions)
full_results = np.array(
[
[bool(result & (1 << q)) for q in reversed(range(num_qubits))]
for result in raw_results
]
)

for key, op in meas_ops.items():
Expand All @@ -393,6 +381,13 @@ def _sample_measure_results(
results[key] = full_results[:, meas_indices] ^ invert_mask

else:
if noisy:
translator_fn_name = "translate_cirq_to_qtrajectory"
sampler_fn = self._sim_module.qtrajectory_sample
else:
translator_fn_name = "translate_cirq_to_qsim"
sampler_fn = self._sim_module.qsim_sample

options["c"], _ = self._translate_circuit(
program,
translator_fn_name,
Expand Down

0 comments on commit 15595ec

Please sign in to comment.