Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor quantum trajectory simulator. #312

Merged
merged 2 commits into from
Mar 18, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 40 additions & 11 deletions lib/circuit_noisy.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,33 +26,45 @@ namespace qsim {
* Noisy circuit.
*/
template <typename Gate>
using NoisyCircuit = std::vector<Channel<Gate>>;
struct NoisyCircuit {
unsigned num_qubits;
std::vector<Channel<Gate>> channels;
};

template <typename Gate>
using ncircuit_iterator = typename std::vector<Channel<Gate>>::const_iterator;

/**
* Makes a noisy circuit from the clean circuit.
* Channels are added after each qubit of each gate of the clean cicuit.
* Roughly equivalent to cirq.Circuit.with_noise.
* @param num_qubits The number of circuit qubits.
* @param gates The circuit gates.
* @param gbeg, gend The iterator range [gbeg, gend) of circuit gates.
* @param A channel factory to construct channels.
* @return The output noisy circuit.
*/
template <typename Gate, typename ChannelFactory>
inline NoisyCircuit<Gate> MakeNoisy(unsigned num_qubits,
const std::vector<Gate>& gates,
const ChannelFactory& channel_factory) {
inline NoisyCircuit<Gate> MakeNoisy(
unsigned num_qubits,
typename std::vector<Gate>::const_iterator gbeg,
typename std::vector<Gate>::const_iterator gend,
const ChannelFactory& channel_factory) {
NoisyCircuit<Gate> ncircuit;
ncircuit.reserve(4 * gates.size());

for (const auto& gate : gates) {
ncircuit.push_back(MakeChannelFromGate(2 * gate.time, gate));
ncircuit.num_qubits = num_qubits;
ncircuit.channels.reserve(4 * std::size_t(gend - gbeg));

for (auto it = gbeg; it != gend; ++it) {
const auto& gate = *it;

ncircuit.channels.push_back(MakeChannelFromGate(2 * gate.time, gate));

for (auto q : gate.qubits) {
ncircuit.push_back(channel_factory.Create(2 * gate.time + 1, q));
ncircuit.channels.push_back(channel_factory.Create(2 * gate.time + 1, q));
}

for (auto q : gate.controlled_by) {
ncircuit.push_back(channel_factory.Create(2 * gate.time + 1, q));
ncircuit.channels.push_back(channel_factory.Create(2 * gate.time + 1, q));
}
}

Expand All @@ -64,14 +76,31 @@ inline NoisyCircuit<Gate> MakeNoisy(unsigned num_qubits,
* Channels are added after each qubit of each gate of the clean cicuit.
* Roughly equivalent to cirq.Circuit.with_noise.
* @param num_qubits The number of circuit qubits.
* @param gates The circuit gates.
* @param A channel factory to construct channels.
* @return The output noisy circuit.
*/
template <typename Gate, typename ChannelFactory>
inline NoisyCircuit<Gate> MakeNoisy(unsigned num_qubits,
const std::vector<Gate>& gates,
const ChannelFactory& channel_factory) {
return
MakeNoisy<Gate>(num_qubits, gates.begin(), gates.end(), channel_factory);
}

/**
* Makes a noisy circuit from the clean circuit.
* Channels are added after each qubit of each gate of the clean cicuit.
* Roughly equivalent to cirq.Circuit.with_noise.
* @param circuit The input cicuit.
* @param A channel factory to construct channels.
* @return The output noisy circuit.
*/
template <typename Gate, typename ChannelFactory>
inline NoisyCircuit<Gate> MakeNoisy(const Circuit<Gate>& circuit,
const ChannelFactory& channel_factory) {
return MakeNoisy(circuit.num_qubits, circuit.gates, channel_factory);
return MakeNoisy<Gate>(circuit.num_qubits, circuit.gates.begin(),
circuit.gates.end(), channel_factory);
}

} // namespace qsim
Expand Down
110 changes: 58 additions & 52 deletions lib/qtrajectory.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,17 +36,13 @@ class QuantumTrajectorySimulator {
public:
using Fuser = FuserT<IO, const Gate*>;
using StateSpace = typename Simulator::StateSpace;
using State = typename StateSpace::State;
using State = typename Simulator::State;
using MeasurementResult = typename StateSpace::MeasurementResult;

/**
* User-specified parameters for the simulator.
*/
struct Parameter : public Fuser::Parameter {
/**
* Number of threads for the backend quantum circuit simulator.
*/
unsigned num_threads = 1;
/**
* If true, collect statistics of sampled Kraus operator indices.
*/
Expand All @@ -65,9 +61,11 @@ class QuantumTrajectorySimulator {
* Runs the given noisy circuit performing repetitions. Each repetition is
* seeded by repetition ID.
* @param param Options for the quantum trajectory simulator.
* @param num_qubits The number of qubits acted on by 'circuit'.
* @param circuit The noisy circuit to be simulated.
* @param r0, r1 The range of repetition IDs [r0, r1) to perform repetitions.
* @param state_space StateSpace object required to manipulate state vector.
* @param simulator Simulator object. Provides specific implementations for
* applying gates.
* @param measure Function that performs measurements (in the sense of
* computing expectation values, etc). This function should have three
* required parameters [repetition ID (uint64_t), final state vector
Expand All @@ -78,21 +76,26 @@ class QuantumTrajectorySimulator {
* @return True if the simulation completed successfully; false otherwise.
*/
template <typename MeasurementFunc, typename... Args>
static bool Run(const Parameter& param, unsigned num_qubits,
const NoisyCircuit<Gate>& circuit, uint64_t r0, uint64_t r1,
MeasurementFunc&& measure, Args&&... args) {
return Run(param, num_qubits,
circuit.begin(), circuit.end(), r0, r1, measure, args...);
static bool RunBatch(const Parameter& param,
const NoisyCircuit<Gate>& circuit,
uint64_t r0, uint64_t r1, const StateSpace& state_space,
const Simulator& simulator, MeasurementFunc&& measure,
Args&&... args) {
return RunBatch(param, circuit.num_qubits, circuit.channels.begin(),
circuit.channels.end(), r0, r1, state_space, simulator,
measure, args...);
}

/**
* Runs the given noisy circuit performing repetitions. Each repetition is
* seeded by repetition ID.
* @param param Options for the quantum trajectory simulator.
* @param num_qubits The number of qubits acted on by the circuit.
* @param cfirst, clast The range of channels [cfirst, clast) to run
* the circuit.
* @param cbeg, cend The range of channels [cbeg, cend) to run the circuit.
* @param r0, r1 The range of repetition IDs [r0, r1) to perform repetitions.
* @param state_space StateSpace object required to manipulate state vector.
* @param simulator Simulator object. Provides specific implementations for
* applying gates.
* @param measure Function that performs measurements (in the sense of
* computing expectation values, etc). This function should have three
* required parameters [repetition ID (uint64_t), final state vector
Expand All @@ -103,15 +106,15 @@ class QuantumTrajectorySimulator {
* @return True if the simulation completed successfully; false otherwise.
*/
template <typename MeasurementFunc, typename... Args>
static bool Run(const Parameter& param, unsigned num_qubits,
typename NoisyCircuit<Gate>::const_iterator cfirst,
typename NoisyCircuit<Gate>::const_iterator clast,
uint64_t r0, uint64_t r1,
MeasurementFunc&& measure, Args&&... args) {
static bool RunBatch(const Parameter& param, unsigned num_qubits,
ncircuit_iterator<Gate> cbeg,
ncircuit_iterator<Gate> cend,
uint64_t r0, uint64_t r1, const StateSpace& state_space,
const Simulator& simulator, MeasurementFunc&& measure,
Args&&... args) {
std::vector<const Gate*> gates;
gates.reserve(4 * std::size_t(clast - cfirst));
gates.reserve(4 * std::size_t(cend - cbeg));

StateSpace state_space(param.num_threads);
State state = state_space.Null();
State scratch = state_space.Null();

Expand All @@ -122,8 +125,8 @@ class QuantumTrajectorySimulator {
state_space.SetStateZero(state);
}

if (!RunIteration(r, param, num_qubits,
cfirst, clast, gates, scratch, state, stat)) {
if (!RunIteration(param, num_qubits, cbeg, cend, r,
state_space, simulator, gates, scratch, state, stat)) {
return false;
}

Expand All @@ -136,68 +139,73 @@ class QuantumTrajectorySimulator {
/**
* Runs the given noisy circuit one time.
* @param param Options for the quantum trajectory simulator.
* @param num_qubits The number of qubits acted on by 'circuit'.
* @param circuit The noisy circuit to be simulated.
* @param r The repetition ID. The random number generator is seeded by 'r'.
* @param state_space StateSpace object required to manipulate state vector.
* @param simulator Simulator object. Provides specific implementations for
* applying gates.
* @param scratch A temporary state vector. Used for samping Kraus operators.
* @param state The state of the system, to be updated by this method.
* @param stat Statistics of sampled Kraus operator indices and/or measured
* bitstrings, to be populated by this method.
* @return True if the simulation completed successfully; false otherwise.
*/
static bool Run(const Parameter& param, unsigned num_qubits,
const NoisyCircuit<Gate>& circuit, uint64_t r,
State& scratch, State& state, std::vector<uint64_t>& stat) {
return Run(param, num_qubits,
circuit.begin(), circuit.end(), r, scratch, state, stat);
static bool RunOnce(const Parameter& param,
const NoisyCircuit<Gate>& circuit, uint64_t r,
const StateSpace& state_space, const Simulator& simulator,
State& scratch, State& state,
std::vector<uint64_t>& stat) {
return RunOnce(param, circuit.num_qubits, circuit.channels.begin(),
circuit.channels.end(), r, state_space, simulator,
scratch, state, stat);
}

/**
* Runs the given noisy circuit one time.
* @param param Options for the quantum trajectory simulator.
* @param num_qubits The number of qubits acted on by the circuit.
* @param cfirst, clast The range of channels [cfirst, clast) to run
* the circuit.
* @param cbeg, cend The range of channels [cbeg, cend) to run the circuit.
* @param circuit The noisy circuit to be simulated.
* @param r The repetition ID. The random number generator is seeded by 'r'.
* @param state_space StateSpace object required to manipulate state vector.
* @param simulator Simulator object. Provides specific implementations for
* applying gates.
* @param scratch A temporary state vector. Used for samping Kraus operators.
* @param state The state of the system, to be updated by this method.
* @param stat Statistics of sampled Kraus operator indices and/or measured
* bitstrings, to be populated by this method.
* @return True if the simulation completed successfully; false otherwise.
*/
static bool Run(const Parameter& param, unsigned num_qubits,
typename NoisyCircuit<Gate>::const_iterator cfirst,
typename NoisyCircuit<Gate>::const_iterator clast,
uint64_t r, State& scratch, State& state,
std::vector<uint64_t>& stat) {
static bool RunOnce(const Parameter& param, unsigned num_qubits,
ncircuit_iterator<Gate> cbeg,
ncircuit_iterator<Gate> cend,
uint64_t r, const StateSpace& state_space,
const Simulator& simulator, State& scratch, State& state,
std::vector<uint64_t>& stat) {
std::vector<const Gate*> gates;
gates.reserve(4 * std::size_t(clast - cfirst));
gates.reserve(4 * std::size_t(cend - cbeg));

if (!RunIteration(r, param, num_qubits,
cfirst, clast, gates, scratch, state, stat)) {
if (!RunIteration(param, num_qubits, cbeg, cend, r,
state_space, simulator, gates, scratch, state, stat)) {
return false;
}

return true;
}

private:
static bool RunIteration(uint64_t rep,
const Parameter& param, unsigned num_qubits,
typename NoisyCircuit<Gate>::const_iterator cfirst,
typename NoisyCircuit<Gate>::const_iterator clast,
std::vector<const Gate*>& gates,
State& scratch, State& state,
std::vector<uint64_t>& stat) {
static bool RunIteration(const Parameter& param, unsigned num_qubits,
ncircuit_iterator<Gate> cbeg,
ncircuit_iterator<Gate> cend,
uint64_t rep, const StateSpace& state_space,
const Simulator& simulator,
std::vector<const Gate*>& gates, State& scratch,
State& state, std::vector<uint64_t>& stat) {
if (param.collect_kop_stat || param.collect_mea_stat) {
stat.reserve(std::size_t(clast - cfirst));
stat.reserve(std::size_t(cend - cbeg));
stat.resize(0);
}

StateSpace state_space(param.num_threads);
Simulator simulator(param.num_threads);

if (state_space.IsNull(state)) {
state = CreateState(num_qubits, state_space);
if (state_space.IsNull(state)) {
Expand All @@ -215,9 +223,7 @@ class QuantumTrajectorySimulator {

bool unitary = true;

typename NoisyCircuit<Gate>::const_iterator it = cfirst;

for (; it != clast; ++it) {
for (auto it = cbeg; it != cend; ++it) {
const auto& channel = *it;

if (channel.size() == 0) continue;
Expand Down
Loading