From 3f6a3372ee55e7afb073ff9133e0edfdf296a85e Mon Sep 17 00:00:00 2001 From: Sergei Isakov <54642992+sergeisakov@users.noreply.github.com> Date: Wed, 8 Apr 2026 17:12:47 +0200 Subject: [PATCH 1/8] Refactor gate interface. --- apps/qsim_amplitudes.cc | 6 +- apps/qsim_base.cc | 6 +- apps/qsim_base_cuda.cu | 6 +- apps/qsim_base_custatevec.cu | 6 +- apps/qsim_base_custatevecex.cu | 4 +- apps/qsim_qtrajectory_cuda.cu | 72 +- apps/qsim_von_neumann.cc | 6 +- apps/qsimh_amplitudes.cc | 10 +- apps/qsimh_base.cc | 10 +- lib/BUILD | 69 +- lib/channel.h | 56 +- lib/channels_cirq.h | 167 +- lib/channels_qsim.h | 50 +- lib/circuit.h | 54 +- lib/circuit_noisy.h | 68 +- lib/circuit_qsim_parser.h | 129 +- lib/expect.h | 37 +- lib/fuser.h | 178 +- lib/fuser_basic.h | 506 ++-- lib/fuser_mqubit.h | 661 +++-- lib/gate.h | 317 ++- lib/gate_appl.h | 202 +- lib/gates_cirq.h | 112 +- lib/gates_qsim.h | 11 +- lib/hybrid.h | 289 +- lib/operation.h | 72 + lib/operation_base.h | 331 +++ lib/qtrajectory.h | 167 +- lib/run_custatevecex.h | 62 +- lib/run_qsim.h | 38 +- lib/run_qsimh.h | 32 +- pybind_interface/avx2/pybind_main_avx2.cpp | 7 +- .../avx512/pybind_main_avx512.cpp | 7 +- pybind_interface/basic/pybind_main_basic.cpp | 7 +- pybind_interface/cuda/pybind_main_cuda.cpp | 7 +- .../custatevec/pybind_main_custatevec.cpp | 7 +- .../custatevecex/pybind_main_custatevecex.cpp | 3 +- pybind_interface/hip/pybind_main_hip.cpp | 7 +- pybind_interface/pybind_main.cpp | 212 +- pybind_interface/pybind_main.h | 94 +- pybind_interface/sse/pybind_main_sse.cpp | 7 +- qsimcirq/qsim_circuit.py | 36 +- qsimcirq_tests/qsimcirq_test.py | 6 +- tests/BUILD | 74 +- tests/channel_test.cc | 241 +- tests/channels_cirq_test.cc | 39 +- tests/circuit_qsim_parser_test.cc | 96 +- tests/expect_test.cc | 6 +- tests/fuser_basic_test.cc | 2318 ++++++++++------- tests/fuser_mqubit_test.cc | 879 ++++--- tests/fuser_testfixture.h | 191 ++ tests/gates_cirq_testfixture.h | 183 +- tests/gates_qsim_test.cc | 9 +- tests/hybrid_testfixture.h | 76 +- tests/mps_simulator_test.cc | 50 +- tests/operation_test.cc | 334 +++ tests/qtrajectory_avx_test.cc | 14 +- tests/qtrajectory_cuda_test.cu | 14 +- tests/qtrajectory_custatevec_test.cu | 14 +- tests/qtrajectory_testfixture.h | 544 ++-- tests/run_custatevecex_test.cu | 13 +- tests/run_qsim_test.cc | 23 +- tests/run_qsimh_test.cc | 18 +- tests/simulator_testfixture.h | 34 +- tests/statespace_testfixture.h | 15 +- tests/unitary_calculator_testfixture.h | 10 +- 66 files changed, 5428 insertions(+), 3901 deletions(-) create mode 100644 lib/operation.h create mode 100644 lib/operation_base.h create mode 100644 tests/fuser_testfixture.h create mode 100644 tests/operation_test.cc diff --git a/apps/qsim_amplitudes.cc b/apps/qsim_amplitudes.cc index d37fdd6b9..21461a19a 100644 --- a/apps/qsim_amplitudes.cc +++ b/apps/qsim_amplitudes.cc @@ -25,8 +25,8 @@ #include "../lib/circuit_qsim_parser.h" #include "../lib/formux.h" #include "../lib/fuser_mqubit.h" -#include "../lib/gates_qsim.h" #include "../lib/io_file.h" +#include "../lib/operation.h" #include "../lib/run_qsim.h" #include "../lib/simmux.h" #include "../lib/util.h" @@ -160,7 +160,7 @@ int main(int argc, char* argv[]) { return 1; } - Circuit> circuit; + Circuit> circuit; unsigned maxtime = opt.times.back(); if (!CircuitQsimParser::FromFile(maxtime, opt.circuit_file, circuit)) { @@ -191,7 +191,7 @@ int main(int argc, char* argv[]) { using Simulator = Factory::Simulator; using StateSpace = Simulator::StateSpace; using State = StateSpace::State; - using Fuser = MultiQubitGateFuser>; + using Fuser = MultiQubitGateFuser; using Runner = QSimRunner; auto measure = [&opt, &circuit]( diff --git a/apps/qsim_base.cc b/apps/qsim_base.cc index 5a7604d5b..a5a7a0b1b 100644 --- a/apps/qsim_base.cc +++ b/apps/qsim_base.cc @@ -22,8 +22,8 @@ #include "../lib/circuit_qsim_parser.h" #include "../lib/formux.h" #include "../lib/fuser_mqubit.h" -#include "../lib/gates_qsim.h" #include "../lib/io_file.h" +#include "../lib/operation.h" #include "../lib/run_qsim.h" #include "../lib/simmux.h" #include "../lib/util_cpu.h" @@ -114,7 +114,7 @@ int main(int argc, char* argv[]) { return 1; } - Circuit> circuit; + Circuit> circuit; if (!CircuitQsimParser::FromFile(opt.maxtime, opt.circuit_file, circuit)) { return 1; @@ -144,7 +144,7 @@ int main(int argc, char* argv[]) { using Simulator = Factory::Simulator; using StateSpace = Simulator::StateSpace; using State = StateSpace::State; - using Fuser = MultiQubitGateFuser>; + using Fuser = MultiQubitGateFuser; using Runner = QSimRunner; StateSpace state_space = Factory(opt.num_threads).CreateStateSpace(); diff --git a/apps/qsim_base_cuda.cu b/apps/qsim_base_cuda.cu index b4af79674..f046055a5 100644 --- a/apps/qsim_base_cuda.cu +++ b/apps/qsim_base_cuda.cu @@ -22,8 +22,8 @@ #include "../lib/circuit_qsim_parser.h" #include "../lib/formux.h" #include "../lib/fuser_mqubit.h" -#include "../lib/gates_qsim.h" #include "../lib/io_file.h" +#include "../lib/operation.h" #include "../lib/run_qsim.h" #include "../lib/simulator_cuda.h" @@ -114,7 +114,7 @@ int main(int argc, char* argv[]) { using fp_type = float; - Circuit> circuit; + Circuit> circuit; if (!CircuitQsimParser::FromFile(opt.maxtime, opt.circuit_file, circuit)) { return 1; @@ -140,7 +140,7 @@ int main(int argc, char* argv[]) { using Simulator = Factory::Simulator; using StateSpace = Simulator::StateSpace; using State = StateSpace::State; - using Fuser = MultiQubitGateFuser>; + using Fuser = MultiQubitGateFuser; using Runner = QSimRunner; StateSpace::Parameter param1; diff --git a/apps/qsim_base_custatevec.cu b/apps/qsim_base_custatevec.cu index 00b32be9c..9bb2d3580 100644 --- a/apps/qsim_base_custatevec.cu +++ b/apps/qsim_base_custatevec.cu @@ -24,8 +24,8 @@ #include "../lib/circuit_qsim_parser.h" #include "../lib/formux.h" #include "../lib/fuser_mqubit.h" -#include "../lib/gates_qsim.h" #include "../lib/io_file.h" +#include "../lib/operation.h" #include "../lib/run_qsim.h" #include "../lib/simulator_custatevec.h" #include "../lib/util_custatevec.h" @@ -108,7 +108,7 @@ int main(int argc, char* argv[]) { using fp_type = float; - Circuit> circuit; + Circuit> circuit; if (!CircuitQsimParser::FromFile(opt.maxtime, opt.circuit_file, circuit)) { return 1; @@ -143,7 +143,7 @@ int main(int argc, char* argv[]) { using Simulator = Factory::Simulator; using StateSpace = Simulator::StateSpace; using State = StateSpace::State; - using Fuser = MultiQubitGateFuser>; + using Fuser = MultiQubitGateFuser; using Runner = QSimRunner; Factory factory; diff --git a/apps/qsim_base_custatevecex.cu b/apps/qsim_base_custatevecex.cu index 041cfba37..c34c39294 100644 --- a/apps/qsim_base_custatevecex.cu +++ b/apps/qsim_base_custatevecex.cu @@ -21,9 +21,9 @@ #include "../lib/circuit_qsim_parser.h" #include "../lib/formux.h" -#include "../lib/gates_qsim.h" #include "../lib/io_file.h" #include "../lib/multiprocess_custatevecex.h" +#include "../lib/operation.h" #include "../lib/run_custatevecex.h" #include "../lib/simulator_custatevecex.h" #include "../lib/util_custatevec.h" @@ -106,7 +106,7 @@ int main(int argc, char* argv[]) { using fp_type = float; - Circuit> circuit; + Circuit> circuit; if (!CircuitQsimParser::FromFile(opt.maxtime, opt.circuit_file, circuit)) { return 1; diff --git a/apps/qsim_qtrajectory_cuda.cu b/apps/qsim_qtrajectory_cuda.cu index 006d3944f..c839fb384 100644 --- a/apps/qsim_qtrajectory_cuda.cu +++ b/apps/qsim_qtrajectory_cuda.cu @@ -27,8 +27,8 @@ #include "../lib/circuit_qsim_parser.h" #include "../lib/expect.h" #include "../lib/fuser_mqubit.h" -#include "../lib/gates_qsim.h" #include "../lib/io_file.h" +#include "../lib/operation.h" #include "../lib/qtrajectory.h" #include "../lib/run_qsim.h" #include "../lib/simulator_cuda.h" @@ -44,10 +44,10 @@ struct Options { unsigned verbosity = 0; }; -constexpr char usage[] = "usage:\n ./qsim_qtrajectory_cuda.x " +constexpr char usage[] = "usage:\n ./qsim_qtrajectory.x " "-c circuit_file -d times_to_calculate_observables " "-a amplitude_damping_const -p phase_damping_const " - "-t traj0 -n num_trajectories -f max_fused_size " + "-0 traj0 -n num_trajectories -f max_fused_size " "-v verbosity\n"; Options GetOptions(int argc, char* argv[]) { @@ -59,7 +59,7 @@ Options GetOptions(int argc, char* argv[]) { return std::atoi(word.c_str()); }; - while ((k = getopt(argc, argv, "c:d:a:p:t:n:f:v:")) != -1) { + while ((k = getopt(argc, argv, "c:d:a:p:0:n:f:v:")) != -1) { switch (k) { case 'c': opt.circuit_file = optarg; @@ -73,7 +73,7 @@ Options GetOptions(int argc, char* argv[]) { case 'p': opt.phase_damp_const = std::atof(optarg); break; - case 't': + case '0': opt.traj0 = std::atoi(optarg); break; case 'n': @@ -85,7 +85,6 @@ Options GetOptions(int argc, char* argv[]) { case 'v': opt.verbosity = std::atoi(optarg); break; - break; default: qsim::IO::errorf(usage); exit(1); @@ -120,47 +119,52 @@ bool ValidateOptions(const Options& opt) { return true; } -template -std::vector> AddNoise( - const qsim::Circuit& circuit, const std::vector& times, +template +std::vector>> AddNoise( + const qsim::Circuit>& circuit, + const std::vector& times, const Channel1& channel1, const Channel2& channel2) { - std::vector> ncircuits; + std::vector>> ncircuits; ncircuits.reserve(times.size()); - qsim::NoisyCircuit ncircuit; + qsim::Circuit> ncircuit; ncircuit.num_qubits = circuit.num_qubits; - ncircuit.channels.reserve(5 * circuit.gates.size()); + ncircuit.ops.reserve(5 * circuit.ops.size()); unsigned cur_time_index = 0; - for (std::size_t i = 0; i < circuit.gates.size(); ++i) { - const auto& gate = circuit.gates[i]; + for (std::size_t i = 0; i < circuit.ops.size(); ++i) { + const auto& op = circuit.ops[i]; + + unsigned time = OpTime(op); + const auto& qubits = OpQubits(op); - ncircuit.channels.push_back(qsim::MakeChannelFromGate(3 * gate.time, gate)); + ncircuit.ops.push_back(op); + OpBaseOperation(ncircuit.ops.back()).time = 3 * time; - for (auto q : gate.qubits) { - ncircuit.channels.push_back(channel1.Create(3 * gate.time + 1, q)); + for (auto q : qubits) { + ncircuit.ops.push_back(channel1.Create(3 * time + 1, q)); } - for (auto q : gate.qubits) { - ncircuit.channels.push_back(channel2.Create(3 * gate.time + 2, q)); + for (auto q : qubits) { + ncircuit.ops.push_back(channel2.Create(3 * time + 2, q)); } unsigned t = times[cur_time_index]; - if (i == circuit.gates.size() - 1 || t < circuit.gates[i + 1].time) { + if (i == circuit.ops.size() - 1 || t < OpTime(circuit.ops[i + 1])) { ncircuits.push_back(std::move(ncircuit)); ncircuit = {}; - if (i < circuit.gates.size() - 1) { - if (circuit.gates[i + 1].time > times.back()) { + if (i < circuit.ops.size() - 1) { + if (OpTime(circuit.ops[i + 1]) > times.back()) { break; } ncircuit.num_qubits = circuit.num_qubits; - ncircuit.channels.reserve(5 * circuit.gates.size()); + ncircuit.ops.reserve(5 * circuit.ops.size()); } ++cur_time_index; @@ -170,13 +174,13 @@ std::vector> AddNoise( return ncircuits; } -template -std::vector>> GetObservables( +template +std::vector>> GetObservables( unsigned num_qubits) { - std::vector>> observables; + std::vector>> observables; observables.reserve(num_qubits); - using X = qsim::GateX; + using X = qsim::GateX; for (unsigned q = 0; q < num_qubits; ++q) { observables.push_back({{{1.0, 0.0}, {X::Create(0, q)}}}); @@ -210,18 +214,16 @@ int main(int argc, char* argv[]) { using Simulator = Factory::Simulator; using StateSpace = Simulator::StateSpace; using State = StateSpace::State; - using Gate = GateQSim; - using Fuser = MultiQubitGateFuser; - using FuserQT = MultiQubitGateFuser; - using RunnerQT = QSimRunner; - using QTSimulator = QuantumTrajectorySimulator; + using Fuser = MultiQubitGateFuser; + using Runner = QSimRunner; + using QTSimulator = QuantumTrajectorySimulator; auto opt = GetOptions(argc, argv); if (!ValidateOptions(opt)) { return 1; } - Circuit circuit; + Circuit> circuit; unsigned maxtime = opt.times.back(); if (!CircuitQsimParser::FromFile(maxtime, opt.circuit_file, circuit)) { @@ -230,7 +232,7 @@ int main(int argc, char* argv[]) { if (opt.times.size() == 1 && opt.times[0] == std::numeric_limits::max()) { - opt.times[0] = circuit.gates.back().time; + opt.times[0] = OpTime(circuit.ops.back()); } StateSpace::Parameter param1; @@ -256,7 +258,7 @@ int main(int argc, char* argv[]) { auto noisy_circuits = AddNoise(circuit, opt.times, channel1, channel2); - auto observables = GetObservables(circuit.num_qubits); + auto observables = GetObservables(circuit.num_qubits); std::vector>>> results; results.reserve(opt.num_trajectories); diff --git a/apps/qsim_von_neumann.cc b/apps/qsim_von_neumann.cc index 465760830..fed4a04f9 100644 --- a/apps/qsim_von_neumann.cc +++ b/apps/qsim_von_neumann.cc @@ -24,8 +24,8 @@ #include "../lib/circuit_qsim_parser.h" #include "../lib/formux.h" #include "../lib/fuser_mqubit.h" -#include "../lib/gates_qsim.h" #include "../lib/io_file.h" +#include "../lib/operation.h" #include "../lib/run_qsim.h" #include "../lib/simmux.h" #include "../lib/util_cpu.h" @@ -99,7 +99,7 @@ int main(int argc, char* argv[]) { return 1; } - Circuit> circuit; + Circuit> circuit; if (!CircuitQsimParser::FromFile(opt.maxtime, opt.circuit_file, circuit)) { return 1; @@ -129,7 +129,7 @@ int main(int argc, char* argv[]) { using Simulator = Factory::Simulator; using StateSpace = Simulator::StateSpace; using State = StateSpace::State; - using Fuser = MultiQubitGateFuser>; + using Fuser = MultiQubitGateFuser; using Runner = QSimRunner; auto measure = [&opt, &circuit]( diff --git a/apps/qsimh_amplitudes.cc b/apps/qsimh_amplitudes.cc index 7cb1b085e..e1983bca4 100644 --- a/apps/qsimh_amplitudes.cc +++ b/apps/qsimh_amplitudes.cc @@ -25,8 +25,8 @@ #include "../lib/circuit_qsim_parser.h" #include "../lib/formux.h" #include "../lib/fuser_basic.h" -#include "../lib/gates_qsim.h" #include "../lib/io_file.h" +#include "../lib/operation.h" #include "../lib/run_qsimh.h" #include "../lib/simmux.h" #include "../lib/util.h" @@ -174,7 +174,7 @@ int main(int argc, char* argv[]) { return 1; } - Circuit> circuit; + Circuit> circuit; if (!CircuitQsimParser::FromFile(opt.maxtime, opt.circuit_file, circuit)) { return 1; @@ -213,9 +213,9 @@ int main(int argc, char* argv[]) { unsigned num_threads; }; - using HybridSimulator = HybridSimulator, BasicGateFuser, - For>; - using Runner = QSimHRunner; + using Fuser = BasicGateFuser; + using HybridSimulator = HybridSimulator; + using Runner = QSimHRunner; Runner::Parameter param; param.prefix = opt.prefix; diff --git a/apps/qsimh_base.cc b/apps/qsimh_base.cc index eb0c9c6ca..f4a3ee8c7 100644 --- a/apps/qsimh_base.cc +++ b/apps/qsimh_base.cc @@ -25,8 +25,8 @@ #include "../lib/circuit_qsim_parser.h" #include "../lib/formux.h" #include "../lib/fuser_basic.h" -#include "../lib/gates_qsim.h" #include "../lib/io_file.h" +#include "../lib/operation.h" #include "../lib/run_qsimh.h" #include "../lib/simmux.h" #include "../lib/util.h" @@ -136,7 +136,7 @@ int main(int argc, char* argv[]) { return 1; } - Circuit> circuit; + Circuit> circuit; if (!CircuitQsimParser::FromFile(opt.maxtime, opt.circuit_file, circuit)) { return 1; @@ -178,9 +178,9 @@ int main(int argc, char* argv[]) { unsigned num_threads; }; - using HybridSimulator = HybridSimulator, BasicGateFuser, - For>; - using Runner = QSimHRunner; + using Fuser = BasicGateFuser; + using HybridSimulator = HybridSimulator; + using Runner = QSimHRunner; Runner::Parameter param; param.prefix = opt.prefix; diff --git a/lib/BUILD b/lib/BUILD index 60fd0e51a..08e7d85b4 100644 --- a/lib/BUILD +++ b/lib/BUILD @@ -27,6 +27,7 @@ cc_library( "bitstring.h", "channel.h", "channels_cirq.h", + "channels_qsim.h", "circuit.h", "circuit_noisy.h", "circuit_qsim_parser.h", @@ -45,6 +46,8 @@ cc_library( "matrix.h", "mps_simulator.h", "mps_statespace.h", + "operation.h", + "operation_base.h", "parfor.h", "qtrajectory.h", "run_qsim.h", @@ -99,6 +102,7 @@ cuda_library( "bitstring.h", "channel.h", "channels_cirq.h", + "channels_qsim.h", "circuit.h", "circuit_noisy.h", "circuit_qsim_parser.h", @@ -117,6 +121,8 @@ cuda_library( "matrix.h", "mps_simulator.h", "mps_statespace.h", + "operation.h", + "operation_base.h", "parfor.h", "qtrajectory.h", "run_qsim.h", @@ -168,6 +174,7 @@ cuda_library( "bitstring.h", "channel.h", "channels_cirq.h", + "channels_qsim.h", "circuit.h", "circuit_noisy.h", "circuit_qsim_parser.h", @@ -187,6 +194,8 @@ cuda_library( "mps_simulator.h", "mps_statespace.h", "multiprocess_custatevecex.h", + "operation.h", + "operation_base.h", "parfor.h", "qtrajectory.h", "run_custatevecex.h", @@ -255,6 +264,8 @@ cc_library( "io.h", "io_file.h", "matrix.h", + "operation.h", + "operation_base.h", "parfor.h", "run_qsim.h", "seqfor.h", @@ -302,6 +313,8 @@ cc_library( "io.h", "io_file.h", "matrix.h", + "operation.h", + "operation_base.h", "parfor.h", "run_qsimh.h", "seqfor.h", @@ -407,19 +420,37 @@ cc_library( ### Gate libraries ### +cc_library( + name = "operation_base", + hdrs = ["operation_base.h"], +) + +cc_library( + name = "operation", + hdrs = ["operation.h"], + deps = [ + ":channel", + ":gate", + ":operation_base", + ], +) + cc_library( name = "gate", hdrs = ["gate.h"], - deps = [":matrix"], + deps = [ + ":matrix", + ":operation_base", + ], ) cc_library( name = "gate_appl", hdrs = ["gate_appl.h"], deps = [ - ":fuser", ":gate", ":matrix", + ":operation_base", ], ) @@ -435,7 +466,10 @@ cc_library( cc_library( name = "gates_qsim", hdrs = ["gates_qsim.h"], - deps = [":gate"], + deps = [ + ":gate", + ":matrix", + ], ) ### Circuit libraries ### @@ -452,6 +486,8 @@ cc_library( deps = [ ":circuit", ":gates_qsim", + ":operation", + ":operation_base", ], ) @@ -463,6 +499,7 @@ cc_library( deps = [ ":gate", ":matrix", + ":operation_base", ], ) @@ -472,6 +509,8 @@ cc_library( deps = [ ":fuser", ":gate", + ":operation", + ":operation_base", ], ) @@ -481,6 +520,8 @@ cc_library( deps = [ ":fuser", ":gate", + ":operation", + ":operation_base", ], ) @@ -491,7 +532,9 @@ cc_library( hdrs = ["expect.h"], deps = [ ":fuser", + ":gate", ":gate_appl", + ":operation_base", ], ) @@ -505,6 +548,7 @@ cc_library( ":gate", ":gate_appl", ":util", + ":operation_base", ], ) @@ -512,7 +556,9 @@ cc_library( name = "run_qsimh", hdrs = ["run_qsimh.h"], deps = [ + ":circuit", ":hybrid", + ":operation", ":util", ], ) @@ -763,6 +809,7 @@ cc_library( deps = [ ":gate", ":matrix", + ":operation_base", ], ) @@ -770,8 +817,8 @@ cc_library( name = "circuit_noisy", hdrs = ["circuit_noisy.h"], deps = [ - ":channel", - ":circuit", + ":operation", + ":operation_base", ], ) @@ -784,15 +831,25 @@ cc_library( ], ) +cc_library( + name = "channels_qsim", + hdrs = ["channels_qsim.h"], + deps = [ + ":channel", + ":gates_qsim", + ], +) + ### Quantum trajectory simulator ### cc_library( name = "qtrajectory", hdrs = ["qtrajectory.h"], deps = [ - ":circuit_noisy", ":gate", ":gate_appl", + ":operation", + ":operation_base", ], ) diff --git a/lib/channel.h b/lib/channel.h index 372a174c9..928ebd0a1 100644 --- a/lib/channel.h +++ b/lib/channel.h @@ -20,25 +20,17 @@ #include "gate.h" #include "matrix.h" +#include "operation_base.h" namespace qsim { /** - * Kraus operator. + * A Kraus operator. */ -template +template struct KrausOperator { - using fp_type = typename Gate::fp_type; - - enum Kind { - kNormal = 0, - kMeasurement = gate::kMeasurement, - }; - - /** - * Kraus operator type; - */ - Kind kind; + using fp_type = FP; + using Gate = qsim::Gate; /** * If true, the Kraus operator is a unitary operator times a constant. @@ -120,25 +112,41 @@ struct KrausOperator { }; /** - * Quantum channel. + * A Quantum channel. Currently `BaseOperation`s fields are not used. */ -template -using Channel = std::vector>; +template +struct Channel : public BaseOperation { + Channel() {} + + Channel(BaseOperation&& bop, std::vector>&& kops) + : BaseOperation(std::move(bop)), kops(std::move(kops)) {} + + std::vector> kops; +}; /** * Makes a channel from the gate. - * @param time The time to place the channel at. * @param gate The input gate. * @return The output channel. */ -template -Channel MakeChannelFromGate(unsigned time, const Gate& gate) { - auto normal = KrausOperator::kNormal; - auto measurement = KrausOperator::kMeasurement; - - auto kind = gate.kind == gate::kMeasurement ? measurement : normal; +template +Channel MakeChannelFromGate(const Gate& gate) { + return Channel{ + {kChannel, gate.time, gate.qubits}, {{true, 1.0, {gate}}} + }; +} - Channel channel = {{kind, true, 1, {gate}}}; +/** + * Makes a channel from the gate. + * @param time The time to place the channel at. + * @param gate The input gate. + * @return The output channel. + */ +template +Channel MakeChannelFromGate(unsigned time, const Gate& gate) { + Channel channel{ + {kChannel, gate.time, gate.qubits}, {{true, 1.0, {gate}}} + }; channel[0].ops[0].time = time; return channel; diff --git a/lib/channels_cirq.h b/lib/channels_cirq.h index 69f1df9d5..473d1d64f 100644 --- a/lib/channels_cirq.h +++ b/lib/channels_cirq.h @@ -26,9 +26,6 @@ namespace qsim { namespace Cirq { -template -using Channel = qsim::Channel>; - /** * Asymmetric depolarizing channel factory. */ @@ -43,12 +40,15 @@ struct AsymmetricDepolarizingChannel { double p_x, double p_y, double p_z) { double p1 = 1 - p_x - p_y - p_z; - auto normal = KrausOperator>::kNormal; - - return {{normal, 1, p1, {}}, - {normal, 1, p_x, {X::Create(time, q)}}, - {normal, 1, p_y, {Y::Create(time, q)}}, - {normal, 1, p_z, {Z::Create(time, q)}}}; + return { + {kChannel, time, {q}}, + { + {true, p1, {}}, + {true, p_x, {X::Create(time, q)}}, + {true, p_y, {Y::Create(time, q)}}, + {true, p_z, {Z::Create(time, q)}}, + } + }; } static Channel Create(unsigned time, @@ -56,16 +56,14 @@ struct AsymmetricDepolarizingChannel { double p_x, double p_y, double p_z) { double p1 = 1 - p_x - p_y - p_z; - auto normal = KrausOperator>::kNormal; - uint64_t size = uint64_t{1} << (2 * qubits.size()); - Channel channel; - channel.reserve(size); + Channel channel{{kChannel, time, qubits}, {}}; + channel.kops.reserve(size); for (uint64_t i = 0; i < size; ++i) { - channel.push_back({normal, 1, 0, {}}); - auto& kop = channel.back(); + channel.kops.push_back({true, 0, {}}); + auto& kop = channel.kops.back(); kop.ops.reserve(qubits.size()); @@ -135,12 +133,15 @@ struct DepolarizingChannel { double p1 = 1 - p; double p2 = p / 3; - auto normal = KrausOperator>::kNormal; - - return {{normal, 1, p1, {}}, - {normal, 1, p2, {X::Create(time, q)}}, - {normal, 1, p2, {Y::Create(time, q)}}, - {normal, 1, p2, {Z::Create(time, q)}}}; + return { + {kChannel, time, {q}}, + { + {true, p1, {}}, + {true, p2, {X::Create(time, q)}}, + {true, p2, {Y::Create(time, q)}}, + {true, p2, {Z::Create(time, q)}} + } + }; } static Channel Create( @@ -148,16 +149,14 @@ struct DepolarizingChannel { double p1 = 1 - p; double p2 = p / 3; - auto normal = KrausOperator>::kNormal; - uint64_t size = uint64_t{1} << (2 * qubits.size()); - Channel channel; - channel.reserve(size); + Channel channel{{kChannel, time, qubits}, {}}; + channel.kops.reserve(size); for (uint64_t i = 0; i < size; ++i) { - channel.push_back({normal, 1, 0, {}}); - auto& kop = channel.back(); + channel.kops.push_back({true, 0, {}}); + auto& kop = channel.kops.back(); kop.ops.reserve(qubits.size()); @@ -235,25 +234,20 @@ struct GeneralizedAmplitudeDampingChannel { fp_type s2 = std::sqrt((1 - p) * gamma); using M = Cirq::MatrixGate1; - auto normal = KrausOperator>::kNormal; - - return {{normal, 0, p1, - {M::Create(time, q, {t1, 0, 0, 0, 0, 0, r1, 0})}, - {t1 * t1, 0, 0, 0, 0, 0, r1 * r1, 0}, {q}, - }, - {normal, 0, p2, - {M::Create(time, q, {r2, 0, 0, 0, 0, 0, t2, 0})}, - {r2 * r2, 0, 0, 0, 0, 0, t2 * t2, 0}, {q}, - }, - {normal, 0, p3, - {M::Create(time, q, {0, 0, s1, 0, 0, 0, 0, 0})}, - {0, 0, 0, 0, 0, 0, s1 * s1, 0}, {q}, - }, - {normal, 0, p3, - {M::Create(time, q, {0, 0, 0, 0, s2, 0, 0, 0})}, - {s2 * s2, 0, 0, 0, 0, 0, 0, 0}, {q}, - }, - }; + + return { + {kChannel, time, {q}}, + { + {false, p1, {M::Create(time, q, {t1, 0, 0, 0, 0, 0, r1, 0})}, + {t1 * t1, 0, 0, 0, 0, 0, r1 * r1, 0}, {q}}, + {false, p2, {M::Create(time, q, {r2, 0, 0, 0, 0, 0, t2, 0})}, + {r2 * r2, 0, 0, 0, 0, 0, t2 * t2, 0}, {q}}, + {false, p3, {M::Create(time, q, {0, 0, s1, 0, 0, 0, 0, 0})}, + {0, 0, 0, 0, 0, 0, s1 * s1, 0}, {q}}, + {false, p3, {M::Create(time, q, {0, 0, 0, 0, s2, 0, 0, 0})}, + {s2 * s2, 0, 0, 0, 0, 0, 0, 0}, {q}}, + } + }; } Channel Create(unsigned time, unsigned q) const { @@ -290,17 +284,16 @@ struct AmplitudeDampingChannel { fp_type s = std::sqrt(gamma); using M = Cirq::MatrixGate1; - auto normal = KrausOperator>::kNormal; - - return {{normal, 0, p1, - {M::Create(time, q, {1, 0, 0, 0, 0, 0, r, 0})}, - {1, 0, 0, 0, 0, 0, r * r, 0}, {q}, - }, - {normal, 0, p2, - {M::Create(time, q, {0, 0, s, 0, 0, 0, 0, 0})}, - {0, 0, 0, 0, 0, 0, s * s, 0}, {q}, - }, - }; + + return { + {kChannel, time, {q}}, + { + {false, p1, {M::Create(time, q, {1, 0, 0, 0, 0, 0, r, 0})}, + {1, 0, 0, 0, 0, 0, r * r, 0}, {q}}, + {false, p2, {M::Create(time, q, {0, 0, s, 0, 0, 0, 0, 0})}, + {0, 0, 0, 0, 0, 0, s * s, 0}, {q}}, + } + }; } Channel Create(unsigned time, unsigned q) const { @@ -335,17 +328,16 @@ struct PhaseDampingChannel { fp_type s = std::sqrt(gamma); using M = Cirq::MatrixGate1; - auto normal = KrausOperator>::kNormal; - - return {{normal, 0, p1, - {M::Create(time, q, {1, 0, 0, 0, 0, 0, r, 0})}, - {1, 0, 0, 0, 0, 0, r * r, 0}, {q}, - }, - {normal, 0, p2, - {M::Create(time, q, {0, 0, 0, 0, 0, 0, s, 0})}, - {0, 0, 0, 0, 0, 0, s * s, 0}, {q}, - }, - }; + + return { + {kChannel, time, {q}}, + { + {false, p1, {M::Create(time, q, {1, 0, 0, 0, 0, 0, r, 0})}, + {1, 0, 0, 0, 0, 0, r * r, 0}, {q}}, + {false, p2, {M::Create(time, q, {0, 0, 0, 0, 0, 0, s, 0})}, + {0, 0, 0, 0, 0, 0, s * s, 0}, {q}}, + } + }; } Channel Create(unsigned time, unsigned q) const { @@ -372,17 +364,16 @@ struct ResetChannel { static Channel Create(unsigned time, unsigned q) { using M = Cirq::MatrixGate1; - auto normal = KrausOperator>::kNormal; - - return {{normal, 0, 0, - {M::Create(time, q, {1, 0, 0, 0, 0, 0, 0, 0})}, - {1, 0, 0, 0, 0, 0, 0, 0}, {q}, - }, - {normal, 0, 0, - {M::Create(time, q, {0, 0, 1, 0, 0, 0, 0, 0})}, - {0, 0, 0, 0, 0, 0, 1, 0}, {q}, - }, - }; + + return { + {kChannel, time, {q}}, + { + {false, 0, {M::Create(time, q, {1, 0, 0, 0, 0, 0, 0, 0})}, + {1, 0, 0, 0, 0, 0, 0, 0}, {q}}, + {false, 0, {M::Create(time, q, {0, 0, 1, 0, 0, 0, 0, 0})}, + {0, 0, 0, 0, 0, 0, 1, 0}, {q}}, + } + }; } }; @@ -407,11 +398,10 @@ struct PhaseFlipChannel { double p1 = 1 - p; double p2 = p; - auto normal = KrausOperator>::kNormal; - - return {{normal, 1, p1, {}}, - {normal, 1, p2, {Z::Create(time, q)}} - }; + return { + {kChannel, time, {q}}, + {{true, p1, {}}, {true, p2, {Z::Create(time, q)}}} + }; } Channel Create(unsigned time, unsigned q) const { @@ -442,11 +432,10 @@ struct BitFlipChannel { double p1 = 1 - p; double p2 = p; - auto normal = KrausOperator>::kNormal; - - return {{normal, 1, p1, {}}, - {normal, 1, p2, {X::Create(time, q)}} - }; + return { + {kChannel, time, {q}}, + {{true, p1, {}}, {true, p2, {X::Create(time, q)}}} + }; } Channel Create(unsigned time, unsigned q) const { diff --git a/lib/channels_qsim.h b/lib/channels_qsim.h index 5c07bccf7..084f1ee17 100644 --- a/lib/channels_qsim.h +++ b/lib/channels_qsim.h @@ -31,7 +31,7 @@ template struct AmplitudeDampingChannel { AmplitudeDampingChannel(double gamma) : gamma(gamma) {} - static Channel> Create( + static Channel Create( unsigned time, unsigned q, double gamma) { double p1 = 1 - gamma; double p2 = 0; @@ -40,20 +40,19 @@ struct AmplitudeDampingChannel { fp_type s = std::sqrt(gamma); using M = GateMatrix1; - auto normal = KrausOperator>::kNormal; - - return {{normal, 0, p1, - {M::Create(time, q, {1, 0, 0, 0, 0, 0, r, 0})}, - {1, 0, 0, 0, 0, 0, r * r, 0}, {q}, - }, - {normal, 0, p2, - {M::Create(time, q, {0, 0, s, 0, 0, 0, 0, 0})}, - {0, 0, 0, 0, 0, 0, s * s, 0}, {q}, - }, - }; + + return { + {kChannel, time, {q}}, + { + {false, p1, {M::Create(time, q, {1, 0, 0, 0, 0, 0, r, 0})}, + {1, 0, 0, 0, 0, 0, r * r, 0}, {q}}, + {false, p2, {M::Create(time, q, {0, 0, s, 0, 0, 0, 0, 0})}, + {0, 0, 0, 0, 0, 0, s * s, 0}, {q}}, + } + }; } - Channel> Create(unsigned time, unsigned q) const { + Channel Create(unsigned time, unsigned q) const { return Create(time, q, gamma); } @@ -75,7 +74,7 @@ template struct PhaseDampingChannel { PhaseDampingChannel(double gamma) : gamma(gamma) {} - static Channel> Create( + static Channel Create( unsigned time, unsigned q, double gamma) { double p1 = 1 - gamma; double p2 = 0; @@ -84,20 +83,19 @@ struct PhaseDampingChannel { fp_type s = std::sqrt(gamma); using M = GateMatrix1; - auto normal = KrausOperator>::kNormal; - - return {{normal, 0, p1, - {M::Create(time, q, {1, 0, 0, 0, 0, 0, r, 0})}, - {1, 0, 0, 0, 0, 0, r * r, 0}, {q}, - }, - {normal, 0, p2, - {M::Create(time, q, {0, 0, 0, 0, 0, 0, s, 0})}, - {0, 0, 0, 0, 0, 0, s * s, 0}, {q}, - }, - }; + + return { + {kChannel, time, {q}}, + { + {false, p1, {M::Create(time, q, {1, 0, 0, 0, 0, 0, r, 0})}, + {1, 0, 0, 0, 0, 0, r * r, 0}, {q}}, + {false, p2, {M::Create(time, q, {0, 0, 0, 0, 0, 0, s, 0})}, + {0, 0, 0, 0, 0, 0, s * s, 0}, {q}}, + } + }; } - Channel> Create(unsigned time, unsigned q) const { + Channel Create(unsigned time, unsigned q) const { return Create(time, q, gamma); } diff --git a/lib/circuit.h b/lib/circuit.h index 7f0cbb503..bf9238fff 100644 --- a/lib/circuit.h +++ b/lib/circuit.h @@ -20,60 +20,46 @@ namespace qsim { /** - * A collection of gates. This object is consumed by `QSim[h]Runner.Run()`. + * A collection of operations. */ -template +template struct Circuit { unsigned num_qubits; /** - * The set of gates to be run. Gate times should be ordered. + * The set of operations to be run. Operation time steps should be ordered. */ - std::vector gates; + std::vector ops; }; -namespace detail { - /** - * An adapter for vectors of gates. + * An adapter for vectors of operations. */ template -struct Gates; - -template -struct Gates> { - static const std::vector& get(const qsim::Circuit& circuit) { - return circuit.gates; - } +struct Operations; - static const Gate& gate(const Gate& g) { - return g; +template +struct Operations> { + static const std::vector& get( + const qsim::Circuit& circuit) { + return circuit.ops; } }; -template -struct Gates> { - static const std::vector& get(const std::vector& gates) { - return gates; - } - - static const Gate& gate(const Gate& g) { - return g; +template +struct Operations> { + static const std::vector& get(const std::vector& ops) { + return ops; } }; -template -struct Gates> { - static const std::vector& get(const std::vector& gates) { - return gates; - } - - static const Gate& gate(const Gate* g) { - return *g; +template +struct Operations> { + static const std::vector& get( + const std::vector& ops) { + return ops; } }; -} // namespace detail - } // namespace qsim #endif // CIRCUIT_H_ diff --git a/lib/circuit_noisy.h b/lib/circuit_noisy.h index 40a228d9c..b5e49919e 100644 --- a/lib/circuit_noisy.h +++ b/lib/circuit_noisy.h @@ -17,23 +17,10 @@ #include -#include "circuit.h" -#include "channel.h" +#include "operation.h" namespace qsim { -/** - * Noisy circuit. - */ -template -struct NoisyCircuit { - unsigned num_qubits; - std::vector> channels; -}; - -template -using ncircuit_iterator = typename std::vector>::const_iterator; - /** * Makes a noisy circuit from the clean circuit. * Channels are added after each qubit of each gate of the clean cicuit. @@ -43,28 +30,33 @@ using ncircuit_iterator = typename std::vector>::const_iterator; * @param A channel factory to construct channels. * @return The output noisy circuit. */ -template -inline NoisyCircuit MakeNoisy( +template +inline Circuit> MakeNoisy( unsigned num_qubits, - typename std::vector::const_iterator gbeg, - typename std::vector::const_iterator gend, + typename std::vector>::const_iterator obeg, + typename std::vector>::const_iterator oend, const ChannelFactory& channel_factory) { - NoisyCircuit ncircuit; + Circuit> ncircuit; ncircuit.num_qubits = num_qubits; - ncircuit.channels.reserve(4 * std::size_t(gend - gbeg)); + ncircuit.ops.reserve(4 * std::size_t(oend - obeg)); - for (auto it = gbeg; it != gend; ++it) { - const auto& gate = *it; + for (auto it = obeg; it != oend; ++it) { + const auto& op = *it; - ncircuit.channels.push_back(MakeChannelFromGate(2 * gate.time, gate)); + const auto& bop = OpBaseOperation(op); - for (auto q : gate.qubits) { - ncircuit.channels.push_back(channel_factory.Create(2 * gate.time + 1, q)); + ncircuit.ops.push_back(op); + OpBaseOperation(ncircuit.ops.back()).time = 2 * bop.time; + + for (auto q : bop.qubits) { + ncircuit.ops.push_back(channel_factory.Create(2 * bop.time + 1, q)); } - for (auto q : gate.controlled_by) { - ncircuit.channels.push_back(channel_factory.Create(2 * gate.time + 1, q)); + if (const auto* pg = OpGetAlternative>(op)) { + for (auto q : pg->controlled_by) { + ncircuit.ops.push_back(channel_factory.Create(2 * bop.time + 1, q)); + } } } @@ -80,12 +72,11 @@ inline NoisyCircuit MakeNoisy( * @param A channel factory to construct channels. * @return The output noisy circuit. */ -template -inline NoisyCircuit MakeNoisy(unsigned num_qubits, - const std::vector& gates, - const ChannelFactory& channel_factory) { - return - MakeNoisy(num_qubits, gates.begin(), gates.end(), channel_factory); +template +inline Circuit> MakeNoisy( + unsigned num_qubits, const std::vector>& ops, + const ChannelFactory& channel_factory) { + return MakeNoisy(num_qubits, ops.begin(), ops.end(), channel_factory); } /** @@ -96,11 +87,12 @@ inline NoisyCircuit MakeNoisy(unsigned num_qubits, * @param A channel factory to construct channels. * @return The output noisy circuit. */ -template -inline NoisyCircuit MakeNoisy(const Circuit& circuit, - const ChannelFactory& channel_factory) { - return MakeNoisy(circuit.num_qubits, circuit.gates.begin(), - circuit.gates.end(), channel_factory); +template +inline Circuit> MakeNoisy( + const Circuit>& circuit, + const ChannelFactory& channel_factory) { + return MakeNoisy(circuit.num_qubits, circuit.ops.begin(), + circuit.ops.end(), channel_factory); } } // namespace qsim diff --git a/lib/circuit_qsim_parser.h b/lib/circuit_qsim_parser.h index b4afa04d8..0d2404282 100644 --- a/lib/circuit_qsim_parser.h +++ b/lib/circuit_qsim_parser.h @@ -23,6 +23,8 @@ #include "circuit.h" #include "gates_qsim.h" +#include "operation.h" +#include "operation_base.h" namespace qsim { @@ -48,11 +50,11 @@ class CircuitQsimParser final { */ template static bool FromStream(unsigned maxtime, const std::string& provider, - Stream& fs, Circuit>& circuit) { + Stream& fs, Circuit>& circuit) { circuit.num_qubits = 0; - circuit.gates.resize(0); - circuit.gates.reserve(1024); + circuit.ops.resize(0); + circuit.ops.reserve(1024); unsigned k = 0; @@ -100,33 +102,34 @@ class CircuitQsimParser final { } if (gate_name == "c") { - if (!ParseControlledGate(ss, time, - circuit.num_qubits, circuit.gates)) { + if (!ParseControlledGate(ss, time, circuit.num_qubits, circuit.ops)) { InvalidGateError(provider, k); return false; } - } else if (!ParseGate(ss, time, circuit.num_qubits, - gate_name, circuit.gates)) { + } else if (!ParseGate(ss, time, circuit.num_qubits, + gate_name, false, circuit.ops)) { InvalidGateError(provider, k); return false; } - const auto& gate = circuit.gates.back(); + const auto& op = circuit.ops.back(); + bool is_measurement = OpGetAlternative(op) != nullptr; - if (time < prev_mea_time - || (gate.kind == gate::kMeasurement && time < max_time)) { - IO::errorf("gate crosses the time boundary set by measurement " - "gates in line %u in %s.\n", k, provider.c_str()); + if (time < prev_mea_time || (is_measurement && time < max_time)) { + IO::errorf("operation crosses the time boundary set by measurement " + "operations in line %u in/ %s.\n", k, provider.c_str()); return false; } - if (gate.kind == gate::kMeasurement) { + if (is_measurement) { prev_mea_time = time; } - if (GateIsOutOfOrder(time, gate.qubits, last_times) - || GateIsOutOfOrder(time, gate.controlled_by, last_times)) { - IO::errorf("gate is out of time order in line %u in %s.\n", + const auto* pg = OpGetAlternative>(op); + + if (GateIsOutOfOrder(time, OpQubits(op), last_times) + || (pg && GateIsOutOfOrder(time, pg->controlled_by, last_times))) { + IO::errorf("operation is out of time order in line %u in %s.\n", k, provider.c_str()); return false; } @@ -150,7 +153,7 @@ class CircuitQsimParser final { */ template static bool FromFile(unsigned maxtime, const std::string& file, - Circuit>& circuit) { + Circuit>& circuit) { auto fs = IO::StreamFromFile(file); if (!fs) { @@ -207,13 +210,12 @@ class CircuitQsimParser final { * @param qubits Indices of affected qubits. */ static bool ValidateGate(std::stringstream& ss, unsigned num_qubits, - const std::vector& qubits) { + const Qubits& qubits) { return ss && ValidateQubits(num_qubits, qubits); } static bool ValidateControlledGate( - unsigned num_qubits, const std::vector& qubits, - const std::vector& controlled_by) { + unsigned num_qubits, const Qubits& qubits, const Qubits& controlled_by) { if (!ValidateQubits(num_qubits, controlled_by)) return false; std::size_t i = 0, j = 0; @@ -231,8 +233,7 @@ class CircuitQsimParser final { return true; } - static bool ValidateQubits(unsigned num_qubits, - const std::vector& qubits) { + static bool ValidateQubits(unsigned num_qubits, const Qubits& qubits) { if (qubits.size() == 0 || qubits[0] >= num_qubits) return false; // qubits should be sorted. @@ -246,8 +247,7 @@ class CircuitQsimParser final { return true; } - static bool GateIsOutOfOrder(unsigned time, - const std::vector& qubits, + static bool GateIsOutOfOrder(unsigned time, const Qubits& qubits, std::vector& last_times) { for (auto q : qubits) { if (last_times[q] != unsigned(-1) && time <= last_times[q]) { @@ -260,103 +260,108 @@ class CircuitQsimParser final { return false; } - template + template static bool ParseGate(Stream& ss, unsigned time, unsigned num_qubits, - const std::string& gate_name, - std::vector& gates) { + const std::string& gate_name, bool controlled, + std::vector>& ops) { unsigned q0, q1; fp_type phi, theta; if (gate_name == "p") { ss >> phi; if (!ValidateGate(ss)) return false; - gates.push_back(GateGPh::Create(time, phi)); + ops.push_back(GateGPh::Create(time, phi)); } else if (gate_name == "id1") { ss >> q0; if (!ValidateGate(ss, num_qubits, q0)) return false; - gates.push_back(GateId1::Create(time, q0)); + ops.push_back(GateId1::Create(time, q0)); } else if (gate_name == "h") { ss >> q0; if (!ValidateGate(ss, num_qubits, q0)) return false; - gates.push_back(GateHd::Create(time, q0)); + ops.push_back(GateHd::Create(time, q0)); } else if (gate_name == "t") { ss >> q0; if (!ValidateGate(ss, num_qubits, q0)) return false; - gates.push_back(GateT::Create(time, q0)); + ops.push_back(GateT::Create(time, q0)); } else if (gate_name == "x") { ss >> q0; if (!ValidateGate(ss, num_qubits, q0)) return false; - gates.push_back(GateX::Create(time, q0)); + ops.push_back(GateX::Create(time, q0)); } else if (gate_name == "y") { ss >> q0; if (!ValidateGate(ss, num_qubits, q0)) return false; - gates.push_back(GateY::Create(time, q0)); + ops.push_back(GateY::Create(time, q0)); } else if (gate_name == "z") { ss >> q0; if (!ValidateGate(ss, num_qubits, q0)) return false; - gates.push_back(GateZ::Create(time, q0)); + ops.push_back(GateZ::Create(time, q0)); } else if (gate_name == "x_1_2") { ss >> q0; if (!ValidateGate(ss, num_qubits, q0)) return false; - gates.push_back(GateX2::Create(time, q0)); + ops.push_back(GateX2::Create(time, q0)); } else if (gate_name == "y_1_2") { ss >> q0; if (!ValidateGate(ss, num_qubits, q0)) return false; - gates.push_back(GateY2::Create(time, q0)); + ops.push_back(GateY2::Create(time, q0)); } else if (gate_name == "rx") { ss >> q0 >> phi; if (!ValidateGate(ss, num_qubits, q0)) return false; - gates.push_back(GateRX::Create(time, q0, phi)); + ops.push_back(GateRX::Create(time, q0, phi)); } else if (gate_name == "ry") { ss >> q0 >> phi; if (!ValidateGate(ss, num_qubits, q0)) return false; - gates.push_back(GateRY::Create(time, q0, phi)); + ops.push_back(GateRY::Create(time, q0, phi)); } else if (gate_name == "rz") { ss >> q0 >> phi; if (!ValidateGate(ss, num_qubits, q0)) return false; - gates.push_back(GateRZ::Create(time, q0, phi)); + ops.push_back(GateRZ::Create(time, q0, phi)); } else if (gate_name == "rxy") { ss >> q0 >> theta >> phi; if (!ValidateGate(ss, num_qubits, q0)) return false; - gates.push_back(GateRXY::Create(time, q0, theta, phi)); + ops.push_back(GateRXY::Create(time, q0, theta, phi)); } else if (gate_name == "hz_1_2") { ss >> q0; if (!ValidateGate(ss, num_qubits, q0)) return false; - gates.push_back(GateHZ2::Create(time, q0)); + ops.push_back(GateHZ2::Create(time, q0)); } else if (gate_name == "s") { ss >> q0; if (!ValidateGate(ss, num_qubits, q0)) return false; - gates.push_back(GateS::Create(time, q0)); + ops.push_back(GateS::Create(time, q0)); } else if (gate_name == "id2") { ss >> q0 >> q1; if (!ValidateGate(ss, num_qubits, q0, q1)) return false; - gates.push_back(GateId2::Create(time, q0, q1)); + ops.push_back(GateId2::Create(time, q0, q1)); } else if (gate_name == "cz") { ss >> q0 >> q1; if (!ValidateGate(ss, num_qubits, q0, q1)) return false; - gates.push_back(GateCZ::Create(time, q0, q1)); + ops.push_back(GateCZ::Create(time, q0, q1)); } else if (gate_name == "cnot" || gate_name == "cx") { ss >> q0 >> q1; if (!ValidateGate(ss, num_qubits, q0, q1)) return false; - gates.push_back(GateCNot::Create(time, q0, q1)); + ops.push_back(GateCNot::Create(time, q0, q1)); } else if (gate_name == "sw") { ss >> q0 >> q1; if (!ValidateGate(ss, num_qubits, q0, q1)) return false; - gates.push_back(GateSwap::Create(time, q0, q1)); + ops.push_back(GateSwap::Create(time, q0, q1)); } else if (gate_name == "is") { ss >> q0 >> q1; if (!ValidateGate(ss, num_qubits, q0, q1)) return false; - gates.push_back(GateIS::Create(time, q0, q1)); + ops.push_back(GateIS::Create(time, q0, q1)); } else if (gate_name == "fs") { ss >> q0 >> q1 >> theta >> phi; if (!ValidateGate(ss, num_qubits, q0, q1)) return false; - gates.push_back(GateFS::Create(time, q0, q1, theta, phi)); + ops.push_back(GateFS::Create(time, q0, q1, theta, phi)); } else if (gate_name == "cp") { ss >> q0 >> q1 >> phi; if (!ValidateGate(ss, num_qubits, q0, q1)) return false; - gates.push_back(GateCP::Create(time, q0, q1, phi)); + ops.push_back(GateCP::Create(time, q0, q1, phi)); } else if (gate_name == "m") { - std::vector qubits; + if (controlled) { + IO::errorf("measurement gate cannot be controlled.\n"); + return false; + } + + Qubits qubits; qubits.reserve(num_qubits); while (ss.good()) { @@ -368,10 +373,9 @@ class CircuitQsimParser final { } } - gates.push_back(gate::Measurement>::Create( - time, std::move(qubits))); + if (!ValidateQubits(num_qubits, qubits)) return false; - if (!ValidateQubits(num_qubits, gates.back().qubits)) return false; + ops.push_back(CreateMeasurement(time, std::move(qubits))); } else { return false; } @@ -379,11 +383,11 @@ class CircuitQsimParser final { return true; } - template + template static bool ParseControlledGate(Stream& ss, unsigned time, unsigned num_qubits, - std::vector& gates) { - std::vector controlled_by; + std::vector>& ops) { + Qubits controlled_by; controlled_by.reserve(64); std::string gate_name; @@ -421,18 +425,19 @@ class CircuitQsimParser final { ss >> gate_name; - if (!ss.good() || !ParseGate(ss, time, - num_qubits, gate_name, gates)) { + if (!ss.good() || !ParseGate(ss, time, num_qubits, gate_name, true, ops)) { return false; } - gates.back().ControlledBy(std::move(controlled_by)); - - if (!ValidateControlledGate(num_qubits, gates.back().qubits, - gates.back().controlled_by)) { + if (!ValidateControlledGate(num_qubits, OpQubits(ops.back()), + controlled_by)) { return false; } + auto& lop = ops.back(); + lop = OpGetAlternative>(lop)->ControlledBy( + std::move(controlled_by)); + return true; } }; diff --git a/lib/expect.h b/lib/expect.h index 518d516f9..595d80f90 100644 --- a/lib/expect.h +++ b/lib/expect.h @@ -18,14 +18,16 @@ #include #include "fuser.h" +#include "gate.h" #include "gate_appl.h" +#include "operation_base.h" namespace qsim { -template +template struct OpString { std::complex weight; - std::vector ops; + std::vector> ops; }; /** @@ -42,10 +44,10 @@ struct OpString { * @param ket Temporary state vector. * @return The computed expectation value. */ -template +template std::complex ExpectationValue( const typename Fuser::Parameter& param, - const std::vector>& strings, + const std::vector>& strings, const typename Simulator::StateSpace& state_space, const Simulator& simulator, const typename Simulator::State& state, typename Simulator::State& ket) { @@ -78,7 +80,7 @@ std::complex ExpectationValue( } for (const auto& fgate : fused_gates) { - ApplyFusedGate(simulator, fgate, ket); + ApplyGate(simulator, fgate, ket); } } @@ -100,9 +102,9 @@ std::complex ExpectationValue( * @param state The state of the system. * @return The computed expectation value. */ -template +template std::complex ExpectationValue( - const std::vector>& strings, + const std::vector>& strings, const Simulator& simulator, const typename Simulator::State& state) { std::complex eval = 0; @@ -125,18 +127,23 @@ std::complex ExpectationValue( break; } - const auto& fgate = fused_gates[0]; - - if (fgate.qubits.size() > 6) { - IO::errorf("operator string acts on too many qubits; " + if (const auto* pg = OpGetAlternative>(fused_gates[0])) { + if (pg->qubits.size() > 6) { + IO::errorf("operator string acts on too many qubits; " + "cannot compute the expectation value.\n"); + eval = 0; + break; + } + + auto r = simulator.ExpectationValue( + pg->qubits, pg->matrix.data(), state); + eval += str.weight * r; + } else { + IO::errorf("gate fusion error; " "cannot compute the expectation value.\n"); eval = 0; break; } - - auto r = simulator.ExpectationValue( - fgate.qubits, fgate.matrix.data(), state); - eval += str.weight * r; } } diff --git a/lib/fuser.h b/lib/fuser.h index e4f3c3ba8..96272c3a5 100644 --- a/lib/fuser.h +++ b/lib/fuser.h @@ -20,121 +20,94 @@ #include "gate.h" #include "matrix.h" +#include "operation_base.h" namespace qsim { /** - * A collection of "fused" gates which can be multiplied together before being - * applied to the state vector. + * Base class for fuser classes with some common functions. */ -template -struct GateFused { - /** - * Kind of the first ("parent") gate. - */ - typename Gate::GateKind kind; - /** - * The time index of the first ("parent") gate. - */ - unsigned time; - /** - * A list of qubits these gates act upon. Control qubits for - * explicitly-controlled gates are excluded from this list. - */ - std::vector qubits; - /** - * Pointer to the first ("parent") gate. - */ - const Gate* parent; - /** - * Ordered list of component gates. - */ - std::vector gates; - /** - * Fused gate matrix. - */ - Matrix matrix; -}; - -/** - * A base class for fuser classes with some common functions. - */ -template +template class Fuser { protected: - using RGate = typename std::remove_pointer::type; - - static const RGate& GateToConstRef(const RGate& gate) { - return gate; + template + static const Operation& OperationToConstRef(const Operation& op) { + return op; } - static const RGate& GateToConstRef(const RGate* gate) { - return *gate; + template + static const Operation& OperationToConstRef(const Operation* op) { + return *op; } + template static std::vector MergeWithMeasurementTimes( - typename std::vector::const_iterator gfirst, - typename std::vector::const_iterator glast, + typename std::vector::const_iterator obeg, + typename std::vector::const_iterator oend, const std::vector& times) { - std::vector epochs; - epochs.reserve(glast - gfirst + times.size()); + std::vector windows; + windows.reserve(oend - obeg + times.size()); std::size_t last = 0; unsigned max_time = 0; - for (auto gate_it = gfirst; gate_it < glast; ++gate_it) { - const auto& gate = GateToConstRef(*gate_it); + for (auto op_it = obeg; op_it < oend; ++op_it) { + const auto& op = OperationToConstRef(*op_it); - if (gate.time > max_time) { - max_time = gate.time; + unsigned time = OpTime(op); + + if (time > max_time) { + max_time = time; } - if (epochs.size() > 0 && gate.time < epochs.back()) { + if (windows.size() > 0 && time < windows.back()) { IO::errorf("gate crosses the time boundary.\n"); - epochs.resize(0); - return epochs; + windows.resize(0); + return windows; } - if (gate.kind == gate::kMeasurement) { - if (epochs.size() == 0 || epochs.back() < gate.time) { - if (!AddBoundary(gate.time, max_time, epochs)) { - epochs.resize(0); - return epochs; + if (OpGetAlternative(op)) { + if (windows.size() == 0 || windows.back() < time) { + if (!AddBoundary(time, max_time, windows)) { + windows.resize(0); + return windows; } } } - while (last < times.size() && times[last] <= gate.time) { + while (last < times.size() && times[last] <= time) { unsigned prev = times[last++]; - epochs.push_back(prev); - if (!AddBoundary(prev, max_time, epochs)) { - epochs.resize(0); - return epochs; + windows.push_back(prev); + if (!AddBoundary(prev, max_time, windows)) { + windows.resize(0); + return windows; } while (last < times.size() && times[last] <= prev) ++last; } } - if (epochs.size() == 0 || epochs.back() < max_time) { - epochs.push_back(max_time); + if (windows.size() == 0 || windows.back() < max_time) { + windows.push_back(max_time); } - return epochs; + return windows; } - template + template static void FuseZeroQubitGates(const GateSeq0& gate_seq0, - Parent parent, std::size_t first, - std::vector& fused_gates) { - GateFused* fuse_to = nullptr; + Parent&& parent, std::size_t first, + std::vector& fused_ops) { + using FusedGate = std::variant_alternative_t<0, OperationF>; + using fp_type = typename FusedGate::fp_type; + using Gate = qsim::Gate; + + FusedGate* fuse_to = nullptr; - for (std::size_t i = first; i < fused_gates.size(); ++i) { - auto& fgate = fused_gates[i]; + for (std::size_t i = first; i < fused_ops.size(); ++i) { + auto& fop = fused_ops[i]; - if (fgate.kind != gate::kMeasurement && fgate.kind != gate::kDecomp - && fgate.parent->controlled_by.size() == 0 - && !fgate.parent->unfusible) { - fuse_to = &fgate; + fuse_to = OpGetAlternative(fop); + if (fuse_to != nullptr) { break; } } @@ -142,15 +115,17 @@ class Fuser { if (fuse_to != nullptr) { // Fuse zero-qubit gates with the first available fused gate. for (const auto& g : gate_seq0) { - fuse_to->gates.push_back(parent(g)); + fuse_to->gates.push_back(OpGetAlternative(*parent(g))); } } else { - auto g0 = parent(gate_seq0[0]); - fused_gates.push_back({g0->kind, g0->time, {}, g0, {g0}, {}}); + const auto& gate0 = *OpGetAlternative(*parent(gate_seq0[0])); + FusedGate fgate{gate0.kind, gate0.time, {}, &gate0, {&gate0}, {}}; for (std::size_t i = 1; i < gate_seq0.size(); ++i) { - fused_gates.back().gates.push_back(parent(gate_seq0[i])); + fgate.gates.push_back(OpGetAlternative(*parent(gate_seq0[i]))); } + + fused_ops.push_back(std::move(fgate)); } } @@ -171,19 +146,24 @@ class Fuser { * Multiplies component gate matrices of a fused gate. * @param gate Fused gate. */ -template -inline void CalculateFusedMatrix(FusedGate& gate) { +template +inline void CalculateFusedMatrix(FusedGate& gate) { MatrixIdentity(unsigned{1} << gate.qubits.size(), gate.matrix); - for (auto pgate : gate.gates) { - if (pgate->qubits.size() == 0) { - MatrixScalarMultiply(pgate->matrix[0], pgate->matrix[1], gate.matrix); - } else if (gate.qubits.size() == pgate->qubits.size()) { - MatrixMultiply(gate.qubits.size(), pgate->matrix, gate.matrix); + for (const auto& pgate : gate.gates) { + const auto* pg = OpGetAlternative>(pgate); + const auto& pqubits = OpQubits(pgate); + const auto& pmatrix = + pg ? pg->matrix : OpGetAlternative>(pgate)->matrix; + + if (pqubits.size() == 0) { + MatrixScalarMultiply(pmatrix[0], pmatrix[1], gate.matrix); + } else if (gate.qubits.size() == pqubits.size()) { + MatrixMultiply(gate.qubits.size(), pmatrix, gate.matrix); } else { unsigned mask = 0; - for (auto q : pgate->qubits) { + for (auto q : pqubits) { for (std::size_t i = 0; i < gate.qubits.size(); ++i) { if (q == gate.qubits[i]) { mask |= unsigned{1} << i; @@ -192,34 +172,12 @@ inline void CalculateFusedMatrix(FusedGate& gate) { } } - MatrixMultiply(mask, pgate->qubits.size(), pgate->matrix, + MatrixMultiply(mask, pqubits.size(), pmatrix, gate.qubits.size(), gate.matrix); } } } -/** - * Multiplies component gate matrices for a range of fused gates. - * @param gbeg, gend The iterator range [gbeg, gend) of fused gates. - */ -template -inline void CalculateFusedMatrices(Iterator gbeg, Iterator gend) { - for (auto g = gbeg; g != gend; ++g) { - if (g->kind != gate::kMeasurement) { - CalculateFusedMatrix(*g); - } - } -} - -/** - * Multiplies component gate matrices for a vector of fused gates. - * @param gates The vector of fused gates. - */ -template -inline void CalculateFusedMatrices(std::vector& gates) { - CalculateFusedMatrices(gates.begin(), gates.end()); -} - } // namespace qsim #endif // FUSER_H_ diff --git a/lib/fuser_basic.h b/lib/fuser_basic.h index 3191bd2d3..6c1113da8 100644 --- a/lib/fuser_basic.h +++ b/lib/fuser_basic.h @@ -20,29 +20,27 @@ #include #include -#include "gate.h" #include "fuser.h" +#include "gate.h" +#include "operation.h" +#include "operation_base.h" namespace qsim { /** - * Stateless object with methods for aggregating `Gate`s into `GateFused`. - * Measurement gates with equal times are fused together. - * User-defined controlled gates (controlled_by.size() > 0) and gates acting on - * more than two qubits are not fused. - * The template parameter Gate can be Gate type or a pointer to Gate type. + * Stateless object with methods for aggregating matrix `Gate`s into + * `FusedGate`s. Measurement gates with equal times are fused together. + * Non-matrix gates (such as user-defined controlled gates) and matrix-gates + * acting on more than two qubits are not fused. * This class is deprecated. It is recommended to use MultiQubitGateFuser * from fuser_mqubit.h. */ -template -class BasicGateFuser final : public Fuser { +template +class BasicGateFuser final : public Fuser { private: - using Base = Fuser; - using RGate = typename Base::RGate; + using Base = Fuser; public: - using GateFused = qsim::GateFused; - /** * User-specified parameters for gate fusion. * BasicGateFuser does not use any parameters. @@ -53,352 +51,432 @@ class BasicGateFuser final : public Fuser { /** * Stores sets of gates that can be applied together. Only one- and - * two-qubit gates will get fused. To respect specific time boundaries while - * fusing gates, use the other version of this method below. + * two-qubit matrix gates (represented by the Gate struct) are fused. + * To respect specific time boundaries while fusing gates, use the other + * version of this method below. * @param param Options for gate fusion. - * @param max_qubit1 The maximum qubit index (plus one) acted on by 'gates'. - * @param gates The gates (or pointers to the gates) to be fused. - * Gate times of the gates that act on the same qubits should be ordered. - * Gates that are out of time order should not cross the time boundaries - * set by measurement gates. + * @param max_qubit1 The maximum qubit index (plus one) acted on by + * operations. + * @param ops Operations to be fused. Operation times for gates acting + * on the same qubits must be ordered. Operations that are out of time + * order must not cross the time boundaries set by `times_to_split_at` + * or by measurement gates. * @param fuse_matrix If true, multiply gate matrices together. - * @return A vector of fused gate objects. Each element is a set of gates - * acting on a specific pair of qubits which can be applied as a group. + * @return A vector of fused operations. Each element is a variant + * containing: + * - A `FusedGate` object for merged gates. + * - A `Measurement` object (representing a fused measurement). + * - A `const Operation*` pointing to the original operation if it was not + * eligible for fusion (e.g., multi-controlled gates). + * Note: The input operation container must outlive the returned vector, + * as the variant may hold pointers to the original operations. */ - static std::vector FuseGates(const Parameter& param, - unsigned max_qubit1, - const std::vector& gates, - bool fuse_matrix = true) { - return FuseGates( - param, max_qubit1, gates.cbegin(), gates.cend(), {}, fuse_matrix); + template + static auto FuseGates(const Parameter& param, unsigned max_qubit1, + const std::vector& ops, + bool fuse_matrix = true) { + return FuseGates( + param, max_qubit1, ops.cbegin(), ops.cend(), {}, fuse_matrix); } /** * Stores sets of gates that can be applied together. Only one- and - * two-qubit gates will get fused. + * two-qubit matrix gates (represented by the Gate struct) are fused. * @param param Options for gate fusion. - * @param max_qubit1 The maximum qubit index (plus one) acted on by 'gates'. - * @param gates The gates (or pointers to the gates) to be fused. - * Gate times of the gates that act on the same qubits should be ordered. - * Gates that are out of time order should not cross the time boundaries - * set by `times_to_split_at` or by measurement gates. + * @param max_qubit1 The maximum qubit index (plus one) acted on by + * operations. + * @param ops Operations to be fused. Operation times for gates acting + * on the same qubits must be ordered. Operations that are out of time + * order must not cross the time boundaries set by `times_to_split_at` + * or by measurement gates. * @param times_to_split_at Ordered list of time steps (boundaries) at which - * to separate fused gates. Each element of the output will contain gates - * from a single 'window' in this list. + * to separate fused gates. Each element of the output contains gates + * from a single 'window' defined by this list. * @param fuse_matrix If true, multiply gate matrices together. - * @return A vector of fused gate objects. Each element is a set of gates - * acting on a specific pair of qubits which can be applied as a group. + * @return A vector of fused operations. Each element is a variant + * containing: + * - A `FusedGate` object for merged gates. + * - A `Measurement` object (representing a fused measurement). + * - A `const Operation*` pointing to the original operation if it was not + * eligible for fusion (e.g., multi-controlled gates). + * Note: The input operation container must outlive the returned vector, + * as the variant may hold pointers to the original operations. */ - static std::vector FuseGates( - const Parameter& param, - unsigned max_qubit1, const std::vector& gates, - const std::vector& times_to_split_at, - bool fuse_matrix = true) { - return FuseGates(param, max_qubit1, gates.cbegin(), gates.cend(), - times_to_split_at, fuse_matrix); + template + static auto FuseGates(const Parameter& param, unsigned max_qubit1, + const std::vector& ops, + const std::vector& times_to_split_at, + bool fuse_matrix = true) { + return FuseGates(param, max_qubit1, ops.cbegin(), ops.cend(), + times_to_split_at, fuse_matrix); } /** * Stores sets of gates that can be applied together. Only one- and - * two-qubit gates will get fused. To respect specific time boundaries while - * fusing gates, use the other version of this method below. + * two-qubit matrix gates (represented by the Gate struct) are fused. + * To respect specific time boundaries while fusing gates, use the other + * version of this method below. * @param param Options for gate fusion. - * @param max_qubit1 The maximum qubit index (plus one) acted on by 'gates'. - * @param gfirst, glast The iterator range [gfirst, glast) to fuse gates - * (or pointers to gates) in. Gate times of the gates that act on the same - * qubits should be ordered. Gates that are out of time order should not - * cross the time boundaries set by measurement gates. + * @param max_qubit1 The maximum qubit index (plus one) acted on by + * operations. + * @param obeg, oend The iterator range [obeg, oend) of operations + * to be fused. Operation times for gates acting on the same qubits + * must be ordered. Operations that are out of time order must + * not cross the time boundaries set by `times_to_split_at` or + * by measurement gates. * @param fuse_matrix If true, multiply gate matrices together. - * @return A vector of fused gate objects. Each element is a set of gates - * acting on a specific pair of qubits which can be applied as a group. + * @return A vector of fused operations. Each element is a variant + * containing: + * - A `FusedGate` object for merged gates. + * - A `Measurement` object (representing a fused measurement). + * - A `const Operation*` pointing to the original operation if it was not + * eligible for fusion (e.g., multi-controlled gates). + * Note: The input operation container must outlive the returned vector, + * as the variant may hold pointers to the original operations. */ - static std::vector FuseGates( + template + static auto FuseGates( const Parameter& param, unsigned max_qubit1, - typename std::vector::const_iterator gfirst, - typename std::vector::const_iterator glast, + typename std::vector::const_iterator obeg, + typename std::vector::const_iterator oend, bool fuse_matrix = true) { - return FuseGates(param, max_qubit1, gfirst, glast, {}, fuse_matrix); + return FuseGates( + param, max_qubit1, obeg, oend, {}, fuse_matrix); } /** * Stores sets of gates that can be applied together. Only one- and - * two-qubit gates will get fused. + * two-qubit matrix gates (represented by the Gate struct) are fused. * @param param Options for gate fusion. - * @param max_qubit1 The maximum qubit index (plus one) acted on by 'gates'. - * @param gfirst, glast The iterator range [gfirst, glast) to fuse gates - * (or pointers to gates) in. Gate times of the gates that act on the same - * qubits should be ordered. Gates that are out of time order should not - * cross the time boundaries set by `times_to_split_at` or by measurement - * gates. + * @param max_qubit1 The maximum qubit index (plus one) acted on by + * operations. + * @param obeg, oend The iterator range [obeg, oend) of operations + * to be fused. Operation times for gates acting on the same qubits + * must be ordered. Operations that are out of time order must + * not cross the time boundaries set by `times_to_split_at` or + * by measurement gates. * @param times_to_split_at Ordered list of time steps (boundaries) at which - * to separate fused gates. Each element of the output will contain gates - * from a single 'window' in this list. + * to separate fused gates. Each element of the output contains gates + * from a single 'window' defined by this list. * @param fuse_matrix If true, multiply gate matrices together. - * @return A vector of fused gate objects. Each element is a set of gates - * acting on a specific pair of qubits which can be applied as a group. + * @return A vector of fused operations. Each element is a variant + * containing: + * - A `FusedGate` object for merged gates. + * - A `Measurement` object (representing a fused measurement). + * - A `const Operation*` pointing to the original operation if it was not + * eligible for fusion (e.g., multi-controlled gates). + * Note: The input operation container must outlive the returned vector, + * as the variant may hold pointers to the original operations. */ - static std::vector FuseGates( + template + static auto FuseGates( const Parameter& param, unsigned max_qubit1, - typename std::vector::const_iterator gfirst, - typename std::vector::const_iterator glast, + typename std::vector::const_iterator obeg, + typename std::vector::const_iterator oend, const std::vector& times_to_split_at, bool fuse_matrix = true) { - std::vector gates_fused; + using Operation = std::remove_pointer_t; + using fp_type = OpFpType; + using Gate = qsim::Gate; + using ControlledGate = qsim::ControlledGate; + using DecomposedGate = qsim::DecomposedGate; + using FusedGate = qsim::FusedGate; + using OperationF = std::variant; - if (gfirst >= glast) return gates_fused; + std::vector fused_ops; - std::size_t num_gates = glast - gfirst; + if (obeg >= oend) return fused_ops; - gates_fused.reserve(num_gates); + std::size_t num_ops = oend - obeg; + + fused_ops.reserve(num_ops); // Merge with measurement gate times to separate fused gates at. - auto times = - Base::MergeWithMeasurementTimes(gfirst, glast, times_to_split_at); + auto times = Base::template MergeWithMeasurementTimes( + obeg, oend, times_to_split_at); // Map to keep track of measurement gates with equal times. - std::map> measurement_gates; + std::map> measurement_gates; - // Sequence of top level gates the other gates get fused to. - std::vector gates_seq; + // Sequence of top level operations the other gates get fused to. + std::vector ops_seq; // Sequence of zero-qubit gates. - std::vector gates_seq0; + std::vector ops_seq0; - // Lattice of gates: qubits "hyperplane" and time direction. - std::vector> gates_lat(max_qubit1); + // Lattice of operations: qubits "hyperplane" and time direction. + std::vector> ops_lat(max_qubit1); - // Current unfused gate. - auto gate_it = gfirst; + // Current unfused operation. + auto op_it = obeg; - std::size_t last_fused_gate_index = 0; + std::size_t last_fused_op_index = 0; + // Iterate over time windows. for (std::size_t l = 0; l < times.size(); ++l) { - gates_seq.resize(0); - gates_seq.reserve(num_gates); + ops_seq.resize(0); + ops_seq.reserve(num_ops); - gates_seq0.resize(0); - gates_seq0.reserve(num_gates); + ops_seq0.resize(0); + ops_seq0.reserve(num_ops); for (unsigned k = 0; k < max_qubit1; ++k) { - gates_lat[k].resize(0); - gates_lat[k].reserve(128); + ops_lat[k].resize(0); + ops_lat[k].reserve(128); } - // Fill gates_seq and gates_lat in. - for (; gate_it < glast; ++gate_it) { - const auto& gate = Base::GateToConstRef(*gate_it); + // Iterate over input operations and fill ops_seq and ops_lat in. + for (; op_it < oend; ++op_it) { + const auto& op = Base::OperationToConstRef(*op_it); + const auto& bop = OpBaseOperation(op); - if (gate.time > times[l]) break; + if (bop.time > times[l]) break; - if (!ValidateGate(gate, max_qubit1, gates_lat)) { - gates_fused.resize(0); - return gates_fused; + if (!ValidateOp(op, bop, max_qubit1, ops_lat)) { + fused_ops.resize(0); + return fused_ops; } - if (gate.kind == gate::kMeasurement) { - auto& mea_gates_at_time = measurement_gates[gate.time]; + if (OpGetAlternative(op)) { + auto& mea_gates_at_time = measurement_gates[bop.time]; if (mea_gates_at_time.size() == 0) { - gates_seq.push_back(&gate); + ops_seq.push_back(&op); mea_gates_at_time.reserve(max_qubit1); } - mea_gates_at_time.push_back(&gate); - } else if (gate.controlled_by.size() > 0 || gate.qubits.size() > 2) { - for (auto q : gate.qubits) { - gates_lat[q].push_back(&gate); + mea_gates_at_time.push_back(&op); + } else { + for (auto q : bop.qubits) { + ops_lat[q].push_back(&op); } - for (auto q : gate.controlled_by) { - gates_lat[q].push_back(&gate); + + if (const auto* pg = OpGetAlternative(op)) { + for (auto q : pg->controlled_by) { + ops_lat[q].push_back(&op); + } } - gates_seq.push_back(&gate); - } else if (gate.qubits.size() == 1) { - gates_lat[gate.qubits[0]].push_back(&gate); - if (gate.unfusible) { - gates_seq.push_back(&gate); + + bool is_gate = OpGetAlternative(op) != nullptr; + + if (is_gate && bop.qubits.size() == 0) { + ops_seq0.push_back(&op); + } else if (!is_gate || bop.qubits.size() != 1) { + ops_seq.push_back(&op); } - } else if (gate.qubits.size() == 2) { - gates_lat[gate.qubits[0]].push_back(&gate); - gates_lat[gate.qubits[1]].push_back(&gate); - gates_seq.push_back(&gate); - } else { - gates_seq0.push_back(&gate); } } std::vector last(max_qubit1, 0); - const RGate* delayed_measurement_gate = nullptr; + const Operation* deferred_measurement = nullptr; // Fuse gates. - for (auto pgate : gates_seq) { - if (pgate->kind == gate::kMeasurement) { - delayed_measurement_gate = pgate; - } else if (pgate->qubits.size() > 2 - || pgate->controlled_by.size() > 0) { + for (auto pop : ops_seq) { + const auto& qubits = OpQubits(*pop); + + if (OpGetAlternative(*pop)) { + deferred_measurement = pop; + } else if (const auto* pg = OpGetAlternative(*pop)) { + unsigned q0 = qubits[0]; + FusedGate fgate{pg->kind, pg->time, {q0}, pg, {}, {}}; + + last[q0] = Advance(last[q0], ops_lat[q0], fgate.gates); + + // Here pop == ops_lat[q0][last[q0]]. + + fgate.gates.push_back(pg); + last[q0] = Advance(last[q0] + 1, ops_lat[q0], fgate.gates); + + fused_ops.push_back(std::move(fgate)); + } else if (!OpGetAlternative(*pop) || qubits.size() > 2) { // Multi-qubit or controlled gate. - for (auto q : pgate->qubits) { + for (auto q : qubits) { unsigned l = last[q]; - if (gates_lat[q][l] != pgate) { - last[q] = AddOrphanedQubit(q, l, gates_lat, gates_fused); + if (ops_lat[q][l] != pop) { + last[q] = AddOrphanedQubit(q, l, ops_lat, fused_ops); } ++last[q]; } - for (auto q : pgate->controlled_by) { - unsigned l = last[q]; - if (gates_lat[q][l] != pgate) { - last[q] = AddOrphanedQubit(q, l, gates_lat, gates_fused); + if (const auto* pg = OpGetAlternative(*pop)) { + for (auto q : pg->controlled_by) { + unsigned l = last[q]; + if (ops_lat[q][l] != pop) { + last[q] = AddOrphanedQubit(q, l, ops_lat, fused_ops); + } + ++last[q]; } - ++last[q]; } - gates_fused.push_back({pgate->kind, pgate->time, pgate->qubits, - pgate, {pgate}, {}}); - } else if (pgate->qubits.size() == 1) { - unsigned q0 = pgate->qubits[0]; - - GateFused gate_f = {pgate->kind, pgate->time, {q0}, pgate, {}, {}}; + fused_ops.push_back(pop); + } else { + // Two-qubit gate. - last[q0] = Advance(last[q0], gates_lat[q0], gate_f.gates); - gate_f.gates.push_back(gates_lat[q0][last[q0]]); - last[q0] = Advance(last[q0] + 1, gates_lat[q0], gate_f.gates); + unsigned q0 = qubits[0]; + unsigned q1 = qubits[1]; - gates_fused.push_back(std::move(gate_f)); - } else if (pgate->qubits.size() == 2) { - unsigned q0 = pgate->qubits[0]; - unsigned q1 = pgate->qubits[1]; + const auto& gate = *OpGetAlternative(*pop); - if (Done(last[q0], pgate->time, gates_lat[q0])) continue; + if (Done(last[q0], gate.time, ops_lat[q0])) continue; - GateFused gate_f = - {pgate->kind, pgate->time, {q0, q1}, pgate, {}, {}}; + FusedGate fgate = {gate.kind, gate.time, {q0, q1}, &gate, {}, {}}; do { - last[q0] = Advance(last[q0], gates_lat[q0], gate_f.gates); - last[q1] = Advance(last[q1], gates_lat[q1], gate_f.gates); - // Here gates_lat[q0][last[q0]] == gates_lat[q1][last[q1]]. + last[q0] = Advance(last[q0], ops_lat[q0], fgate.gates); + last[q1] = Advance(last[q1], ops_lat[q1], fgate.gates); + + // Here ops_lat[q0][last[q0]] == ops_lat[q1][last[q1]]. - gate_f.gates.push_back(gates_lat[q0][last[q0]]); + const auto& gate2 = *OpGetAlternative(*ops_lat[q0][last[q0]]); + fgate.gates.push_back(&gate2); - last[q0] = Advance(last[q0] + 1, gates_lat[q0], gate_f.gates); - last[q1] = Advance(last[q1] + 1, gates_lat[q1], gate_f.gates); - } while (NextGate(last[q0], gates_lat[q0], last[q1], gates_lat[q1])); + last[q0] = Advance(last[q0] + 1, ops_lat[q0], fgate.gates); + last[q1] = Advance(last[q1] + 1, ops_lat[q1], fgate.gates); + } while (NextGate(last[q0], ops_lat[q0], last[q1], ops_lat[q1])); - gates_fused.push_back(std::move(gate_f)); + fused_ops.push_back(std::move(fgate)); } } for (unsigned q = 0; q < max_qubit1; ++q) { auto l = last[q]; - if (l == gates_lat[q].size()) continue; + if (l == ops_lat[q].size()) continue; // Orphaned qubit. - AddOrphanedQubit(q, l, gates_lat, gates_fused); + AddOrphanedQubit(q, l, ops_lat, fused_ops); } - if (delayed_measurement_gate != nullptr) { - auto pgate = delayed_measurement_gate; + if (deferred_measurement != nullptr) { + const auto& mea_gates_at_time = + measurement_gates[OpTime(*deferred_measurement)]; - const auto& mea_gates_at_time = measurement_gates[pgate->time]; + if (mea_gates_at_time.size() == 1) { + fused_ops.push_back(deferred_measurement); + } else { + Measurement mfused = + *OpGetAlternative(*deferred_measurement); - GateFused gate_f = {pgate->kind, pgate->time, {}, pgate, {}, {}}; - gate_f.gates.reserve(mea_gates_at_time.size()); + // Fuse measurement gates with equal times. + for (const auto* pop : mea_gates_at_time) { + if (pop == deferred_measurement) continue; - // Fuse measurement gates with equal times. + const auto& qs = OpQubits(*pop); + mfused.qubits.insert(mfused.qubits.end(), qs.begin(), qs.end()); + } - for (const auto* pgate : mea_gates_at_time) { - gate_f.qubits.insert(gate_f.qubits.end(), - pgate->qubits.begin(), pgate->qubits.end()); - gate_f.gates.push_back(pgate); + fused_ops.push_back(std::move(mfused)); } - - gates_fused.push_back(std::move(gate_f)); } - if (gates_seq0.size() != 0) { - Base::FuseZeroQubitGates(gates_seq0, [](const RGate* g) { return g; }, - last_fused_gate_index, gates_fused); + if (ops_seq0.size() != 0) { + Base::FuseZeroQubitGates(ops_seq0, [](const Operation* g) { return g; }, + last_fused_op_index, fused_ops); } - if (gate_it == glast) break; + if (op_it == oend) break; - last_fused_gate_index = gates_fused.size(); + last_fused_op_index = fused_ops.size(); } if (fuse_matrix) { - for (auto& gate_f : gates_fused) { - if (gate_f.kind != gate::kMeasurement && gate_f.kind != gate::kDecomp) { - CalculateFusedMatrix(gate_f); + for (auto& op : fused_ops) { + if (auto* pg = OpGetAlternative(op)) { + if (!pg->ParentIsDecomposed()) { + CalculateFusedMatrix(*pg); + } } } } - return gates_fused; + return fused_ops; } private: - static unsigned Advance(unsigned k, const std::vector& wl, - std::vector& gates) { - while (k < wl.size() && wl[k]->qubits.size() == 1 - && wl[k]->controlled_by.size() == 0 && !wl[k]->unfusible) { - gates.push_back(wl[k++]); + template + static unsigned Advance(unsigned k, const std::vector& wl, + std::vector& gates) { + using Gate = qsim::Gate>; + + while (k < wl.size()) { + if (const auto* pg = OpGetAlternative(*wl[k])) { + if (pg->qubits.size() == 1) { + gates.push_back(pg); + } else { + break; + } + } else { + break; + } + ++k; } return k; } + template static bool Done( - unsigned k, unsigned t, const std::vector& wl) { - return k >= wl.size() || wl[k]->time > t; + unsigned k, unsigned t, const std::vector& wl) { + return k >= wl.size() || OpTime(*wl[k]) > t; } - static bool NextGate(unsigned k1, const std::vector& wl1, - unsigned k2, const std::vector& wl2) { - return k1 < wl1.size() && k2 < wl2.size() && wl1[k1] == wl2[k2] - && wl1[k1]->qubits.size() < 3 && wl1[k1]->controlled_by.size() == 0; + template + static bool NextGate(unsigned k1, const std::vector& wl1, + unsigned k2, const std::vector& wl2) { + return k1 < wl1.size() && k2 < wl2.size() + && wl1[k1] == wl2[k2] && OpQubits(*wl1[k1]).size() < 3 + && OpGetAlternative>>(*wl1[k1]); } - template + template static unsigned AddOrphanedQubit(unsigned q, unsigned k, - const GatesLat& gates_lat, - std::vector& gates_fused) { - auto pgate = gates_lat[q][k]; + const OpsLat& ops_lat, + std::vector& fused_ops) { + using FusedGate = std::variant_alternative_t<0, OperationF>; + using fp_type = typename FusedGate::fp_type; + using Gate = qsim::Gate; + + const auto& gate1 = *OpGetAlternative(*ops_lat[q][k]); - GateFused gate_f = {pgate->kind, pgate->time, {q}, pgate, {}, {}}; - gate_f.gates.push_back(pgate); + FusedGate fgate{gate1.kind, gate1.time, {q}, &gate1, {}, {}}; + fgate.gates.push_back(&gate1); - k = Advance(k + 1, gates_lat[q], gate_f.gates); + k = Advance(k + 1, ops_lat[q], fgate.gates); - gates_fused.push_back(std::move(gate_f)); + fused_ops.push_back(std::move(fgate)); return k; } - template - static bool ValidateGate(const Gate2& gate, unsigned max_qubit1, - const GatesLat& gates_lat) { - for (unsigned q : gate.qubits) { + template + static bool ValidateOp(const Operation& op, const BaseOperation& bop, + unsigned max_qubit1, const OpsLat& ops_lat) { + using ControlledGate = qsim::ControlledGate>; + + for (unsigned q : bop.qubits) { if (q >= max_qubit1) { IO::errorf("fuser: gate qubit %u is out of range " "(should be smaller than %u).\n", q, max_qubit1); return false; } - if (!gates_lat[q].empty() && gate.time <= gates_lat[q].back()->time) { - IO::errorf("fuser: gate at time %u is out of time order.\n", gate.time); + if (!ops_lat[q].empty() && bop.time <= OpTime(*ops_lat[q].back())) { + IO::errorf("fuser: gate at time %u is out of time order.\n", bop.time); return false; } } - for (unsigned q : gate.controlled_by) { - if (q >= max_qubit1) { - IO::errorf("fuser: gate qubit %u is out of range " - "(should be smaller than %u).\n", q, max_qubit1); - return false; - } - if (!gates_lat[q].empty() && gate.time <= gates_lat[q].back()->time) { - IO::errorf("fuser: gate at time %u is out of time order.\n", gate.time); - return false; + if (const auto& pg = OpGetAlternative(op)) { + for (unsigned q : pg->controlled_by) { + if (q >= max_qubit1) { + IO::errorf("fuser: gate qubit %u is out of range " + "(should be smaller than %u).\n", q, max_qubit1); + return false; + } + if (!ops_lat[q].empty() && bop.time <= OpTime(*ops_lat[q].back())) { + IO::errorf( + "fuser: gate at time %u is out of time order.\n", bop.time); + return false; + } } } diff --git a/lib/fuser_mqubit.h b/lib/fuser_mqubit.h index 11da98557..2c1e55ec6 100644 --- a/lib/fuser_mqubit.h +++ b/lib/fuser_mqubit.h @@ -22,22 +22,23 @@ #include #include -#include "gate.h" #include "fuser.h" +#include "gate.h" +#include "operation.h" +#include "operation_base.h" namespace qsim { /** - * Multi-qubit gate fuser. - * Measurement gates with equal times are fused together. - * User-defined controlled gates (controlled_by.size() > 0) are not fused. - * The template parameter Gate can be Gate type or a pointer to Gate type. + * Stateless object with methods for aggregating matrix `Gate`s into + * `FusedGate`s. Measurement gates with equal times are fused together. + * Non-matrix gates (such as user-defined controlled gates) are not fused. + * This fuser can fuse into up to six-qbuit gates. */ -template -class MultiQubitGateFuser final : public Fuser { +template +class MultiQubitGateFuser final : public Fuser { private: - using Base = Fuser; - using RGate = typename Base::RGate; + using Base = Fuser; // Auxillary classes and structs. @@ -79,18 +80,24 @@ class MultiQubitGateFuser final : public Fuser { std::vector links_; }; + template struct GateF; - using LinkManager = LinkManagerT; - using Link = typename LinkManager::Link; + template + using LinkManager = LinkManagerT*>; + template + using Link = typename LinkManager::Link; // Intermediate representation of a fused gate. + template struct GateF { - const RGate* parent; - std::vector qubits; - std::vector gates; // Gates that get fused to this gate. - std::vector links; // Gate "lattice" links. - uint64_t mask; // Qubit mask. + using fp_type = OpFpType; + + const Parent* parent; + Qubits qubits; + std::vector gates; // Gates that get fused to this gate. + std::vector*> links; // Gate "lattice" links. + uint64_t mask; // Qubit mask. unsigned visited; }; @@ -98,175 +105,223 @@ class MultiQubitGateFuser final : public Fuser { // Note that MakeGateSequence assignes values from kSecond to the number of // gates in the sequence plus one, see below. enum Visited { - kZero = 0, // Start value for "normal" gates. - kFirst = 1, // Value after the first pass for partially fused - // "normal" gates. - kSecond = 2, // Start value to assign values in MakeGateSequence. - kCompress = 99999997, // Used to compress links. - kMeaCnt = 99999998, // Start value for controlled or measurement gates. - kFinal = 99999999, // Value after the second pass for fused "normal" - // gates or for controlled and measurement gates. + kZero = 0, // Start value for matrix gates. + kFirst = 1, // Value after the first pass for partially fused + // matrix gates. + kSecond = 2, // Start value to assign values in MakeGateSequence. + kCompress = 99999997, // Used to compress links. + kUnfusible = 99999998, // Start value for controlled or measurement gates. + kFinal = 99999999, // Value after the first pass for child gates or + // after the second pass for fused matrix gates, + // controlled gates, and measurement gates. }; struct Stat { - unsigned num_mea_gates = 0; - unsigned num_fused_mea_gates = 0; + unsigned num_measurements = 0; + unsigned num_fused_measurements = 0; unsigned num_fused_gates = 0; - unsigned num_controlled_gates = 0; + unsigned num_unfusible_gates = 0; std::vector num_gates; }; // Gate that is added to a sequence of gates to fuse together. + template struct GateA { - GateF* gate; - std::vector qubits; // Added qubits. - std::vector links; // Added lattice links. + GateF* gate; + Qubits qubits; // Added qubits. + std::vector*> links; // Added lattice links. }; + template struct Scratch { - std::vector data; - std::vector prev1; - std::vector prev2; - std::vector next1; - std::vector next2; - std::vector longest_seq; - std::vector stack; - std::vector gates; + std::vector> data; + std::vector*> prev1; + std::vector*> prev2; + std::vector*> next1; + std::vector*> next2; + std::vector*> longest_seq; + std::vector*> stack; + std::vector*> gates; unsigned count = 0; }; public: - using GateFused = qsim::GateFused; - /** * User-specified parameters for gate fusion. */ struct Parameter { /** * Maximum number of qubits in a fused gate. It can take values from 2 to - * 6 (0 and 1 are equivalent to 2). It is not recommended to use 5 or 6 as - * that might degrade performance for not very fast machines. + * 6 (0 and 1 are equivalent to 2). For optimal performance, a value of + * 4 or 5 is recommended to balance memory bandwidth and computational + * overhead. */ unsigned max_fused_size = 2; unsigned verbosity = 0; }; /** - * Stores sets of gates that can be applied together. To respect specific - * time boundaries while fusing gates, use the other version of this method - * below. + * Stores sets of gates that can be applied together. Only one- and + * two-qubit matrix gates (represented by the Gate struct) are fused. + * To respect specific time boundaries while fusing gates, use the other + * version of this method below. * @param param Options for gate fusion. - * @param max_qubit1 The maximum qubit index (plus one) acted on by 'gates'. - * @param gates The gates (or pointers to the gates) to be fused. - * Gate times of the gates that act on the same qubits should be ordered. - * Gates that are out of time order should not cross the time boundaries - * set by measurement gates. + * @param max_qubit1 The maximum qubit index (plus one) acted on by + * operations. + * @param ops Operations to be fused. Operation times for gates acting + * on the same qubits must be ordered. Operations that are out of time + * order must not cross the time boundaries set by `times_to_split_at` + * or by measurement gates. * @param fuse_matrix If true, multiply gate matrices together. - * @return A vector of fused gate objects. Each element is a set of gates - * acting on a specific pair of qubits which can be applied as a group. + * @return A vector of fused operations. Each element is a variant + * containing: + * - A `FusedGate` object for merged gates. + * - A `Measurement` object (representing a fused measurement). + * - A `const Operation*` pointing to the original operation if it was not + * eligible for fusion (e.g., multi-controlled gates). + * Note: The input operation container must outlive the returned vector, + * as the variant may hold pointers to the original operations. */ - static std::vector FuseGates(const Parameter& param, - unsigned max_qubit1, - const std::vector& gates, - bool fuse_matrix = true) { - return FuseGates( - param, max_qubit1, gates.cbegin(), gates.cend(), {}, fuse_matrix); + template + static auto FuseGates(const Parameter& param, unsigned max_qubit1, + const std::vector& ops, + bool fuse_matrix = true) { + return FuseGates( + param, max_qubit1, ops.cbegin(), ops.cend(), {}, fuse_matrix); } /** - * Stores sets of gates that can be applied together. + * Stores sets of gates that can be applied together. Only one- and + * two-qubit matrix gates (represented by the Gate struct) are fused. * @param param Options for gate fusion. - * @param max_qubit1 The maximum qubit index (plus one) acted on by 'gates'. - * @param gates The gates (or pointers to the gates) to be fused. - * Gate times of the gates that act on the same qubits should be ordered. - * Gates that are out of time order should not cross the time boundaries - * set by `times_to_split_at` or by measurement gates. + * @param max_qubit1 The maximum qubit index (plus one) acted on by + * operations. + * @param ops Operations to be fused. Operation times for gates acting + * on the same qubits must be ordered. Operations that are out of time + * order must not cross the time boundaries set by `times_to_split_at` + * or by measurement gates. * @param times_to_split_at Ordered list of time steps (boundaries) at which - * to separate fused gates. Each element of the output will contain gates - * from a single 'window' in this list. + * to separate fused gates. Each element of the output contains gates + * from a single 'window' defined by this list. * @param fuse_matrix If true, multiply gate matrices together. - * @return A vector of fused gate objects. Each element is a set of gates - * acting on a specific pair of qubits which can be applied as a group. + * @return A vector of fused operations. Each element is a variant + * containing: + * - A `FusedGate` object for merged gates. + * - A `Measurement` object (representing a fused measurement). + * - A `const Operation*` pointing to the original operation if it was not + * eligible for fusion (e.g., multi-controlled gates). + * Note: The input operation container must outlive the returned vector, + * as the variant may hold pointers to the original operations. */ - static std::vector FuseGates( - const Parameter& param, - unsigned max_qubit1, const std::vector& gates, - const std::vector& times_to_split_at, - bool fuse_matrix = true) { - return FuseGates(param, max_qubit1, gates.cbegin(), gates.cend(), - times_to_split_at, fuse_matrix); + template + static auto FuseGates(const Parameter& param, unsigned max_qubit1, + const std::vector& ops, + const std::vector& times_to_split_at, + bool fuse_matrix = true) { + return FuseGates(param, max_qubit1, ops.cbegin(), ops.cend(), + times_to_split_at, fuse_matrix); } /** - * Stores sets of gates that can be applied together. To respect specific - * time boundaries while fusing gates, use the other version of this method - * below. + * Stores sets of gates that can be applied together. Only one- and + * two-qubit matrix gates (represented by the Gate struct) are fused. + * To respect specific time boundaries while fusing gates, use the other + * version of this method below. * @param param Options for gate fusion. - * @param max_qubit1 The maximum qubit index (plus one) acted on by 'gates'. - * @param gfirst, glast The iterator range [gfirst, glast) to fuse gates - * (or pointers to gates) in. Gate times of the gates that act on the same - * qubits should be ordered. Gates that are out of time order should not - * cross the time boundaries set by measurement gates. + * @param max_qubit1 The maximum qubit index (plus one) acted on by + * operations. + * @param obeg, oend The iterator range [obeg, oend) of operations + * to be fused. Operation times for gates acting on the same qubits + * must be ordered. Operations that are out of time order must + * not cross the time boundaries set by `times_to_split_at` or + * by measurement gates. * @param fuse_matrix If true, multiply gate matrices together. - * @return A vector of fused gate objects. Each element is a set of gates - * acting on a specific pair of qubits which can be applied as a group. + * @return A vector of fused operations. Each element is a variant + * containing: + * - A `FusedGate` object for merged gates. + * - A `Measurement` object (representing a fused measurement). + * - A `const Operation*` pointing to the original operation if it was not + * eligible for fusion (e.g., multi-controlled gates). + * Note: The input operation container must outlive the returned vector, + * as the variant may hold pointers to the original operations. */ - static std::vector FuseGates( + template + static auto FuseGates( const Parameter& param, unsigned max_qubit1, - typename std::vector::const_iterator gfirst, - typename std::vector::const_iterator glast, + typename std::vector::const_iterator obeg, + typename std::vector::const_iterator oend, bool fuse_matrix = true) { - return FuseGates(param, max_qubit1, gfirst, glast, {}, fuse_matrix); + return FuseGates( + param, max_qubit1, obeg, oend, {}, fuse_matrix); } /** - * Stores sets of gates that can be applied together. + * Stores sets of gates that can be applied together. Only one- and + * two-qubit matrix gates (represented by the Gate struct) are fused. * @param param Options for gate fusion. - * @param max_qubit1 The maximum qubit index (plus one) acted on by 'gates'. - * @param gfirst, glast The iterator range [gfirst, glast) to fuse gates - * (or pointers to gates) in. Gate times of the gates that act on the same - * qubits should be ordered. Gates that are out of time order should not - * cross the time boundaries set by `times_to_split_at` or by measurement - * gates. + * @param max_qubit1 The maximum qubit index (plus one) acted on by + * operations. + * @param obeg, oend The iterator range [obeg, oend) of operations + * to be fused. Operation times for gates acting on the same qubits + * must be ordered. Operations that are out of time order must + * not cross the time boundaries set by `times_to_split_at` or + * by measurement gates. * @param times_to_split_at Ordered list of time steps (boundaries) at which - * to separate fused gates. Each element of the output will contain gates - * from a single 'window' in this list. + * to separate fused gates. Each element of the output contains gates + * from a single 'window' defined by this list. * @param fuse_matrix If true, multiply gate matrices together. - * @return A vector of fused gate objects. Each element is a set of gates - * acting on a specific pair of qubits which can be applied as a group. + * @return A vector of fused operations. Each element is a variant + * containing: + * - A `FusedGate` object for merged gates. + * - A `Measurement` object (representing a fused measurement). + * - A `const Operation*` pointing to the original operation if it was not + * eligible for fusion (e.g., multi-controlled gates). + * Note: The input operation container must outlive the returned vector, + * as the variant may hold pointers to the original operations. */ - static std::vector FuseGates( + template + static auto FuseGates( const Parameter& param, unsigned max_qubit1, - typename std::vector::const_iterator gfirst, - typename std::vector::const_iterator glast, + typename std::vector::const_iterator obeg, + typename std::vector::const_iterator oend, const std::vector& times_to_split_at, bool fuse_matrix = true) { - std::vector fused_gates; + using Operation = std::remove_pointer_t; + using fp_type = OpFpType; + using Gate = qsim::Gate; + using ControlledGate = qsim::ControlledGate; + using FusedGate = qsim::FusedGate; + using PGate = typename FusedGate::PGate; + using OperationF = std::variant; - if (gfirst >= glast) return fused_gates; + std::vector fused_ops; - std::size_t num_gates = glast - gfirst; + if (obeg >= oend) return fused_ops; - fused_gates.reserve(num_gates); + std::size_t num_ops = oend - obeg; + + fused_ops.reserve(num_ops); // Merge with measurement gate times to separate fused gates at. - auto epochs = - Base::MergeWithMeasurementTimes(gfirst, glast, times_to_split_at); + auto times = Base::template MergeWithMeasurementTimes( + obeg, oend, times_to_split_at); + + using GateF = MultiQubitGateFuser::GateF; - LinkManager link_manager(max_qubit1 * num_gates); + LinkManager link_manager(max_qubit1 * num_ops); // Auxillary data structures. // Sequence of intermediate fused gates. std::vector gates_seq; // Gate "lattice". - std::vector gates_lat; + std::vector*> gates_lat; // Sequences of intermediate fused gates ordered by gate size. std::vector> fgates(max_qubit1 + 1); - gates_seq.reserve(num_gates); + gates_seq.reserve(num_ops); gates_lat.reserve(max_qubit1); - Scratch scratch; + Scratch scratch; scratch.data.reserve(1024); scratch.prev1.reserve(32); @@ -283,10 +338,10 @@ class MultiQubitGateFuser final : public Fuser { max_fused_size = std::min(max_fused_size, max_qubit1); std::size_t last_fused_gate_index = 0; - auto gate_it = gfirst; + auto op_it = obeg; - // Iterate over epochs. - for (std::size_t l = 0; l < epochs.size(); ++l) { + // Iterate over time windows. + for (std::size_t l = 0; l < times.size(); ++l) { gates_seq.resize(0); gates_lat.resize(0); gates_lat.resize(max_qubit1, nullptr); @@ -296,55 +351,57 @@ class MultiQubitGateFuser final : public Fuser { } uint64_t max_gate_size = 0; - GateF* last_mea_gate = nullptr; + GateF* last_measurement = nullptr; - // Iterate over input gates. - for (; gate_it < glast; ++gate_it) { - const auto& gate = Base::GateToConstRef(*gate_it); + // Iterate over input operations. + for (; op_it < oend; ++op_it) { + const auto& op = Base::OperationToConstRef(*op_it); + const auto& bop = OpBaseOperation(op); - if (gate.time > epochs[l]) break; + if (bop.time > times[l]) break; - if (!ValidateGate(gate, max_qubit1, gates_lat)) { - fused_gates.resize(0); - return fused_gates; + if (!ValidateOp(op, max_qubit1, gates_lat)) { + fused_ops.resize(0); + return fused_ops; } // Fill in auxillary data structures. - if (gate.kind == gate::kMeasurement) { + if (OpGetAlternative(op)) { // Measurement gate. - if (last_mea_gate == nullptr - || last_mea_gate->parent->time != gate.time) { - gates_seq.push_back({&gate, {}, {}, {}, 0, kMeaCnt}); - last_mea_gate = &gates_seq.back(); + if (last_measurement == nullptr + || OpTime(*last_measurement->parent) != bop.time) { + gates_seq.push_back({&op, {}, {}, {}, 0, kUnfusible}); + last_measurement = &gates_seq.back(); - last_mea_gate->qubits.reserve(max_qubit1); - last_mea_gate->links.reserve(max_qubit1); + last_measurement->qubits.reserve(max_qubit1); + last_measurement->links.reserve(max_qubit1); - ++stat.num_fused_mea_gates; + ++stat.num_fused_measurements; } - for (auto q : gate.qubits) { - last_mea_gate->qubits.push_back(q); - last_mea_gate->mask |= uint64_t{1} << q; - gates_lat[q] = link_manager.AddBack(last_mea_gate, gates_lat[q]); - last_mea_gate->links.push_back(gates_lat[q]); + for (auto q : bop.qubits) { + last_measurement->qubits.push_back(q); + last_measurement->mask |= uint64_t{1} << q; + gates_lat[q] = link_manager.AddBack(last_measurement, gates_lat[q]); + last_measurement->links.push_back(gates_lat[q]); } - last_mea_gate->gates.push_back(&gate); - - ++stat.num_mea_gates; + ++stat.num_measurements; } else { - gates_seq.push_back({&gate, {}, {}, {}, 0, kZero}); + gates_seq.push_back({&op, {}, {}, {}, 0, kZero}); auto& fgate = gates_seq.back(); - if (gate.controlled_by.size() == 0) { - if (max_gate_size < gate.qubits.size()) { - max_gate_size = gate.qubits.size(); + unsigned num_gate_qubits = bop.qubits.size(); + + if (OpGetAlternative(op)) { + // Matrix gates that are fused. + + if (max_gate_size < num_gate_qubits) { + max_gate_size = num_gate_qubits; } - unsigned num_gate_qubits = gate.qubits.size(); unsigned size = std::max(max_fused_size, num_gate_qubits); fgate.qubits.reserve(size); @@ -353,47 +410,52 @@ class MultiQubitGateFuser final : public Fuser { fgate.links.reserve(size); if (fgates[num_gate_qubits].empty()) { - fgates[num_gate_qubits].reserve(num_gates); + fgates[num_gate_qubits].reserve(num_ops); } fgates[num_gate_qubits].push_back(&fgate); + for (auto q : bop.qubits) { + fgate.qubits.push_back(q); + } + ++stat.num_gates[num_gate_qubits]; } else { - // Controlled gate. - // Controlled gates are not fused with other gates. + // Other gates are not fused. - uint64_t size = gate.qubits.size() + gate.controlled_by.size(); + uint64_t size = num_gate_qubits; - fgate.qubits.reserve(gate.qubits.size()); - fgate.links.reserve(size); + if (const auto* pg = OpGetAlternative(op)) { + size += pg->controlled_by.size(); + } - fgate.visited = kMeaCnt; - fgate.gates.push_back(&gate); + fgate.links.reserve(size); + fgate.visited = kUnfusible; - ++stat.num_controlled_gates; + ++stat.num_unfusible_gates; } - for (auto q : gate.qubits) { - fgate.qubits.push_back(q); + for (auto q : bop.qubits) { fgate.mask |= uint64_t{1} << q; gates_lat[q] = link_manager.AddBack(&fgate, gates_lat[q]); fgate.links.push_back(gates_lat[q]); } - for (auto q : gate.controlled_by) { - fgate.mask |= uint64_t{1} << q; - gates_lat[q] = link_manager.AddBack(&fgate, gates_lat[q]); - fgate.links.push_back(gates_lat[q]); + if (const auto* pg = OpGetAlternative(op)) { + for (auto q : pg->controlled_by) { + fgate.mask |= uint64_t{1} << q; + gates_lat[q] = link_manager.AddBack(&fgate, gates_lat[q]); + fgate.links.push_back(gates_lat[q]); + } } } } // Fuse large gates with smaller gates. - FuseGates(max_gate_size, fgates); + FuseGates(max_gate_size, fgates); if (max_fused_size > 2) { FuseGateSequences( - max_fused_size, max_qubit1, scratch, gates_seq, stat, fused_gates); + max_fused_size, max_qubit1, scratch, gates_seq, stat, fused_ops); } else { unsigned prev_time = 0; @@ -401,63 +463,71 @@ class MultiQubitGateFuser final : public Fuser { orphaned_gates.reserve(max_qubit1); for (auto& fgate : gates_seq) { - if (fgate.gates.size() == 0) continue; + if (fgate.visited != kUnfusible && fgate.gates.size() == 0) continue; + + unsigned time = OpTime(*fgate.parent); - if (prev_time != fgate.parent->time) { + if (prev_time != time) { if (orphaned_gates.size() > 0) { FuseOrphanedGates( - max_fused_size, stat, orphaned_gates, fused_gates); + max_fused_size, stat, orphaned_gates, fused_ops); orphaned_gates.resize(0); } - prev_time = fgate.parent->time; + prev_time = time; } if (fgate.qubits.size() == 1 && max_fused_size > 1 - && fgate.visited != kMeaCnt && !fgate.parent->unfusible) { + && fgate.visited != kUnfusible) { orphaned_gates.push_back(&fgate); continue; } - // Assume fgate.qubits (gate.qubits) are sorted. - fused_gates.push_back({fgate.parent->kind, fgate.parent->time, - std::move(fgate.qubits), fgate.parent, - std::move(fgate.gates), {}}); + if (fgate.visited == kUnfusible) { + AddUnfusible(fgate, fused_ops); + } else { + // Assume fgate.qubits (gate.qubits) are sorted. + const Gate& parent = *OpGetAlternative(*fgate.parent); + fused_ops.push_back(FusedGate{parent.kind, parent.time, + std::move(fgate.qubits), &parent, + std::move(fgate.gates), {}}); - if (fgate.visited != kMeaCnt) { ++stat.num_fused_gates; } } if (orphaned_gates.size() > 0) { - FuseOrphanedGates(max_fused_size, stat, orphaned_gates, fused_gates); + FuseOrphanedGates(max_fused_size, stat, orphaned_gates, fused_ops); } } if (fgates[0].size() != 0) { Base::FuseZeroQubitGates(fgates[0], [](const GateF* g) { return g->parent; }, - last_fused_gate_index, fused_gates); + last_fused_gate_index, fused_ops); } - last_fused_gate_index = fused_gates.size(); + last_fused_gate_index = fused_ops.size(); } if (fuse_matrix) { - for (auto& fgate : fused_gates) { - if (fgate.kind != gate::kMeasurement && fgate.kind != gate::kDecomp) { - CalculateFusedMatrix(fgate); + for (auto& op : fused_ops) { + if (auto* pg = OpGetAlternative(op)) { + if (!pg->ParentIsDecomposed()) { + CalculateFusedMatrix(*pg); + } } } } - PrintStat(param.verbosity, stat, fused_gates); + PrintStat(param.verbosity, stat, fused_ops); - return fused_gates; + return fused_ops; } private: // Fuse large gates with smaller gates. + template static void FuseGates(uint64_t max_gate_size, std::vector>& fgates) { // Traverse gates in order of decreasing size. @@ -465,14 +535,19 @@ class MultiQubitGateFuser final : public Fuser { std::size_t pos = 0; for (auto fgate : fgates[max_gate_size - i]) { - if (fgate->visited > kZero) continue; + if (fgate->visited > kZero) { + if (fgate->gates.size() == 0) { + fgate->visited = kFinal; + } + continue; + } fgates[max_gate_size - i][pos++] = fgate; fgate->visited = kFirst; FusePrev(0, *fgate); - fgate->gates.push_back(fgate->parent); + fgate->gates.push_back(OpGetAlternative(*fgate->parent)); FuseNext(0, *fgate); } @@ -490,38 +565,48 @@ class MultiQubitGateFuser final : public Fuser { // max_fused_size = 5: _-_- or -_-_ // // max_fused_size = 6: _-_-_ + template static void FuseGateSequences(unsigned max_fused_size, unsigned max_qubit1, Scratch& scratch, std::vector& gates_seq, Stat& stat, - std::vector& fused_gates) { + std::vector& fused_ops) { + using FusedGate = std::variant_alternative_t<0, OperationF>; + using fp_type = typename FusedGate::fp_type; + using Gate = qsim::Gate; + unsigned prev_time = 0; std::vector orphaned_gates; orphaned_gates.reserve(max_qubit1); for (auto& fgate : gates_seq) { - if (prev_time != fgate.parent->time) { + unsigned time = OpTime(*fgate.parent); + + if (prev_time != time) { if (orphaned_gates.size() > 0) { - FuseOrphanedGates(max_fused_size, stat, orphaned_gates, fused_gates); + FuseOrphanedGates(max_fused_size, stat, orphaned_gates, fused_ops); orphaned_gates.resize(0); } - prev_time = fgate.parent->time; + prev_time = time; } - if (fgate.visited == kFinal || fgate.gates.size() == 0) continue; + if (fgate.visited == kFinal) continue; - if (fgate.visited == kMeaCnt || fgate.qubits.size() >= max_fused_size - || fgate.parent->unfusible) { - if (fgate.visited != kMeaCnt) { - ++stat.num_fused_gates; - } + if (fgate.visited == kUnfusible) { + AddUnfusible(fgate, fused_ops); - fgate.visited = kFinal; + continue; + } + + if (fgate.qubits.size() >= max_fused_size) { + const Gate& parent = *OpGetAlternative(*fgate.parent); + fused_ops.push_back(FusedGate{parent.kind, parent.time, + std::move(fgate.qubits), &parent, + std::move(fgate.gates), {}}); - fused_gates.push_back({fgate.parent->kind, fgate.parent->time, - std::move(fgate.qubits), fgate.parent, - std::move(fgate.gates), {}}); + fgate.visited = kFinal; + ++stat.num_fused_gates; continue; } @@ -544,9 +629,10 @@ class MultiQubitGateFuser final : public Fuser { for (auto fgate : scratch.gates) { std::sort(fgate->qubits.begin(), fgate->qubits.end()); - fused_gates.push_back({fgate->parent->kind, fgate->parent->time, - std::move(fgate->qubits), fgate->parent, - std::move(fgate->gates), {}}); + const Gate& parent = *OpGetAlternative(*fgate->parent); + fused_ops.push_back(FusedGate{parent.kind, parent.time, + std::move(fgate->qubits), &parent, + std::move(fgate->gates), {}}); ++stat.num_fused_gates; } @@ -554,13 +640,18 @@ class MultiQubitGateFuser final : public Fuser { } if (orphaned_gates.size() > 0) { - FuseOrphanedGates(max_fused_size, stat, orphaned_gates, fused_gates); + FuseOrphanedGates(max_fused_size, stat, orphaned_gates, fused_ops); } } + template static void FuseOrphanedGates(unsigned max_fused_size, Stat& stat, std::vector& orphaned_gates, - std::vector& fused_gates) { + std::vector& fused_ops) { + using FusedGate = std::variant_alternative_t<0, OperationF>; + using fp_type = typename FusedGate::fp_type; + using Gate = qsim::Gate; + for (std::size_t i = 0; i < orphaned_gates.size(); ++i) { auto ogate1 = orphaned_gates[i]; @@ -601,16 +692,43 @@ class MultiQubitGateFuser final : public Fuser { std::sort(ogate1->qubits.begin(), ogate1->qubits.end()); - fused_gates.push_back({ogate1->parent->kind, ogate1->parent->time, - std::move(ogate1->qubits), ogate1->parent, - std::move(ogate1->gates), {}}); + const Gate& parent = *OpGetAlternative(*ogate1->parent); + fused_ops.push_back(FusedGate{parent.kind, parent.time, + std::move(ogate1->qubits), &parent, + std::move(ogate1->gates), {}}); ++stat.num_fused_gates; } } - static void MakeGateSequence( - unsigned max_fused_size, Scratch& scratch, GateF& fgate) { + template + static void AddUnfusible(const GateF& fgate, + std::vector& fused_ops) { + using FusedGate = std::variant_alternative_t<0, OperationF>; + using fp_type = typename FusedGate::fp_type; + using DecomposedGate = qsim::DecomposedGate; + + if (const auto& pg = OpGetAlternative(*fgate.parent)) { + if (pg->qubits.size() == fgate.qubits.size()) { + fused_ops.push_back(fgate.parent); + } else { + Measurement mfused = *pg; + mfused.qubits = fgate.qubits; + fused_ops.push_back(std::move(mfused)); + } + } else { + if (const auto* pg = OpGetAlternative(*fgate.parent)) { + fused_ops.push_back( + FusedGate{pg->kind, pg->time, {pg->qubits[0]}, pg, {pg}, {}}); + } else { + fused_ops.push_back(fgate.parent); + } + } + } + + template + static void MakeGateSequence(unsigned max_fused_size, + Scratch& scratch, GateF& fgate) { unsigned level = kSecond + scratch.count; FindLongestGateSequence(max_fused_size, level, scratch, fgate); @@ -644,7 +762,7 @@ class MultiQubitGateFuser final : public Fuser { } while (link->next != nullptr && link->next->val->visited == kCompress) { - LinkManager::Delete(link->next); + LinkManager::Delete(link->next); } } @@ -679,12 +797,14 @@ class MultiQubitGateFuser final : public Fuser { scratch.gates.push_back(&fgate); } - static void AddGatesFromNext(std::vector& gates, GateF& fgate) { + template + static void AddGatesFromNext(std::vector& gates, GateF& fgate) { for (auto gate : gates) { fgate.gates.push_back(gate); } } + template static void AddGatesFromPrev(unsigned max_fused_size, const GateF& pfgate, Scratch& scratch, GateF& fgate) { for (auto gate : pfgate.gates) { @@ -702,8 +822,9 @@ class MultiQubitGateFuser final : public Fuser { } } - static void FindLongestGateSequence( - unsigned max_fused_size, unsigned level, Scratch& scratch, GateF& fgate) { + template + static void FindLongestGateSequence(unsigned max_fused_size, unsigned level, + Scratch& scratch, GateF& fgate) { scratch.data.push_back({&fgate, {}, {}}); scratch.longest_seq.resize(0); @@ -717,7 +838,8 @@ class MultiQubitGateFuser final : public Fuser { unsigned max_size = cur_size; - GetNextAvailableGates(max_fused_size, cur_size, fgate, nullptr, + GetNextAvailableGates(max_fused_size, cur_size, fgate, + (const GateF*) nullptr, scratch.data, scratch.next1); for (auto n1 : scratch.next1) { @@ -725,7 +847,8 @@ class MultiQubitGateFuser final : public Fuser { if (cur_size2 > max_fused_size) continue; bool feasible = GetPrevAvailableGates(max_fused_size, cur_size, - level, *n1->gate, nullptr, + level, *n1->gate, + (const GateF*) nullptr, scratch.data, scratch.prev1); if (!feasible) continue; @@ -803,6 +926,7 @@ class MultiQubitGateFuser final : public Fuser { } } + template static void Push(unsigned level, unsigned cur_size2, unsigned& cur_size, unsigned& max_size, Scratch& scratch, GateA* agate) { agate->gate->visited = level; @@ -815,12 +939,14 @@ class MultiQubitGateFuser final : public Fuser { } } + template static void Pop(unsigned& cur_size, Scratch& scratch, GateA* agate) { agate->gate->visited = kFirst; cur_size -= agate->qubits.size(); scratch.stack.pop_back(); } + template static void GetNextAvailableGates(unsigned max_fused_size, unsigned cur_size, const GateF& pgate1, const GateF* pgate2, std::vector& scratch, @@ -832,7 +958,7 @@ class MultiQubitGateFuser final : public Fuser { auto ngate = link->next->val; - if (ngate->visited > kFirst || ngate->parent->unfusible) continue; + if (ngate->visited > kFirst) continue; GateA next = {ngate, {}, {}}; next.qubits.reserve(8); @@ -847,6 +973,7 @@ class MultiQubitGateFuser final : public Fuser { } } + template static bool GetPrevAvailableGates(unsigned max_fused_size, unsigned cur_size, unsigned level, const GateF& ngate1, const GateF* ngate2, @@ -861,7 +988,7 @@ class MultiQubitGateFuser final : public Fuser { if (pgate->visited == kFinal || pgate->visited == level) continue; - if (pgate->visited > kFirst || pgate->parent->unfusible) { + if (pgate->visited > kFirst) { prev_gates.resize(0); return false; } @@ -877,7 +1004,7 @@ class MultiQubitGateFuser final : public Fuser { for (auto link : pgate->links) { if (link->prev == nullptr) continue; - if (link->prev->val->visited <= kMeaCnt) { + if (link->prev->val->visited <= kUnfusible) { all_prev_visited = false; break; } @@ -899,6 +1026,7 @@ class MultiQubitGateFuser final : public Fuser { return true; } + template static void GetAddedQubits(const GateF& fgate0, const GateF* fgate1, const GateF& fgate2, GateA& added) { for (std::size_t i = 0; i < fgate2.qubits.size(); ++i) { @@ -917,8 +1045,11 @@ class MultiQubitGateFuser final : public Fuser { } // Fuse smaller gates with fgate back in gate time. - static void FusePrev(unsigned pass, GateF& fgate) { - std::vector gates; + template + static void FusePrev(unsigned pass, GateF& fgate) { + using Link = Link; + + std::vector gates; gates.reserve(fgate.gates.capacity()); auto neighbor = [](const Link* link) -> const Link* { @@ -933,7 +1064,10 @@ class MultiQubitGateFuser final : public Fuser { } // Fuse smaller gates with fgate forward in gate time. - static void FuseNext(unsigned pass, GateF& fgate) { + template + static void FuseNext(unsigned pass, GateF& fgate) { + using Link = Link; + auto neighbor = [](const Link* link) -> const Link* { return link->next; }; @@ -941,9 +1075,13 @@ class MultiQubitGateFuser final : public Fuser { FusePrevOrNext>(pass, neighbor, fgate, fgate.gates); } - template - static void FusePrevOrNext(unsigned pass, Neighbor neighb, GateF& fgate, - std::vector& gates) { + template + static void FusePrevOrNext(unsigned pass, Neighbor neighb, + GateF& fgate, + std::vector& gates) { + using Link = Link; + using Gate = qsim::Gate::fp_type>; + uint64_t bad_mask = 0; auto links = fgate.links; @@ -958,7 +1096,8 @@ class MultiQubitGateFuser final : public Fuser { auto rn = neighb(r); if (ln != nullptr && rn != nullptr) { - return R()(ln->val->parent->time, rn->val->parent->time); + return R()(OpTime(*ln->val->parent), + OpTime(*rn->val->parent)); } else { // nullptrs are larger than everything else and // equivalent among each other. @@ -974,13 +1113,14 @@ class MultiQubitGateFuser final : public Fuser { auto g = n->val; if (!QubitsAreIn(fgate.mask, g->mask) || (g->mask & bad_mask) != 0 - || g->visited > pass || g->parent->unfusible) { + || g->visited > pass) { bad_mask |= g->mask; } else { g->visited = pass == 0 ? kFirst : kFinal; if (pass == 0) { - gates.push_back(g->parent); + // g->parent must hold the type Gate here. + gates.push_back(OpGetAlternative(*g->parent)); } else { for (auto gate : g->gates) { gates.push_back(gate); @@ -988,7 +1128,7 @@ class MultiQubitGateFuser final : public Fuser { } for (auto link : g->links) { - LinkManager::Delete(link); + LinkManager::Delete(link); } may_have_gates_to_fuse = true; @@ -1002,20 +1142,25 @@ class MultiQubitGateFuser final : public Fuser { return ((mask0 | mask) ^ mask0) == 0; } + template static void PrintStat(unsigned verbosity, const Stat& stat, - const std::vector& fused_gates) { + const std::vector& fused_ops) { + using FusedGate = std::variant_alternative_t<0, OperationF>; + using fp_type = typename FusedGate::fp_type; + using ControlledGate = qsim::ControlledGate; + if (verbosity < 3) return; - if (stat.num_controlled_gates > 0) { - IO::messagef("%u controlled gates\n", stat.num_controlled_gates); + if (stat.num_unfusible_gates > 0) { + IO::messagef("%u unfusible gates\n", stat.num_unfusible_gates); } - if (stat.num_mea_gates > 0) { - IO::messagef("%u measurement gates", stat.num_mea_gates); - if (stat.num_fused_mea_gates == stat.num_mea_gates) { + if (stat.num_measurements > 0) { + IO::messagef("%u measurement gates", stat.num_measurements); + if (stat.num_fused_measurements == stat.num_measurements) { IO::messagef("\n"); } else { - IO::messagef(" are fused into %u gates\n", stat.num_fused_mea_gates); + IO::messagef(" are fused into %u gates\n", stat.num_fused_measurements); } } @@ -1036,13 +1181,15 @@ class MultiQubitGateFuser final : public Fuser { if (verbosity < 5) return; IO::messagef("fused gate qubits:\n"); - for (const auto& g : fused_gates) { - IO::messagef("%6u ", g.parent->time); - if (g.parent->kind == gate::kMeasurement) { + for (const auto& op : fused_ops) { + const auto& bop = OpBaseOperation(op); + IO::messagef("%6u ", bop.time); + + if (OpGetAlternative(op)) { IO::messagef("m"); - } else if (g.parent->controlled_by.size() > 0) { + } else if (const auto* pg = OpGetAlternative(op)) { IO::messagef("c"); - for (auto q : g.parent->controlled_by) { + for (auto q : pg->controlled_by) { IO::messagef("%3u", q); } IO::messagef(" t"); @@ -1050,39 +1197,45 @@ class MultiQubitGateFuser final : public Fuser { IO::messagef(" "); } - for (auto q : g.qubits) { + for (auto q : bop.qubits) { IO::messagef("%3u", q); } IO::messagef("\n"); } } - template - static bool ValidateGate(const Gate2& gate, unsigned max_qubit1, - const GatesLat& gates_lat) { - for (unsigned q : gate.qubits) { + template + static bool ValidateOp(const Operation& op, unsigned max_qubit1, + const GatesLat& gates_lat) { + using ControlledGate = qsim::ControlledGate>; + + const auto& bop = OpBaseOperation(op); + + for (unsigned q : bop.qubits) { if (q >= max_qubit1) { IO::errorf("fuser: gate qubit %u is out of range " "(should be smaller than %u).\n", q, max_qubit1); return false; } if (gates_lat[q] != nullptr - && gate.time <= gates_lat[q]->val->parent->time) { - IO::errorf("fuser: gate at time %u is out of time order.\n", gate.time); + && bop.time <= OpTime(*gates_lat[q]->val->parent)) { + IO::errorf("fuser: gate at time %u is out of time order.\n", time); return false; } } - for (unsigned q : gate.controlled_by) { - if (q >= max_qubit1) { - IO::errorf("fuser: gate qubit %u is out of range " - "(should be smaller than %u).\n", q, max_qubit1); - return false; - } - if (gates_lat[q] != nullptr - && gate.time <= gates_lat[q]->val->parent->time) { - IO::errorf("fuser: gate at time %u is out of time order.\n", gate.time); - return false; + if (const auto* pg = OpGetAlternative(op)) { + for (unsigned q : pg->controlled_by) { + if (q >= max_qubit1) { + IO::errorf("fuser: gate qubit %u is out of range " + "(should be smaller than %u).\n", q, max_qubit1); + return false; + } + if (gates_lat[q] != nullptr + && bop.time <= OpTime(*gates_lat[q]->val->parent)) { + IO::errorf("fuser: gate at time %u is out of time order.\n", time); + return false; + } } } diff --git a/lib/gate.h b/lib/gate.h index a457acbec..99f2dece8 100644 --- a/lib/gate.h +++ b/lib/gate.h @@ -1,4 +1,4 @@ -// Copyright 2019 Google LLC. All Rights Reserved. +// Copyright 2026 Google LLC. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -18,46 +18,83 @@ #include #include #include +#include #include #include "matrix.h" +#include "operation_base.h" namespace qsim { -namespace detail { - -template -inline void SortQubits(Gate& gate) { - for (std::size_t i = 1; i < gate.qubits.size(); ++i) { - if (gate.qubits[i - 1] > gate.qubits[i]) { - if (!GateDef::symmetric) { - auto perm = NormalToGateOrderPermutation(gate.qubits); - MatrixShuffle(perm, gate.qubits.size(), gate.matrix); - } +// Forward declaration of Gate. +template +struct Gate; - gate.swapped = true; - std::sort(gate.qubits.begin(), gate.qubits.end()); - break; - } - } -} - -} // namespace detail - -template , typename Gate> -inline Gate& MakeControlledGate(Qubits&& controlled_by, Gate& gate) { - gate.controlled_by = std::forward(controlled_by); - gate.cmask = (uint64_t{1} << gate.controlled_by.size()) - 1; +/** + * A matrix gate controlled by a number of qubits. + */ +template +struct ControlledGate : public Gate { + ControlledGate() {} + + /** + * Constructs a controlled gate from a base matrix gate. All control values + * are set to 1. + * @param base The underlying matrix gate (already permuted if necessary). + * @param controlled_by The indices of the control qubits. + */ + template + ControlledGate(const Gate& base, Q&& controlled_by) + : Gate{base}, controlled_by(std::forward(controlled_by)), + cmask((uint64_t{1} << this->controlled_by.size()) - 1) {} + + /** + * Constructs a controlled gate from a base matrix gate. + * @param base The underlying matrix gate (already permuted if necessary). + * @param controlled_by The indices of the control qubits. + * @param cmask A bitmask representing the required control values (0 or 1) + * of the control qubits. + */ + template + ControlledGate(const Gate& base, Q&& controlled_by, uint64_t cmask) + : Gate{base}, controlled_by(std::forward(controlled_by)), + cmask(cmask) {} + + /** The indices of the qubits used as controls. */ + Qubits controlled_by; + /** + * The required bit values for the control qubits, stored as a bitmask, + * where the i-th bit corresponds to the i-th qubit in `controlled_by`. + */ + uint64_t cmask; +}; - std::sort(gate.controlled_by.begin(), gate.controlled_by.end()); +/** + * Creates a controlled gate from a matrix gate. + * @param gate The base gate to be controlled. + * @param controlled_by The control qubit indices. + * @return The resulting controlled gate object. + */ +template +inline ControlledGate MakeControlledGate( + const Gate& gate, Q&& controlled_by) { + ControlledGate cgate{gate, std::forward(controlled_by)}; + std::sort(cgate.controlled_by.begin(), cgate.controlled_by.end()); - return gate; + return cgate; } -template , typename Gate> -inline Gate& MakeControlledGate(Qubits&& controlled_by, - const std::vector& control_values, - Gate& gate) { +/** + * Creates a controlled gate from a matrix gate. + * @param gate The base gate to be controlled. + * @param controlled_by The control qubit indices. + * @param control_values The control values (0 or 1) for each control qubit. + * @return The resulting controlled gate object. + */ +template +inline ControlledGate MakeControlledGate( + const Gate& gate, Q&& controlled_by, + const std::vector& control_values) { // Assume controlled_by.size() == control_values.size(). bool sorted = true; @@ -70,12 +107,13 @@ inline Gate& MakeControlledGate(Qubits&& controlled_by, } if (sorted) { - gate.controlled_by = std::forward(controlled_by); - gate.cmask = 0; + uint64_t cmask = 0; for (std::size_t i = 0; i < control_values.size(); ++i) { - gate.cmask |= (control_values[i] & 1) << i; + cmask |= (control_values[i] & 1) << i; } + + return ControlledGate{gate, std::forward(controlled_by), cmask}; } else { struct ControlPair { unsigned q; @@ -83,9 +121,9 @@ inline Gate& MakeControlledGate(Qubits&& controlled_by, }; std::vector cpairs; - cpairs.reserve(controlled_by.size()); + cpairs.reserve(control_values.size()); - for (std::size_t i = 0; i < controlled_by.size(); ++i) { + for (std::size_t i = 0; i < control_values.size(); ++i) { cpairs.push_back({controlled_by[i], control_values[i]}); } @@ -95,122 +133,175 @@ inline Gate& MakeControlledGate(Qubits&& controlled_by, return l.q < r.q; }); - gate.cmask = 0; - gate.controlled_by.reserve(controlled_by.size()); + uint64_t cmask = 0; + + Qubits controlled_by; + controlled_by.reserve(control_values.size()); for (std::size_t i = 0; i < cpairs.size(); ++i) { - gate.cmask |= (cpairs[i].v & 1) << i; - gate.controlled_by.push_back(cpairs[i].q); + cmask |= (cpairs[i].v & 1) << i; + controlled_by.push_back(cpairs[i].q); } - } - return gate; + return ControlledGate{gate, std::move(controlled_by), cmask}; + } } -namespace gate { +/** + * A generic matrix gate whose action is defined by a matrix. + */ +template +struct Gate : public BaseOperation { + using fp_type = FP; -constexpr int kDecomp = 100001; // gate from Schmidt decomposition -constexpr int kMeasurement = 100002; // measurement gate + /** + * Gate parameters (e.g., rotation angles). + * Note: Currently utilized only in qsimh. + */ + std::vector params; + /** + * The (not necessarily unitary) matrix representing the gate operation. + */ + Matrix matrix; + /** + * Indicates if the qubits were swapped to ensure they are in ascending order. + */ + bool swapped; + + template + auto ControlledBy(Q&& qubits) const { + return MakeControlledGate(*this, std::move(qubits)); + } -} // namespace gate + template + auto ControlledBy(Q&& qubits, + const std::vector& control_values) const { + return MakeControlledGate(*this, std::move(qubits), control_values); + } +}; -enum GateAnyKind { - kGateAny = -1, +/** + * Represents a gate that has undergone Schmidt decomposition. + * Note: This struct is utilized only in the qsimh hybrid simulator. + */ +template +struct DecomposedGate : public Gate { + /** A pointer to the original two-qubit gate that was decomposed. */ + const Gate* parent; + /** + * A unique identifier used to sort decomposed gate pointers into + * a specific execution order. + */ + unsigned id; }; /** - * A generic gate to make it easier to use qsim with external gate sets. + * An operation that measures a specific set of qubits at a given time step. + */ +struct Measurement : public BaseOperation {}; + + +/** + * A collection of gates fused into a single operation. + * Component gates are typically multiplied into a single unitary matrix. + * Note for qsimh: If the collection contains a `DecomposedGate`, the + * `matrix` remains empty during the initial fusion pass. The fused + * matrix is computed later once the Schmidt decomposition components + * are populated. */ -template -struct Gate { +template +struct FusedGate : public BaseOperation { using fp_type = FP; - using GateKind = GK; - GateKind kind; - unsigned time; - std::vector qubits; - std::vector controlled_by; - uint64_t cmask; - std::vector params; + /** Pointer to either a standard matrix gate or a Schmidt-decomposed gate. */ + using PGate = std::variant*, + const DecomposedGate*>; + + /** The primary gate that initiated this fusion block. */ + PGate parent; + /** Ordered sequence of all component gates in this block. */ + std::vector gates; + /** + * The fused matrix. May be empty if `fuse_matrix` is false or if the block + * contains a `DecomposedGate`. + */ Matrix matrix; - bool unfusible; // If true, the gate is fused as a parent. - bool swapped; // If true, the gate qubits are swapped to make qubits - // ordered in ascending order. This does not apply to - // control qubits of explicitly-controlled gates. - - template > - Gate&& ControlledBy(Qubits&& controlled_by) { - MakeControlledGate(std::forward(controlled_by), *this); - return std::move(*this); - } - template > - Gate&& ControlledBy(Qubits&& controlled_by, - const std::vector& control_values) { - MakeControlledGate( - std::forward(controlled_by), control_values, *this); - return std::move(*this); + /** Returns true if the primary gate is a decomposed gate. */ + bool ParentIsDecomposed() const { + return std::holds_alternative*>(parent); } }; -template , - typename M = Matrix> -inline Gate CreateGate(unsigned time, Qubits&& qubits, M&& matrix = {}, - std::vector&& params = {}) { - Gate gate = {GateDef::kind, time, std::forward(qubits), {}, 0, - std::move(params), std::forward(matrix), false, false}; +namespace detail { - if (GateDef::kind != gate::kMeasurement) { - switch (gate.qubits.size()) { - case 1: - break; - case 2: - if (gate.qubits[0] > gate.qubits[1]) { - gate.swapped = true; - std::swap(gate.qubits[0], gate.qubits[1]); - if (!GateDef::symmetric) { - MatrixShuffle({1, 0}, 2, gate.matrix); - } +template +inline void SortQubits(Gate& gate) { + for (std::size_t i = 1; i < gate.qubits.size(); ++i) { + if (gate.qubits[i - 1] > gate.qubits[i]) { + if (!GateDef::symmetric) { + auto perm = NormalToGateOrderPermutation(gate.qubits); + MatrixShuffle(perm, gate.qubits.size(), gate.matrix); } + + gate.swapped = true; + std::sort(gate.qubits.begin(), gate.qubits.end()); break; - default: - detail::SortQubits(gate); } } - - return gate; } -namespace gate { +} // namespace detail /** - * A gate that simulates measurement of one or more qubits, collapsing the - * state vector and storing the measured results. + * A helper function to create a matrix gate. Qubit indices are sorted, + * and the gate matrix is permuted accordingly if the gate is non-symmetric. + * If a permutation occurs, the gate's `swapped` field is set to true. + * @param time The time step at which the gate is applied. + * @param qubits The qubit indices the gate acts on. + * @param matrix The initial gate matrix (permuted during creation if needed). + * @param params The gate parameters (utilized in qsimh). + * @return The resulting gate object with a canonical qubit ordering. */ -template -struct Measurement { - using GateKind = typename Gate::GateKind; +template > +inline Gate CreateGate(unsigned time, Q&& qubits, M&& matrix = {}, + std::vector&& params = {}) { + Gate gate = {GateDef::kind, time, std::forward(qubits), + std::move(params), std::forward(matrix), false}; + + switch (gate.qubits.size()) { + case 1: + break; + case 2: + if (gate.qubits[0] > gate.qubits[1]) { + gate.swapped = true; + std::swap(gate.qubits[0], gate.qubits[1]); + if (!GateDef::symmetric) { + MatrixShuffle({1, 0}, 2, gate.matrix); + } + } + break; + default: + detail::SortQubits(gate); + } - static constexpr GateKind kind = GateKind::kMeasurement; - static constexpr char name[] = "m"; - static constexpr bool symmetric = false; + return gate; +} - template > - static Gate Create(unsigned time, Qubits&& qubits) { - return CreateGate(time, std::forward(qubits)); - } +enum OtherGateKind { + kMeasurement = 100002, + kChannel = 100003, }; -} // namespace gate +template +inline Measurement CreateMeasurement(unsigned time, Q&& qubits) { + return Measurement{kMeasurement, time, std::forward(qubits)}; +} template using schmidt_decomp_type = std::vector>>; -template -schmidt_decomp_type GetSchmidtDecomp( - GateKind kind, const std::vector& params); - } // namespace qsim #endif // GATE_H_ diff --git a/lib/gate_appl.h b/lib/gate_appl.h index bfd3df7cc..11ac5a996 100644 --- a/lib/gate_appl.h +++ b/lib/gate_appl.h @@ -18,63 +18,71 @@ #include #include -#include "fuser.h" #include "gate.h" #include "matrix.h" +#include "operation_base.h" namespace qsim { /** - * Applies the given gate to the simulator state. Ignores measurement gates. + * Applies the given operation to the simulator state. Ignores measurement + * gates. * @param simulator Simulator object. Provides specific implementations for * applying gates. - * @param gate The gate to be applied. + * @param op The operation to be applied. * @param state The state of the system, to be updated by this method. */ -template -inline void ApplyGate(const Simulator& simulator, const Gate& gate, +template +inline void ApplyGate(const Simulator& simulator, const Operation& op, typename Simulator::State& state) { - if (gate.kind != gate::kMeasurement) { - if (gate.controlled_by.size() == 0) { - simulator.ApplyGate(gate.qubits, gate.matrix.data(), state); - } else { - simulator.ApplyControlledGate(gate.qubits, gate.controlled_by, - gate.cmask, gate.matrix.data(), state); - } + using FP = typename Simulator::fp_type; + + if (const auto* pg = OpGetAlternative>(op)) { + simulator.ApplyGate(pg->qubits, pg->matrix.data(), state); + } else if (const auto* pg = OpGetAlternative>(op)) { + simulator.ApplyGate(pg->qubits, pg->matrix.data(), state); + } else if (const auto* pg = OpGetAlternative>(op)) { + simulator.ApplyControlledGate(pg->qubits, pg->controlled_by, + pg->cmask, pg->matrix.data(), state); } } /** - * Applies the given gate dagger to the simulator state. If the gate matrix is - * unitary then this is equivalent to applying the inverse gate. Ignores - * measurement gates. + * Applies the given operation dagger to the simulator state. If the gate + * matrix is unitary then this is equivalent to applying the inverse gate. + * Ignores measurement gates. * @param simulator Simulator object. Provides specific implementations for * applying gates. - * @param gate The gate to be applied. + * @param op The operation to be applied. * @param state The state of the system, to be updated by this method. */ -template -inline void ApplyGateDagger(const Simulator& simulator, const Gate& gate, +template +inline void ApplyGateDagger(const Simulator& simulator, const Operation& op, typename Simulator::State& state) { - if (gate.kind != gate::kMeasurement) { - auto matrix = gate.matrix; - MatrixDagger(unsigned{1} << gate.qubits.size(), matrix); - - if (gate.controlled_by.size() == 0) { - simulator.ApplyGate(gate.qubits, matrix.data(), state); - } else { - simulator.ApplyControlledGate(gate.qubits, gate.controlled_by, - gate.cmask, matrix.data(), state); - } + using FP = typename Simulator::fp_type; + + if (const auto* pg = OpGetAlternative>(op)) { + auto matrix = pg->matrix; + MatrixDagger(unsigned{1} << pg->qubits.size(), matrix); + simulator.ApplyGate(pg->qubits, matrix.data(), state); + } else if (const auto* pg = OpGetAlternative>(op)) { + auto matrix = pg->matrix; + MatrixDagger(unsigned{1} << pg->qubits.size(), matrix); + simulator.ApplyGate(pg->qubits, matrix.data(), state); + } else if (const auto* pg = OpGetAlternative>(op)) { + auto matrix = pg->matrix; + MatrixDagger(unsigned{1} << pg->qubits.size(), matrix); + simulator.ApplyControlledGate(pg->qubits, pg->controlled_by, + pg->cmask, matrix.data(), state); } } /** - * Applies the given gate to the simulator state. + * Applies the given operation to the simulator state. * @param state_space StateSpace object required to perform measurements. * @param simulator Simulator object. Provides specific implementations for * applying gates. - * @param gate The gate to be applied. + * @param op The operation to be applied. * @param rgen Random number generator to perform measurements. * @param state The state of the system, to be updated by this method. * @param mresults As an input parameter, this can be empty or this can @@ -83,156 +91,48 @@ inline void ApplyGateDagger(const Simulator& simulator, const Gate& gate, * this. * @return True if the measurement performed successfully; false otherwise. */ -template +template inline bool ApplyGate( const typename Simulator::StateSpace& state_space, - const Simulator& simulator, const Gate& gate, Rgen& rgen, + const Simulator& simulator, const Operation& op, Rgen& rgen, typename Simulator::State& state, std::vector& mresults) { - if (gate.kind == gate::kMeasurement) { - auto measure_result = state_space.Measure(gate.qubits, rgen, state); + if (const auto* pg = OpGetAlternative(op)) { + auto measure_result = state_space.Measure(pg->qubits, rgen, state); if (measure_result.valid) { mresults.push_back(std::move(measure_result)); } else { return false; } } else { - ApplyGate(simulator, gate, state); + ApplyGate(simulator, op, state); } return true; } /** - * Applies the given gate to the simulator state, discarding measurement + * Applies the given operation to the simulator state, discarding measurement * results. * @param state_space StateSpace object required to perform measurements. * @param simulator Simulator object. Provides specific implementations for * applying gates. - * @param gate The gate to be applied. + * @param op The operation to be applied. * @param rgen Random number generator to perform measurements. * @param state The state of the system, to be updated by this method. * @return True if the measurement performed successfully; false otherwise. */ -template +template inline bool ApplyGate(const typename Simulator::StateSpace& state_space, - const Simulator& simulator, const Gate& gate, Rgen& rgen, - typename Simulator::State& state) { - if (gate.kind == gate::kMeasurement) { - auto measure_result = state_space.Measure(gate.qubits, rgen, state); - if (!measure_result.valid) { - return false; - } - } else { - ApplyGate(state_space, simulator, gate); - } - - return true; -} - -/** - * Applies the given fused gate to the simulator state. Ignores measurement - * gates. - * @param simulator Simulator object. Provides specific implementations for - * applying gates. - * @param gate The gate to be applied. - * @param state The state of the system, to be updated by this method. - */ -template -inline void ApplyFusedGate(const Simulator& simulator, const Gate& gate, - typename Simulator::State& state) { - if (gate.kind != gate::kMeasurement) { - if (gate.parent->controlled_by.size() == 0) { - simulator.ApplyGate(gate.qubits, gate.matrix.data(), state); - } else { - simulator.ApplyControlledGate(gate.qubits, gate.parent->controlled_by, - gate.parent->cmask, gate.matrix.data(), - state); - } - } -} - -/** - * Applies the given fused gate dagger to the simulator state. If the gate - * matrix is unitary then this is equivalent to applying the inverse gate. - * Ignores measurement gates. - * @param simulator Simulator object. Provides specific implementations for - * applying gates. - * @param gate The gate to be applied. - * @param state The state of the system, to be updated by this method. - */ -template -inline void ApplyFusedGateDagger(const Simulator& simulator, const Gate& gate, - typename Simulator::State& state) { - if (gate.kind != gate::kMeasurement) { - auto matrix = gate.matrix; - MatrixDagger(unsigned{1} << gate.qubits.size(), matrix); - - if (gate.parent->controlled_by.size() == 0) { - simulator.ApplyGate(gate.qubits, matrix.data(), state); - } else { - simulator.ApplyControlledGate(gate.qubits, gate.parent->controlled_by, - gate.parent->cmask, matrix.data(), state); - } - } -} - -/** - * Applies the given fused gate to the simulator state. - * @param state_space StateSpace object required to perform measurements. - * @param simulator Simulator object. Provides specific implementations for - * applying gates. - * @param gate The gate to be applied. - * @param rgen Random number generator to perform measurements. - * @param state The state of the system, to be updated by this method. - * @param mresults As an input parameter, this can be empty or this can - * contain the results of the previous measurements. If gate is a measurement - * gate then after a successful run, the measurement result will be added to - * this. - * @return True if the measurement performed successfully; false otherwise. - */ -template -inline bool ApplyFusedGate( - const typename Simulator::StateSpace& state_space, - const Simulator& simulator, const Gate& gate, Rgen& rgen, - typename Simulator::State& state, - std::vector& mresults) { - if (gate.kind == gate::kMeasurement) { - auto measure_result = state_space.Measure(gate.qubits, rgen, state); - if (measure_result.valid) { - mresults.push_back(std::move(measure_result)); - } else { - return false; - } - } else { - ApplyFusedGate(simulator, gate, state); - } - - return true; -} - -/** - * Applies the given fused gate to the simulator state, discarding measurement - * results. - * @param state_space StateSpace object required to perform measurements. - * @param simulator Simulator object. Provides specific implementations for - * applying gates. - * @param gate The gate to be applied. - * @param rgen Random number generator to perform measurements. - * @param state The state of the system, to be updated by this method. - * @return True if the measurement performed successfully; false otherwise. - */ -template -inline bool ApplyFusedGate(const typename Simulator::StateSpace& state_space, - const Simulator& simulator, const Gate& gate, - Rgen& rgen, typename Simulator::State& state) { - if (gate.kind == gate::kMeasurement) { - auto measure_result = state_space.Measure(gate.qubits, rgen, state); + const Simulator& simulator, const Operation& op, + Rgen& rgen, typename Simulator::State& state) { + if (const auto* pg = OpGetAlternative(op)) { + auto measure_result = state_space.Measure(pg->qubits, rgen, state); if (!measure_result.valid) { return false; } } else { - ApplyFusedGate(simulator, gate, state); + ApplyGate(simulator, op, state); } return true; diff --git a/lib/gates_cirq.h b/lib/gates_cirq.h index d76795969..2fc5960aa 100644 --- a/lib/gates_cirq.h +++ b/lib/gates_cirq.h @@ -75,12 +75,10 @@ enum GateKind { kMatrixGate2, // Two-qubit matrix gate. kMatrixGate, // Multi-qubit matrix gate. kGlobalPhaseGate, - kDecomp = gate::kDecomp, - kMeasurement = gate::kMeasurement, }; template -using GateCirq = Gate; +using GateCirq = Gate; constexpr double h_double = 0.5; constexpr double pi_double = 3.14159265358979323846264338327950288; @@ -95,7 +93,7 @@ template struct GlobalPhaseGate { static constexpr GateKind kind = kGlobalPhaseGate; static constexpr char name[] = "GlobalPhaseGate"; - static constexpr unsigned num_qubits = 1; + static constexpr unsigned num_qubits = 0; static constexpr bool symmetric = true; static GateCirq Create(unsigned time, fp_type phi) { @@ -163,11 +161,12 @@ struct I { static constexpr char name[] = "I"; static constexpr bool symmetric = true; - static GateCirq Create(unsigned time, - const std::vector& qubits) { + template + static GateCirq Create(unsigned time, Q&& qubits) { Matrix matrix; MatrixIdentity(1 << qubits.size(), matrix); - return CreateGate, I>(time, qubits, std::move(matrix)); + return CreateGate, I>( + time, std::forward(qubits), std::move(matrix)); } }; @@ -1540,11 +1539,10 @@ struct MatrixGate1 { static constexpr unsigned num_qubits = 1; static constexpr bool symmetric = true; - static GateCirq Create(unsigned time, unsigned q0, - const Matrix& m) { - auto m2 = m; - return - CreateGate, MatrixGate1>(time, {q0}, std::move(m2)); + template > + static GateCirq Create(unsigned time, unsigned q0, M&& m) { + return CreateGate, MatrixGate1>( + time, {q0}, std::forward(m)); } }; @@ -1561,8 +1559,8 @@ struct MatrixGate2 { template > static GateCirq Create( unsigned time, unsigned q0, unsigned q1, M&& m) { - return CreateGate, MatrixGate2>(time, {q1, q0}, - std::forward(m)); + return CreateGate, MatrixGate2>( + time, {q1, q0}, std::forward(m)); } }; @@ -1579,62 +1577,62 @@ struct MatrixGate { static GateCirq Create(unsigned time, std::vector qubits, M&& m) { std::reverse(qubits.begin(), qubits.end()); - return CreateGate, MatrixGate>(time, std::move(qubits), - std::forward(m)); + return CreateGate, MatrixGate>( + time, qubits, std::forward(m)); } }; -} // namesapce Cirq - template inline schmidt_decomp_type GetSchmidtDecomp( - Cirq::GateKind kind, const std::vector& params) { + unsigned kind, const std::vector& params) { switch (kind) { - case Cirq::kI2: - return Cirq::I2::SchmidtDecomp(); - case Cirq::kCZPowGate: - return Cirq::CZPowGate::SchmidtDecomp(params[0], params[1]); - case Cirq::kCXPowGate: - return Cirq::CXPowGate::SchmidtDecomp(params[0], params[1]); - case Cirq::kCZ: - return Cirq::CZ::SchmidtDecomp(); - case Cirq::kCX: - return Cirq::CX::SchmidtDecomp(); - case Cirq::kXXPowGate: - return Cirq::XXPowGate::SchmidtDecomp(params[0], params[1]); - case Cirq::kYYPowGate: - return Cirq::YYPowGate::SchmidtDecomp(params[0], params[1]); - case Cirq::kZZPowGate: - return Cirq::ZZPowGate::SchmidtDecomp(params[0], params[1]); - case Cirq::kXX: - return Cirq::XX::SchmidtDecomp(); - case Cirq::kYY: - return Cirq::YY::SchmidtDecomp(); - case Cirq::kZZ: - return Cirq::ZZ::SchmidtDecomp(); - case Cirq::kSwapPowGate: - return Cirq::SwapPowGate::SchmidtDecomp(params[0], params[1]); - case Cirq::kISwapPowGate: - return Cirq::ISwapPowGate::SchmidtDecomp(params[0], params[1]); - case Cirq::kriswap: - return Cirq::riswap::SchmidtDecomp(params[0]); - case Cirq::kSWAP: - return Cirq::SWAP::SchmidtDecomp(); - case Cirq::kISWAP: - return Cirq::ISWAP::SchmidtDecomp(); - case Cirq::kPhasedISwapPowGate: - return Cirq::PhasedISwapPowGate::SchmidtDecomp( + case kI2: + return I2::SchmidtDecomp(); + case kCZPowGate: + return CZPowGate::SchmidtDecomp(params[0], params[1]); + case kCXPowGate: + return CXPowGate::SchmidtDecomp(params[0], params[1]); + case kCZ: + return CZ::SchmidtDecomp(); + case kCX: + return CX::SchmidtDecomp(); + case kXXPowGate: + return XXPowGate::SchmidtDecomp(params[0], params[1]); + case kYYPowGate: + return YYPowGate::SchmidtDecomp(params[0], params[1]); + case kZZPowGate: + return ZZPowGate::SchmidtDecomp(params[0], params[1]); + case kXX: + return XX::SchmidtDecomp(); + case kYY: + return YY::SchmidtDecomp(); + case kZZ: + return ZZ::SchmidtDecomp(); + case kSwapPowGate: + return SwapPowGate::SchmidtDecomp(params[0], params[1]); + case kISwapPowGate: + return ISwapPowGate::SchmidtDecomp(params[0], params[1]); + case kriswap: + return riswap::SchmidtDecomp(params[0]); + case kSWAP: + return SWAP::SchmidtDecomp(); + case kISWAP: + return ISWAP::SchmidtDecomp(); + case kPhasedISwapPowGate: + return PhasedISwapPowGate::SchmidtDecomp( params[0], params[1]); - case Cirq::kgivens: - return Cirq::givens::SchmidtDecomp(params[0]); - case Cirq::kFSimGate: - return Cirq::FSimGate::SchmidtDecomp(params[0], params[1]); + case kgivens: + return givens::SchmidtDecomp(params[0]); + case kFSimGate: + return FSimGate::SchmidtDecomp(params[0], params[1]); default: // Single qubit gates of gates with unimplemented Schmidt decomposition. return schmidt_decomp_type{}; } } +} // namesapce Cirq + } // namespace qsim #endif // GATES_CIRQ_H_ diff --git a/lib/gates_qsim.h b/lib/gates_qsim.h index 366c4f1f2..8f186c090 100644 --- a/lib/gates_qsim.h +++ b/lib/gates_qsim.h @@ -20,12 +20,13 @@ #include #include "gate.h" +#include "matrix.h" namespace qsim { // Gate set implemented in qsim contains the following gates. enum GateKind { - kGateId1 = 0, // one-qubit Id + kGateId1 = 1000, // one-qubit Id kGateHd, // Hadamard kGateT, // T kGateX, // X @@ -49,13 +50,11 @@ enum GateKind { kGateMatrix1, // one-qubit matrix gate kGateMatrix2, // two-qubit matrix gate kGateGPh, // global phase gate - kDecomp = gate::kDecomp, - kMeasurement = gate::kMeasurement, }; // Specialization of Gate (defined in gate.h) for the qsim gate set. template -using GateQSim = Gate; +using GateQSim = Gate; constexpr double h_double = 0.5; constexpr double is2_double = 0.7071067811865475; @@ -69,7 +68,7 @@ template struct GateGPh { static constexpr GateKind kind = kGateGPh; static constexpr char name[] = "p"; - static constexpr unsigned num_qubits = 1; + static constexpr unsigned num_qubits = 0; static constexpr bool symmetric = true; static GateQSim Create(unsigned time, fp_type phi) { @@ -634,7 +633,7 @@ struct GateMatrix2 { template inline schmidt_decomp_type GetSchmidtDecomp( - GateKind kind, const std::vector& params) { + unsigned kind, const std::vector& params) { switch (kind) { case kGateId2: return GateId2::SchmidtDecomp(); diff --git a/lib/hybrid.h b/lib/hybrid.h index 44fad5b5a..526cdef4e 100644 --- a/lib/hybrid.h +++ b/lib/hybrid.h @@ -22,68 +22,66 @@ #include "gate.h" #include "gate_appl.h" +#include "gates_cirq.h" +#include "gates_qsim.h" namespace qsim { +namespace detail { + +template +inline schmidt_decomp_type GetSchmidtDecomp( + unsigned kind, const std::vector& params) { + if (kind >= kGateId1) { + return qsim::GetSchmidtDecomp(kind, params); + } else { + return qsim::Cirq::GetSchmidtDecomp(kind, params); + } +} + +} // namespace detail + /** * Hybrid Feynman-Schrodinger simulator. */ -template class FuserT, typename For> +template struct HybridSimulator final { - public: - using Gate = GateT; - using GateKind = typename Gate::GateKind; - using fp_type = typename Gate::fp_type; - private: - // Note that one can use "struct GateHybrid : public Gate {" in C++17. - struct GateHybrid { - using GateKind = HybridSimulator::GateKind; - using fp_type = HybridSimulator::fp_type; - - GateKind kind; - unsigned time; - std::vector qubits; - std::vector controlled_by; - uint64_t cmask; - std::vector params; - Matrix matrix; - bool unfusible; - bool swapped; - - const Gate* parent; - unsigned id; - }; - + template struct GateX { - GateHybrid* decomposed0; - GateHybrid* decomposed1; + using fp_type = FP; + + DecomposedGate* decomposed0; + DecomposedGate* decomposed1; schmidt_decomp_type schmidt_decomp; unsigned schmidt_bits; unsigned swapped; }; - public: - using Fuser = FuserT; - using GateFused = typename Fuser::GateFused; + struct Empty {}; + public: /** * Contextual data for hybrid simulation. */ + template struct HybridData { + using fp_type = FP; + using OperationD = detail::append_to_variant_t, + DecomposedGate>; + /** - * List of gates on the "0" side of the cut. + * List of operations on the "0" side of the cut. */ - std::vector gates0; + std::vector ops0; /** - * List of gates on the "1" side of the cut. + * List of operations on the "1" side of the cut. */ - std::vector gates1; + std::vector ops1; /** * List of gates on the cut. */ - std::vector gatexs; + std::vector> gatexs; /** * Global qubit index to local qubit index map. */ @@ -105,7 +103,8 @@ struct HybridSimulator final { /** * User-specified parameters for gate fusion and hybrid simulation. */ - struct Parameter : public Fuser::Parameter { + template + struct Parameter : public ParameterF { /** * Fixed bitstring indicating values to assign to Schmidt decomposition * indices of prefix gates. @@ -131,18 +130,25 @@ struct HybridSimulator final { * Splits the lattice into two parts, using Schmidt decomposition for gates * on the cut. * @param parts Lattice sections to be simulated. - * @param gates List of all gates in the circuit. + * @param ops List of all operations in the circuit. Only matrix gates are + * supported. * @param hd Output data with split parts. * @return True if the splitting done successfully; false otherwise. */ + template static bool SplitLattice(const std::vector& parts, - const std::vector& gates, HybridData& hd) { + const std::vector& ops, + HybridData& hd) { + using fp_type = FP; + using Gate = qsim::Gate; + using DecomposedGate = qsim::DecomposedGate; + hd.num_gatexs = 0; hd.num_qubits0 = 0; hd.num_qubits1 = 0; - hd.gates0.reserve(gates.size()); - hd.gates1.reserve(gates.size()); + hd.ops0.reserve(ops.size()); + hd.ops1.reserve(ops.size()); hd.qubit_map.reserve(parts.size()); unsigned count0 = 0; @@ -155,68 +161,60 @@ struct HybridSimulator final { } // Split the lattice. - for (const auto& gate : gates) { - if (gate.kind == gate::kMeasurement) { - IO::errorf("measurement gates are not suported by qsimh.\n"); + for (const auto& op : ops) { + if (!OpGetAlternative(op)) { + IO::errorf("measurement, controlled or other non-matrix gates " + "are not suported by qsimh.\n"); return false; } - if (gate.controlled_by.size() > 0) { - IO::errorf("controlled gates are not suported by qsimh.\n"); - return false; - } + const auto& gate = *OpGetAlternative(op); switch (gate.qubits.size()) { case 1: // Single qubit gates. switch (parts[gate.qubits[0]]) { case 0: - hd.gates0.emplace_back(GateHybrid{gate.kind, gate.time, - {hd.qubit_map[gate.qubits[0]]}, {}, 0, gate.params, gate.matrix, - false, false, nullptr, 0}); + hd.ops0.push_back(Gate{gate.kind, gate.time, + {hd.qubit_map[gate.qubits[0]]}, gate.params, gate.matrix}); break; case 1: - hd.gates1.emplace_back(GateHybrid{gate.kind, gate.time, - {hd.qubit_map[gate.qubits[0]]}, {}, 0, gate.params, gate.matrix, - false, false, nullptr, 0}); + hd.ops1.push_back(Gate{gate.kind, gate.time, + {hd.qubit_map[gate.qubits[0]]}, gate.params, gate.matrix}); break; } break; case 2: // Two qubit gates. - { - switch ((parts[gate.qubits[1]] << 1) | parts[gate.qubits[0]]) { - case 0: // Both qubits in part 0. - hd.gates0.emplace_back(GateHybrid{gate.kind, gate.time, + switch ((parts[gate.qubits[1]] << 1) | parts[gate.qubits[0]]) { + case 0: // Both qubits in part 0. + hd.ops0.push_back(Gate{gate.kind, gate.time, {hd.qubit_map[gate.qubits[0]], hd.qubit_map[gate.qubits[1]]}, - {}, 0, gate.params, gate.matrix, false, gate.swapped, - nullptr, 0}); - break; - case 1: // Gate on the cut, qubit 0 in part 1, qubit 1 in part 0. - hd.gates0.emplace_back(GateHybrid{GateKind::kDecomp, gate.time, - {hd.qubit_map[gate.qubits[1]]}, {}, 0, gate.params, {}, - true, gate.swapped, &gate, hd.num_gatexs}); - hd.gates1.emplace_back(GateHybrid{GateKind::kDecomp, gate.time, - {hd.qubit_map[gate.qubits[0]]}, {}, 0, gate.params, {}, - true, gate.swapped, &gate, hd.num_gatexs}); - - ++hd.num_gatexs; - break; - case 2: // Gate on the cut, qubit 0 in part 0, qubit 1 in part 1. - hd.gates0.emplace_back(GateHybrid{GateKind::kDecomp, gate.time, - {hd.qubit_map[gate.qubits[0]]}, {}, 0, gate.params, {}, - true, gate.swapped, &gate, hd.num_gatexs}); - hd.gates1.emplace_back(GateHybrid{GateKind::kDecomp, gate.time, - {hd.qubit_map[gate.qubits[1]]}, {}, 0, gate.params, {}, - true, gate.swapped, &gate, hd.num_gatexs}); - - ++hd.num_gatexs; - break; - case 3: // Both qubits in part 1. - hd.gates1.emplace_back(GateHybrid{gate.kind, gate.time, + gate.params, gate.matrix}); + break; + case 1: // Gate on the cut, qubit 0 in part 1, qubit 1 in part 0. + hd.ops0.push_back(DecomposedGate{gate.kind, gate.time, + {hd.qubit_map[gate.qubits[1]]}, gate.params, {}, + gate.swapped, &gate, hd.num_gatexs}); + hd.ops1.push_back(DecomposedGate{gate.kind, gate.time, + {hd.qubit_map[gate.qubits[0]]}, gate.params, {}, + gate.swapped, &gate, hd.num_gatexs}); + + ++hd.num_gatexs; + break; + case 2: // Gate on the cut, qubit 0 in part 0, qubit 1 in part 1. + hd.ops0.push_back(DecomposedGate{gate.kind, gate.time, + {hd.qubit_map[gate.qubits[0]]}, gate.params, {}, + gate.swapped, &gate, hd.num_gatexs}); + hd.ops1.push_back(DecomposedGate{gate.kind, gate.time, + {hd.qubit_map[gate.qubits[1]]}, gate.params, {}, + gate.swapped, &gate, hd.num_gatexs}); + + ++hd.num_gatexs; + break; + case 3: // Both qubits in part 1. + hd.ops1.push_back(Gate{gate.kind, gate.time, {hd.qubit_map[gate.qubits[0]], hd.qubit_map[gate.qubits[1]]}, - {}, 0, gate.params, gate.matrix, false, gate.swapped, - nullptr, 0}); - break; - } + gate.params, gate.matrix}); + break; } break; default: @@ -225,45 +223,54 @@ struct HybridSimulator final { } } - auto compare = [](const GateHybrid& l, const GateHybrid& r) -> bool { - return l.time < r.time || (l.time == r.time && - (l.parent < r.parent || (l.parent == r.parent && l.id < r.id))); + using OperationD = typename HybridData::OperationD; + + auto compare = [](const OperationD& lop, const OperationD& rop) -> bool { + unsigned ltime = OpTime(lop); + unsigned rtime = OpTime(rop); + + const auto* ld = OpGetAlternative(lop); + const auto* rd = OpGetAlternative(rop); + + return ltime < rtime || + (ltime == rtime && ((!ld && rd) || (ld && rd && ld->id < rd->id))); }; - // Sort gates. - std::sort(hd.gates0.begin(), hd.gates0.end(), compare); - std::sort(hd.gates1.begin(), hd.gates1.end(), compare); + // Sort ops. + std::sort(hd.ops0.begin(), hd.ops0.end(), compare); + std::sort(hd.ops1.begin(), hd.ops1.end(), compare); hd.gatexs.reserve(hd.num_gatexs); // Get Schmidt matrices. - for (auto& gate0 : hd.gates0) { - if (gate0.parent != nullptr) { - auto d = GetSchmidtDecomp(gate0.parent->kind, gate0.parent->params); + for (auto& op0 : hd.ops0) { + if (auto* pg = OpGetAlternative(op0)) { + auto d = detail::GetSchmidtDecomp(pg->parent->kind, pg->parent->params); if (d.size() == 0) { IO::errorf("no Schmidt decomposition for gate kind %u.\n", - gate0.parent->kind); + pg->parent->kind); return false; } unsigned schmidt_bits = SchmidtBits(d.size()); if (schmidt_bits > 2) { IO::errorf("Schmidt rank is too large for gate kind %u.\n", - gate0.parent->kind); + pg->parent->kind); return false; } - unsigned swapped = parts[gate0.parent->qubits[0]]; - if (gate0.parent->swapped) swapped = 1 - swapped; - hd.gatexs.emplace_back(GateX{&gate0, nullptr, std::move(d), - schmidt_bits, swapped}); + unsigned swapped = parts[pg->parent->qubits[0]]; + if (pg->parent->swapped) swapped = 1 - swapped; + + hd.gatexs.push_back(GateX{pg, nullptr, std::move(d), + schmidt_bits, swapped}); } } unsigned count = 0; - for (auto& gate1 : hd.gates1) { - if (gate1.parent != nullptr) { - hd.gatexs[count++].decomposed1 = &gate1; + for (auto& op1 : hd.ops1) { + if (auto* pg = OpGetAlternative(op1)) { + hd.gatexs[count++].decomposed1 = pg; } } @@ -284,18 +291,20 @@ struct HybridSimulator final { * @param hd Container object for gates on the boundary between lattice * sections. * @param parts Lattice sections to be simulated. - * @param fgates0 List of gates from one section of the lattice. - * @param fgates1 List of gates from the other section of the lattice. + * @param ops0 List of (fused) operations from one section of the lattice. + * @param ops1 List of (fused) operations from the other section of + * the lattice. * @param bitstrings List of output states to simulate, as bitstrings. * @param results Output vector of amplitudes. After a successful run, this * will be populated with amplitudes for each state in 'bitstrings'. * @return True if the simulation completed successfully; false otherwise. */ - template - bool Run(const Parameter& param, const Factory& factory, - HybridData& hd, const std::vector& parts, - const std::vector& fgates0, - const std::vector& fgates1, + template + bool Run(const Parameter& param, const Factory& factory, + HybridData& hd, const std::vector& parts, + const std::vector& ops0, + const std::vector& ops1, const std::vector& bitstrings, Results& results) const { using Simulator = typename Factory::Simulator; using StateSpace = typename Simulator::StateSpace; @@ -309,8 +318,8 @@ struct HybridSimulator final { uint64_t rmax = uint64_t{1} << bits.num_r_bits; uint64_t smax = uint64_t{1} << bits.num_s_bits; - auto loc0 = CheckpointLocations(param, fgates0); - auto loc1 = CheckpointLocations(param, fgates1); + auto loc0 = CheckpointLocations(param, ops0); + auto loc1 = CheckpointLocations(param, ops1); struct Index { unsigned i0; @@ -374,8 +383,8 @@ struct HybridSimulator final { if (gatex_index == 0) { // Apply gates before the first checkpoint. - ApplyGates(fgates0, 0, loc0[0], simulator, state0p); - ApplyGates(fgates1, 0, loc1[0], simulator, state1p); + ApplyGates(ops0, 0, loc0[0], simulator, state0p); + ApplyGates(ops1, 0, loc1[0], simulator, state1p); } else { IO::errorf("invalid prefix %lu for prefix gate index %u.\n", param.prefix, gatex_index - 1); @@ -392,8 +401,8 @@ struct HybridSimulator final { if (SetSchmidtMatrices(num_p_gates, num_pr_gates, r, prev, hd.gatexs) == 0) { // Apply gates before the second checkpoint. - ApplyGates(fgates0, loc0[0], loc0[1], simulator, state0r); - ApplyGates(fgates1, loc1[0], loc1[1], simulator, state1r); + ApplyGates(ops0, loc0[0], loc0[1], simulator, state0r); + ApplyGates(ops1, loc1[0], loc1[1], simulator, state1r); } else { continue; } @@ -408,8 +417,8 @@ struct HybridSimulator final { if (SetSchmidtMatrices(num_pr_gates, hd.num_gatexs, s, prev, hd.gatexs) == 0) { // Apply the rest of the gates. - ApplyGates(fgates0, loc0[1], fgates0.size(), simulator, state0s); - ApplyGates(fgates1, loc1[1], fgates1.size(), simulator, state1s); + ApplyGates(ops0, loc0[1], ops0.size(), simulator, state0s); + ApplyGates(ops1, loc1[1], ops1.size(), simulator, state1s); } else { continue; } @@ -429,7 +438,6 @@ struct HybridSimulator final { state_space, *rstate0, *rstate1, indices, results); } } - return true; } @@ -439,24 +447,26 @@ struct HybridSimulator final { * runs with different cut-index values to reuse parts of the simulation. * @param param Options for parallelism and logging. Also specifies the size * of the 'prefix' and 'root' sections of the lattice. - * @param fgates Set of gates for which to find checkpoint locations. + * @param ops Set of (fused) operations for which to find checkpoint + * locations. * @return A pair of numbers specifying how many gates to apply before the * first and second checkpoints, respectively. */ + template static std::array CheckpointLocations( - const Parameter& param, const std::vector& fgates) { + const Parameter& param, const std::vector& ops) { + using FusedGate = std::variant_alternative_t<0, OperationF>; + std::array loc{0, 0}; unsigned num_decomposed = 0; unsigned num_p_gates = param.num_prefix_gatexs; unsigned num_pr_gates = num_p_gates + param.num_root_gatexs; - for (std::size_t i = 0; i < fgates.size(); ++i) { - for (auto gate: fgates[i].gates) { - if (gate->parent != nullptr) { + for (std::size_t i = 0; i < ops.size(); ++i) { + if (const auto* pg = OpGetAlternative(ops[i])) { + if (pg->ParentIsDecomposed()) { ++num_decomposed; - // There should be only one decomposed gate in fused gate. - break; } } @@ -478,6 +488,7 @@ struct HybridSimulator final { unsigned num_s_bits; }; + template static Bits CountSchmidtBits( const Parameter& param, const std::vector& gatexs) { Bits bits{0, 0, 0}; @@ -499,6 +510,7 @@ struct HybridSimulator final { return bits; } + template static unsigned SetSchmidtMatrices(std::size_t i0, std::size_t i1, uint64_t path, std::vector& prev_k, @@ -532,6 +544,7 @@ struct HybridSimulator final { return 0; } + template static void FillSchmidtMatrices(unsigned k, const GateX& gatex) { unsigned part0 = gatex.swapped; unsigned part1 = 1 - part0; @@ -549,18 +562,24 @@ struct HybridSimulator final { } } - template - static void ApplyGates(const std::vector& gates, + template + static void ApplyGates(const std::vector& ops, std::size_t i0, std::size_t i1, const Simulator& simulator, typename Simulator::State& state) { + using FusedGate = std::variant_alternative_t<0, OperationF>; + for (std::size_t i = i0; i < i1; ++i) { - if (gates[i].matrix.size() > 0) { - ApplyFusedGate(simulator, gates[i], state); + if (const auto* pg = OpGetAlternative(ops[i])) { + if (!pg->ParentIsDecomposed()) { + ApplyGate(simulator, ops[i], state); + } else { + auto fgate = *pg; + CalculateFusedMatrix(fgate); + ApplyGate(simulator, fgate, state); + } } else { - auto gate = gates[i]; - CalculateFusedMatrix(gate); - ApplyFusedGate(simulator, gate, state); + ApplyGate(simulator, ops[i], state); } } } diff --git a/lib/operation.h b/lib/operation.h new file mode 100644 index 000000000..83060abd8 --- /dev/null +++ b/lib/operation.h @@ -0,0 +1,72 @@ +// Copyright 2026 Google LLC. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef OPERATION_H_ +#define OPERATION_H_ + +#include +#include +#include + +#include "channel.h" +#include "gate.h" +#include "operation_base.h" + +namespace qsim { + +// Forward declaration of classically controlled operation. +template +struct ClassicallyControlledOperation; + +/** + * A generic operation. + */ +template +using Operation = std::variant, ControlledGate, Measurement, + Channel, ClassicallyControlledOperation>; + +/** + * A classically controlled operation. Not implemented yet. + */ +template +struct ClassicallyControlledOperation : public BaseOperation { + std::vector> sub_ops; +}; + +namespace detail { + +template +struct op_fp_type { + using type = typename T::fp_type; +}; + +template +struct op_fp_type { + using type = typename T::fp_type; +}; + +template +struct op_fp_type> { + using T = std::variant_alternative_t<0, std::variant>; + using type = typename op_fp_type::type; +}; + +} // namespace detail + +template +using OpFpType = typename detail::op_fp_type>::type; + +} // namespace qsim + +#endif // OPERATION_H_ diff --git a/lib/operation_base.h b/lib/operation_base.h new file mode 100644 index 000000000..6803e6c7a --- /dev/null +++ b/lib/operation_base.h @@ -0,0 +1,331 @@ +// Copyright 2026 Google LLC. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef OPERATION_BASE_H_ +#define OPERATION_BASE_H_ + +#include +#include +#include +#include +#include + +namespace qsim { + +using Qubits = std::vector; + +/** + * A base operation acting on a specific set of qubits at a given time step. + * Note: The `kind` field is currently utilized only in qsimh. + */ +struct BaseOperation { + unsigned kind; + unsigned time; + Qubits qubits; +}; + +namespace detail { + +template +struct append_to_variant {}; + +template +struct append_to_variant, A> { + using type = std::variant; +}; + +template +using append_to_variant_t = typename append_to_variant::type; + +template +struct is_type_in_variant {}; + +template +struct is_type_in_variant, A> + : std::disjunction...> {}; + +template +inline constexpr bool is_type_in_variant_v = is_type_in_variant::value; + +template +struct is_variant : std::false_type {}; + +template +struct is_variant> : std::true_type {}; + +template +inline constexpr bool is_variant_v = is_variant::value; + +} // namespace detail + +/** + * Recursively searches for a value of type T within a (potentially nested) + * variant. Traverses the provided `op` to find an alternative that matches + * type T. This function handles recursive `std::variant` structures and can + * transparently dereference pointers (e.g., `std::variant`; + * note, however, that `std::variant` does not work) to + * find the target type. + * + * @tparam T The target type to extract. + * @tparam Operation One of gate types, an `std::variant`, or a pointer + * to one of these. + * @param op The input operation, variant container, or pointer to search. + * @return A pointer to the value of type T if found; otherwise, nullptr. + */ +template +T* OpGetAlternative(Operation& op) { + using O = std::decay_t; + + auto f = [](auto& v) -> T* { + return OpGetAlternative(v); + }; + + if constexpr (std::is_pointer_v) { + if constexpr (std::is_const_v>) { + return nullptr; + } else { + return OpGetAlternative(*op); + } + } else if constexpr (std::is_same_v) { + return &op; + } else if constexpr (detail::is_variant_v) { + if constexpr (detail::is_type_in_variant_v) { + if (auto* p = std::get_if(&op)) { + return p; + } else { + return std::visit(f, op); + } + } else if constexpr (detail::is_type_in_variant_v) { + if (auto* p = std::get_if(&op)) { + return *p; + } else { + return std::visit(f, op); + } + } else { + return std::visit(f, op); + } + } else { + return nullptr; + } +} + +/** + * Recursively searches for a value of type T within a (potentially nested) + * variant. Traverses the provided `op` to find an alternative that matches + * type T. This function handles recursive `std::variant` structures and can + * transparently dereference pointers (e.g., `std::variant` or + * `std::variant`) to find the target type. + * + * @tparam T The target type to extract. + * @tparam Operation One of gate types, an `std::variant`, or a pointer + * to one of these. + * @param op The input operation, variant container, or pointer to search. + * @return A const pointer to the value of type T if found; otherwise, nullptr. + */ +template +const T* OpGetAlternative(const Operation& op) { + using O = std::decay_t; + + auto f = [](const auto& v) -> const T* { + return OpGetAlternative(v); + }; + + if constexpr (std::is_pointer_v) { + return OpGetAlternative(*op); + } else if constexpr (std::is_same_v) { + return &op; + } else if constexpr (detail::is_variant_v) { + if constexpr (detail::is_type_in_variant_v) { + if (const auto* p = std::get_if(&op)) { + return p; + } else { + return std::visit(f, op); + } + } else if constexpr (detail::is_type_in_variant_v) { + if (const auto* p = std::get_if(&op)) { + return *p; + } else { + return std::visit(f, op); + } + } else { + return std::visit(f, op); + } + } else { + return nullptr; + } +} + +/** + * Recursively retrieves the time step from a (nested) variant. + * This function traverses the provided `op` (concrete type, pointer, + * or variant) to access the `time` memeber in the underlying `BaseOperation`. + * This function handles recursive `std::variant` structures and can + * transparently dereference pointers (e.g., `std::variant` + * or `std::variant`) to find the target value. + * + * Note: this function assumes that the input contains a type that is + * derived from `BaseOperation`. If an incompatible type is encountered, + * the program will terminate. + * + * @tparam Operation A type derived from `BaseOperation`, an `std::variant`, + * or a pointer to one of these. + * @param op The input operation, variant container, or pointer to search. + * @return The time step of the underlying `BaseOperation`. + */ +template +inline unsigned OpTime(const Operation& op) { + using O = std::decay_t; + + if constexpr (std::is_pointer_v) { + return OpTime(*op); + } else if constexpr (std::is_base_of_v) { + return op.time; + } else if constexpr (detail::is_variant_v) { + auto f = [](const auto& v) { + return OpTime(v); + }; + + return std::visit(f, op); + } else { + // This branch is reached if the input contains a type that is not derived + // from BaseOperation. + std::fprintf(stderr, "fatal error: OpTime encountered an invalid type.\n"); + std::terminate(); + } +} + +/** + * Recursively retrieves the qubit indices from a (nested) variant. + * This function traverses the provided `op` (concrete type, pointer, + * or variant) to access the `qubits` memeber in the underlying `BaseOperation`. + * This function handles recursive `std::variant` structures and can + * transparently dereference pointers (e.g., `std::variant` + * or `std::variant`) to find the target value. + * + * Note: this function assumes that the input contains a type that is + * derived from `BaseOperation`. If an incompatible type is encountered, + * the program will terminate. + * + * @tparam Operation A type derived from `BaseOperation`, an `std::variant`, + * or a pointer to one of these. + * @param op The input operation, variant container, or pointer to search. + * @return A const reference to qubit indices of the underlying `BaseOperation`. + */ +template +inline const Qubits& OpQubits(const Operation& op) { + using O = std::decay_t; + + if constexpr (std::is_pointer_v) { + return OpQubits(*op); + } else if constexpr (std::is_base_of_v) { + return op.qubits; + } else if constexpr (detail::is_variant_v) { + auto f = [](const auto& v) -> const Qubits& { + return OpQubits(v); + }; + + return std::visit(f, op); + } else { + // This branch is reached if the input contains a type that is not derived + // from BaseOperation. + std::fprintf( + stderr, "fatal error: OpQubits encountered an invalid type.\n"); + std::terminate(); + } +} + +/** + * Recursively retrieves the BaseOperation from a (nested) variant. + * This function traverses the provided `op` (concrete type, pointer, + * or variant) to access the underlying BaseOperation. + * This function handles recursive `std::variant` structures and can + * transparently dereference pointers (e.g., `std::variant`; + * note, however, that `std::variant` does not work) to + * find the target value. + * + * Note: this function assumes that the input contains a type that is + * derived from `BaseOperation`. If an incompatible type is encountered, + * the program will terminate. + * + * @tparam Operation A type derived from `BaseOperation`, an `std::variant`, + * or a pointer to one of these. + * @param op The input operation, variant container, or pointer to search. + * @return A reference to the underlying `BaseOperation`. + */ +template +inline BaseOperation& OpBaseOperation(Operation& op) { + using O = std::decay_t; + + if constexpr (std::is_pointer_v) { + return OpBaseOperation(*op); + } else if constexpr (std::is_base_of_v) { + return op; + } else if constexpr (detail::is_variant_v) { + auto f = [](auto& v) -> BaseOperation& { + return OpBaseOperation(v); + }; + + return std::visit(f, op); + } else { + // This branch is reached if the input contains a type that is not derived + // from BaseOperation. + std::fprintf( + stderr, "fatal error: OpBaseOperation encountered an invalid type.\n"); + std::terminate(); + } +} + +/** + * Recursively retrieves the BaseOperation from a (nested) variant. + * This function traverses the provided `op` (concrete type, pointer, + * or variant) to access the underlying BaseOperation. + * This function handles recursive `std::variant` structures and can + * transparently dereference pointers (e.g., `std::variant` + * or `std::variant`) to find the target value. + * + * Note: this function assumes that the input contains a type that is + * derived from `BaseOperation`. If an incompatible type is encountered, + * the program will terminate. + * + * @tparam Operation A type derived from `BaseOperation`, an `std::variant`, + * or a pointer to one of these. + * @param op The input operation, variant container, or pointer to search. + * @return A const reference to the underlying `BaseOperation`. + */ +template +inline const BaseOperation& OpBaseOperation(const Operation& op) { + using O = std::decay_t; + + if constexpr (std::is_pointer_v) { + return OpBaseOperation(*op); + } else if constexpr (std::is_base_of_v) { + return op; + } else if constexpr (detail::is_variant_v) { + auto f = [](const auto& v) -> const BaseOperation& { + return OpBaseOperation(v); + }; + + return std::visit(f, op); + } else { + // This branch is reached if the input contains a type that is not derived + // from BaseOperation. + std::fprintf( + stderr, "fatal error: OpBaseOperation encountered an invalid type.\n"); + std::terminate(); + } +} + +} // namespace qsim + +#endif // OPERATION_BASE_H_ diff --git a/lib/qtrajectory.h b/lib/qtrajectory.h index 3ea2a4fa5..1b6ae2d6f 100644 --- a/lib/qtrajectory.h +++ b/lib/qtrajectory.h @@ -21,18 +21,22 @@ #include #include -#include "circuit_noisy.h" #include "gate.h" #include "gate_appl.h" +#include "operation.h" +#include "operation_base.h" namespace qsim { /** * Quantum trajectory simulator. */ -template +template class QuantumTrajectorySimulator { + private: + template + using GateOrOperation = std::variant*, const Operation*>; + public: using Simulator = typename Runner::Simulator; using StateSpace = typename Simulator::StateSpace; @@ -105,15 +109,15 @@ class QuantumTrajectorySimulator { * @param args Optional arguments for the 'measure' function. * @return True if the simulation completed successfully; false otherwise. */ - template + template static bool RunBatch(const Parameter& param, - const NoisyCircuit& circuit, - uint64_t r0, uint64_t r1, const StateSpace& state_space, + const Circuit& 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...); + return RunBatch(param, circuit.num_qubits, circuit.ops.begin(), + circuit.ops.end(), r0, r1, state_space, + simulator, measure, args...); } /** @@ -121,7 +125,7 @@ class QuantumTrajectorySimulator { * 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 cbeg, cend The range of channels [cbeg, cend) to run the circuit. + * @param obeg, oend The range of operations [obeg, oend) 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 @@ -134,15 +138,17 @@ class QuantumTrajectorySimulator { * @param args Optional arguments for the 'measure' function. * @return True if the simulation completed successfully; false otherwise. */ - template + template static bool RunBatch(const Parameter& param, unsigned num_qubits, - ncircuit_iterator cbeg, - ncircuit_iterator cend, + typename std::vector::const_iterator obeg, + typename std::vector::const_iterator oend, uint64_t r0, uint64_t r1, const StateSpace& state_space, const Simulator& simulator, MeasurementFunc&& measure, Args&&... args) { - std::vector gates; - gates.reserve(4 * std::size_t(cend - cbeg)); + using fp_type = OpFpType; + + std::vector> deferred_ops; + deferred_ops.reserve(4 * std::size_t(oend - obeg)); State state = state_space.Null(); @@ -157,8 +163,9 @@ class QuantumTrajectorySimulator { bool apply_last_deferred_ops = param.apply_last_deferred_ops || !had_primary_realization; - if (!RunIteration(param, apply_last_deferred_ops, num_qubits, cbeg, cend, - r, state_space, simulator, gates, state, stat)) { + if (!RunIteration(param, apply_last_deferred_ops, + num_qubits, obeg, oend, r, state_space, + simulator, deferred_ops, state, stat)) { return false; } @@ -185,20 +192,20 @@ class QuantumTrajectorySimulator { * bitstrings, to be populated by this method. * @return True if the simulation completed successfully; false otherwise. */ - static bool RunOnce(const Parameter& param, - const NoisyCircuit& circuit, uint64_t r, - const StateSpace& state_space, const Simulator& simulator, - State& state, Stat& stat) { - return RunOnce(param, circuit.num_qubits, circuit.channels.begin(), - circuit.channels.end(), r, state_space, simulator, - state, stat); + template + static bool RunOnce(const Parameter& param, const Circuit& circuit, + uint64_t r, const StateSpace& state_space, + const Simulator& simulator,State& state, Stat& stat) { + return RunOnce(param, circuit.num_qubits, circuit.ops.begin(), + circuit.ops.end(), r, state_space, simulator, + 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 cbeg, cend The range of channels [cbeg, cend) to run the circuit. + * @param obeg, oend The range of operations [obeg, oend) 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. @@ -209,16 +216,20 @@ class QuantumTrajectorySimulator { * bitstrings, to be populated by this method. * @return True if the simulation completed successfully; false otherwise. */ + template static bool RunOnce(const Parameter& param, unsigned num_qubits, - ncircuit_iterator cbeg, - ncircuit_iterator cend, + typename std::vector::const_iterator obeg, + typename std::vector::const_iterator oend, uint64_t r, const StateSpace& state_space, const Simulator& simulator, State& state, Stat& stat) { - std::vector gates; - gates.reserve(4 * std::size_t(cend - cbeg)); + using fp_type = OpFpType; - if (!RunIteration(param, param.apply_last_deferred_ops, num_qubits, cbeg, - cend, r, state_space, simulator, gates, state, stat)) { + std::vector> deferred_ops; + deferred_ops.reserve(4 * std::size_t(oend - obeg)); + + if (!RunIteration(param, param.apply_last_deferred_ops, + num_qubits, obeg, oend, r, state_space, + simulator, deferred_ops, state, stat)) { return false; } @@ -226,16 +237,20 @@ class QuantumTrajectorySimulator { } private: + template static bool RunIteration(const Parameter& param, bool apply_last_deferred_ops, unsigned num_qubits, - ncircuit_iterator cbeg, - ncircuit_iterator cend, + typename std::vector::const_iterator obeg, + typename std::vector::const_iterator oend, uint64_t rep, const StateSpace& state_space, const Simulator& simulator, - std::vector& gates, + std::vector& deferred_ops, State& state, Stat& stat) { + using fp_type = OpFpType; + using Channel = qsim::Channel; + if (param.collect_kop_stat || param.collect_mea_stat) { - stat.samples.reserve(std::size_t(cend - cbeg)); + stat.samples.reserve(std::size_t(oend - obeg)); stat.samples.resize(0); } @@ -248,7 +263,7 @@ class QuantumTrajectorySimulator { state_space.SetStateZero(state); } - gates.resize(0); + deferred_ops.resize(0); RGen rgen(rep); std::uniform_real_distribution distr(0.0, 1.0); @@ -256,24 +271,22 @@ class QuantumTrajectorySimulator { bool unitary = true; stat.primary = true; - for (auto it = cbeg; it != cend; ++it) { - const auto& channel = *it; + for (auto it = obeg; it != oend; ++it) { + const auto& op = *it; - if (channel.size() == 0) continue; + if (const auto* pg = OpGetAlternative(op)) { + // Measurement gate. - if (channel[0].kind == gate::kMeasurement) { - // Measurement channel. - - if (!ApplyDeferredOps( - param, num_qubits, state_space, simulator, gates, state)) { + if (!ApplyDeferredOps(param, num_qubits, + state_space, simulator, deferred_ops, state)) { return false; } bool normalize = !unitary && param.normalize_before_mea_gates; NormalizeState(normalize, state_space, unitary, state); - auto mresult = ApplyMeasurementGate(state_space, channel[0].ops[0], - rgen, state); + auto mresult = + ApplyMeasurementGate(state_space, pg->qubits, rgen, state); if (!mresult.valid) { return false; @@ -286,19 +299,29 @@ class QuantumTrajectorySimulator { continue; } - // "Normal" channel. - double r = distr(rgen); double cp = 0; + const auto* pg = OpGetAlternative(op); + + if (!pg) { + DeferOp(op, deferred_ops); + CollectStat(param.collect_kop_stat, 0, stat); + continue; + } + + // Channel. + + const auto& channel = *pg; + // Perform sampling of Kraus operators using probability bounds. - for (std::size_t i = 0; i < channel.size(); ++i) { - const auto& kop = channel[i]; + for (std::size_t i = 0; i < channel.kops.size(); ++i) { + const auto& kop = channel.kops[i]; cp += kop.prob; if (r < cp) { - DeferOps(kop.ops, gates); + DeferOps(kop.ops, deferred_ops); CollectStat(param.collect_kop_stat, i, stat); unitary = unitary && kop.unitary; @@ -309,8 +332,8 @@ class QuantumTrajectorySimulator { if (r < cp) continue; - if (!ApplyDeferredOps( - param, num_qubits, state_space, simulator, gates, state)) { + if (!ApplyDeferredOps(param, num_qubits, + state_space, simulator, deferred_ops, state)) { return false; } @@ -320,8 +343,8 @@ class QuantumTrajectorySimulator { std::size_t max_prob_index = 0; // Perform sampling of Kraus operators using norms of updated states. - for (std::size_t i = 0; i < channel.size(); ++i) { - const auto& kop = channel[i]; + for (std::size_t i = 0; i < channel.kops.size(); ++i) { + const auto& kop = channel.kops[i]; if (kop.unitary) continue; @@ -335,13 +358,13 @@ class QuantumTrajectorySimulator { cp += prob - kop.prob; - if (r < cp || i == channel.size() - 1) { + if (r < cp || i == channel.kops.size() - 1) { // Sample ith Kraus operator if r < cp // Sample the highest probability Kraus operator if r is greater // than the sum of all probablities due to round-off errors. uint64_t k = r < cp ? i : max_prob_index; - DeferOps(channel[k].ops, gates); + DeferOps(channel.kops[k].ops, deferred_ops); CollectStat(param.collect_kop_stat, k, stat); unitary = false; @@ -352,8 +375,8 @@ class QuantumTrajectorySimulator { } if (apply_last_deferred_ops || !stat.primary) { - if (!ApplyDeferredOps( - param, num_qubits, state_space, simulator, gates, state)) { + if (!ApplyDeferredOps(param, num_qubits, + state_space, simulator, deferred_ops, state)) { return false; } @@ -373,13 +396,14 @@ class QuantumTrajectorySimulator { return state; } + template static bool ApplyDeferredOps( const Parameter& param, unsigned num_qubits, const StateSpace& state_space, const Simulator& simulator, - std::vector& gates, State& state) { - if (gates.size() > 0) { - bool rc = Runner::Run(param, gates, state_space, simulator, state); - gates.resize(0); + std::vector& deferred_ops, State& state) { + if (deferred_ops.size() > 0) { + bool rc = Runner::Run(param, deferred_ops, state_space, simulator, state); + deferred_ops.resize(0); return rc; } @@ -387,9 +411,9 @@ class QuantumTrajectorySimulator { } static MeasurementResult ApplyMeasurementGate( - const StateSpace& state_space, const Gate& gate, + const StateSpace& state_space, const Qubits& qubits, RGen& rgen, State& state) { - auto result = state_space.Measure(gate.qubits, rgen, state); + auto result = state_space.Measure(qubits, rgen, state); if (!result.valid) { IO::errorf("measurement failed.\n"); @@ -398,10 +422,17 @@ class QuantumTrajectorySimulator { return result; } + template + static void DeferOp( + const Operation& op, std::vector& deferred_ops) { + deferred_ops.push_back(&op); + } + + template static void DeferOps( - const std::vector& ops, std::vector& gates) { - for (const auto& op : ops) { - gates.push_back(&op); + const std::vector& gates, std::vector& deferred_ops) { + for (const auto& gate : gates) { + deferred_ops.push_back(&gate); } } diff --git a/lib/run_custatevecex.h b/lib/run_custatevecex.h index 2a2b8b1da..3ceba91b0 100644 --- a/lib/run_custatevecex.h +++ b/lib/run_custatevecex.h @@ -22,6 +22,8 @@ #include #include "circuit.h" +#include "gate.h" +#include "operation_base.h" #include "util.h" #include "util_custatevec.h" #include "util_custatevecex.h" @@ -39,6 +41,12 @@ struct CuStateVecExRunner final { using State = typename StateSpace::State; using MeasurementResult = typename StateSpace::MeasurementResult; + private: + using fp_type = typename Simulator::fp_type; + using Gate = qsim::Gate; + using ControlledGate = qsim::ControlledGate; + + public: /** * User-specified parameters for simulation. */ @@ -53,7 +61,7 @@ struct CuStateVecExRunner final { /** * Runs the given circuit, only measuring at the end. - * @param param Options for gate fusion, parallelism and logging. + * @param param Options for the random number generator seed. * @param factory Object to create simulators and state spaces. * @param circuit The circuit to be simulated. * @param measure Function that performs measurements (in the sense of @@ -63,12 +71,12 @@ struct CuStateVecExRunner final { template static bool Run(const Parameter& param, const Factory& factory, const Circuit& circuit, MeasurementFunc measure) { - return Run(param, factory, {circuit.gates.back().time}, circuit, measure); + return Run(param, factory, {OpTime(circuit.ops.back())}, circuit, measure); } /** * Runs the given circuit, measuring at user-specified times. - * @param param Options for gate fusion, parallelism and logging. + * @param param Options for the random number generator seed. * @param factory Object to create simulators and state spaces. * @param times_to_measure_at Time steps at which to perform measurements. * @param circuit The circuit to be simulated. @@ -100,7 +108,7 @@ struct CuStateVecExRunner final { /** * Runs the given circuit and make the final state available to the caller, * recording the result of any intermediate measurements in the circuit. - * @param param Options for gate fusion, parallelism and logging. + * @param param Options for the random number generator seed. * @param factory Object to create simulators and state spaces. * @param circuit The circuit to be simulated. * @param state As an input parameter, this should contain the initial state @@ -127,7 +135,7 @@ struct CuStateVecExRunner final { /** * Runs the given circuit and make the final state available to the caller, * discarding the result of any intermediate measurements in the circuit. - * @param param Options for gate fusion, parallelism and logging. + * @param param Options for the random number generator seed. * @param factory Object to create simulators and state spaces. * @param circuit The circuit to be simulated. * @param state As an input parameter, this should contain the initial state @@ -152,7 +160,7 @@ struct CuStateVecExRunner final { /** * Runs the given circuit and make the final state available to the caller, * recording the result of any intermediate measurements in the circuit. - * @param param Options for gate fusion, parallelism and logging. + * @param param Options for the random number generator seed. * @param circuit The circuit to be simulated. * @param state_space StateSpace object required to perform measurements. * @param simulator Simulator object. Provides specific implementations for @@ -179,7 +187,7 @@ struct CuStateVecExRunner final { /** * Runs the given circuit and make the final state available to the caller, * discarding the result of any intermediate measurements in the circuit. - * @param param Options for gate fusion, parallelism and logging. + * @param param Options for the random number generator seed. * @param circuit The circuit to be simulated. * @param state_space StateSpace object required to perform measurements. * @param simulator Simulator object. Provides specific implementations for @@ -229,60 +237,60 @@ struct CuStateVecExRunner final { unsigned cur_time_index = 0; - using Gates = detail::Gates; - const auto& gates = Gates::get(circuit); + const auto& ops = Operations::get(circuit); - for (std::size_t i = 0; i < gates.size(); ++i) { - const auto& gate = Gates::gate(gates[i]); - unsigned num_qubits = gate.qubits.size(); - unsigned num_cqubits = gate.controlled_by.size(); + for (std::size_t i = 0; i < ops.size(); ++i) { + const auto& op = ops[i]; - if (gate.kind == gate::kMeasurement) { + if (const auto* pg = OpGetAlternative(op)) { ErrorCheck( custatevecExSVUpdaterApply(sv_updater, state.get(), nullptr, 0)); ErrorCheck(custatevecExSVUpdaterClear(sv_updater)); - auto measure_result = state_space.Measure(gate.qubits, rgen, state); + auto measure_result = state_space.Measure(pg->qubits, rgen, state); if (measure_result.valid) { measure_results.push_back(std::move(measure_result)); } else { IO::errorf("measurement failed.\n"); return false; } - } else if (num_cqubits == 0) { - if (num_qubits == 0) { + } else if (const auto* pg = OpGetAlternative(op)) { + if (pg->qubits.size() == 0) { ErrorCheck( custatevecExSVUpdaterApply(sv_updater, state.get(), nullptr, 0)); ErrorCheck(custatevecExSVUpdaterClear(sv_updater)); - simulator.ApplyGate(gate.qubits, gate.matrix.data(), state); + simulator.ApplyGate(pg->qubits, pg->matrix.data(), state); } else { ErrorCheck(custatevecExSVUpdaterEnqueueMatrix( - sv_updater, gate.matrix.data(), StateSpace::kMatrixDataType, + sv_updater, pg->matrix.data(), StateSpace::kMatrixDataType, StateSpace::kExMatrixType, StateSpace::kMatrixLayout, 0, - reinterpret_cast(gate.qubits.data()), - num_qubits, nullptr, nullptr, 0)); + reinterpret_cast(pg->qubits.data()), + pg->qubits.size(), nullptr, nullptr, 0)); } - } else { + } else if (const auto* pg = OpGetAlternative(op)) { + unsigned num_qubits = pg->qubits.size(); + unsigned num_cqubits = pg->controlled_by.size(); + std::vector control_bits; control_bits.reserve(num_cqubits); for (std::size_t i = 0; i < num_cqubits; ++i) { - control_bits.push_back((gate.cmask >> i) & 1); + control_bits.push_back((pg->cmask >> i) & 1); } ErrorCheck(custatevecExSVUpdaterEnqueueMatrix( - sv_updater, gate.matrix.data(), StateSpace::kMatrixDataType, + sv_updater, pg->matrix.data(), StateSpace::kMatrixDataType, StateSpace::kExMatrixType, StateSpace::kMatrixLayout, 0, - reinterpret_cast(gate.qubits.data()), num_qubits, - reinterpret_cast(gate.controlled_by.data()), + reinterpret_cast(pg->qubits.data()), num_qubits, + reinterpret_cast(pg->controlled_by.data()), control_bits.data(), num_cqubits)); } if (times_to_measure_at.size() > 0) { unsigned t = times_to_measure_at[cur_time_index]; - if (i == gates.size() - 1 || t < Gates::gate(gates[i + 1]).time) { + if (i == ops.size() - 1 || t < OpTime(ops[i + 1])) { ErrorCheck( custatevecExSVUpdaterApply(sv_updater, state.get(), nullptr, 0)); ErrorCheck(custatevecExSVUpdaterClear(sv_updater)); diff --git a/lib/run_qsim.h b/lib/run_qsim.h index d718c6b31..1c3b8a970 100644 --- a/lib/run_qsim.h +++ b/lib/run_qsim.h @@ -22,6 +22,7 @@ #include "circuit.h" #include "gate.h" #include "gate_appl.h" +#include "operation_base.h" #include "util.h" namespace qsim { @@ -60,7 +61,8 @@ struct QSimRunner final { template static bool Run(const Parameter& param, const Factory& factory, const Circuit& circuit, MeasurementFunc measure) { - return Run(param, factory, {circuit.gates.back().time}, circuit, measure); + unsigned time = OpTime(circuit.ops.back()); + return Run(param, factory, {time}, circuit, measure); } /** @@ -103,10 +105,11 @@ struct QSimRunner final { t0 = GetTime(); } - auto fused_gates = Fuser::FuseGates(param, circuit.num_qubits, - circuit.gates, times_to_measure_at); + const auto& ops = Operations::get(circuit); + auto fused_ops = Fuser::FuseGates(param, circuit.num_qubits, + ops, times_to_measure_at); - if (fused_gates.size() == 0 && circuit.gates.size() > 0) { + if (fused_ops.size() == 0 && circuit.ops.size() > 0) { return false; } @@ -121,14 +124,13 @@ struct QSimRunner final { unsigned cur_time_index = 0; - // Apply fused gates. - for (std::size_t i = 0; i < fused_gates.size(); ++i) { + // Apply fused operations. + for (std::size_t i = 0; i < fused_ops.size(); ++i) { if (param.verbosity > 3) { t1 = GetTime(); } - if (!ApplyFusedGate(state_space, simulator, fused_gates[i], rgen, - state)) { + if (!ApplyGate(state_space, simulator, fused_ops[i], rgen, state)) { IO::errorf("measurement failed.\n"); return false; } @@ -141,7 +143,7 @@ struct QSimRunner final { unsigned t = times_to_measure_at[cur_time_index]; - if (i == fused_gates.size() - 1 || t < fused_gates[i + 1].time) { + if (i == fused_ops.size() - 1 || t < OpTime(fused_ops[i + 1])) { // Call back to perform measurements. measure(cur_time_index, state_space, state); ++cur_time_index; @@ -240,16 +242,14 @@ struct QSimRunner final { t0 = GetTime(); } - using Gates = detail::Gates; - const auto& gates = Gates::get(circuit); + const auto& ops = Operations::get(circuit); + auto fused_ops = Fuser::FuseGates(param, state.num_qubits(), ops); - auto fused_gates = Fuser::FuseGates(param, state.num_qubits(), gates); - - if (fused_gates.size() == 0 && gates.size() > 0) { + if (fused_ops.size() == 0 && ops.size() > 0) { return false; } - measure_results.reserve(fused_gates.size()); + measure_results.reserve(fused_ops.size()); if (param.verbosity > 1) { t1 = GetTime(); @@ -260,14 +260,14 @@ struct QSimRunner final { t0 = GetTime(); } - // Apply fused gates. - for (std::size_t i = 0; i < fused_gates.size(); ++i) { + // Apply fused operations. + for (std::size_t i = 0; i < fused_ops.size(); ++i) { if (param.verbosity > 3) { t1 = GetTime(); } - if (!ApplyFusedGate(state_space, simulator, fused_gates[i], rgen, state, - measure_results)) { + if (!ApplyGate(state_space, simulator, fused_ops[i], rgen, state, + measure_results)) { IO::errorf("measurement failed.\n"); return false; } diff --git a/lib/run_qsimh.h b/lib/run_qsimh.h index c1534d3a2..c3b2faded 100644 --- a/lib/run_qsimh.h +++ b/lib/run_qsimh.h @@ -26,14 +26,9 @@ namespace qsim { /** * Helper struct for running qsimh. */ -template +template struct QSimHRunner final { - using Gate = typename HybridSimulator::Gate; - using fp_type = typename HybridSimulator::fp_type; - - using Parameter = typename HybridSimulator::Parameter; - using HybridData = typename HybridSimulator::HybridData; - using Fuser = typename HybridSimulator::Fuser; + using Parameter = typename HybridSimulator::Parameter; /** * Evaluates the amplitudes for a given circuit and set of output states. @@ -47,11 +42,11 @@ struct QSimHRunner final { * will be populated with amplitudes for each state in 'bitstrings'. * @return True if the simulation completed successfully; false otherwise. */ - template + template static bool Run(const Parameter& param, const Factory& factory, const Circuit& circuit, const std::vector& parts, const std::vector& bitstrings, - std::vector>& results) { + std::vector>& results) { if (circuit.num_qubits != parts.size()) { IO::errorf("parts size is not equal to the number of qubits."); return false; @@ -63,8 +58,12 @@ struct QSimHRunner final { t0 = GetTime(); } - HybridData hd; - bool rc = HybridSimulator::SplitLattice(parts, circuit.gates, hd); + const auto& ops = Operations::get(circuit); + + using fp_type = OpFpType; + + typename HybridSimulator::HybridData hd; + bool rc = HybridSimulator::SplitLattice(parts, ops, hd); if (!rc) { return false; @@ -82,18 +81,18 @@ struct QSimHRunner final { PrintInfo(param, hd); } - auto fgates0 = Fuser::FuseGates(param, hd.num_qubits0, hd.gates0); - if (fgates0.size() == 0 && hd.gates0.size() > 0) { + auto fops0 = Fuser::FuseGates(param, hd.num_qubits0, hd.ops0); + if (fops0.size() == 0 && hd.ops0.size() > 0) { return false; } - auto fgates1 = Fuser::FuseGates(param, hd.num_qubits1, hd.gates1); - if (fgates1.size() == 0 && hd.gates1.size() > 0) { + auto fops1 = Fuser::FuseGates(param, hd.num_qubits1, hd.ops1); + if (fops1.size() == 0 && hd.ops1.size() > 0) { return false; } rc = HybridSimulator(param.num_threads).Run( - param, factory, hd, parts, fgates0, fgates1, bitstrings, results); + param, factory, hd, parts, fops0, fops1, bitstrings, results); if (rc && param.verbosity > 0) { double t1 = GetTime(); @@ -104,6 +103,7 @@ struct QSimHRunner final { } private: + template static void PrintInfo(const Parameter& param, const HybridData& hd) { unsigned num_suffix_gates = hd.num_gatexs - param.num_prefix_gatexs - param.num_root_gatexs; diff --git a/pybind_interface/avx2/pybind_main_avx2.cpp b/pybind_interface/avx2/pybind_main_avx2.cpp index 193ed97e3..cd3a9c058 100644 --- a/pybind_interface/avx2/pybind_main_avx2.cpp +++ b/pybind_interface/avx2/pybind_main_avx2.cpp @@ -35,11 +35,10 @@ namespace qsim { using StateSpace = Simulator::StateSpace; using Gate = Cirq::GateCirq; - using Runner = QSimRunner, Factory>; - using RunnerQT = - QSimRunner, Factory>; + using Operation = qsim::Operation; + using Runner = QSimRunner, Factory>; using RunnerParameter = Runner::Parameter; - using NoisyRunner = qsim::QuantumTrajectorySimulator; + using NoisyRunner = qsim::QuantumTrajectorySimulator; using NoisyRunnerParameter = NoisyRunner::Parameter; StateSpace CreateStateSpace() const { diff --git a/pybind_interface/avx512/pybind_main_avx512.cpp b/pybind_interface/avx512/pybind_main_avx512.cpp index b730a0d6d..906eac8fe 100644 --- a/pybind_interface/avx512/pybind_main_avx512.cpp +++ b/pybind_interface/avx512/pybind_main_avx512.cpp @@ -35,11 +35,10 @@ namespace qsim { using StateSpace = Simulator::StateSpace; using Gate = Cirq::GateCirq; - using Runner = QSimRunner, Factory>; - using RunnerQT = - QSimRunner, Factory>; + using Operation = qsim::Operation; + using Runner = QSimRunner, Factory>; using RunnerParameter = Runner::Parameter; - using NoisyRunner = qsim::QuantumTrajectorySimulator; + using NoisyRunner = qsim::QuantumTrajectorySimulator; using NoisyRunnerParameter = NoisyRunner::Parameter; StateSpace CreateStateSpace() const { diff --git a/pybind_interface/basic/pybind_main_basic.cpp b/pybind_interface/basic/pybind_main_basic.cpp index 6db3f5a5e..b8e0191d8 100644 --- a/pybind_interface/basic/pybind_main_basic.cpp +++ b/pybind_interface/basic/pybind_main_basic.cpp @@ -35,11 +35,10 @@ namespace qsim { using StateSpace = Simulator::StateSpace; using Gate = Cirq::GateCirq; - using Runner = QSimRunner, Factory>; - using RunnerQT = - QSimRunner, Factory>; + using Operation = qsim::Operation; + using Runner = QSimRunner, Factory>; using RunnerParameter = Runner::Parameter; - using NoisyRunner = qsim::QuantumTrajectorySimulator; + using NoisyRunner = qsim::QuantumTrajectorySimulator; using NoisyRunnerParameter = NoisyRunner::Parameter; StateSpace CreateStateSpace() const { diff --git a/pybind_interface/cuda/pybind_main_cuda.cpp b/pybind_interface/cuda/pybind_main_cuda.cpp index d399de4c8..8d0e82dda 100644 --- a/pybind_interface/cuda/pybind_main_cuda.cpp +++ b/pybind_interface/cuda/pybind_main_cuda.cpp @@ -33,11 +33,10 @@ namespace qsim { using StateSpace = Simulator::StateSpace; using Gate = Cirq::GateCirq; - using Runner = QSimRunner, Factory>; - using RunnerQT = - QSimRunner, Factory>; + using Operation = qsim::Operation; + using Runner = QSimRunner, Factory>; using RunnerParameter = Runner::Parameter; - using NoisyRunner = qsim::QuantumTrajectorySimulator; + using NoisyRunner = qsim::QuantumTrajectorySimulator; using NoisyRunnerParameter = NoisyRunner::Parameter; StateSpace CreateStateSpace() const { diff --git a/pybind_interface/custatevec/pybind_main_custatevec.cpp b/pybind_interface/custatevec/pybind_main_custatevec.cpp index 6e61831cf..5e9a53e7b 100644 --- a/pybind_interface/custatevec/pybind_main_custatevec.cpp +++ b/pybind_interface/custatevec/pybind_main_custatevec.cpp @@ -41,11 +41,10 @@ namespace qsim { using StateSpace = Simulator::StateSpace; using Gate = Cirq::GateCirq; - using Runner = QSimRunner, Factory>; - using RunnerQT = - QSimRunner, Factory>; + using Operation = qsim::Operation; + using Runner = QSimRunner, Factory>; using RunnerParameter = Runner::Parameter; - using NoisyRunner = qsim::QuantumTrajectorySimulator; + using NoisyRunner = qsim::QuantumTrajectorySimulator; using NoisyRunnerParameter = NoisyRunner::Parameter; StateSpace CreateStateSpace() const { diff --git a/pybind_interface/custatevecex/pybind_main_custatevecex.cpp b/pybind_interface/custatevecex/pybind_main_custatevecex.cpp index 400baa220..688b4391e 100644 --- a/pybind_interface/custatevecex/pybind_main_custatevecex.cpp +++ b/pybind_interface/custatevecex/pybind_main_custatevecex.cpp @@ -73,12 +73,13 @@ namespace qsim { } using Gate = Cirq::GateCirq; + using Operation = qsim::Operation; using Runner = CuStateVecExRunner; struct RunnerParameter : public Runner::Parameter { // max_fused_size is not used, but kept for consistency. unsigned max_fused_size = 2; }; - using NoisyRunner = qsim::QuantumTrajectorySimulator; + using NoisyRunner = qsim::QuantumTrajectorySimulator; struct NoisyRunnerParameter : public NoisyRunner::Parameter { // max_fused_size is not used, but kept for consistency. unsigned max_fused_size = 2; diff --git a/pybind_interface/hip/pybind_main_hip.cpp b/pybind_interface/hip/pybind_main_hip.cpp index 726048bcd..fc2e6789b 100644 --- a/pybind_interface/hip/pybind_main_hip.cpp +++ b/pybind_interface/hip/pybind_main_hip.cpp @@ -33,11 +33,10 @@ namespace qsim { using StateSpace = Simulator::StateSpace; using Gate = Cirq::GateCirq; - using Runner = QSimRunner, Factory>; - using RunnerQT = - QSimRunner, Factory>; + using Operation = qsim::Operation; + using Runner = QSimRunner, Factory>; using RunnerParameter = Runner::Parameter; - using NoisyRunner = qsim::QuantumTrajectorySimulator; + using NoisyRunner = qsim::QuantumTrajectorySimulator; using NoisyRunnerParameter = NoisyRunner::Parameter; StateSpace CreateStateSpace() const { diff --git a/pybind_interface/pybind_main.cpp b/pybind_interface/pybind_main.cpp index 1efba5484..3ca152a60 100644 --- a/pybind_interface/pybind_main.cpp +++ b/pybind_interface/pybind_main.cpp @@ -14,10 +14,12 @@ #include "pybind_main.h" +#include +#include #include +#include +#include #include -#include -#include #include #include @@ -34,17 +36,9 @@ using namespace qsim; namespace { -Circuit getCircuit(const py::dict &options) { - try { - return options["c\0"].cast>(); - } catch (const std::invalid_argument &exp) { - throw; - } -} - -NoisyCircuit getNoisyCircuit(const py::dict &options) { +Circuit getCircuit(const py::dict &options) { try { - return options["c\0"].cast>(); + return options["c\0"].cast>(); } catch (const std::invalid_argument &exp) { throw; } @@ -70,9 +64,9 @@ std::vector getBitstrings(const py::dict &options, int num_qubits) { } // namespace Factory::Gate create_gate(const qsim::Cirq::GateKind gate_kind, - const unsigned time, - const std::vector& qubits, - const std::map& params) { + const unsigned time, + const std::vector& qubits, + const std::map& params) { switch (gate_kind) { case Cirq::kI1: return Cirq::I1::Create(time, qubits[0]); @@ -189,11 +183,6 @@ Factory::Gate create_gate(const qsim::Cirq::GateKind gate_kind, return Cirq::CCZ::Create(time, qubits[0], qubits[1], qubits[2]); case Cirq::kCCX: return Cirq::CCX::Create(time, qubits[0], qubits[1], qubits[2]); - case Cirq::kMeasurement: { - std::vector qubits_ = qubits; - return gate::Measurement::Create( - time, std::move(qubits_)); - } // Matrix gates are handled in the add_matrix methods below. default: throw std::invalid_argument("GateKind not supported."); @@ -201,8 +190,8 @@ Factory::Gate create_gate(const qsim::Cirq::GateKind gate_kind, } Factory::Gate create_diagonal_gate(const unsigned time, - const std::vector& qubits, - const std::vector& angles) { + const std::vector& qubits, + const std::vector& angles) { switch (qubits.size()) { case 2: return Cirq::TwoQubitDiagonalGate::Create( @@ -217,8 +206,8 @@ Factory::Gate create_diagonal_gate(const unsigned time, } Factory::Gate create_matrix_gate(const unsigned time, - const std::vector& qubits, - const std::vector& matrix) { + const std::vector& qubits, + const std::vector& matrix) { switch (qubits.size()) { case 0: // Global phase gate. @@ -241,81 +230,44 @@ Factory::Gate create_matrix_gate(const unsigned time, void add_gate(const qsim::Cirq::GateKind gate_kind, const unsigned time, const std::vector& qubits, const std::map& params, - Circuit* circuit) { - circuit->gates.push_back(create_gate(gate_kind, time, qubits, params)); + Circuit* circuit) { + circuit->ops.push_back(create_gate(gate_kind, time, qubits, params)); } void add_diagonal_gate(const unsigned time, const std::vector& qubits, const std::vector& angles, - Circuit* circuit) { - circuit->gates.push_back(create_diagonal_gate(time, qubits, angles)); + Circuit* circuit) { + circuit->ops.push_back(create_diagonal_gate(time, qubits, angles)); } -void add_matrix_gate(const unsigned time, - const std::vector& qubits, +void add_matrix_gate(const unsigned time, const std::vector& qubits, const std::vector& matrix, - Circuit* circuit) { - circuit->gates.push_back(create_matrix_gate(time, qubits, matrix)); -} - -void control_last_gate(const std::vector& qubits, - const std::vector& values, - Circuit* circuit) { - MakeControlledGate(qubits, values, circuit->gates.back()); -} - -template -Channel create_single_gate_channel(Gate gate) { - auto gate_kind = KrausOperator::kNormal; - if (gate.kind == gate::kMeasurement) { - gate_kind = KrausOperator::kMeasurement; - } - return {{gate_kind, 1, 1.0, {gate}}}; + Circuit* circuit) { + circuit->ops.push_back(create_matrix_gate(time, qubits, matrix)); } -void add_gate_channel(const qsim::Cirq::GateKind gate_kind, const unsigned time, - const std::vector& qubits, - const std::map& params, - NoisyCircuit* ncircuit) { - ncircuit->channels.push_back(create_single_gate_channel( - create_gate(gate_kind, time, qubits, params))); +void add_measurement(const unsigned time, const std::vector& qubits, + Circuit* circuit) { + circuit->ops.push_back(CreateMeasurement(time, qubits)); } -void add_diagonal_gate_channel(const unsigned time, - const std::vector& qubits, - const std::vector& angles, - NoisyCircuit* ncircuit) { - ncircuit->channels.push_back(create_single_gate_channel( - create_diagonal_gate(time, qubits, angles))); -} - -void add_matrix_gate_channel(const unsigned time, - const std::vector& qubits, - const std::vector& matrix, - NoisyCircuit* ncircuit) { - ncircuit->channels.push_back(create_single_gate_channel( - create_matrix_gate(time, qubits, matrix))); -} - -void control_last_gate_channel(const std::vector& qubits, - const std::vector& values, - NoisyCircuit* ncircuit) { - if (ncircuit->channels.back().size() > 1) { - throw std::invalid_argument( - "Control cannot be added to noisy channels."); - } - for (Factory::Gate& op : ncircuit->channels.back()[0].ops) { - MakeControlledGate(qubits, values, op); - } +void control_last_gate(const std::vector& qubits, + const std::vector& values, + Circuit* circuit) { + auto& lop = circuit->ops.back(); + lop = OpGetAlternative(lop)->ControlledBy(qubits, values); } void add_channel(const unsigned time, const std::vector& qubits, const std::vector, bool>>& prob_matrix_unitary_triples, - NoisyCircuit* ncircuit) { + Circuit* ncircuit) { // Adds a channel to the noisy circuit. - Channel channel; + + std::set qubits_set; + Channel channel{{kChannel, time, {}}, {}}; + // prob_matrix_unitary_triples contains triples with these elements: // 0. The lower-bound probability of applying the matrix. // 1. The matrix to be applied. @@ -325,33 +277,42 @@ void add_channel(const unsigned time, const std::vector& mat = std::get<1>(triple); bool is_unitary = std::get<2>(triple); Factory::Gate gate = create_matrix_gate(time, qubits, mat); - channel.emplace_back(KrausOperator{ - KrausOperator::kNormal, is_unitary, prob, {gate} - }); + + for (auto q : gate.qubits) { + qubits_set.insert(q); + } + + channel.kops.push_back(KrausOperator{is_unitary, prob, {gate}}); if (!is_unitary) { - channel.back().CalculateKdKMatrix(); + channel.kops.back().CalculateKdKMatrix(); } } - ncircuit->channels.push_back(channel); + + channel.qubits.reserve(qubits_set.size()); + for (auto it = qubits_set.begin(); it != qubits_set.end(); ++it) { + channel.qubits.push_back(*it); + } + + ncircuit->ops.push_back(channel); } -void add_gate_to_opstring(const Cirq::GateKind gate_kind, +void add_gate_to_opstring(const qsim::Cirq::GateKind gate_kind, const std::vector& qubits, - OpString* opstring) { + OpString* opstring) { static std::map params; opstring->ops.push_back(create_gate(gate_kind, 0, qubits, params)); } void add_matrix_gate_to_opstring(const std::vector& qubits, const std::vector& matrix, - OpString* opstring) { + OpString* opstring) { opstring->ops.push_back(create_matrix_gate(0, qubits, matrix)); } // Methods for simulating amplitudes. std::vector> qsim_simulate(const py::dict &options) { - Circuit circuit; + Circuit circuit; std::vector bitstrings; try { circuit = getCircuit(options); @@ -405,11 +366,11 @@ std::vector> qsim_simulate(const py::dict &options) { } std::vector> qtrajectory_simulate(const py::dict &options) { - NoisyCircuit ncircuit; + Circuit ncircuit; unsigned num_qubits; std::vector bitstrings; try { - ncircuit = getNoisyCircuit(options); + ncircuit = getCircuit(options); num_qubits = ncircuit.num_qubits; bitstrings = getBitstrings(options, num_qubits); } catch (const std::invalid_argument &exp) { @@ -532,7 +493,7 @@ class SimulatorHelper { static std::vector> simulate_expectation_values( const py::dict &options, const std::vector>, + std::vector>, unsigned>>& opsums_and_qubit_counts, bool is_noisy, const StateType& input_state) { auto helper = SimulatorHelper(options, is_noisy); @@ -570,7 +531,7 @@ class SimulatorHelper { simulate_moment_expectation_values( const py::dict &options, const std::vector>, + std::tuple>, unsigned>>>>& opsums_and_qubit_counts, bool is_noisy, const StateType& input_state) { auto helper = SimulatorHelper(options, is_noisy); @@ -640,7 +601,7 @@ class SimulatorHelper { try { if (is_noisy) { - ncircuit = getNoisyCircuit(options); + ncircuit = getCircuit(options); num_qubits = ncircuit.num_qubits; noisy_reps = ParseOptions(options, "r\0"); } else { @@ -730,18 +691,18 @@ class SimulatorHelper { Simulator simulator = factory.CreateSimulator(); StateSpace state_space = factory.CreateStateSpace(); - result = NoisyRunner::RunOnce( + result = NoisyRunner::RunOnce( params, ncircuit.num_qubits, - ncircuit.channels.begin() + begin, - ncircuit.channels.begin() + end, + ncircuit.ops.begin() + begin, + ncircuit.ops.begin() + end, seed, state_space, simulator, state, stat ); } else { - Circuit subcircuit; + Circuit subcircuit; subcircuit.num_qubits = circuit.num_qubits; - subcircuit.gates = std::vector( - circuit.gates.begin() + begin, - circuit.gates.begin() + end + subcircuit.ops = std::vector( + circuit.ops.begin() + begin, + circuit.ops.begin() + end ); result = Runner::Run(get_params(), factory, subcircuit, state); } @@ -764,11 +725,11 @@ class SimulatorHelper { } std::vector> get_expectation_value( - const std::vector>, + const std::vector>, unsigned>>& opsums_and_qubit_counts) { Simulator simulator = factory.CreateSimulator(); StateSpace state_space = factory.CreateStateSpace(); - using Fuser = MultiQubitGateFuser; + using Fuser = MultiQubitGateFuser; std::vector> results; results.reserve(opsums_and_qubit_counts.size()); @@ -797,8 +758,8 @@ class SimulatorHelper { bool is_noisy; // Only one of these will be populated, as specified by is_noisy. - Circuit circuit; - NoisyCircuit ncircuit; + Circuit circuit; + Circuit ncircuit; Factory factory; State state; @@ -841,7 +802,7 @@ py::array_t qtrajectory_simulate_fullstate( std::vector> qsim_simulate_expectation_values( const py::dict &options, const std::vector>, + std::vector>, unsigned>>& opsums_and_qubit_counts, uint64_t input_state) { return SimulatorHelper::simulate_expectation_values( @@ -851,7 +812,7 @@ std::vector> qsim_simulate_expectation_values( std::vector> qsim_simulate_expectation_values( const py::dict &options, const std::vector>, + std::vector>, unsigned>>& opsums_and_qubit_counts, const py::array_t &input_vector) { return SimulatorHelper::simulate_expectation_values( @@ -862,7 +823,7 @@ std::vector>> qsim_simulate_moment_expectation_values( const py::dict &options, const std::vector>, unsigned> + std::tuple>, unsigned> >>>& opsums_and_qubit_counts, uint64_t input_state) { return SimulatorHelper::simulate_moment_expectation_values( @@ -873,7 +834,7 @@ std::vector>> qsim_simulate_moment_expectation_values( const py::dict &options, const std::vector>, unsigned> + std::tuple>, unsigned> >>>& opsums_and_qubit_counts, const py::array_t &input_vector) { return SimulatorHelper::simulate_moment_expectation_values( @@ -883,7 +844,7 @@ qsim_simulate_moment_expectation_values( std::vector> qtrajectory_simulate_expectation_values( const py::dict &options, const std::vector>, + std::vector>, unsigned>>& opsums_and_qubit_counts, uint64_t input_state) { return SimulatorHelper::simulate_expectation_values( @@ -893,7 +854,7 @@ std::vector> qtrajectory_simulate_expectation_values( std::vector> qtrajectory_simulate_expectation_values( const py::dict &options, const std::vector>, + std::vector>, unsigned>>& opsums_and_qubit_counts, const py::array_t &input_vector) { return SimulatorHelper::simulate_expectation_values( @@ -904,7 +865,7 @@ std::vector>> qtrajectory_simulate_moment_expectation_values( const py::dict &options, const std::vector>, unsigned> + std::tuple>, unsigned> >>>& opsums_and_qubit_counts, uint64_t input_state) { return SimulatorHelper::simulate_moment_expectation_values( @@ -915,7 +876,7 @@ std::vector>> qtrajectory_simulate_moment_expectation_values( const py::dict &options, const std::vector>, unsigned> + std::tuple>, unsigned> >>>& opsums_and_qubit_counts, const py::array_t &input_vector) { return SimulatorHelper::simulate_moment_expectation_values( @@ -935,7 +896,7 @@ std::vector qtrajectory_sample_final( } std::vector qsim_sample(const py::dict &options) { - Circuit circuit; + Circuit circuit; try { circuit = getCircuit(options); } catch (const std::invalid_argument &exp) { @@ -991,9 +952,9 @@ std::vector qsim_sample(const py::dict &options) { } std::vector qtrajectory_sample(const py::dict &options) { - NoisyCircuit ncircuit; + Circuit ncircuit; try { - ncircuit = getNoisyCircuit(options); + ncircuit = getCircuit(options); } catch (const std::invalid_argument &exp) { IO::errorf("%s", exp.what()); return {}; @@ -1029,13 +990,13 @@ std::vector qtrajectory_sample(const py::dict &options) { // Converts stat (which matches the MeasurementResult 'bits' field) into // bitstrings matching the MeasurementResult 'bitstring' field. unsigned idx = 0; - for (const auto& channel : ncircuit.channels) { - if (channel[0].kind != gate::kMeasurement) - continue; - for (const auto& op : channel[0].ops) { + for (const auto& op : ncircuit.ops) { + if (const auto* pg = OpGetAlternative(op)) { std::vector bitstring; + bitstring.reserve(pg->qubits.size()); + uint64_t val = stat.samples[idx]; - for (const auto& q : op.qubits) { + for (const auto& q : pg->qubits) { bitstring.push_back((val >> q) & 1); } results.push_back(bitstring); @@ -1073,11 +1034,10 @@ std::vector qtrajectory_sample(const py::dict &options) { // Method for running the hybrid simulator. std::vector> qsimh_simulate(const py::dict &options) { - using HybridSimulator = HybridSimulator; - using Runner = QSimHRunner; + using HybridSimulator = HybridSimulator; + using Runner = QSimHRunner, HybridSimulator>; - Circuit circuit; + Circuit circuit; std::vector bitstrings; Runner::Parameter param; py::list dense_parts; diff --git a/pybind_interface/pybind_main.h b/pybind_interface/pybind_main.h index 325b9a91e..92a98c942 100644 --- a/pybind_interface/pybind_main.h +++ b/pybind_interface/pybind_main.h @@ -29,64 +29,46 @@ namespace py = pybind11; #include "../lib/circuit.h" #include "../lib/expect.h" #include "../lib/gates_cirq.h" +#include "../lib/operation.h" #include "../lib/qtrajectory.h" // Methods for mutating noiseless circuits. void add_gate(const qsim::Cirq::GateKind gate_kind, const unsigned time, const std::vector& qubits, const std::map& params, - qsim::Circuit>* circuit); + qsim::Circuit>* circuit); void add_diagonal_gate(const unsigned time, const std::vector& qubits, const std::vector& angles, - qsim::Circuit>* circuit); + qsim::Circuit>* circuit); void add_matrix_gate(const unsigned time, const std::vector& qubits, const std::vector& matrix, - qsim::Circuit>* circuit); + qsim::Circuit>* circuit); + +void add_measurement(const unsigned time, const std::vector& qubits, + qsim::Circuit>* circuit); void control_last_gate(const std::vector& qubits, const std::vector& values, - qsim::Circuit>* circuit); - -// Methods for mutating noisy circuits. -void add_gate_channel( - const qsim::Cirq::GateKind gate_kind, - const unsigned time, - const std::vector& qubits, - const std::map& params, - qsim::NoisyCircuit>* ncircuit); - -void add_diagonal_gate_channel( - const unsigned time, const std::vector& qubits, - const std::vector& angles, - qsim::NoisyCircuit>* ncircuit); - -void add_matrix_gate_channel( - const unsigned time, const std::vector& qubits, - const std::vector& matrix, - qsim::NoisyCircuit>* ncircuit); - -void control_last_gate_channel( - const std::vector& qubits, const std::vector& values, - qsim::NoisyCircuit>* ncircuit); + qsim::Circuit>* circuit); void add_channel(const unsigned time, const std::vector& qubits, const std::vector, bool>>& prob_matrix_unitary_triples, - qsim::NoisyCircuit>* ncircuit); + qsim::Circuit>* ncircuit); // Methods for populating opstrings. void add_gate_to_opstring( const qsim::Cirq::GateKind gate_kind, const std::vector& qubits, - qsim::OpString>* opstring); + qsim::OpString* opstring); void add_matrix_gate_to_opstring( const std::vector& qubits, const std::vector& matrix, - qsim::OpString>* opstring); + qsim::OpString* opstring); // Methods for simulating noiseless circuits. std::vector> qsim_simulate(const py::dict &options); @@ -116,15 +98,13 @@ std::vector qtrajectory_sample_final( std::vector> qsim_simulate_expectation_values( const py::dict &options, const std::vector>>, + std::vector>, unsigned>>& opsums_and_qubit_counts, uint64_t input_state); std::vector> qsim_simulate_expectation_values( const py::dict &options, const std::vector>>, + std::vector>, unsigned>>& opsums_and_qubit_counts, const py::array_t &input_vector); std::vector>> @@ -132,7 +112,7 @@ qsim_simulate_moment_expectation_values( const py::dict &options, const std::vector>>, + std::vector>, unsigned >>>>& opsums_and_qubit_counts, uint64_t input_state); @@ -141,22 +121,20 @@ qsim_simulate_moment_expectation_values( const py::dict &options, const std::vector>>, + std::vector>, unsigned >>>>& opsums_and_qubit_counts, const py::array_t &input_vector); std::vector> qtrajectory_simulate_expectation_values( const py::dict &options, const std::vector>>, + std::vector>, unsigned>>& opsums_and_qubit_counts, uint64_t input_state); std::vector> qtrajectory_simulate_expectation_values( const py::dict &options, const std::vector>>, + std::vector>, unsigned>>& opsums_and_qubit_counts, const py::array_t &input_vector); std::vector>> @@ -164,7 +142,7 @@ qtrajectory_simulate_moment_expectation_values( const py::dict &options, const std::vector>>, + std::vector>, unsigned >>>>& opsums_and_qubit_counts, uint64_t input_state); @@ -173,7 +151,7 @@ qtrajectory_simulate_moment_expectation_values( const py::dict &options, const std::vector>>, + std::vector>, unsigned >>>>& opsums_and_qubit_counts, const py::array_t &input_vector); @@ -228,8 +206,7 @@ T ParseOptions(const py::dict& options, const char* key) { m.def("qtrajectory_sample_final", &qtrajectory_sample_final, \ "Call the qtrajectory final-state sampler"); \ \ - using GateCirq = qsim::Cirq::GateCirq; \ - using OpString = qsim::OpString; \ + using OpString = qsim::OpString; \ \ /* Methods for returning expectation values */ \ m.def("qsim_simulate_expectation_values", \ @@ -306,24 +283,23 @@ T ParseOptions(const py::dict& options, const char* key) { m.def("qsimh_simulate", &qsimh_simulate, "Call the qsimh simulator"); \ \ using GateKind = qsim::Cirq::GateKind; \ - using Circuit = qsim::Circuit; \ - using NoisyCircuit = qsim::NoisyCircuit; \ + using OtherGateKind = qsim::OtherGateKind; \ + using Circuit = qsim::Circuit>; \ \ py::class_(m, "Circuit") \ .def(py::init<>()) \ .def_readwrite("num_qubits", &Circuit::num_qubits) \ - .def_readwrite("gates", &Circuit::gates); \ - \ - py::class_(m, "NoisyCircuit") \ - .def(py::init<>()) \ - .def_readwrite("num_qubits", &NoisyCircuit::num_qubits) \ - .def_readwrite("channels", &NoisyCircuit::channels); \ + .def_readwrite("ops", &Circuit::ops); \ \ py::class_(m, "OpString") \ .def(py::init<>()) \ .def_readwrite("weight", &OpString::weight) \ .def_readwrite("ops", &OpString::ops); \ \ + py::enum_(m, "OtherGateKind") \ + .value("kMeasurement", OtherGateKind::kMeasurement) \ + .export_values(); \ + \ py::enum_(m, "GateKind") \ .value("kI1", GateKind::kI1) \ .value("kI2", GateKind::kI2) \ @@ -369,7 +345,6 @@ T ParseOptions(const py::dict& options, const char* key) { .value("kCCZ", GateKind::kCCZ) \ .value("kCCX", GateKind::kCCX) \ .value("kMatrixGate", GateKind::kMatrixGate) \ - .value("kMeasurement", GateKind::kMeasurement) \ .export_values(); \ \ m.def("add_gate", &add_gate, "Adds a gate to the given circuit."); \ @@ -377,18 +352,11 @@ T ParseOptions(const py::dict& options, const char* key) { "Adds a two- or three-qubit diagonal gate to the given circuit."); \ m.def("add_matrix_gate", &add_matrix_gate, \ "Adds a matrix-defined gate to the given circuit."); \ + m.def("add_measurement", &add_measurement, \ + "Adds a measurement gate to the given circuit."); \ m.def("control_last_gate", &control_last_gate, \ "Applies controls to the final gate of a circuit."); \ \ - m.def("add_gate_channel", &add_gate_channel, \ - "Adds a gate to the given noisy circuit."); \ - m.def("add_diagonal_gate_channel", &add_diagonal_gate_channel, \ - "Adds a two- or three-qubit diagonal gate to the given noisy circuit."); \ - m.def("add_matrix_gate_channel", &add_matrix_gate_channel, \ - "Adds a matrix-defined gate to the given noisy circuit."); \ - m.def("control_last_gate_channel", &control_last_gate_channel, \ - "Applies controls to the final channel of a noisy circuit."); \ - \ m.def("add_channel", &add_channel, \ "Adds a channel to the given noisy circuit."); \ \ @@ -397,7 +365,7 @@ T ParseOptions(const py::dict& options, const char* key) { m.def("add_matrix_gate_to_opstring", &add_matrix_gate_to_opstring, \ "Adds a matrix gate to the given opstring."); -#define GPU_MODULE_BINDINGS \ +#define GPU_MODULE_BINDINGS \ m.doc() = "pybind11 plugin"; /* optional module docstring */ \ /* Methods for returning amplitudes */ \ m.def("qsim_simulate", &qsim_simulate, "Call the qsim simulator"); \ @@ -435,7 +403,7 @@ T ParseOptions(const py::dict& options, const char* key) { "Call the qtrajectory final-state sampler"); \ \ using GateCirq = qsim::Cirq::GateCirq; \ - using OpString = qsim::OpString; \ + using OpString = qsim::OpString; \ \ /* Methods for returning expectation values */ \ m.def("qsim_simulate_expectation_values", \ diff --git a/pybind_interface/sse/pybind_main_sse.cpp b/pybind_interface/sse/pybind_main_sse.cpp index 21287d31f..cf4202eeb 100644 --- a/pybind_interface/sse/pybind_main_sse.cpp +++ b/pybind_interface/sse/pybind_main_sse.cpp @@ -35,11 +35,10 @@ namespace qsim { using StateSpace = Simulator::StateSpace; using Gate = Cirq::GateCirq; - using Runner = QSimRunner, Factory>; - using RunnerQT = - QSimRunner, Factory>; + using Operation = qsim::Operation; + using Runner = QSimRunner, Factory>; using RunnerParameter = Runner::Parameter; - using NoisyRunner = qsim::QuantumTrajectorySimulator; + using NoisyRunner = qsim::QuantumTrajectorySimulator; using NoisyRunnerParameter = NoisyRunner::Parameter; StateSpace CreateStateSpace() const { diff --git a/qsimcirq/qsim_circuit.py b/qsimcirq/qsim_circuit.py index a5b4ae277..642ac996d 100644 --- a/qsimcirq/qsim_circuit.py +++ b/qsimcirq/qsim_circuit.py @@ -266,7 +266,7 @@ def add_op_to_circuit( qsim_op: cirq.GateOperation, time: int, qubit_to_index_dict: Dict[cirq.Qid, int], - circuit: Union[qsim.Circuit, qsim.NoisyCircuit], + circuit: qsim.Circuit, ): """Adds an operation to a noisy or noiseless circuit.""" qsim_gate = qsim_op.gate @@ -297,22 +297,16 @@ def add_op_to_circuit( gate_kind == qsim.kTwoQubitDiagonalGate or gate_kind == qsim.kThreeQubitDiagonalGate ): - if isinstance(circuit, qsim.Circuit): - qsim.add_diagonal_gate( - time, qsim_qubits, qsim_gate._diag_angles_radians, circuit - ) - else: - qsim.add_diagonal_gate_channel( - time, qsim_qubits, qsim_gate._diag_angles_radians, circuit - ) + qsim.add_diagonal_gate( + time, qsim_qubits, qsim_gate._diag_angles_radians, circuit + ) elif gate_kind == qsim.kMatrixGate: m = [ val for i in list(cirq.unitary(qsim_gate).flat) for val in [i.real, i.imag] ] - if isinstance(circuit, qsim.Circuit): - qsim.add_matrix_gate(time, qsim_qubits, m, circuit) - else: - qsim.add_matrix_gate_channel(time, qsim_qubits, m, circuit) + qsim.add_matrix_gate(time, qsim_qubits, m, circuit) + elif gate_kind == qsim.kMeasurement: + qsim.add_measurement(time, qsim_qubits, circuit) else: params = {} for p, val in vars(qsim_gate).items(): @@ -323,16 +317,10 @@ def add_op_to_circuit( params[key] = val else: raise ValueError("Parameters must be numeric.") - if isinstance(circuit, qsim.Circuit): - qsim.add_gate(gate_kind, time, qsim_qubits, params, circuit) - else: - qsim.add_gate_channel(gate_kind, time, qsim_qubits, params, circuit) + qsim.add_gate(gate_kind, time, qsim_qubits, params, circuit) if is_controlled: - if isinstance(circuit, qsim.Circuit): - qsim.control_last_gate(control_qubits, control_values, circuit) - else: - qsim.control_last_gate_channel(control_qubits, control_values, circuit) + qsim.control_last_gate(control_qubits, control_values, circuit) class QSimCircuit(cirq.Circuit): @@ -435,13 +423,13 @@ def to_matrix(op: cirq.GateOperation): def translate_cirq_to_qtrajectory( self, qubit_order: cirq.QubitOrderOrList = cirq.QubitOrder.DEFAULT - ) -> qsim.NoisyCircuit: + ) -> qsim.Circuit: """Translates this noisy Cirq circuit to the qsim representation. :qubit_order: Ordering of qubits - :return: a tuple of (C++ qsim NoisyCircuit object, moment boundary + :return: a tuple of (C++ qsim Circuit object, moment boundary gate indices) """ - qsim_ncircuit = qsim.NoisyCircuit() + qsim_ncircuit = qsim.Circuit() ordered_qubits = cirq.QubitOrder.as_qubit_order(qubit_order).order_for( self.all_qubits() ) diff --git a/qsimcirq_tests/qsimcirq_test.py b/qsimcirq_tests/qsimcirq_test.py index 85e1ac443..9f9e69837 100644 --- a/qsimcirq_tests/qsimcirq_test.py +++ b/qsimcirq_tests/qsimcirq_test.py @@ -214,7 +214,7 @@ def test_translate_cirq_to_qtrajectory(): qsim_circuit = qsimcirq.QSimCircuit(circuit) qsim_ncircuit, moment_indices = qsim_circuit.translate_cirq_to_qtrajectory() - assert isinstance(qsim_ncircuit, qsimcirq.qsim.NoisyCircuit) + assert isinstance(qsim_ncircuit, qsimcirq.qsim.Circuit) assert qsim_ncircuit.num_qubits == 2 # The circuit has 3 moments, and 4 gates are translated in total. assert moment_indices == [1, 2, 4] @@ -226,7 +226,7 @@ def test_translate_cirq_to_qtrajectory(): qsim_circuit_empty.translate_cirq_to_qtrajectory() ) - assert isinstance(qsim_ncircuit_empty, qsimcirq.qsim.NoisyCircuit) + assert isinstance(qsim_ncircuit_empty, qsimcirq.qsim.Circuit) assert qsim_ncircuit_empty.num_qubits == 0 assert moment_indices_empty == [] @@ -237,7 +237,7 @@ def test_translate_cirq_to_qtrajectory(): qsim_circuit_unitary.translate_cirq_to_qtrajectory() ) - assert isinstance(qsim_ncircuit_unitary, qsimcirq.qsim.NoisyCircuit) + assert isinstance(qsim_ncircuit_unitary, qsimcirq.qsim.Circuit) assert qsim_ncircuit_unitary.num_qubits == 2 assert moment_indices_unitary == [2] diff --git a/tests/BUILD b/tests/BUILD index 2473cc08c..93a6a2932 100644 --- a/tests/BUILD +++ b/tests/BUILD @@ -82,10 +82,12 @@ cc_test( deps = [ "//lib:channels_cirq", "//lib:circuit", + "//lib:circuit_noisy", "//lib:formux", "//lib:fuser_mqubit", "//lib:gates_cirq", "//lib:io", + "//lib:operation", "//lib:qtrajectory", "//lib:run_qsim", "//lib:simulator", @@ -103,7 +105,9 @@ cc_test( }), deps = [ "//lib:circuit_qsim_parser", - "//lib:gates_qsim", + "//lib:gate", + "//lib:operation", + "//lib:operation_base", "@com_google_googletest//:gtest_main", ], ) @@ -128,6 +132,18 @@ cc_test( ], ) +cc_library( + name = "fuser_testfixture", + testonly = 1, + hdrs = ["fuser_testfixture.h"], + deps = [ + "//lib:gate", + "//lib:operation", + "//lib:operation_base", + "@com_google_googletest//:gtest_main", + ], +) + cc_test( name = "fuser_basic_test", size = "small", @@ -137,9 +153,12 @@ cc_test( "//conditions:default": [], }), deps = [ + ":fuser_testfixture", "//lib:circuit_qsim_parser", "//lib:fuser_basic", - "//lib:gates_qsim", + "//lib:gate", + "//lib:operation", + "//lib:operation_base", "@com_google_googletest//:gtest_main", ], ) @@ -153,11 +172,15 @@ cc_test( "//conditions:default": [], }), deps = [ + ":fuser_testfixture", "//lib:formux", + "//lib:fuser", "//lib:fuser_mqubit", "//lib:gate", "//lib:gate_appl", "//lib:matrix", + "//lib:operation", + "//lib:operation_base", "//lib:simulator", "@com_google_googletest//:gtest_main", ], @@ -170,6 +193,7 @@ cc_library( deps = [ "//lib:circuit", "//lib:gates_cirq", + "//lib:operation", "@com_google_googletest//:gtest_main", ], ) @@ -183,6 +207,7 @@ cc_test( "//conditions:default": [], }), deps = [ + "//lib:gate", "//lib:gates_qsim", "@com_google_googletest//:gtest_main", ], @@ -200,9 +225,9 @@ cc_library( "//lib:circuit_qsim_parser", "//lib:formux", "//lib:fuser_basic", - "//lib:gates_qsim", "//lib:hybrid", "//lib:io", + "//lib:operation", "@com_google_googletest//:gtest_main", ], ) @@ -238,6 +263,22 @@ cc_test( ], ) +cc_test( + name = "operation_test", + size = "small", + srcs = ["operation_test.cc"], + copts = select({ + ":windows": windows_copts, + "//conditions:default": [], + }), + deps = [ + "//lib:gate", + "//lib:operation", + "//lib:operation_base", + "@com_google_googletest//:gtest_main", + ], +) + cc_library( name = "qtrajectory_testfixture", testonly = 1, @@ -249,12 +290,16 @@ cc_library( deps = [ "//lib:channel", "//lib:channels_cirq", + "//lib:circuit", "//lib:circuit_noisy", "//lib:expect", "//lib:fuser_mqubit", + "//lib:gate", "//lib:gate_appl", "//lib:gates_cirq", "//lib:io", + "//lib:operation", + "//lib:operation_base", "//lib:qtrajectory", "@com_google_googletest//:gtest_main", ], @@ -291,7 +336,13 @@ cc_test( }), deps = [ ":gates_cirq_testfixture", - "//lib:run_qsim_lib", + "//lib:circuit_qsim_parser", + "//lib:formux", + "//lib:fuser_basic", + "//lib:io", + "//lib:operation", + "//lib:run_qsim", + "//lib:simulator", "@com_google_googletest//:gtest_main", ], ) @@ -306,7 +357,13 @@ cc_test( }), deps = [ ":gates_cirq_testfixture", - "//lib:run_qsimh_lib", + "//lib:circuit_qsim_parser", + "//lib:formux", + "//lib:fuser_basic", + "//lib:io", + "//lib:operation", + "//lib:run_qsimh", + "//lib:simulator_basic", "@com_google_googletest//:gtest_main", ], ) @@ -321,10 +378,12 @@ cc_library( }), deps = [ "//lib:expect", + "//lib:fuser", "//lib:fuser_mqubit", "//lib:gate_appl", "//lib:gates_qsim", "//lib:io", + "//lib:operation", "//lib:util_cpu", "@com_google_googletest//:gtest_main", ], @@ -414,6 +473,7 @@ cc_library( "//lib:fuser_basic", "//lib:gates_qsim", "//lib:io", + "//lib:operation", "//lib:run_qsim", "@com_google_googletest//:gtest_main", ], @@ -584,6 +644,7 @@ cc_library( }), deps = [ "//lib:fuser", + "//lib:gate", "//lib:gate_appl", "//lib:gates_cirq", "@com_google_googletest//:gtest_main", @@ -701,9 +762,10 @@ cc_test( }), deps = [ "//lib:formux", + "//lib:fuser", + "//lib:gate", "//lib:gate_appl", "//lib:gates_cirq", - "//lib:gates_qsim", "//lib:mps_simulator", "@com_google_googletest//:gtest_main", ], diff --git a/tests/channel_test.cc b/tests/channel_test.cc index 15b2307eb..b619a0633 100644 --- a/tests/channel_test.cc +++ b/tests/channel_test.cc @@ -82,142 +82,111 @@ void TestNonUnitaryKrausOparator(const KrausOperator& kop, TEST(ChannelTest, UnitaryKdKMatrix) { using fp_type = Simulator::fp_type; - using Gate = Cirq::GateCirq; - auto normal = KrausOperator::kNormal; - - Channel channel = { - { - normal, 1, 0.2, { - Cirq::FSimGate::Create(0, 3, 4, 0.1, 1.4), - } - }, - { - normal, 1, 0.2, { - Cirq::rx::Create(0, 0, 0.1), - Cirq::ry::Create(0, 1, 0.2), - Cirq::FSimGate::Create(1, 0, 1, 0.2, 1.3), - } - }, - { - normal, 1, 0.2, { - Cirq::rz::Create(0, 3, 0.3), - Cirq::rx::Create(0, 1, 0.4), - Cirq::ry::Create(0, 4, 0.5), - Cirq::rz::Create(0, 0, 0.6), - } - }, + Channel channel = { + {kChannel, 0, {0, 1, 2, 3, 4, 5, 6, 7}}, { - normal, 1, 0.2, { - Cirq::rx::Create(0, 4, 0.7), - Cirq::ry::Create(0, 3, 0.8), - Cirq::rz::Create(0, 1, 0.9), - Cirq::rx::Create(0, 0, 1.0), - Cirq::FSimGate::Create(1, 1, 3, 0.3, 1.2), - Cirq::FSimGate::Create(1, 0, 4, 0.4, 1.1), - } - }, - { - normal, 1, 0.2, { - Cirq::ry::Create(0, 7, 1.1), - Cirq::rz::Create(0, 5, 1.2), - Cirq::rx::Create(0, 1, 1.3), - Cirq::ry::Create(0, 3, 1.4), - Cirq::rz::Create(0, 2, 1.5), - Cirq::rx::Create(0, 4, 1.6), - Cirq::FSimGate::Create(1, 4, 5, 0.5, 1.0), - Cirq::FSimGate::Create(1, 1, 3, 0.6, 0.9), - Cirq::FSimGate::Create(1, 2, 7, 0.7, 0.8), - } - }, + {true, 0.2, {Cirq::FSimGate::Create(0, 3, 4, 0.1, 1.4)}}, + {true, 0.2, {Cirq::rx::Create(0, 0, 0.1), + Cirq::ry::Create(0, 1, 0.2), + Cirq::FSimGate::Create(1, 0, 1, 0.2, 1.3)}}, + {true, 0.2, {Cirq::rz::Create(0, 3, 0.3), + Cirq::rx::Create(0, 1, 0.4), + Cirq::ry::Create(0, 4, 0.5), + Cirq::rz::Create(0, 0, 0.6)}}, + {true, 0.2, {Cirq::rx::Create(0, 4, 0.7), + Cirq::ry::Create(0, 3, 0.8), + Cirq::rz::Create(0, 1, 0.9), + Cirq::rx::Create(0, 0, 1.0), + Cirq::FSimGate::Create(1, 1, 3, 0.3, 1.2), + Cirq::FSimGate::Create(1, 0, 4, 0.4, 1.1)}}, + {true, 0.2, {Cirq::ry::Create(0, 7, 1.1), + Cirq::rz::Create(0, 5, 1.2), + Cirq::rx::Create(0, 1, 1.3), + Cirq::ry::Create(0, 3, 1.4), + Cirq::rz::Create(0, 2, 1.5), + Cirq::rx::Create(0, 4, 1.6), + Cirq::FSimGate::Create(1, 4, 5, 0.5, 1.0), + Cirq::FSimGate::Create(1, 1, 3, 0.6, 0.9), + Cirq::FSimGate::Create(1, 2, 7, 0.7, 0.8)}}, + } }; - channel[0].CalculateKdKMatrix(); - ASSERT_EQ(channel[0].kd_k.size(), 32); - ASSERT_EQ(channel[0].qubits.size(), 2); - EXPECT_EQ(channel[0].qubits[0], 3); - EXPECT_EQ(channel[0].qubits[1], 4); - TestUnitaryKrausOparator(channel[0]); - - channel[1].CalculateKdKMatrix(); - ASSERT_EQ(channel[1].kd_k.size(), 32); - ASSERT_EQ(channel[1].qubits.size(), 2); - EXPECT_EQ(channel[1].qubits[0], 0); - EXPECT_EQ(channel[1].qubits[1], 1); - TestUnitaryKrausOparator(channel[1]); - - channel[2].CalculateKdKMatrix(); - ASSERT_EQ(channel[2].kd_k.size(), 512); - ASSERT_EQ(channel[2].qubits.size(), 4); - EXPECT_EQ(channel[2].qubits[0], 0); - EXPECT_EQ(channel[2].qubits[1], 1); - EXPECT_EQ(channel[2].qubits[2], 3); - EXPECT_EQ(channel[2].qubits[3], 4); - TestUnitaryKrausOparator(channel[2]); - - channel[3].CalculateKdKMatrix(); - ASSERT_EQ(channel[3].kd_k.size(), 512); - ASSERT_EQ(channel[3].qubits.size(), 4); - EXPECT_EQ(channel[3].qubits[0], 0); - EXPECT_EQ(channel[3].qubits[1], 1); - EXPECT_EQ(channel[3].qubits[2], 3); - EXPECT_EQ(channel[3].qubits[3], 4); - TestUnitaryKrausOparator(channel[3]); - - channel[4].CalculateKdKMatrix(); - ASSERT_EQ(channel[4].kd_k.size(), 8192); - ASSERT_EQ(channel[4].qubits.size(), 6); - EXPECT_EQ(channel[4].qubits[0], 1); - EXPECT_EQ(channel[4].qubits[1], 2); - EXPECT_EQ(channel[4].qubits[2], 3); - EXPECT_EQ(channel[4].qubits[3], 4); - EXPECT_EQ(channel[4].qubits[4], 5); - EXPECT_EQ(channel[4].qubits[5], 7); - TestUnitaryKrausOparator(channel[4]); + channel.kops[0].CalculateKdKMatrix(); + ASSERT_EQ(channel.kops[0].kd_k.size(), 32); + ASSERT_EQ(channel.kops[0].qubits.size(), 2); + EXPECT_EQ(channel.kops[0].qubits[0], 3); + EXPECT_EQ(channel.kops[0].qubits[1], 4); + TestUnitaryKrausOparator(channel.kops[0]); + + channel.kops[1].CalculateKdKMatrix(); + ASSERT_EQ(channel.kops[1].kd_k.size(), 32); + ASSERT_EQ(channel.kops[1].qubits.size(), 2); + EXPECT_EQ(channel.kops[1].qubits[0], 0); + EXPECT_EQ(channel.kops[1].qubits[1], 1); + TestUnitaryKrausOparator(channel.kops[1]); + + channel.kops[2].CalculateKdKMatrix(); + ASSERT_EQ(channel.kops[2].kd_k.size(), 512); + ASSERT_EQ(channel.kops[2].qubits.size(), 4); + EXPECT_EQ(channel.kops[2].qubits[0], 0); + EXPECT_EQ(channel.kops[2].qubits[1], 1); + EXPECT_EQ(channel.kops[2].qubits[2], 3); + EXPECT_EQ(channel.kops[2].qubits[3], 4); + TestUnitaryKrausOparator(channel.kops[2]); + + channel.kops[3].CalculateKdKMatrix(); + ASSERT_EQ(channel.kops[3].kd_k.size(), 512); + ASSERT_EQ(channel.kops[3].qubits.size(), 4); + EXPECT_EQ(channel.kops[3].qubits[0], 0); + EXPECT_EQ(channel.kops[3].qubits[1], 1); + EXPECT_EQ(channel.kops[3].qubits[2], 3); + EXPECT_EQ(channel.kops[3].qubits[3], 4); + TestUnitaryKrausOparator(channel.kops[3]); + + channel.kops[4].CalculateKdKMatrix(); + ASSERT_EQ(channel.kops[4].kd_k.size(), 8192); + ASSERT_EQ(channel.kops[4].qubits.size(), 6); + EXPECT_EQ(channel.kops[4].qubits[0], 1); + EXPECT_EQ(channel.kops[4].qubits[1], 2); + EXPECT_EQ(channel.kops[4].qubits[2], 3); + EXPECT_EQ(channel.kops[4].qubits[3], 4); + EXPECT_EQ(channel.kops[4].qubits[4], 5); + EXPECT_EQ(channel.kops[4].qubits[5], 7); + TestUnitaryKrausOparator(channel.kops[4]); } TEST(ChannelTest, NonUnitaryKdKMatrix) { using StateSpace = Simulator::StateSpace; using State = StateSpace::State; using fp_type = StateSpace::fp_type; - using Gate = Cirq::GateCirq; using M1 = Cirq::MatrixGate1; using M2 = Cirq::MatrixGate2; - unsigned num_qubits = 8; - auto normal = KrausOperator::kNormal; + unsigned num_qubits = 8; - Channel channel = { + Channel channel = { + {kChannel, 0, {0, 1, 3, 4}}, { - normal, 0, 0, { - M1::Create(0, 0, - {0.1, 0.2, 0.3, 0.4, 0.1, 0.2, 0.3, 0.4}), - M1::Create(0, 1, - {0.2, 0.3, 0.4, 0.1, 0.2, 0.3, 0.4, 0.1}), - M2::Create(0, 0, 1, - {0.1, 0.2, 0.3, 0.4, 0.1, 0.2, 0.3, 0.4, - 0.2, 0.3, 0.4, 0.1, 0.2, 0.3, 0.4, 0.1, - 0.3, 0.4, 0.1, 0.2, 0.3, 0.4, 0.1, 0.2, - 0.4, 0.1, 0.2, 0.3, 0.4, 0.1, 0.2, 0.3}), - } - }, - { - normal, 0, 0, { - M1::Create(0, 4, - {0.1, 0.2, 0.3, 0.4, 0.1, 0.2, 0.3, 0.4}), - M1::Create(0, 3, - {0.2, 0.3, 0.4, 0.1, 0.2, 0.3, 0.4, 0.1}), - M1::Create(0, 1, - {0.3, 0.4, 0.1, 0.2, 0.3, 0.4, 0.1, 0.2}), - M1::Create(0, 0, - {0.4, 0.1, 0.2, 0.3, 0.4, 0.1, 0.2, 0.3}), - M2::Create(0, 0, 4, - {0.1, 0.2, 0.3, 0.4, 0.1, 0.2, 0.3, 0.4, - 0.2, 0.3, 0.4, 0.1, 0.2, 0.3, 0.4, 0.1, - 0.3, 0.4, 0.1, 0.2, 0.3, 0.4, 0.1, 0.2, - 0.4, 0.1, 0.2, 0.3, 0.4, 0.1, 0.2, 0.3}), - } - }, + {false, 0, {M1::Create(0, 0, {0.1, 0.2, 0.3, 0.4, 0.1, 0.2, 0.3, 0.4}), + M1::Create(0, 1, {0.2, 0.3, 0.4, 0.1, 0.2, 0.3, 0.4, 0.1}), + M2::Create(0, 0, 1, {0.1, 0.2, 0.3, 0.4, 0.1, 0.2, 0.3, 0.4, + 0.2, 0.3, 0.4, 0.1, 0.2, 0.3, 0.4, 0.1, + 0.3, 0.4, 0.1, 0.2, 0.3, 0.4, 0.1, 0.2, + 0.4, 0.1, 0.2, 0.3, 0.4, 0.1, 0.2, 0.3}) + } + }, + {false, 0, {M1::Create(0, 4, {0.1, 0.2, 0.3, 0.4, 0.1, 0.2, 0.3, 0.4}), + M1::Create(0, 3, {0.2, 0.3, 0.4, 0.1, 0.2, 0.3, 0.4, 0.1}), + M1::Create(0, 1, {0.3, 0.4, 0.1, 0.2, 0.3, 0.4, 0.1, 0.2}), + M1::Create(0, 0, {0.4, 0.1, 0.2, 0.3, 0.4, 0.1, 0.2, 0.3}), + M2::Create(0, 0, 4, {0.1, 0.2, 0.3, 0.4, 0.1, 0.2, 0.3, 0.4, + 0.2, 0.3, 0.4, 0.1, 0.2, 0.3, 0.4, 0.1, + 0.3, 0.4, 0.1, 0.2, 0.3, 0.4, 0.1, 0.2, + 0.4, 0.1, 0.2, 0.3, 0.4, 0.1, 0.2, 0.3}) + } + }, + } }; StateSpace state_space(1); @@ -229,23 +198,23 @@ TEST(ChannelTest, NonUnitaryKdKMatrix) { State state1 = state_space.Create(num_qubits); ASSERT_FALSE(state_space.IsNull(state1)); - channel[0].CalculateKdKMatrix(); - ASSERT_EQ(channel[0].kd_k.size(), 32); - ASSERT_EQ(channel[0].qubits.size(), 2); - EXPECT_EQ(channel[0].qubits[0], 0); - EXPECT_EQ(channel[0].qubits[1], 1); + channel.kops[0].CalculateKdKMatrix(); + ASSERT_EQ(channel.kops[0].kd_k.size(), 32); + ASSERT_EQ(channel.kops[0].qubits.size(), 2); + EXPECT_EQ(channel.kops[0].qubits[0], 0); + EXPECT_EQ(channel.kops[0].qubits[1], 1); TestNonUnitaryKrausOparator( - channel[0], state_space, simulator, state0, state1); - - channel[1].CalculateKdKMatrix(); - ASSERT_EQ(channel[1].kd_k.size(), 512); - ASSERT_EQ(channel[1].qubits.size(), 4); - EXPECT_EQ(channel[1].qubits[0], 0); - EXPECT_EQ(channel[1].qubits[1], 1); - EXPECT_EQ(channel[1].qubits[2], 3); - EXPECT_EQ(channel[1].qubits[3], 4); + channel.kops[0], state_space, simulator, state0, state1); + + channel.kops[1].CalculateKdKMatrix(); + ASSERT_EQ(channel.kops[1].kd_k.size(), 512); + ASSERT_EQ(channel.kops[1].qubits.size(), 4); + EXPECT_EQ(channel.kops[1].qubits[0], 0); + EXPECT_EQ(channel.kops[1].qubits[1], 1); + EXPECT_EQ(channel.kops[1].qubits[2], 3); + EXPECT_EQ(channel.kops[1].qubits[3], 4); TestNonUnitaryKrausOparator( - channel[1], state_space, simulator, state0, state1); + channel.kops[1], state_space, simulator, state0, state1); } } // namespace qsim diff --git a/tests/channels_cirq_test.cc b/tests/channels_cirq_test.cc index e82fcb3cf..680b669c4 100644 --- a/tests/channels_cirq_test.cc +++ b/tests/channels_cirq_test.cc @@ -20,10 +20,12 @@ #include "../lib/channels_cirq.h" #include "../lib/circuit.h" +#include "../lib/circuit_noisy.h" #include "../lib/formux.h" #include "../lib/fuser_mqubit.h" #include "../lib/gates_cirq.h" #include "../lib/io.h" +#include "../lib/operation.h" #include "../lib/qtrajectory.h" #include "../lib/run_qsim.h" #include "../lib/simmux.h" @@ -52,11 +54,12 @@ using StateSpace = Simulator::StateSpace; using State = StateSpace::State; using fp_type = StateSpace::fp_type; using Gate = Cirq::GateCirq; -using Fuser = MultiQubitGateFuser; +using Operation = Operation; +using Fuser = MultiQubitGateFuser; using Runner = QSimRunner>; -using QTSimulator = QuantumTrajectorySimulator; +using QTSimulator = QuantumTrajectorySimulator; -Circuit CleanCircuit() { +Circuit CleanCircuit() { using Hd = Cirq::H; using IS = Cirq::ISWAP; using Rx = Cirq::rx; @@ -74,12 +77,12 @@ Circuit CleanCircuit() { Ry::Create(4, 0, 0.4), Rx::Create(4, 1, 0.7), IS::Create(5, 0, 1), - gate::Measurement::Create(6, {0, 1}), + CreateMeasurement(6, {0, 1}), }, }; } -void RunBatch(const NoisyCircuit& ncircuit, +void RunBatch(const Circuit& ncircuit, const std::vector& expected_results, unsigned num_reps = 25000) { unsigned num_qubits = 2; @@ -109,23 +112,25 @@ void RunBatch(const NoisyCircuit& ncircuit, } template -inline NoisyCircuit MakeNoisy2( - const std::vector& gates, const ChannelFactory& channel_factory) { - NoisyCircuit ncircuit; +inline Circuit MakeNoisy2( + const std::vector& ops, const ChannelFactory& channel_factory) { + Circuit ncircuit; ncircuit.num_qubits = 2; - ncircuit.channels.reserve(2 * gates.size()); + ncircuit.ops.reserve(2 * ops.size()); unsigned prev_time = 0; - for (const auto& gate : gates) { - if (gate.time > prev_time) { - ncircuit.channels.push_back( - channel_factory.Create(2 * prev_time + 1, {0, 1})); - prev_time = gate.time; + for (const auto& op : ops) { + unsigned time = OpTime(op); + + if (time > prev_time) { + ncircuit.ops.push_back(channel_factory.Create(2 * prev_time + 1, {0, 1})); + prev_time = time; } - ncircuit.channels.push_back(MakeChannelFromGate(2 * gate.time, gate)); + ncircuit.ops.push_back(op); + OpBaseOperation(ncircuit.ops.back()).time *= 2; } return ncircuit; @@ -188,7 +193,7 @@ for key, val in sorted(res.histogram(key='m').items()): auto ncircuit = MakeNoisy(circuit, channel); RunBatch(ncircuit, expected_results); - auto ncircuit2 = MakeNoisy2(circuit.gates, channel); + auto ncircuit2 = MakeNoisy2(circuit.ops, channel); RunBatch(ncircuit2, expected_results); } @@ -234,7 +239,7 @@ for key, val in sorted(res.histogram(key='m').items()): auto ncircuit = MakeNoisy(circuit, channel); RunBatch(ncircuit, expected_results); - auto ncircuit2 = MakeNoisy2(circuit.gates, channel); + auto ncircuit2 = MakeNoisy2(circuit.ops, channel); RunBatch(ncircuit2, expected_results); } diff --git a/tests/circuit_qsim_parser_test.cc b/tests/circuit_qsim_parser_test.cc index 36b2cac47..b61db1869 100644 --- a/tests/circuit_qsim_parser_test.cc +++ b/tests/circuit_qsim_parser_test.cc @@ -17,7 +17,9 @@ #include "gtest/gtest.h" #include "../lib/circuit_qsim_parser.h" -#include "../lib/gates_qsim.h" +#include "../lib/gate.h" +#include "../lib/operation.h" +#include "../lib/operation_base.h" namespace qsim { @@ -57,18 +59,18 @@ R"(2 15 p 0.3 )"; - Circuit> circuit; + Circuit> circuit; std::stringstream ss1(valid_circuit); EXPECT_TRUE(CircuitQsimParser::FromStream(99, provider, ss1, circuit)); EXPECT_EQ(circuit.num_qubits, 2); - EXPECT_EQ(circuit.gates.size(), 22); + EXPECT_EQ(circuit.ops.size(), 22); std::stringstream ss2(valid_circuit); EXPECT_TRUE(CircuitQsimParser::FromStream(4, provider, ss2, circuit)); EXPECT_EQ(circuit.num_qubits, 2); - EXPECT_EQ(circuit.gates.size(), 10); + EXPECT_EQ(circuit.ops.size(), 10); } TEST(CircuitQsimParserTest, ValidCircuitWithControlledGates) { @@ -79,17 +81,25 @@ R"(5 2 c 2 4 p 0.5 )"; - Circuit> circuit; + using CG = ControlledGate; + + Circuit> circuit; std::stringstream ss(valid_circuit); EXPECT_TRUE(CircuitQsimParser::FromStream(99, provider, ss, circuit)); EXPECT_EQ(circuit.num_qubits, 5); - EXPECT_EQ(circuit.gates.size(), 3); - EXPECT_EQ(circuit.gates[0].qubits.size(), 1); - EXPECT_EQ(circuit.gates[0].controlled_by.size(), 2); - EXPECT_EQ(circuit.gates[1].qubits.size(), 2); - EXPECT_EQ(circuit.gates[1].controlled_by.size(), 3); - EXPECT_EQ(circuit.gates[2].qubits.size(), 0); - EXPECT_EQ(circuit.gates[2].controlled_by.size(), 2); + EXPECT_EQ(circuit.ops.size(), 3); + const auto* pg0 = OpGetAlternative(circuit.ops[0]); + const auto* pg1 = OpGetAlternative(circuit.ops[1]); + const auto* pg2 = OpGetAlternative(circuit.ops[2]); + ASSERT_NE(pg0, nullptr); + ASSERT_NE(pg1, nullptr); + ASSERT_NE(pg2, nullptr); + EXPECT_EQ(pg0->qubits.size(), 1); + EXPECT_EQ(pg0->controlled_by.size(), 2); + EXPECT_EQ(pg1->qubits.size(), 2); + EXPECT_EQ(pg1->controlled_by.size(), 3); + EXPECT_EQ(pg2->qubits.size(), 0); + EXPECT_EQ(pg2->controlled_by.size(), 2); } TEST(CircuitQsimParserTest, ValidTimeOrder) { @@ -111,11 +121,11 @@ R"(4 9 h 3 )"; - Circuit> circuit; + Circuit> circuit; std::stringstream ss(valid_circuit); EXPECT_TRUE(CircuitQsimParser::FromStream(99, provider, ss, circuit)); EXPECT_EQ(circuit.num_qubits, 4); - EXPECT_EQ(circuit.gates.size(), 14); + EXPECT_EQ(circuit.ops.size(), 14); } TEST(CircuitQsimParserTest, InvalidGateName) { @@ -126,7 +136,7 @@ R"(2 1 badgate 0)"; std::stringstream ss(invalid_circuit); - Circuit> circuit; + Circuit> circuit; EXPECT_FALSE(CircuitQsimParser::FromStream(99, provider, ss, circuit)); } @@ -139,7 +149,7 @@ R"(2 1 cz 0 1 )"; std::stringstream ss(invalid_circuit); - Circuit> circuit; + Circuit> circuit; EXPECT_FALSE(CircuitQsimParser::FromStream(99, provider, ss, circuit)); } @@ -152,7 +162,7 @@ R"(2 1 cz 0 1)"; std::stringstream ss(invalid_circuit); - Circuit> circuit; + Circuit> circuit; EXPECT_FALSE(CircuitQsimParser::FromStream(99, provider, ss, circuit)); } @@ -165,7 +175,7 @@ R"(2 1 cz 0 1)"; std::stringstream ss(invalid_circuit); - Circuit> circuit; + Circuit> circuit; EXPECT_FALSE(CircuitQsimParser::FromStream(99, provider, ss, circuit)); } @@ -178,7 +188,7 @@ R"(2 1 cz 0 1)"; std::stringstream ss(invalid_circuit); - Circuit> circuit; + Circuit> circuit; EXPECT_FALSE(CircuitQsimParser::FromStream(99, provider, ss, circuit)); } @@ -191,7 +201,7 @@ R"(2 1 cz 1 1)"; std::stringstream ss(invalid_circuit); - Circuit> circuit; + Circuit> circuit; EXPECT_FALSE(CircuitQsimParser::FromStream(99, provider, ss, circuit)); } @@ -204,7 +214,7 @@ R"(2 1 cz 0 1)"; std::stringstream ss(invalid_circuit); - Circuit> circuit; + Circuit> circuit; EXPECT_FALSE(CircuitQsimParser::FromStream(99, provider, ss, circuit)); } @@ -217,7 +227,7 @@ R"(2 1 cz 0)"; std::stringstream ss(invalid_circuit); - Circuit> circuit; + Circuit> circuit; EXPECT_FALSE(CircuitQsimParser::FromStream(99, provider, ss, circuit)); } @@ -231,7 +241,7 @@ R"(2 2 rx 0)"; std::stringstream ss(invalid_circuit); - Circuit> circuit; + Circuit> circuit; EXPECT_FALSE(CircuitQsimParser::FromStream(99, provider, ss, circuit)); } @@ -245,7 +255,7 @@ R"(2 2 ry 0)"; std::stringstream ss(invalid_circuit); - Circuit> circuit; + Circuit> circuit; EXPECT_FALSE(CircuitQsimParser::FromStream(99, provider, ss, circuit)); } @@ -259,7 +269,7 @@ R"(2 2 rz 0)"; std::stringstream ss(invalid_circuit); - Circuit> circuit; + Circuit> circuit; EXPECT_FALSE(CircuitQsimParser::FromStream(99, provider, ss, circuit)); } @@ -273,7 +283,7 @@ R"(2 2 rxy 0 0.7)"; std::stringstream ss(invalid_circuit); - Circuit> circuit; + Circuit> circuit; EXPECT_FALSE(CircuitQsimParser::FromStream(99, provider, ss, circuit)); } @@ -286,7 +296,7 @@ R"(2 1 fs 0 1 0.5)"; std::stringstream ss(invalid_circuit); - Circuit> circuit; + Circuit> circuit; EXPECT_FALSE(CircuitQsimParser::FromStream(99, provider, ss, circuit)); } @@ -299,7 +309,7 @@ R"(2 1 cp 0 1)"; std::stringstream ss(invalid_circuit); - Circuit> circuit; + Circuit> circuit; EXPECT_FALSE(CircuitQsimParser::FromStream(99, provider, ss, circuit)); } @@ -315,7 +325,7 @@ R"(2 1 cz 0 1)"; std::stringstream ss(invalid_circuit); - Circuit> circuit; + Circuit> circuit; EXPECT_FALSE(CircuitQsimParser::FromStream(99, provider, ss, circuit)); } @@ -328,7 +338,7 @@ R"(2 1 m 0 2)"; std::stringstream ss(invalid_circuit); - Circuit> circuit; + Circuit> circuit; EXPECT_FALSE(CircuitQsimParser::FromStream(99, provider, ss, circuit)); } @@ -341,7 +351,7 @@ R"(2 1 m 0 i)"; std::stringstream ss(invalid_circuit); - Circuit> circuit; + Circuit> circuit; EXPECT_FALSE(CircuitQsimParser::FromStream(99, provider, ss, circuit)); } @@ -354,7 +364,7 @@ R"(2 1 m 0 )"; std::stringstream ss(invalid_circuit); - Circuit> circuit; + Circuit> circuit; EXPECT_FALSE(CircuitQsimParser::FromStream(99, provider, ss, circuit)); } @@ -367,7 +377,7 @@ R"(2 1 m 0 0)"; std::stringstream ss(invalid_circuit); - Circuit> circuit; + Circuit> circuit; EXPECT_FALSE(CircuitQsimParser::FromStream(99, provider, ss, circuit)); } @@ -379,7 +389,7 @@ R"(4 )"; std::stringstream ss(invalid_circuit); - Circuit> circuit; + Circuit> circuit; EXPECT_FALSE(CircuitQsimParser::FromStream(99, provider, ss, circuit)); } @@ -391,7 +401,7 @@ R"(4 )"; std::stringstream ss(invalid_circuit); - Circuit> circuit; + Circuit> circuit; EXPECT_FALSE(CircuitQsimParser::FromStream(99, provider, ss, circuit)); } @@ -403,7 +413,7 @@ R"(4 )"; std::stringstream ss(invalid_circuit); - Circuit> circuit; + Circuit> circuit; EXPECT_FALSE(CircuitQsimParser::FromStream(99, provider, ss, circuit)); } @@ -415,7 +425,7 @@ R"(4 )"; std::stringstream ss(invalid_circuit); - Circuit> circuit; + Circuit> circuit; EXPECT_FALSE(CircuitQsimParser::FromStream(99, provider, ss, circuit)); } @@ -427,7 +437,7 @@ R"(4 )"; std::stringstream ss(invalid_circuit); - Circuit> circuit; + Circuit> circuit; EXPECT_FALSE(CircuitQsimParser::FromStream(99, provider, ss, circuit)); } @@ -441,7 +451,7 @@ R"(4 )"; std::stringstream ss(invalid_circuit); - Circuit> circuit; + Circuit> circuit; EXPECT_FALSE(CircuitQsimParser::FromStream(99, provider, ss, circuit)); } @@ -455,7 +465,7 @@ R"(4 )"; std::stringstream ss(invalid_circuit); - Circuit> circuit; + Circuit> circuit; EXPECT_FALSE(CircuitQsimParser::FromStream(99, provider, ss, circuit)); } @@ -469,7 +479,7 @@ R"(4 )"; std::stringstream ss(invalid_circuit); - Circuit> circuit; + Circuit> circuit; EXPECT_FALSE(CircuitQsimParser::FromStream(99, provider, ss, circuit)); } @@ -484,7 +494,7 @@ R"(4 )"; std::stringstream ss(invalid_circuit); - Circuit> circuit; + Circuit> circuit; EXPECT_FALSE(CircuitQsimParser::FromStream(99, provider, ss, circuit)); } @@ -499,7 +509,7 @@ R"(4 )"; std::stringstream ss(invalid_circuit); - Circuit> circuit; + Circuit> circuit; EXPECT_FALSE(CircuitQsimParser::FromStream(99, provider, ss, circuit)); } diff --git a/tests/expect_test.cc b/tests/expect_test.cc index 45a4b813f..03378f175 100644 --- a/tests/expect_test.cc +++ b/tests/expect_test.cc @@ -31,7 +31,7 @@ TEST(ExpectTest, ExpectationValue) { using StateSpace = Simulator::StateSpace; using State = StateSpace::State; using fp_type = StateSpace::fp_type; - using Fuser = MultiQubitGateFuser>; + using Fuser = MultiQubitGateFuser; unsigned num_qubits = 16; unsigned depth = 16; @@ -97,7 +97,7 @@ TEST(ExpectTest, ExpectationValue) { State tmp_state = state_space.Null(); for (unsigned k = 1; k <= 6; ++k) { - std::vector>> strings; + std::vector> strings; strings.reserve(num_qubits); for (unsigned i = 0; i <= num_qubits - k; ++i) { @@ -139,7 +139,7 @@ TEST(ExpectTest, ExpectationValue) { double w_re = 0.7; double w_im = 0.3; - std::vector>> strings = {{{w_re, w_im}, {}}}; + std::vector> strings = {{{w_re, w_im}, {}}}; auto evala = ExpectationValue(param, strings, state_space, simulator, state, tmp_state); diff --git a/tests/fuser_basic_test.cc b/tests/fuser_basic_test.cc index 7da2e1d62..c6c233969 100644 --- a/tests/fuser_basic_test.cc +++ b/tests/fuser_basic_test.cc @@ -12,14 +12,18 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include #include +#include + +#include "fuser_testfixture.h" #include "gtest/gtest.h" #include "../lib/circuit_qsim_parser.h" #include "../lib/fuser_basic.h" -#include "../lib/gates_qsim.h" +#include "../lib/gate.h" +#include "../lib/operation.h" +#include "../lib/operation_base.h" namespace qsim { @@ -62,513 +66,717 @@ R"(4 )"; TEST(FuserBasicTest, NoTimesToSplitAt) { + using Gate = qsim::Gate; + using Operation = qsim::Operation; + using FusedGate = qsim::FusedGate; + std::stringstream ss(circuit_string1); - Circuit> circuit; + Circuit circuit; EXPECT_TRUE(CircuitQsimParser::FromStream(99, provider, ss, circuit)); EXPECT_EQ(circuit.num_qubits, 4); - EXPECT_EQ(circuit.gates.size(), 27); + EXPECT_EQ(circuit.ops.size(), 27); - using Fuser = BasicGateFuser>; + using Fuser = BasicGateFuser; Fuser::Parameter param; - auto fused_gates = Fuser::FuseGates(param, circuit.num_qubits, circuit.gates); + auto fused_gates = Fuser::FuseGates(param, circuit.num_qubits, circuit.ops); EXPECT_EQ(fused_gates.size(), 5); - EXPECT_EQ(fused_gates[0].kind, kGateCZ); - EXPECT_EQ(fused_gates[0].time, 1); - EXPECT_EQ(fused_gates[0].qubits.size(), 2); - EXPECT_EQ(fused_gates[0].qubits[0], 0); - EXPECT_EQ(fused_gates[0].qubits[1], 1); - EXPECT_EQ(fused_gates[0].gates.size(), 6); - EXPECT_EQ(fused_gates[0].gates[0]->kind, kGateHd); - EXPECT_EQ(fused_gates[0].gates[0]->time, 0); - EXPECT_EQ(fused_gates[0].gates[0]->qubits.size(), 1); - EXPECT_EQ(fused_gates[0].gates[0]->qubits[0], 0); - EXPECT_EQ(fused_gates[0].gates[1]->kind, kGateHd); - EXPECT_EQ(fused_gates[0].gates[1]->time, 0); - EXPECT_EQ(fused_gates[0].gates[1]->qubits.size(), 1); - EXPECT_EQ(fused_gates[0].gates[1]->qubits[0], 1); - EXPECT_EQ(fused_gates[0].gates[2]->kind, kGateCZ); - EXPECT_EQ(fused_gates[0].gates[2]->time, 1); - EXPECT_EQ(fused_gates[0].gates[2]->qubits.size(), 2); - EXPECT_EQ(fused_gates[0].gates[2]->qubits[0], 0); - EXPECT_EQ(fused_gates[0].gates[2]->qubits[1], 1); - EXPECT_EQ(fused_gates[0].gates[3]->kind, kGateT); - EXPECT_EQ(fused_gates[0].gates[3]->time, 2); - EXPECT_EQ(fused_gates[0].gates[3]->qubits.size(), 1); - EXPECT_EQ(fused_gates[0].gates[3]->qubits[0], 0); - EXPECT_EQ(fused_gates[0].gates[4]->kind, kGateY); - EXPECT_EQ(fused_gates[0].gates[4]->time, 3); - EXPECT_EQ(fused_gates[0].gates[4]->qubits.size(), 1); - EXPECT_EQ(fused_gates[0].gates[4]->qubits[0], 0); - EXPECT_EQ(fused_gates[0].gates[5]->kind, kGateX); - EXPECT_EQ(fused_gates[0].gates[5]->time, 2); - EXPECT_EQ(fused_gates[0].gates[5]->qubits.size(), 1); - EXPECT_EQ(fused_gates[0].gates[5]->qubits[0], 1); - - EXPECT_EQ(fused_gates[1].kind, kGateCZ); - EXPECT_EQ(fused_gates[1].time, 1); - EXPECT_EQ(fused_gates[1].qubits.size(), 2); - EXPECT_EQ(fused_gates[1].qubits[0], 2); - EXPECT_EQ(fused_gates[1].qubits[1], 3); - EXPECT_EQ(fused_gates[1].gates.size(), 6); - EXPECT_EQ(fused_gates[1].gates[0]->kind, kGateHd); - EXPECT_EQ(fused_gates[1].gates[0]->time, 0); - EXPECT_EQ(fused_gates[1].gates[0]->qubits.size(), 1); - EXPECT_EQ(fused_gates[1].gates[0]->qubits[0], 2); - EXPECT_EQ(fused_gates[1].gates[1]->kind, kGateHd); - EXPECT_EQ(fused_gates[1].gates[1]->time, 0); - EXPECT_EQ(fused_gates[1].gates[1]->qubits.size(), 1); - EXPECT_EQ(fused_gates[1].gates[1]->qubits[0], 3); - EXPECT_EQ(fused_gates[1].gates[2]->kind, kGateCZ); - EXPECT_EQ(fused_gates[1].gates[2]->time, 1); - EXPECT_EQ(fused_gates[1].gates[2]->qubits.size(), 2); - EXPECT_EQ(fused_gates[1].gates[2]->qubits[0], 2); - EXPECT_EQ(fused_gates[1].gates[2]->qubits[1], 3); - EXPECT_EQ(fused_gates[1].gates[3]->kind, kGateY); - EXPECT_EQ(fused_gates[1].gates[3]->time, 2); - EXPECT_EQ(fused_gates[1].gates[3]->qubits.size(), 1); - EXPECT_EQ(fused_gates[1].gates[3]->qubits[0], 2); - EXPECT_EQ(fused_gates[1].gates[4]->kind, kGateT); - EXPECT_EQ(fused_gates[1].gates[4]->time, 2); - EXPECT_EQ(fused_gates[1].gates[4]->qubits.size(), 1); - EXPECT_EQ(fused_gates[1].gates[4]->qubits[0], 3); - EXPECT_EQ(fused_gates[1].gates[5]->kind, kGateX); - EXPECT_EQ(fused_gates[1].gates[5]->time, 3); - EXPECT_EQ(fused_gates[1].gates[5]->qubits.size(), 1); - EXPECT_EQ(fused_gates[1].gates[5]->qubits[0], 3); - - EXPECT_EQ(fused_gates[2].kind, kGateCZ); - EXPECT_EQ(fused_gates[2].time, 3); - EXPECT_EQ(fused_gates[2].qubits.size(), 2); - EXPECT_EQ(fused_gates[2].qubits[0], 1); - EXPECT_EQ(fused_gates[2].qubits[1], 2); - EXPECT_EQ(fused_gates[2].gates.size(), 9); - EXPECT_EQ(fused_gates[2].gates[0]->kind, kGateCZ); - EXPECT_EQ(fused_gates[2].gates[0]->time, 3); - EXPECT_EQ(fused_gates[2].gates[0]->qubits.size(), 2); - EXPECT_EQ(fused_gates[2].gates[0]->qubits[0], 1); - EXPECT_EQ(fused_gates[2].gates[0]->qubits[1], 2); - EXPECT_EQ(fused_gates[2].gates[1]->kind, kGateT); - EXPECT_EQ(fused_gates[2].gates[1]->time, 4); - EXPECT_EQ(fused_gates[2].gates[1]->qubits.size(), 1); - EXPECT_EQ(fused_gates[2].gates[1]->qubits[0], 1); - EXPECT_EQ(fused_gates[2].gates[2]->kind, kGateT); - EXPECT_EQ(fused_gates[2].gates[2]->time, 4); - EXPECT_EQ(fused_gates[2].gates[2]->qubits.size(), 1); - EXPECT_EQ(fused_gates[2].gates[2]->qubits[0], 2); - EXPECT_EQ(fused_gates[2].gates[3]->kind, kGateCZ); - EXPECT_EQ(fused_gates[2].gates[3]->time, 5); - EXPECT_EQ(fused_gates[2].gates[3]->qubits.size(), 2); - EXPECT_EQ(fused_gates[2].gates[3]->qubits[0], 1); - EXPECT_EQ(fused_gates[2].gates[3]->qubits[1], 2); - EXPECT_EQ(fused_gates[2].gates[4]->kind, kGateX); - EXPECT_EQ(fused_gates[2].gates[4]->time, 6); - EXPECT_EQ(fused_gates[2].gates[4]->qubits.size(), 1); - EXPECT_EQ(fused_gates[2].gates[4]->qubits[0], 1); - EXPECT_EQ(fused_gates[2].gates[5]->kind, kGateY); - EXPECT_EQ(fused_gates[2].gates[5]->time, 6); - EXPECT_EQ(fused_gates[2].gates[5]->qubits.size(), 1); - EXPECT_EQ(fused_gates[2].gates[5]->qubits[0], 2); - EXPECT_EQ(fused_gates[2].gates[6]->kind, kGateCZ); - EXPECT_EQ(fused_gates[2].gates[6]->time, 7); - EXPECT_EQ(fused_gates[2].gates[6]->qubits.size(), 2); - EXPECT_EQ(fused_gates[2].gates[6]->qubits[0], 1); - EXPECT_EQ(fused_gates[2].gates[6]->qubits[1], 2); - EXPECT_EQ(fused_gates[2].gates[7]->kind, kGateT); - EXPECT_EQ(fused_gates[2].gates[7]->time, 8); - EXPECT_EQ(fused_gates[2].gates[7]->qubits.size(), 1); - EXPECT_EQ(fused_gates[2].gates[7]->qubits[0], 1); - EXPECT_EQ(fused_gates[2].gates[8]->kind, kGateT); - EXPECT_EQ(fused_gates[2].gates[8]->time, 8); - EXPECT_EQ(fused_gates[2].gates[8]->qubits.size(), 1); - EXPECT_EQ(fused_gates[2].gates[8]->qubits[0], 2); - - EXPECT_EQ(fused_gates[3].kind, kGateCZ); - EXPECT_EQ(fused_gates[3].time, 9); - EXPECT_EQ(fused_gates[3].qubits.size(), 2); - EXPECT_EQ(fused_gates[3].qubits[0], 0); - EXPECT_EQ(fused_gates[3].qubits[1], 1); - EXPECT_EQ(fused_gates[3].gates.size(), 3); - EXPECT_EQ(fused_gates[3].gates[0]->kind, kGateCZ); - EXPECT_EQ(fused_gates[3].gates[0]->time, 9); - EXPECT_EQ(fused_gates[3].gates[0]->qubits.size(), 2); - EXPECT_EQ(fused_gates[3].gates[0]->qubits[0], 0); - EXPECT_EQ(fused_gates[3].gates[0]->qubits[1], 1); - EXPECT_EQ(fused_gates[3].gates[1]->kind, kGateHd); - EXPECT_EQ(fused_gates[3].gates[1]->time, 10); - EXPECT_EQ(fused_gates[3].gates[1]->qubits.size(), 1); - EXPECT_EQ(fused_gates[3].gates[1]->qubits[0], 0); - EXPECT_EQ(fused_gates[3].gates[2]->kind, kGateHd); - EXPECT_EQ(fused_gates[3].gates[2]->time, 10); - EXPECT_EQ(fused_gates[3].gates[2]->qubits.size(), 1); - EXPECT_EQ(fused_gates[3].gates[2]->qubits[0], 1); - - EXPECT_EQ(fused_gates[4].kind, kGateCZ); - EXPECT_EQ(fused_gates[4].time, 9); - EXPECT_EQ(fused_gates[4].qubits.size(), 2); - EXPECT_EQ(fused_gates[4].qubits[0], 2); - EXPECT_EQ(fused_gates[4].qubits[1], 3); - EXPECT_EQ(fused_gates[4].gates.size(), 3); - EXPECT_EQ(fused_gates[4].gates[0]->kind, kGateCZ); - EXPECT_EQ(fused_gates[4].gates[0]->time, 9); - EXPECT_EQ(fused_gates[4].gates[0]->qubits.size(), 2); - EXPECT_EQ(fused_gates[4].gates[0]->qubits[0], 2); - EXPECT_EQ(fused_gates[4].gates[0]->qubits[1], 3); - EXPECT_EQ(fused_gates[4].gates[1]->kind, kGateHd); - EXPECT_EQ(fused_gates[4].gates[1]->time, 10); - EXPECT_EQ(fused_gates[4].gates[1]->qubits.size(), 1); - EXPECT_EQ(fused_gates[4].gates[1]->qubits[0], 2); - EXPECT_EQ(fused_gates[4].gates[2]->kind, kGateHd); - EXPECT_EQ(fused_gates[4].gates[2]->time, 10); - EXPECT_EQ(fused_gates[4].gates[2]->qubits.size(), 1); - EXPECT_EQ(fused_gates[4].gates[2]->qubits[0], 3); + const auto* fgate0 = OpGetAlternative(fused_gates[0]); + ASSERT_NE(fgate0, nullptr); + EXPECT_EQ(fgate0->kind, kGateCZ); + EXPECT_EQ(fgate0->time, 1); + EXPECT_EQ(fgate0->qubits.size(), 2); + EXPECT_EQ(fgate0->qubits[0], 0); + EXPECT_EQ(fgate0->qubits[1], 1); + EXPECT_EQ(fgate0->gates.size(), 6); + const auto* gate00 = OpGetAlternative(fgate0->gates[0]); + ASSERT_NE(gate00, nullptr); + EXPECT_EQ(gate00->kind, kGateHd); + EXPECT_EQ(gate00->time, 0); + EXPECT_EQ(gate00->qubits.size(), 1); + EXPECT_EQ(gate00->qubits[0], 0); + const auto* gate01 = OpGetAlternative(fgate0->gates[1]); + ASSERT_NE(gate01, nullptr); + EXPECT_EQ(gate01->kind, kGateHd); + EXPECT_EQ(gate01->time, 0); + EXPECT_EQ(gate01->qubits.size(), 1); + EXPECT_EQ(gate01->qubits[0], 1); + const auto* gate02 = OpGetAlternative(fgate0->gates[2]); + ASSERT_NE(gate02, nullptr); + EXPECT_EQ(gate02->kind, kGateCZ); + EXPECT_EQ(gate02->time, 1); + EXPECT_EQ(gate02->qubits.size(), 2); + EXPECT_EQ(gate02->qubits[0], 0); + EXPECT_EQ(gate02->qubits[1], 1); + const auto* gate03 = OpGetAlternative(fgate0->gates[3]); + ASSERT_NE(gate03, nullptr); + EXPECT_EQ(gate03->kind, kGateT); + EXPECT_EQ(gate03->time, 2); + EXPECT_EQ(gate03->qubits.size(), 1); + EXPECT_EQ(gate03->qubits[0], 0); + const auto* gate04 = OpGetAlternative(fgate0->gates[4]); + ASSERT_NE(gate04, nullptr); + EXPECT_EQ(gate04->kind, kGateY); + EXPECT_EQ(gate04->time, 3); + EXPECT_EQ(gate04->qubits.size(), 1); + EXPECT_EQ(gate04->qubits[0], 0); + const auto* gate05 = OpGetAlternative(fgate0->gates[5]); + ASSERT_NE(gate05, nullptr); + EXPECT_EQ(gate05->kind, kGateX); + EXPECT_EQ(gate05->time, 2); + EXPECT_EQ(gate05->qubits.size(), 1); + EXPECT_EQ(gate05->qubits[0], 1); + + const auto* fgate1 = OpGetAlternative(fused_gates[1]); + ASSERT_NE(fgate1, nullptr); + EXPECT_EQ(fgate1->kind, kGateCZ); + EXPECT_EQ(fgate1->time, 1); + EXPECT_EQ(fgate1->qubits.size(), 2); + EXPECT_EQ(fgate1->qubits[0], 2); + EXPECT_EQ(fgate1->qubits[1], 3); + EXPECT_EQ(fgate1->gates.size(), 6); + const auto* gate10 = OpGetAlternative(fgate1->gates[0]); + ASSERT_NE(gate10, nullptr); + EXPECT_EQ(gate10->kind, kGateHd); + EXPECT_EQ(gate10->time, 0); + EXPECT_EQ(gate10->qubits.size(), 1); + EXPECT_EQ(gate10->qubits[0], 2); + const auto* gate11 = OpGetAlternative(fgate1->gates[1]); + ASSERT_NE(gate11, nullptr); + EXPECT_EQ(gate11->kind, kGateHd); + EXPECT_EQ(gate11->time, 0); + EXPECT_EQ(gate11->qubits.size(), 1); + EXPECT_EQ(gate11->qubits[0], 3); + const auto* gate12 = OpGetAlternative(fgate1->gates[2]); + ASSERT_NE(gate12, nullptr); + EXPECT_EQ(gate12->kind, kGateCZ); + EXPECT_EQ(gate12->time, 1); + EXPECT_EQ(gate12->qubits.size(), 2); + EXPECT_EQ(gate12->qubits[0], 2); + EXPECT_EQ(gate12->qubits[1], 3); + const auto* gate13 = OpGetAlternative(fgate1->gates[3]); + ASSERT_NE(gate13, nullptr); + EXPECT_EQ(gate13->kind, kGateY); + EXPECT_EQ(gate13->time, 2); + EXPECT_EQ(gate13->qubits.size(), 1); + EXPECT_EQ(gate13->qubits[0], 2); + const auto* gate14 = OpGetAlternative(fgate1->gates[4]); + ASSERT_NE(gate14, nullptr); + EXPECT_EQ(gate14->kind, kGateT); + EXPECT_EQ(gate14->time, 2); + EXPECT_EQ(gate14->qubits.size(), 1); + EXPECT_EQ(gate14->qubits[0], 3); + const auto* gate15 = OpGetAlternative(fgate1->gates[5]); + ASSERT_NE(gate15, nullptr); + EXPECT_EQ(gate15->kind, kGateX); + EXPECT_EQ(gate15->time, 3); + EXPECT_EQ(gate15->qubits.size(), 1); + EXPECT_EQ(gate15->qubits[0], 3); + + const auto* fgate2 = OpGetAlternative(fused_gates[2]); + ASSERT_NE(fgate2, nullptr); + EXPECT_EQ(fgate2->kind, kGateCZ); + EXPECT_EQ(fgate2->time, 3); + EXPECT_EQ(fgate2->qubits.size(), 2); + EXPECT_EQ(fgate2->qubits[0], 1); + EXPECT_EQ(fgate2->qubits[1], 2); + EXPECT_EQ(fgate2->gates.size(), 9); + const auto* gate20 = OpGetAlternative(fgate2->gates[0]); + ASSERT_NE(gate20, nullptr); + EXPECT_EQ(gate20->kind, kGateCZ); + EXPECT_EQ(gate20->time, 3); + EXPECT_EQ(gate20->qubits.size(), 2); + EXPECT_EQ(gate20->qubits[0], 1); + EXPECT_EQ(gate20->qubits[1], 2); + const auto* gate21 = OpGetAlternative(fgate2->gates[1]); + ASSERT_NE(gate21, nullptr); + EXPECT_EQ(gate21->kind, kGateT); + EXPECT_EQ(gate21->time, 4); + EXPECT_EQ(gate21->qubits.size(), 1); + EXPECT_EQ(gate21->qubits[0], 1); + const auto* gate22 = OpGetAlternative(fgate2->gates[2]); + ASSERT_NE(gate22, nullptr); + EXPECT_EQ(gate22->kind, kGateT); + EXPECT_EQ(gate22->time, 4); + EXPECT_EQ(gate22->qubits.size(), 1); + EXPECT_EQ(gate22->qubits[0], 2); + const auto* gate23 = OpGetAlternative(fgate2->gates[3]); + ASSERT_NE(gate23, nullptr); + EXPECT_EQ(gate23->kind, kGateCZ); + EXPECT_EQ(gate23->time, 5); + EXPECT_EQ(gate23->qubits.size(), 2); + EXPECT_EQ(gate23->qubits[0], 1); + EXPECT_EQ(gate23->qubits[1], 2); + const auto* gate24 = OpGetAlternative(fgate2->gates[4]); + ASSERT_NE(gate24, nullptr); + EXPECT_EQ(gate24->kind, kGateX); + EXPECT_EQ(gate24->time, 6); + EXPECT_EQ(gate24->qubits.size(), 1); + EXPECT_EQ(gate24->qubits[0], 1); + const auto* gate25 = OpGetAlternative(fgate2->gates[5]); + ASSERT_NE(gate25, nullptr); + EXPECT_EQ(gate25->kind, kGateY); + EXPECT_EQ(gate25->time, 6); + EXPECT_EQ(gate25->qubits.size(), 1); + EXPECT_EQ(gate25->qubits[0], 2); + const auto* gate26 = OpGetAlternative(fgate2->gates[6]); + ASSERT_NE(gate26, nullptr); + EXPECT_EQ(gate26->kind, kGateCZ); + EXPECT_EQ(gate26->time, 7); + EXPECT_EQ(gate26->qubits.size(), 2); + EXPECT_EQ(gate26->qubits[0], 1); + EXPECT_EQ(gate26->qubits[1], 2); + const auto* gate27 = OpGetAlternative(fgate2->gates[7]); + ASSERT_NE(gate27, nullptr); + EXPECT_EQ(gate27->kind, kGateT); + EXPECT_EQ(gate27->time, 8); + EXPECT_EQ(gate27->qubits.size(), 1); + EXPECT_EQ(gate27->qubits[0], 1); + const auto* gate28 = OpGetAlternative(fgate2->gates[8]); + ASSERT_NE(gate28, nullptr); + EXPECT_EQ(gate28->kind, kGateT); + EXPECT_EQ(gate28->time, 8); + EXPECT_EQ(gate28->qubits.size(), 1); + EXPECT_EQ(gate28->qubits[0], 2); + + const auto* fgate3 = OpGetAlternative(fused_gates[3]); + ASSERT_NE(fgate3, nullptr); + EXPECT_EQ(fgate3->kind, kGateCZ); + EXPECT_EQ(fgate3->time, 9); + EXPECT_EQ(fgate3->qubits.size(), 2); + EXPECT_EQ(fgate3->qubits[0], 0); + EXPECT_EQ(fgate3->qubits[1], 1); + EXPECT_EQ(fgate3->gates.size(), 3); + const auto* gate30 = OpGetAlternative(fgate3->gates[0]); + ASSERT_NE(gate30, nullptr); + EXPECT_EQ(gate30->kind, kGateCZ); + EXPECT_EQ(gate30->time, 9); + EXPECT_EQ(gate30->qubits.size(), 2); + EXPECT_EQ(gate30->qubits[0], 0); + EXPECT_EQ(gate30->qubits[1], 1); + const auto* gate31 = OpGetAlternative(fgate3->gates[1]); + ASSERT_NE(gate31, nullptr); + EXPECT_EQ(gate31->kind, kGateHd); + EXPECT_EQ(gate31->time, 10); + EXPECT_EQ(gate31->qubits.size(), 1); + EXPECT_EQ(gate31->qubits[0], 0); + const auto* gate32 = OpGetAlternative(fgate3->gates[2]); + ASSERT_NE(gate32, nullptr); + EXPECT_EQ(gate32->kind, kGateHd); + EXPECT_EQ(gate32->time, 10); + EXPECT_EQ(gate32->qubits.size(), 1); + EXPECT_EQ(gate32->qubits[0], 1); + + const auto* fgate4 = OpGetAlternative(fused_gates[4]); + ASSERT_NE(fgate4, nullptr); + EXPECT_EQ(fgate4->kind, kGateCZ); + EXPECT_EQ(fgate4->time, 9); + EXPECT_EQ(fgate4->qubits.size(), 2); + EXPECT_EQ(fgate4->qubits[0], 2); + EXPECT_EQ(fgate4->qubits[1], 3); + EXPECT_EQ(fgate4->gates.size(), 3); + const auto* gate40 = OpGetAlternative(fgate4->gates[0]); + ASSERT_NE(gate40, nullptr); + EXPECT_EQ(gate40->kind, kGateCZ); + EXPECT_EQ(gate40->time, 9); + EXPECT_EQ(gate40->qubits.size(), 2); + EXPECT_EQ(gate40->qubits[0], 2); + EXPECT_EQ(gate40->qubits[1], 3); + const auto* gate41 = OpGetAlternative(fgate4->gates[1]); + ASSERT_NE(gate41, nullptr); + EXPECT_EQ(gate41->kind, kGateHd); + EXPECT_EQ(gate41->time, 10); + EXPECT_EQ(gate41->qubits.size(), 1); + EXPECT_EQ(gate41->qubits[0], 2); + const auto* gate42 = OpGetAlternative(fgate4->gates[2]); + ASSERT_NE(gate42, nullptr); + EXPECT_EQ(gate42->kind, kGateHd); + EXPECT_EQ(gate42->time, 10); + EXPECT_EQ(gate42->qubits.size(), 1); + EXPECT_EQ(gate42->qubits[0], 3); } - TEST(FuserBasicTest, TimesToSplitAt1) { + using Gate = qsim::Gate; + using Operation = qsim::Operation; + using FusedGate = qsim::FusedGate; + std::stringstream ss(circuit_string1); - Circuit> circuit; + Circuit circuit; EXPECT_TRUE(CircuitQsimParser::FromStream(99, provider, ss, circuit)); EXPECT_EQ(circuit.num_qubits, 4); - EXPECT_EQ(circuit.gates.size(), 27); + EXPECT_EQ(circuit.ops.size(), 27); std::vector times_to_split_at{3, 8, 10}; - using Fuser = BasicGateFuser>; + using Fuser = BasicGateFuser; Fuser::Parameter param; auto fused_gates = Fuser::FuseGates( - param, circuit.num_qubits, circuit.gates, times_to_split_at); + param, circuit.num_qubits, circuit.ops, times_to_split_at); EXPECT_EQ(fused_gates.size(), 6); - - EXPECT_EQ(fused_gates[0].kind, kGateCZ); - EXPECT_EQ(fused_gates[0].time, 1); - EXPECT_EQ(fused_gates[0].qubits.size(), 2); - EXPECT_EQ(fused_gates[0].qubits[0], 0); - EXPECT_EQ(fused_gates[0].qubits[1], 1); - EXPECT_EQ(fused_gates[0].gates.size(), 6); - EXPECT_EQ(fused_gates[0].gates[0]->kind, kGateHd); - EXPECT_EQ(fused_gates[0].gates[0]->time, 0); - EXPECT_EQ(fused_gates[0].gates[0]->qubits.size(), 1); - EXPECT_EQ(fused_gates[0].gates[0]->qubits[0], 0); - EXPECT_EQ(fused_gates[0].gates[1]->kind, kGateHd); - EXPECT_EQ(fused_gates[0].gates[1]->time, 0); - EXPECT_EQ(fused_gates[0].gates[1]->qubits.size(), 1); - EXPECT_EQ(fused_gates[0].gates[1]->qubits[0], 1); - EXPECT_EQ(fused_gates[0].gates[2]->kind, kGateCZ); - EXPECT_EQ(fused_gates[0].gates[2]->time, 1); - EXPECT_EQ(fused_gates[0].gates[2]->qubits.size(), 2); - EXPECT_EQ(fused_gates[0].gates[2]->qubits[0], 0); - EXPECT_EQ(fused_gates[0].gates[2]->qubits[1], 1); - EXPECT_EQ(fused_gates[0].gates[3]->kind, kGateT); - EXPECT_EQ(fused_gates[0].gates[3]->time, 2); - EXPECT_EQ(fused_gates[0].gates[3]->qubits.size(), 1); - EXPECT_EQ(fused_gates[0].gates[3]->qubits[0], 0); - EXPECT_EQ(fused_gates[0].gates[4]->kind, kGateY); - EXPECT_EQ(fused_gates[0].gates[4]->time, 3); - EXPECT_EQ(fused_gates[0].gates[4]->qubits.size(), 1); - EXPECT_EQ(fused_gates[0].gates[4]->qubits[0], 0); - EXPECT_EQ(fused_gates[0].gates[5]->kind, kGateX); - EXPECT_EQ(fused_gates[0].gates[5]->time, 2); - EXPECT_EQ(fused_gates[0].gates[5]->qubits.size(), 1); - EXPECT_EQ(fused_gates[0].gates[5]->qubits[0], 1); - - EXPECT_EQ(fused_gates[1].kind, kGateCZ); - EXPECT_EQ(fused_gates[1].time, 1); - EXPECT_EQ(fused_gates[1].qubits.size(), 2); - EXPECT_EQ(fused_gates[1].qubits[0], 2); - EXPECT_EQ(fused_gates[1].qubits[1], 3); - EXPECT_EQ(fused_gates[1].gates.size(), 6); - EXPECT_EQ(fused_gates[1].gates[0]->kind, kGateHd); - EXPECT_EQ(fused_gates[1].gates[0]->time, 0); - EXPECT_EQ(fused_gates[1].gates[0]->qubits.size(), 1); - EXPECT_EQ(fused_gates[1].gates[0]->qubits[0], 2); - EXPECT_EQ(fused_gates[1].gates[1]->kind, kGateHd); - EXPECT_EQ(fused_gates[1].gates[1]->time, 0); - EXPECT_EQ(fused_gates[1].gates[1]->qubits.size(), 1); - EXPECT_EQ(fused_gates[1].gates[1]->qubits[0], 3); - EXPECT_EQ(fused_gates[1].gates[2]->kind, kGateCZ); - EXPECT_EQ(fused_gates[1].gates[2]->time, 1); - EXPECT_EQ(fused_gates[1].gates[2]->qubits.size(), 2); - EXPECT_EQ(fused_gates[1].gates[2]->qubits[0], 2); - EXPECT_EQ(fused_gates[1].gates[2]->qubits[1], 3); - EXPECT_EQ(fused_gates[1].gates[3]->kind, kGateY); - EXPECT_EQ(fused_gates[1].gates[3]->time, 2); - EXPECT_EQ(fused_gates[1].gates[3]->qubits.size(), 1); - EXPECT_EQ(fused_gates[1].gates[3]->qubits[0], 2); - EXPECT_EQ(fused_gates[1].gates[4]->kind, kGateT); - EXPECT_EQ(fused_gates[1].gates[4]->time, 2); - EXPECT_EQ(fused_gates[1].gates[4]->qubits.size(), 1); - EXPECT_EQ(fused_gates[1].gates[4]->qubits[0], 3); - EXPECT_EQ(fused_gates[1].gates[5]->kind, kGateX); - EXPECT_EQ(fused_gates[1].gates[5]->time, 3); - EXPECT_EQ(fused_gates[1].gates[5]->qubits.size(), 1); - EXPECT_EQ(fused_gates[1].gates[5]->qubits[0], 3); - - EXPECT_EQ(fused_gates[2].kind, kGateCZ); - EXPECT_EQ(fused_gates[2].time, 3); - EXPECT_EQ(fused_gates[2].qubits.size(), 2); - EXPECT_EQ(fused_gates[2].qubits[0], 1); - EXPECT_EQ(fused_gates[2].qubits[1], 2); - EXPECT_EQ(fused_gates[2].gates.size(), 1); - EXPECT_EQ(fused_gates[2].gates[0]->kind, kGateCZ); - EXPECT_EQ(fused_gates[2].gates[0]->time, 3); - EXPECT_EQ(fused_gates[2].gates[0]->qubits.size(), 2); - EXPECT_EQ(fused_gates[2].gates[0]->qubits[0], 1); - EXPECT_EQ(fused_gates[2].gates[0]->qubits[1], 2); - - EXPECT_EQ(fused_gates[3].kind, kGateCZ); - EXPECT_EQ(fused_gates[3].time, 5); - EXPECT_EQ(fused_gates[3].qubits.size(), 2); - EXPECT_EQ(fused_gates[3].qubits[0], 1); - EXPECT_EQ(fused_gates[3].qubits[1], 2); - EXPECT_EQ(fused_gates[3].gates.size(), 8); - EXPECT_EQ(fused_gates[3].gates[0]->kind, kGateT); - EXPECT_EQ(fused_gates[3].gates[0]->time, 4); - EXPECT_EQ(fused_gates[3].gates[0]->qubits.size(), 1); - EXPECT_EQ(fused_gates[3].gates[0]->qubits[0], 1); - EXPECT_EQ(fused_gates[3].gates[1]->kind, kGateT); - EXPECT_EQ(fused_gates[3].gates[1]->time, 4); - EXPECT_EQ(fused_gates[3].gates[1]->qubits.size(), 1); - EXPECT_EQ(fused_gates[3].gates[1]->qubits[0], 2); - EXPECT_EQ(fused_gates[3].gates[2]->kind, kGateCZ); - EXPECT_EQ(fused_gates[3].gates[2]->time, 5); - EXPECT_EQ(fused_gates[3].gates[2]->qubits.size(), 2); - EXPECT_EQ(fused_gates[3].gates[2]->qubits[0], 1); - EXPECT_EQ(fused_gates[3].gates[2]->qubits[1], 2); - EXPECT_EQ(fused_gates[3].gates[3]->kind, kGateX); - EXPECT_EQ(fused_gates[3].gates[3]->time, 6); - EXPECT_EQ(fused_gates[3].gates[3]->qubits.size(), 1); - EXPECT_EQ(fused_gates[3].gates[3]->qubits[0], 1); - EXPECT_EQ(fused_gates[3].gates[4]->kind, kGateY); - EXPECT_EQ(fused_gates[3].gates[4]->time, 6); - EXPECT_EQ(fused_gates[3].gates[4]->qubits.size(), 1); - EXPECT_EQ(fused_gates[3].gates[4]->qubits[0], 2); - EXPECT_EQ(fused_gates[3].gates[5]->kind, kGateCZ); - EXPECT_EQ(fused_gates[3].gates[5]->time, 7); - EXPECT_EQ(fused_gates[3].gates[5]->qubits.size(), 2); - EXPECT_EQ(fused_gates[3].gates[5]->qubits[0], 1); - EXPECT_EQ(fused_gates[3].gates[5]->qubits[1], 2); - EXPECT_EQ(fused_gates[3].gates[6]->kind, kGateT); - EXPECT_EQ(fused_gates[3].gates[6]->time, 8); - EXPECT_EQ(fused_gates[3].gates[6]->qubits.size(), 1); - EXPECT_EQ(fused_gates[3].gates[6]->qubits[0], 1); - EXPECT_EQ(fused_gates[3].gates[7]->kind, kGateT); - EXPECT_EQ(fused_gates[3].gates[7]->time, 8); - EXPECT_EQ(fused_gates[3].gates[7]->qubits.size(), 1); - EXPECT_EQ(fused_gates[3].gates[7]->qubits[0], 2); - - EXPECT_EQ(fused_gates[4].kind, kGateCZ); - EXPECT_EQ(fused_gates[4].time, 9); - EXPECT_EQ(fused_gates[4].qubits.size(), 2); - EXPECT_EQ(fused_gates[4].qubits[0], 0); - EXPECT_EQ(fused_gates[4].qubits[1], 1); - EXPECT_EQ(fused_gates[4].gates.size(), 3); - EXPECT_EQ(fused_gates[4].gates[0]->kind, kGateCZ); - EXPECT_EQ(fused_gates[4].gates[0]->time, 9); - EXPECT_EQ(fused_gates[4].gates[0]->qubits.size(), 2); - EXPECT_EQ(fused_gates[4].gates[0]->qubits[0], 0); - EXPECT_EQ(fused_gates[4].gates[0]->qubits[1], 1); - EXPECT_EQ(fused_gates[4].gates[1]->kind, kGateHd); - EXPECT_EQ(fused_gates[4].gates[1]->time, 10); - EXPECT_EQ(fused_gates[4].gates[1]->qubits.size(), 1); - EXPECT_EQ(fused_gates[4].gates[1]->qubits[0], 0); - EXPECT_EQ(fused_gates[4].gates[2]->kind, kGateHd); - EXPECT_EQ(fused_gates[4].gates[2]->time, 10); - EXPECT_EQ(fused_gates[4].gates[2]->qubits.size(), 1); - EXPECT_EQ(fused_gates[4].gates[2]->qubits[0], 1); - - EXPECT_EQ(fused_gates[5].kind, kGateCZ); - EXPECT_EQ(fused_gates[5].time, 9); - EXPECT_EQ(fused_gates[5].qubits.size(), 2); - EXPECT_EQ(fused_gates[5].qubits[0], 2); - EXPECT_EQ(fused_gates[5].qubits[1], 3); - EXPECT_EQ(fused_gates[5].gates.size(), 3); - EXPECT_EQ(fused_gates[5].gates[0]->kind, kGateCZ); - EXPECT_EQ(fused_gates[5].gates[0]->time, 9); - EXPECT_EQ(fused_gates[5].gates[0]->qubits.size(), 2); - EXPECT_EQ(fused_gates[5].gates[0]->qubits[0], 2); - EXPECT_EQ(fused_gates[5].gates[0]->qubits[1], 3); - EXPECT_EQ(fused_gates[5].gates[1]->kind, kGateHd); - EXPECT_EQ(fused_gates[5].gates[1]->time, 10); - EXPECT_EQ(fused_gates[5].gates[1]->qubits.size(), 1); - EXPECT_EQ(fused_gates[5].gates[1]->qubits[0], 2); - EXPECT_EQ(fused_gates[5].gates[2]->kind, kGateHd); - EXPECT_EQ(fused_gates[5].gates[2]->time, 10); - EXPECT_EQ(fused_gates[5].gates[2]->qubits.size(), 1); - EXPECT_EQ(fused_gates[5].gates[2]->qubits[0], 3); + const auto* fgate0 = OpGetAlternative(fused_gates[0]); + ASSERT_NE(fgate0, nullptr); + EXPECT_EQ(fgate0->kind, kGateCZ); + EXPECT_EQ(fgate0->time, 1); + EXPECT_EQ(fgate0->qubits.size(), 2); + EXPECT_EQ(fgate0->qubits[0], 0); + EXPECT_EQ(fgate0->qubits[1], 1); + EXPECT_EQ(fgate0->gates.size(), 6); + const auto* gate00 = OpGetAlternative(fgate0->gates[0]); + ASSERT_NE(gate00, nullptr); + EXPECT_EQ(gate00->kind, kGateHd); + EXPECT_EQ(gate00->time, 0); + EXPECT_EQ(gate00->qubits.size(), 1); + EXPECT_EQ(gate00->qubits[0], 0); + const auto* gate01 = OpGetAlternative(fgate0->gates[1]); + ASSERT_NE(gate01, nullptr); + EXPECT_EQ(gate01->kind, kGateHd); + EXPECT_EQ(gate01->time, 0); + EXPECT_EQ(gate01->qubits.size(), 1); + EXPECT_EQ(gate01->qubits[0], 1); + const auto* gate02 = OpGetAlternative(fgate0->gates[2]); + ASSERT_NE(gate02, nullptr); + EXPECT_EQ(gate02->kind, kGateCZ); + EXPECT_EQ(gate02->time, 1); + EXPECT_EQ(gate02->qubits.size(), 2); + EXPECT_EQ(gate02->qubits[0], 0); + EXPECT_EQ(gate02->qubits[1], 1); + const auto* gate03 = OpGetAlternative(fgate0->gates[3]); + ASSERT_NE(gate03, nullptr); + EXPECT_EQ(gate03->kind, kGateT); + EXPECT_EQ(gate03->time, 2); + EXPECT_EQ(gate03->qubits.size(), 1); + EXPECT_EQ(gate03->qubits[0], 0); + const auto* gate04 = OpGetAlternative(fgate0->gates[4]); + ASSERT_NE(gate04, nullptr); + EXPECT_EQ(gate04->kind, kGateY); + EXPECT_EQ(gate04->time, 3); + EXPECT_EQ(gate04->qubits.size(), 1); + EXPECT_EQ(gate04->qubits[0], 0); + const auto* gate05 = OpGetAlternative(fgate0->gates[5]); + ASSERT_NE(gate05, nullptr); + EXPECT_EQ(gate05->kind, kGateX); + EXPECT_EQ(gate05->time, 2); + EXPECT_EQ(gate05->qubits.size(), 1); + EXPECT_EQ(gate05->qubits[0], 1); + + const auto* fgate1 = OpGetAlternative(fused_gates[1]); + ASSERT_NE(fgate1, nullptr); + EXPECT_EQ(fgate1->kind, kGateCZ); + EXPECT_EQ(fgate1->time, 1); + EXPECT_EQ(fgate1->qubits.size(), 2); + EXPECT_EQ(fgate1->qubits[0], 2); + EXPECT_EQ(fgate1->qubits[1], 3); + EXPECT_EQ(fgate1->gates.size(), 6); + const auto* gate10 = OpGetAlternative(fgate1->gates[0]); + ASSERT_NE(gate10, nullptr); + EXPECT_EQ(gate10->kind, kGateHd); + EXPECT_EQ(gate10->time, 0); + EXPECT_EQ(gate10->qubits.size(), 1); + EXPECT_EQ(gate10->qubits[0], 2); + const auto* gate11 = OpGetAlternative(fgate1->gates[1]); + ASSERT_NE(gate11, nullptr); + EXPECT_EQ(gate11->kind, kGateHd); + EXPECT_EQ(gate11->time, 0); + EXPECT_EQ(gate11->qubits.size(), 1); + EXPECT_EQ(gate11->qubits[0], 3); + const auto* gate12 = OpGetAlternative(fgate1->gates[2]); + ASSERT_NE(gate12, nullptr); + EXPECT_EQ(gate12->kind, kGateCZ); + EXPECT_EQ(gate12->time, 1); + EXPECT_EQ(gate12->qubits.size(), 2); + EXPECT_EQ(gate12->qubits[0], 2); + EXPECT_EQ(gate12->qubits[1], 3); + const auto* gate13 = OpGetAlternative(fgate1->gates[3]); + ASSERT_NE(gate13, nullptr); + EXPECT_EQ(gate13->kind, kGateY); + EXPECT_EQ(gate13->time, 2); + EXPECT_EQ(gate13->qubits.size(), 1); + EXPECT_EQ(gate13->qubits[0], 2); + const auto* gate14 = OpGetAlternative(fgate1->gates[4]); + ASSERT_NE(gate14, nullptr); + EXPECT_EQ(gate14->kind, kGateT); + EXPECT_EQ(gate14->time, 2); + EXPECT_EQ(gate14->qubits.size(), 1); + EXPECT_EQ(gate14->qubits[0], 3); + const auto* gate15 = OpGetAlternative(fgate1->gates[5]); + ASSERT_NE(gate15, nullptr); + EXPECT_EQ(gate15->kind, kGateX); + EXPECT_EQ(gate15->time, 3); + EXPECT_EQ(gate15->qubits.size(), 1); + EXPECT_EQ(gate15->qubits[0], 3); + + const auto* fgate2 = OpGetAlternative(fused_gates[2]); + ASSERT_NE(fgate2, nullptr); + EXPECT_EQ(fgate2->kind, kGateCZ); + EXPECT_EQ(fgate2->time, 3); + EXPECT_EQ(fgate2->qubits.size(), 2); + EXPECT_EQ(fgate2->qubits[0], 1); + EXPECT_EQ(fgate2->qubits[1], 2); + EXPECT_EQ(fgate2->gates.size(), 1); + const auto* gate20 = OpGetAlternative(fgate2->gates[0]); + ASSERT_NE(gate20, nullptr); + EXPECT_EQ(gate20->kind, kGateCZ); + EXPECT_EQ(gate20->time, 3); + EXPECT_EQ(gate20->qubits.size(), 2); + EXPECT_EQ(gate20->qubits[0], 1); + EXPECT_EQ(gate20->qubits[1], 2); + + const auto* fgate3 = OpGetAlternative(fused_gates[3]); + ASSERT_NE(fgate3, nullptr); + EXPECT_EQ(fgate3->kind, kGateCZ); + EXPECT_EQ(fgate3->time, 5); + EXPECT_EQ(fgate3->qubits.size(), 2); + EXPECT_EQ(fgate3->qubits[0], 1); + EXPECT_EQ(fgate3->qubits[1], 2); + EXPECT_EQ(fgate3->gates.size(), 8); + const auto* gate30 = OpGetAlternative(fgate3->gates[0]); + ASSERT_NE(gate30, nullptr); + EXPECT_EQ(gate30->kind, kGateT); + EXPECT_EQ(gate30->time, 4); + EXPECT_EQ(gate30->qubits.size(), 1); + EXPECT_EQ(gate30->qubits[0], 1); + const auto* gate31 = OpGetAlternative(fgate3->gates[1]); + ASSERT_NE(gate31, nullptr); + EXPECT_EQ(gate31->kind, kGateT); + EXPECT_EQ(gate31->time, 4); + EXPECT_EQ(gate31->qubits.size(), 1); + EXPECT_EQ(gate31->qubits[0], 2); + const auto* gate32 = OpGetAlternative(fgate3->gates[2]); + ASSERT_NE(gate32, nullptr); + EXPECT_EQ(gate32->kind, kGateCZ); + EXPECT_EQ(gate32->time, 5); + EXPECT_EQ(gate32->qubits.size(), 2); + EXPECT_EQ(gate32->qubits[0], 1); + EXPECT_EQ(gate32->qubits[1], 2); + const auto* gate33 = OpGetAlternative(fgate3->gates[3]); + ASSERT_NE(gate33, nullptr); + EXPECT_EQ(gate33->kind, kGateX); + EXPECT_EQ(gate33->time, 6); + EXPECT_EQ(gate33->qubits.size(), 1); + EXPECT_EQ(gate33->qubits[0], 1); + const auto* gate34 = OpGetAlternative(fgate3->gates[4]); + ASSERT_NE(gate34, nullptr); + EXPECT_EQ(gate34->kind, kGateY); + EXPECT_EQ(gate34->time, 6); + EXPECT_EQ(gate34->qubits.size(), 1); + EXPECT_EQ(gate34->qubits[0], 2); + const auto* gate35 = OpGetAlternative(fgate3->gates[5]); + ASSERT_NE(gate35, nullptr); + EXPECT_EQ(gate35->kind, kGateCZ); + EXPECT_EQ(gate35->time, 7); + EXPECT_EQ(gate35->qubits.size(), 2); + EXPECT_EQ(gate35->qubits[0], 1); + EXPECT_EQ(gate35->qubits[1], 2); + const auto* gate36 = OpGetAlternative(fgate3->gates[6]); + ASSERT_NE(gate36, nullptr); + EXPECT_EQ(gate36->kind, kGateT); + EXPECT_EQ(gate36->time, 8); + EXPECT_EQ(gate36->qubits.size(), 1); + EXPECT_EQ(gate36->qubits[0], 1); + const auto* gate37 = OpGetAlternative(fgate3->gates[7]); + ASSERT_NE(gate37, nullptr); + EXPECT_EQ(gate37->kind, kGateT); + EXPECT_EQ(gate37->time, 8); + EXPECT_EQ(gate37->qubits.size(), 1); + EXPECT_EQ(gate37->qubits[0], 2); + + const auto* fgate4 = OpGetAlternative(fused_gates[4]); + ASSERT_NE(fgate4, nullptr); + EXPECT_EQ(fgate4->kind, kGateCZ); + EXPECT_EQ(fgate4->time, 9); + EXPECT_EQ(fgate4->qubits.size(), 2); + EXPECT_EQ(fgate4->qubits[0], 0); + EXPECT_EQ(fgate4->qubits[1], 1); + EXPECT_EQ(fgate4->gates.size(), 3); + const auto* gate40 = OpGetAlternative(fgate4->gates[0]); + ASSERT_NE(gate40, nullptr); + EXPECT_EQ(gate40->kind, kGateCZ); + EXPECT_EQ(gate40->time, 9); + EXPECT_EQ(gate40->qubits.size(), 2); + EXPECT_EQ(gate40->qubits[0], 0); + EXPECT_EQ(gate40->qubits[1], 1); + const auto* gate41 = OpGetAlternative(fgate4->gates[1]); + ASSERT_NE(gate41, nullptr); + EXPECT_EQ(gate41->kind, kGateHd); + EXPECT_EQ(gate41->time, 10); + EXPECT_EQ(gate41->qubits.size(), 1); + EXPECT_EQ(gate41->qubits[0], 0); + const auto* gate42 = OpGetAlternative(fgate4->gates[2]); + ASSERT_NE(gate42, nullptr); + EXPECT_EQ(gate42->kind, kGateHd); + EXPECT_EQ(gate42->time, 10); + EXPECT_EQ(gate42->qubits.size(), 1); + EXPECT_EQ(gate42->qubits[0], 1); + + const auto* fgate5 = OpGetAlternative(fused_gates[5]); + ASSERT_NE(fgate5, nullptr); + EXPECT_EQ(fgate5->kind, kGateCZ); + EXPECT_EQ(fgate5->time, 9); + EXPECT_EQ(fgate5->qubits.size(), 2); + EXPECT_EQ(fgate5->qubits[0], 2); + EXPECT_EQ(fgate5->qubits[1], 3); + EXPECT_EQ(fgate5->gates.size(), 3); + const auto* gate50 = OpGetAlternative(fgate5->gates[0]); + ASSERT_NE(gate50, nullptr); + EXPECT_EQ(gate50->kind, kGateCZ); + EXPECT_EQ(gate50->time, 9); + EXPECT_EQ(gate50->qubits.size(), 2); + EXPECT_EQ(gate50->qubits[0], 2); + EXPECT_EQ(gate50->qubits[1], 3); + const auto* gate51 = OpGetAlternative(fgate5->gates[1]); + ASSERT_NE(gate51, nullptr); + EXPECT_EQ(gate51->kind, kGateHd); + EXPECT_EQ(gate51->time, 10); + EXPECT_EQ(gate51->qubits.size(), 1); + EXPECT_EQ(gate51->qubits[0], 2); + const auto* gate52 = OpGetAlternative(fgate5->gates[2]); + ASSERT_NE(gate52, nullptr); + EXPECT_EQ(gate52->kind, kGateHd); + EXPECT_EQ(gate52->time, 10); + EXPECT_EQ(gate52->qubits.size(), 1); + EXPECT_EQ(gate52->qubits[0], 3); } TEST(FuserBasicTest, TimesToSplitAt2) { + using Gate = qsim::Gate; + using Operation = qsim::Operation; + using FusedGate = qsim::FusedGate; + std::stringstream ss(circuit_string1); - Circuit> circuit; + Circuit circuit; EXPECT_TRUE(CircuitQsimParser::FromStream(99, provider, ss, circuit)); EXPECT_EQ(circuit.num_qubits, 4); - EXPECT_EQ(circuit.gates.size(), 27); + EXPECT_EQ(circuit.ops.size(), 27); std::vector times_to_split_at{2, 10}; - using Fuser = BasicGateFuser>; + using Fuser = BasicGateFuser; Fuser::Parameter param; auto fused_gates = Fuser::FuseGates( - param, circuit.num_qubits, circuit.gates, times_to_split_at); + param, circuit.num_qubits, circuit.ops, times_to_split_at); EXPECT_EQ(fused_gates.size(), 5); - EXPECT_EQ(fused_gates[0].kind, kGateCZ); - EXPECT_EQ(fused_gates[0].time, 1); - EXPECT_EQ(fused_gates[0].qubits.size(), 2); - EXPECT_EQ(fused_gates[0].qubits[0], 0); - EXPECT_EQ(fused_gates[0].qubits[1], 1); - EXPECT_EQ(fused_gates[0].gates.size(), 5); - EXPECT_EQ(fused_gates[0].gates[0]->kind, kGateHd); - EXPECT_EQ(fused_gates[0].gates[0]->time, 0); - EXPECT_EQ(fused_gates[0].gates[0]->qubits.size(), 1); - EXPECT_EQ(fused_gates[0].gates[0]->qubits[0], 0); - EXPECT_EQ(fused_gates[0].gates[1]->kind, kGateHd); - EXPECT_EQ(fused_gates[0].gates[1]->time, 0); - EXPECT_EQ(fused_gates[0].gates[1]->qubits.size(), 1); - EXPECT_EQ(fused_gates[0].gates[1]->qubits[0], 1); - EXPECT_EQ(fused_gates[0].gates[2]->kind, kGateCZ); - EXPECT_EQ(fused_gates[0].gates[2]->time, 1); - EXPECT_EQ(fused_gates[0].gates[2]->qubits.size(), 2); - EXPECT_EQ(fused_gates[0].gates[2]->qubits[0], 0); - EXPECT_EQ(fused_gates[0].gates[2]->qubits[1], 1); - EXPECT_EQ(fused_gates[0].gates[3]->kind, kGateT); - EXPECT_EQ(fused_gates[0].gates[3]->time, 2); - EXPECT_EQ(fused_gates[0].gates[3]->qubits.size(), 1); - EXPECT_EQ(fused_gates[0].gates[3]->qubits[0], 0); - EXPECT_EQ(fused_gates[0].gates[4]->kind, kGateX); - EXPECT_EQ(fused_gates[0].gates[4]->time, 2); - EXPECT_EQ(fused_gates[0].gates[4]->qubits.size(), 1); - EXPECT_EQ(fused_gates[0].gates[4]->qubits[0], 1); - - EXPECT_EQ(fused_gates[1].kind, kGateCZ); - EXPECT_EQ(fused_gates[1].time, 1); - EXPECT_EQ(fused_gates[1].qubits.size(), 2); - EXPECT_EQ(fused_gates[1].qubits[0], 2); - EXPECT_EQ(fused_gates[1].qubits[1], 3); - EXPECT_EQ(fused_gates[1].gates.size(), 5); - EXPECT_EQ(fused_gates[1].gates[0]->kind, kGateHd); - EXPECT_EQ(fused_gates[1].gates[0]->time, 0); - EXPECT_EQ(fused_gates[1].gates[0]->qubits.size(), 1); - EXPECT_EQ(fused_gates[1].gates[0]->qubits[0], 2); - EXPECT_EQ(fused_gates[1].gates[1]->kind, kGateHd); - EXPECT_EQ(fused_gates[1].gates[1]->time, 0); - EXPECT_EQ(fused_gates[1].gates[1]->qubits.size(), 1); - EXPECT_EQ(fused_gates[1].gates[1]->qubits[0], 3); - EXPECT_EQ(fused_gates[1].gates[2]->kind, kGateCZ); - EXPECT_EQ(fused_gates[1].gates[2]->time, 1); - EXPECT_EQ(fused_gates[1].gates[2]->qubits.size(), 2); - EXPECT_EQ(fused_gates[1].gates[2]->qubits[0], 2); - EXPECT_EQ(fused_gates[1].gates[2]->qubits[1], 3); - EXPECT_EQ(fused_gates[1].gates[3]->kind, kGateY); - EXPECT_EQ(fused_gates[1].gates[3]->time, 2); - EXPECT_EQ(fused_gates[1].gates[3]->qubits.size(), 1); - EXPECT_EQ(fused_gates[1].gates[3]->qubits[0], 2); - EXPECT_EQ(fused_gates[1].gates[4]->kind, kGateT); - EXPECT_EQ(fused_gates[1].gates[4]->time, 2); - EXPECT_EQ(fused_gates[1].gates[4]->qubits.size(), 1); - EXPECT_EQ(fused_gates[1].gates[4]->qubits[0], 3); - - EXPECT_EQ(fused_gates[2].kind, kGateCZ); - EXPECT_EQ(fused_gates[2].time, 3); - EXPECT_EQ(fused_gates[2].qubits.size(), 2); - EXPECT_EQ(fused_gates[2].qubits[0], 1); - EXPECT_EQ(fused_gates[2].qubits[1], 2); - EXPECT_EQ(fused_gates[2].gates.size(), 9); - EXPECT_EQ(fused_gates[2].gates[0]->kind, kGateCZ); - EXPECT_EQ(fused_gates[2].gates[0]->time, 3); - EXPECT_EQ(fused_gates[2].gates[0]->qubits.size(), 2); - EXPECT_EQ(fused_gates[2].gates[0]->qubits[0], 1); - EXPECT_EQ(fused_gates[2].gates[0]->qubits[1], 2); - EXPECT_EQ(fused_gates[2].gates[1]->kind, kGateT); - EXPECT_EQ(fused_gates[2].gates[1]->time, 4); - EXPECT_EQ(fused_gates[2].gates[1]->qubits.size(), 1); - EXPECT_EQ(fused_gates[2].gates[1]->qubits[0], 1); - EXPECT_EQ(fused_gates[2].gates[2]->kind, kGateT); - EXPECT_EQ(fused_gates[2].gates[2]->time, 4); - EXPECT_EQ(fused_gates[2].gates[2]->qubits.size(), 1); - EXPECT_EQ(fused_gates[2].gates[2]->qubits[0], 2); - EXPECT_EQ(fused_gates[2].gates[3]->kind, kGateCZ); - EXPECT_EQ(fused_gates[2].gates[3]->time, 5); - EXPECT_EQ(fused_gates[2].gates[3]->qubits.size(), 2); - EXPECT_EQ(fused_gates[2].gates[3]->qubits[0], 1); - EXPECT_EQ(fused_gates[2].gates[3]->qubits[1], 2); - EXPECT_EQ(fused_gates[2].gates[4]->kind, kGateX); - EXPECT_EQ(fused_gates[2].gates[4]->time, 6); - EXPECT_EQ(fused_gates[2].gates[4]->qubits.size(), 1); - EXPECT_EQ(fused_gates[2].gates[4]->qubits[0], 1); - EXPECT_EQ(fused_gates[2].gates[5]->kind, kGateY); - EXPECT_EQ(fused_gates[2].gates[5]->time, 6); - EXPECT_EQ(fused_gates[2].gates[5]->qubits.size(), 1); - EXPECT_EQ(fused_gates[2].gates[5]->qubits[0], 2); - EXPECT_EQ(fused_gates[2].gates[6]->kind, kGateCZ); - EXPECT_EQ(fused_gates[2].gates[6]->time, 7); - EXPECT_EQ(fused_gates[2].gates[6]->qubits.size(), 2); - EXPECT_EQ(fused_gates[2].gates[6]->qubits[0], 1); - EXPECT_EQ(fused_gates[2].gates[6]->qubits[1], 2); - EXPECT_EQ(fused_gates[2].gates[7]->kind, kGateT); - EXPECT_EQ(fused_gates[2].gates[7]->time, 8); - EXPECT_EQ(fused_gates[2].gates[7]->qubits.size(), 1); - EXPECT_EQ(fused_gates[2].gates[7]->qubits[0], 1); - EXPECT_EQ(fused_gates[2].gates[8]->kind, kGateT); - EXPECT_EQ(fused_gates[2].gates[8]->time, 8); - EXPECT_EQ(fused_gates[2].gates[8]->qubits.size(), 1); - EXPECT_EQ(fused_gates[2].gates[8]->qubits[0], 2); - - EXPECT_EQ(fused_gates[3].kind, kGateCZ); - EXPECT_EQ(fused_gates[3].time, 9); - EXPECT_EQ(fused_gates[3].qubits.size(), 2); - EXPECT_EQ(fused_gates[3].qubits[0], 0); - EXPECT_EQ(fused_gates[3].qubits[1], 1); - EXPECT_EQ(fused_gates[3].gates.size(), 4); - EXPECT_EQ(fused_gates[3].gates[0]->kind, kGateY); - EXPECT_EQ(fused_gates[3].gates[0]->time, 3); - EXPECT_EQ(fused_gates[3].gates[0]->qubits.size(), 1); - EXPECT_EQ(fused_gates[3].gates[0]->qubits[0], 0); - EXPECT_EQ(fused_gates[3].gates[1]->kind, kGateCZ); - EXPECT_EQ(fused_gates[3].gates[1]->time, 9); - EXPECT_EQ(fused_gates[3].gates[1]->qubits.size(), 2); - EXPECT_EQ(fused_gates[3].gates[1]->qubits[0], 0); - EXPECT_EQ(fused_gates[3].gates[1]->qubits[1], 1); - EXPECT_EQ(fused_gates[3].gates[2]->kind, kGateHd); - EXPECT_EQ(fused_gates[3].gates[2]->time, 10); - EXPECT_EQ(fused_gates[3].gates[2]->qubits.size(), 1); - EXPECT_EQ(fused_gates[3].gates[2]->qubits[0], 0); - EXPECT_EQ(fused_gates[3].gates[3]->kind, kGateHd); - EXPECT_EQ(fused_gates[3].gates[3]->time, 10); - EXPECT_EQ(fused_gates[3].gates[3]->qubits.size(), 1); - EXPECT_EQ(fused_gates[3].gates[3]->qubits[0], 1); - - EXPECT_EQ(fused_gates[4].kind, kGateCZ); - EXPECT_EQ(fused_gates[4].time, 9); - EXPECT_EQ(fused_gates[4].qubits.size(), 2); - EXPECT_EQ(fused_gates[4].qubits[0], 2); - EXPECT_EQ(fused_gates[4].qubits[1], 3); - EXPECT_EQ(fused_gates[4].gates.size(), 4); - EXPECT_EQ(fused_gates[4].gates[0]->kind, kGateX); - EXPECT_EQ(fused_gates[4].gates[0]->time, 3); - EXPECT_EQ(fused_gates[4].gates[0]->qubits.size(), 1); - EXPECT_EQ(fused_gates[4].gates[0]->qubits[0], 3); - EXPECT_EQ(fused_gates[4].gates[1]->kind, kGateCZ); - EXPECT_EQ(fused_gates[4].gates[1]->time, 9); - EXPECT_EQ(fused_gates[4].gates[1]->qubits.size(), 2); - EXPECT_EQ(fused_gates[4].gates[1]->qubits[0], 2); - EXPECT_EQ(fused_gates[4].gates[1]->qubits[1], 3); - EXPECT_EQ(fused_gates[4].gates[2]->kind, kGateHd); - EXPECT_EQ(fused_gates[4].gates[2]->time, 10); - EXPECT_EQ(fused_gates[4].gates[2]->qubits.size(), 1); - EXPECT_EQ(fused_gates[4].gates[2]->qubits[0], 2); - EXPECT_EQ(fused_gates[4].gates[3]->kind, kGateHd); - EXPECT_EQ(fused_gates[4].gates[3]->time, 10); - EXPECT_EQ(fused_gates[4].gates[3]->qubits.size(), 1); - EXPECT_EQ(fused_gates[4].gates[3]->qubits[0], 3); + const auto* fgate0 = OpGetAlternative(fused_gates[0]); + ASSERT_NE(fgate0, nullptr); + EXPECT_EQ(fgate0->kind, kGateCZ); + EXPECT_EQ(fgate0->time, 1); + EXPECT_EQ(fgate0->qubits.size(), 2); + EXPECT_EQ(fgate0->qubits[0], 0); + EXPECT_EQ(fgate0->qubits[1], 1); + EXPECT_EQ(fgate0->gates.size(), 5); + const auto* gate00 = OpGetAlternative(fgate0->gates[0]); + ASSERT_NE(gate00, nullptr); + EXPECT_EQ(gate00->kind, kGateHd); + EXPECT_EQ(gate00->time, 0); + EXPECT_EQ(gate00->qubits.size(), 1); + EXPECT_EQ(gate00->qubits[0], 0); + const auto* gate01 = OpGetAlternative(fgate0->gates[1]); + ASSERT_NE(gate01, nullptr); + EXPECT_EQ(gate01->kind, kGateHd); + EXPECT_EQ(gate01->time, 0); + EXPECT_EQ(gate01->qubits.size(), 1); + EXPECT_EQ(gate01->qubits[0], 1); + const auto* gate02 = OpGetAlternative(fgate0->gates[2]); + ASSERT_NE(gate02, nullptr); + EXPECT_EQ(gate02->kind, kGateCZ); + EXPECT_EQ(gate02->time, 1); + EXPECT_EQ(gate02->qubits.size(), 2); + EXPECT_EQ(gate02->qubits[0], 0); + EXPECT_EQ(gate02->qubits[1], 1); + const auto* gate03 = OpGetAlternative(fgate0->gates[3]); + ASSERT_NE(gate03, nullptr); + EXPECT_EQ(gate03->kind, kGateT); + EXPECT_EQ(gate03->time, 2); + EXPECT_EQ(gate03->qubits.size(), 1); + EXPECT_EQ(gate03->qubits[0], 0); + const auto* gate04 = OpGetAlternative(fgate0->gates[4]); + ASSERT_NE(gate04, nullptr); + EXPECT_EQ(gate04->kind, kGateX); + EXPECT_EQ(gate04->time, 2); + EXPECT_EQ(gate04->qubits.size(), 1); + EXPECT_EQ(gate04->qubits[0], 1); + + const auto* fgate1 = OpGetAlternative(fused_gates[1]); + ASSERT_NE(fgate1, nullptr); + EXPECT_EQ(fgate1->kind, kGateCZ); + EXPECT_EQ(fgate1->time, 1); + EXPECT_EQ(fgate1->qubits.size(), 2); + EXPECT_EQ(fgate1->qubits[0], 2); + EXPECT_EQ(fgate1->qubits[1], 3); + EXPECT_EQ(fgate1->gates.size(), 5); + const auto* gate10 = OpGetAlternative(fgate1->gates[0]); + ASSERT_NE(gate10, nullptr); + EXPECT_EQ(gate10->kind, kGateHd); + EXPECT_EQ(gate10->time, 0); + EXPECT_EQ(gate10->qubits.size(), 1); + EXPECT_EQ(gate10->qubits[0], 2); + const auto* gate11 = OpGetAlternative(fgate1->gates[1]); + ASSERT_NE(gate11, nullptr); + EXPECT_EQ(gate11->kind, kGateHd); + EXPECT_EQ(gate11->time, 0); + EXPECT_EQ(gate11->qubits.size(), 1); + EXPECT_EQ(gate11->qubits[0], 3); + const auto* gate12 = OpGetAlternative(fgate1->gates[2]); + ASSERT_NE(gate12, nullptr); + EXPECT_EQ(gate12->kind, kGateCZ); + EXPECT_EQ(gate12->time, 1); + EXPECT_EQ(gate12->qubits.size(), 2); + EXPECT_EQ(gate12->qubits[0], 2); + EXPECT_EQ(gate12->qubits[1], 3); + const auto* gate13 = OpGetAlternative(fgate1->gates[3]); + ASSERT_NE(gate13, nullptr); + EXPECT_EQ(gate13->kind, kGateY); + EXPECT_EQ(gate13->time, 2); + EXPECT_EQ(gate13->qubits.size(), 1); + EXPECT_EQ(gate13->qubits[0], 2); + const auto* gate14 = OpGetAlternative(fgate1->gates[4]); + ASSERT_NE(gate14, nullptr); + EXPECT_EQ(gate14->kind, kGateT); + EXPECT_EQ(gate14->time, 2); + EXPECT_EQ(gate14->qubits.size(), 1); + EXPECT_EQ(gate14->qubits[0], 3); + + const auto* fgate2 = OpGetAlternative(fused_gates[2]); + ASSERT_NE(fgate2, nullptr); + EXPECT_EQ(fgate2->kind, kGateCZ); + EXPECT_EQ(fgate2->time, 3); + EXPECT_EQ(fgate2->qubits.size(), 2); + EXPECT_EQ(fgate2->qubits[0], 1); + EXPECT_EQ(fgate2->qubits[1], 2); + EXPECT_EQ(fgate2->gates.size(), 9); + const auto* gate20 = OpGetAlternative(fgate2->gates[0]); + ASSERT_NE(gate20, nullptr); + EXPECT_EQ(gate20->kind, kGateCZ); + EXPECT_EQ(gate20->time, 3); + EXPECT_EQ(gate20->qubits.size(), 2); + EXPECT_EQ(gate20->qubits[0], 1); + EXPECT_EQ(gate20->qubits[1], 2); + const auto* gate21 = OpGetAlternative(fgate2->gates[1]); + ASSERT_NE(gate21, nullptr); + EXPECT_EQ(gate21->kind, kGateT); + EXPECT_EQ(gate21->time, 4); + EXPECT_EQ(gate21->qubits.size(), 1); + EXPECT_EQ(gate21->qubits[0], 1); + const auto* gate22 = OpGetAlternative(fgate2->gates[2]); + ASSERT_NE(gate22, nullptr); + EXPECT_EQ(gate22->kind, kGateT); + EXPECT_EQ(gate22->time, 4); + EXPECT_EQ(gate22->qubits.size(), 1); + EXPECT_EQ(gate22->qubits[0], 2); + const auto* gate23 = OpGetAlternative(fgate2->gates[3]); + ASSERT_NE(gate23, nullptr); + EXPECT_EQ(gate23->kind, kGateCZ); + EXPECT_EQ(gate23->time, 5); + EXPECT_EQ(gate23->qubits.size(), 2); + EXPECT_EQ(gate23->qubits[0], 1); + EXPECT_EQ(gate23->qubits[1], 2); + const auto* gate24 = OpGetAlternative(fgate2->gates[4]); + ASSERT_NE(gate24, nullptr); + EXPECT_EQ(gate24->kind, kGateX); + EXPECT_EQ(gate24->time, 6); + EXPECT_EQ(gate24->qubits.size(), 1); + EXPECT_EQ(gate24->qubits[0], 1); + const auto* gate25 = OpGetAlternative(fgate2->gates[5]); + ASSERT_NE(gate25, nullptr); + EXPECT_EQ(gate25->kind, kGateY); + EXPECT_EQ(gate25->time, 6); + EXPECT_EQ(gate25->qubits.size(), 1); + EXPECT_EQ(gate25->qubits[0], 2); + const auto* gate26 = OpGetAlternative(fgate2->gates[6]); + ASSERT_NE(gate26, nullptr); + EXPECT_EQ(gate26->kind, kGateCZ); + EXPECT_EQ(gate26->time, 7); + EXPECT_EQ(gate26->qubits.size(), 2); + EXPECT_EQ(gate26->qubits[0], 1); + EXPECT_EQ(gate26->qubits[1], 2); + const auto* gate27 = OpGetAlternative(fgate2->gates[7]); + ASSERT_NE(gate27, nullptr); + EXPECT_EQ(gate27->kind, kGateT); + EXPECT_EQ(gate27->time, 8); + EXPECT_EQ(gate27->qubits.size(), 1); + EXPECT_EQ(gate27->qubits[0], 1); + const auto* gate28 = OpGetAlternative(fgate2->gates[8]); + ASSERT_NE(gate28, nullptr); + EXPECT_EQ(gate28->kind, kGateT); + EXPECT_EQ(gate28->time, 8); + EXPECT_EQ(gate28->qubits.size(), 1); + EXPECT_EQ(gate28->qubits[0], 2); + + const auto* fgate3 = OpGetAlternative(fused_gates[3]); + ASSERT_NE(fgate3, nullptr); + EXPECT_EQ(fgate3->kind, kGateCZ); + EXPECT_EQ(fgate3->time, 9); + EXPECT_EQ(fgate3->qubits.size(), 2); + EXPECT_EQ(fgate3->qubits[0], 0); + EXPECT_EQ(fgate3->qubits[1], 1); + EXPECT_EQ(fgate3->gates.size(), 4); + const auto* gate30 = OpGetAlternative(fgate3->gates[0]); + ASSERT_NE(gate30, nullptr); + EXPECT_EQ(gate30->kind, kGateY); + EXPECT_EQ(gate30->time, 3); + EXPECT_EQ(gate30->qubits.size(), 1); + EXPECT_EQ(gate30->qubits[0], 0); + const auto* gate31 = OpGetAlternative(fgate3->gates[1]); + ASSERT_NE(gate31, nullptr); + EXPECT_EQ(gate31->kind, kGateCZ); + EXPECT_EQ(gate31->time, 9); + EXPECT_EQ(gate31->qubits.size(), 2); + EXPECT_EQ(gate31->qubits[0], 0); + EXPECT_EQ(gate31->qubits[1], 1); + const auto* gate32 = OpGetAlternative(fgate3->gates[2]); + ASSERT_NE(gate32, nullptr); + EXPECT_EQ(gate32->kind, kGateHd); + EXPECT_EQ(gate32->time, 10); + EXPECT_EQ(gate32->qubits.size(), 1); + EXPECT_EQ(gate32->qubits[0], 0); + const auto* gate33 = OpGetAlternative(fgate3->gates[3]); + ASSERT_NE(gate33, nullptr); + EXPECT_EQ(gate33->kind, kGateHd); + EXPECT_EQ(gate33->time, 10); + EXPECT_EQ(gate33->qubits.size(), 1); + EXPECT_EQ(gate33->qubits[0], 1); + + const auto* fgate4 = OpGetAlternative(fused_gates[4]); + ASSERT_NE(fgate4, nullptr); + EXPECT_EQ(fgate4->kind, kGateCZ); + EXPECT_EQ(fgate4->time, 9); + EXPECT_EQ(fgate4->qubits.size(), 2); + EXPECT_EQ(fgate4->qubits[0], 2); + EXPECT_EQ(fgate4->qubits[1], 3); + EXPECT_EQ(fgate4->gates.size(), 4); + const auto* gate40 = OpGetAlternative(fgate4->gates[0]); + ASSERT_NE(gate40, nullptr); + EXPECT_EQ(gate40->kind, kGateX); + EXPECT_EQ(gate40->time, 3); + EXPECT_EQ(gate40->qubits.size(), 1); + EXPECT_EQ(gate40->qubits[0], 3); + const auto* gate41 = OpGetAlternative(fgate4->gates[1]); + ASSERT_NE(gate41, nullptr); + EXPECT_EQ(gate41->kind, kGateCZ); + EXPECT_EQ(gate41->time, 9); + EXPECT_EQ(gate41->qubits.size(), 2); + EXPECT_EQ(gate41->qubits[0], 2); + EXPECT_EQ(gate41->qubits[1], 3); + const auto* gate42 = OpGetAlternative(fgate4->gates[2]); + ASSERT_NE(gate42, nullptr); + EXPECT_EQ(gate42->kind, kGateHd); + EXPECT_EQ(gate42->time, 10); + EXPECT_EQ(gate42->qubits.size(), 1); + EXPECT_EQ(gate42->qubits[0], 2); + const auto* gate43 = OpGetAlternative(fgate4->gates[3]); + ASSERT_NE(gate43, nullptr); + EXPECT_EQ(gate43->kind, kGateHd); + EXPECT_EQ(gate43->time, 10); + EXPECT_EQ(gate43->qubits.size(), 1); + EXPECT_EQ(gate43->qubits[0], 3); } constexpr char circuit_string2[] = @@ -585,207 +793,293 @@ R"(3 )"; TEST(FuserBasicTest, OrphanedQubits1) { + using Gate = qsim::Gate; + using Operation = qsim::Operation; + using FusedGate = qsim::FusedGate; + std::stringstream ss(circuit_string2); - Circuit> circuit; + Circuit circuit; EXPECT_TRUE(CircuitQsimParser::FromStream(2, provider, ss, circuit)); EXPECT_EQ(circuit.num_qubits, 3); - EXPECT_EQ(circuit.gates.size(), 7); + EXPECT_EQ(circuit.ops.size(), 7); - using Fuser = BasicGateFuser>; + using Fuser = BasicGateFuser; Fuser::Parameter param; - auto fused_gates = Fuser::FuseGates(param, circuit.num_qubits, circuit.gates); + auto fused_gates = Fuser::FuseGates(param, circuit.num_qubits, circuit.ops); EXPECT_EQ(fused_gates.size(), 2); - EXPECT_EQ(fused_gates[0].kind, kGateCZ); - EXPECT_EQ(fused_gates[0].time, 1); - EXPECT_EQ(fused_gates[0].qubits.size(), 2); - EXPECT_EQ(fused_gates[0].qubits[0], 0); - EXPECT_EQ(fused_gates[0].qubits[1], 1); - EXPECT_EQ(fused_gates[0].gates.size(), 5); - EXPECT_EQ(fused_gates[0].gates[0]->kind, kGateHd); - EXPECT_EQ(fused_gates[0].gates[0]->time, 0); - EXPECT_EQ(fused_gates[0].gates[0]->qubits.size(), 1); - EXPECT_EQ(fused_gates[0].gates[0]->qubits[0], 0); - EXPECT_EQ(fused_gates[0].gates[1]->kind, kGateHd); - EXPECT_EQ(fused_gates[0].gates[1]->time, 0); - EXPECT_EQ(fused_gates[0].gates[1]->qubits.size(), 1); - EXPECT_EQ(fused_gates[0].gates[1]->qubits[0], 1); - EXPECT_EQ(fused_gates[0].gates[2]->kind, kGateCZ); - EXPECT_EQ(fused_gates[0].gates[2]->time, 1); - EXPECT_EQ(fused_gates[0].gates[2]->qubits.size(), 2); - EXPECT_EQ(fused_gates[0].gates[2]->qubits[0], 0); - EXPECT_EQ(fused_gates[0].gates[2]->qubits[1], 1); - EXPECT_EQ(fused_gates[0].gates[3]->kind, kGateT); - EXPECT_EQ(fused_gates[0].gates[3]->time, 2); - EXPECT_EQ(fused_gates[0].gates[3]->qubits.size(), 1); - EXPECT_EQ(fused_gates[0].gates[3]->qubits[0], 0); - EXPECT_EQ(fused_gates[0].gates[4]->kind, kGateX); - EXPECT_EQ(fused_gates[0].gates[4]->time, 2); - EXPECT_EQ(fused_gates[0].gates[4]->qubits.size(), 1); - EXPECT_EQ(fused_gates[0].gates[4]->qubits[0], 1); - - EXPECT_EQ(fused_gates[1].kind, kGateHd); - EXPECT_EQ(fused_gates[1].time, 0); - EXPECT_EQ(fused_gates[1].qubits.size(), 1); - EXPECT_EQ(fused_gates[1].qubits[0], 2); - EXPECT_EQ(fused_gates[1].gates.size(), 2); - EXPECT_EQ(fused_gates[1].gates[0]->kind, kGateHd); - EXPECT_EQ(fused_gates[1].gates[0]->time, 0); - EXPECT_EQ(fused_gates[1].gates[0]->qubits.size(), 1); - EXPECT_EQ(fused_gates[1].gates[0]->qubits[0], 2); - EXPECT_EQ(fused_gates[1].gates[1]->kind, kGateY); - EXPECT_EQ(fused_gates[1].gates[1]->time, 2); - EXPECT_EQ(fused_gates[1].gates[1]->qubits.size(), 1); - EXPECT_EQ(fused_gates[1].gates[1]->qubits[0], 2); + const auto* fgate0 = OpGetAlternative(fused_gates[0]); + ASSERT_NE(fgate0, nullptr); + EXPECT_EQ(fgate0->kind, kGateCZ); + EXPECT_EQ(fgate0->time, 1); + EXPECT_EQ(fgate0->qubits.size(), 2); + EXPECT_EQ(fgate0->qubits[0], 0); + EXPECT_EQ(fgate0->qubits[1], 1); + EXPECT_EQ(fgate0->gates.size(), 5); + const auto* gate00 = OpGetAlternative(fgate0->gates[0]); + ASSERT_NE(gate00, nullptr); + EXPECT_EQ(gate00->kind, kGateHd); + EXPECT_EQ(gate00->time, 0); + EXPECT_EQ(gate00->qubits.size(), 1); + EXPECT_EQ(gate00->qubits[0], 0); + const auto* gate01 = OpGetAlternative(fgate0->gates[1]); + ASSERT_NE(gate01, nullptr); + EXPECT_EQ(gate01->kind, kGateHd); + EXPECT_EQ(gate01->time, 0); + EXPECT_EQ(gate01->qubits.size(), 1); + EXPECT_EQ(gate01->qubits[0], 1); + const auto* gate02 = OpGetAlternative(fgate0->gates[2]); + ASSERT_NE(gate02, nullptr); + EXPECT_EQ(gate02->kind, kGateCZ); + EXPECT_EQ(gate02->time, 1); + EXPECT_EQ(gate02->qubits.size(), 2); + EXPECT_EQ(gate02->qubits[0], 0); + EXPECT_EQ(gate02->qubits[1], 1); + const auto* gate03 = OpGetAlternative(fgate0->gates[3]); + ASSERT_NE(gate03, nullptr); + EXPECT_EQ(gate03->kind, kGateT); + EXPECT_EQ(gate03->time, 2); + EXPECT_EQ(gate03->qubits.size(), 1); + EXPECT_EQ(gate03->qubits[0], 0); + const auto* gate04 = OpGetAlternative(fgate0->gates[4]); + ASSERT_NE(gate04, nullptr); + EXPECT_EQ(gate04->kind, kGateX); + EXPECT_EQ(gate04->time, 2); + EXPECT_EQ(gate04->qubits.size(), 1); + EXPECT_EQ(gate04->qubits[0], 1); + + const auto* fgate1 = OpGetAlternative(fused_gates[1]); + ASSERT_NE(fgate1, nullptr); + EXPECT_EQ(fgate1->kind, kGateHd); + EXPECT_EQ(fgate1->time, 0); + EXPECT_EQ(fgate1->qubits.size(), 1); + EXPECT_EQ(fgate1->qubits[0], 2); + EXPECT_EQ(fgate1->gates.size(), 2); + const auto* gate10 = OpGetAlternative(fgate1->gates[0]); + ASSERT_NE(gate10, nullptr); + EXPECT_EQ(gate10->kind, kGateHd); + EXPECT_EQ(gate10->time, 0); + EXPECT_EQ(gate10->qubits.size(), 1); + EXPECT_EQ(gate10->qubits[0], 2); + const auto* gate11 = OpGetAlternative(fgate1->gates[1]); + ASSERT_NE(gate11, nullptr); + EXPECT_EQ(gate11->kind, kGateY); + EXPECT_EQ(gate11->time, 2); + EXPECT_EQ(gate11->qubits.size(), 1); + EXPECT_EQ(gate11->qubits[0], 2); } TEST(FuserBasicTest, OrphanedQubits2) { + using Gate = qsim::Gate; + using Operation = qsim::Operation; + using FusedGate = qsim::FusedGate; + std::stringstream ss(circuit_string2); - Circuit> circuit; + Circuit circuit; EXPECT_TRUE(CircuitQsimParser::FromStream(99, provider, ss, circuit)); EXPECT_EQ(circuit.num_qubits, 3); - EXPECT_EQ(circuit.gates.size(), 9); + EXPECT_EQ(circuit.ops.size(), 9); std::vector times_to_split_at{1, 4}; - using Fuser = BasicGateFuser>; + using Fuser = BasicGateFuser; Fuser::Parameter param; auto fused_gates = Fuser::FuseGates( - param, circuit.num_qubits, circuit.gates, times_to_split_at); + param, circuit.num_qubits, circuit.ops, times_to_split_at); EXPECT_EQ(fused_gates.size(), 4); - EXPECT_EQ(fused_gates[0].kind, kGateCZ); - EXPECT_EQ(fused_gates[0].time, 1); - EXPECT_EQ(fused_gates[0].qubits.size(), 2); - EXPECT_EQ(fused_gates[0].qubits[0], 0); - EXPECT_EQ(fused_gates[0].qubits[1], 1); - EXPECT_EQ(fused_gates[0].gates.size(), 3); - EXPECT_EQ(fused_gates[0].gates[0]->kind, kGateHd); - EXPECT_EQ(fused_gates[0].gates[0]->time, 0); - EXPECT_EQ(fused_gates[0].gates[0]->qubits.size(), 1); - EXPECT_EQ(fused_gates[0].gates[0]->qubits[0], 0); - EXPECT_EQ(fused_gates[0].gates[1]->kind, kGateHd); - EXPECT_EQ(fused_gates[0].gates[1]->time, 0); - EXPECT_EQ(fused_gates[0].gates[1]->qubits.size(), 1); - EXPECT_EQ(fused_gates[0].gates[1]->qubits[0], 1); - EXPECT_EQ(fused_gates[0].gates[2]->kind, kGateCZ); - EXPECT_EQ(fused_gates[0].gates[2]->time, 1); - EXPECT_EQ(fused_gates[0].gates[2]->qubits.size(), 2); - EXPECT_EQ(fused_gates[0].gates[2]->qubits[0], 0); - EXPECT_EQ(fused_gates[0].gates[2]->qubits[1], 1); - - EXPECT_EQ(fused_gates[1].kind, kGateHd); - EXPECT_EQ(fused_gates[1].time, 0); - EXPECT_EQ(fused_gates[1].qubits.size(), 1); - EXPECT_EQ(fused_gates[1].qubits[0], 2); - EXPECT_EQ(fused_gates[1].gates.size(), 1); - EXPECT_EQ(fused_gates[1].gates[0]->kind, kGateHd); - EXPECT_EQ(fused_gates[1].gates[0]->time, 0); - EXPECT_EQ(fused_gates[1].gates[0]->qubits.size(), 1); - EXPECT_EQ(fused_gates[1].gates[0]->qubits[0], 2); - - EXPECT_EQ(fused_gates[2].kind, kGateCZ); - EXPECT_EQ(fused_gates[2].time, 3); - EXPECT_EQ(fused_gates[2].qubits.size(), 2); - EXPECT_EQ(fused_gates[2].qubits[0], 1); - EXPECT_EQ(fused_gates[2].qubits[1], 2); - EXPECT_EQ(fused_gates[2].gates.size(), 3); - EXPECT_EQ(fused_gates[2].gates[0]->kind, kGateX); - EXPECT_EQ(fused_gates[2].gates[0]->time, 2); - EXPECT_EQ(fused_gates[2].gates[0]->qubits.size(), 1); - EXPECT_EQ(fused_gates[2].gates[0]->qubits[0], 1); - EXPECT_EQ(fused_gates[2].gates[1]->kind, kGateY); - EXPECT_EQ(fused_gates[2].gates[1]->time, 2); - EXPECT_EQ(fused_gates[2].gates[1]->qubits.size(), 1); - EXPECT_EQ(fused_gates[2].gates[1]->qubits[0], 2); - EXPECT_EQ(fused_gates[2].gates[2]->kind, kGateCZ); - EXPECT_EQ(fused_gates[2].gates[2]->time, 3); - EXPECT_EQ(fused_gates[2].gates[2]->qubits.size(), 2); - EXPECT_EQ(fused_gates[2].gates[2]->qubits[0], 1); - EXPECT_EQ(fused_gates[2].gates[2]->qubits[1], 2); - - EXPECT_EQ(fused_gates[3].kind, kGateT); - EXPECT_EQ(fused_gates[3].time, 2); - EXPECT_EQ(fused_gates[3].qubits.size(), 1); - EXPECT_EQ(fused_gates[3].qubits[0], 0); - EXPECT_EQ(fused_gates[3].gates.size(), 2); - EXPECT_EQ(fused_gates[3].gates[0]->kind, kGateT); - EXPECT_EQ(fused_gates[3].gates[0]->time, 2); - EXPECT_EQ(fused_gates[3].gates[0]->qubits.size(), 1); - EXPECT_EQ(fused_gates[3].gates[0]->qubits[0], 0); - EXPECT_EQ(fused_gates[3].gates[1]->kind, kGateX); - EXPECT_EQ(fused_gates[3].gates[1]->time, 4); - EXPECT_EQ(fused_gates[3].gates[1]->qubits.size(), 1); - EXPECT_EQ(fused_gates[3].gates[1]->qubits[0], 0); + const auto* fgate0 = OpGetAlternative(fused_gates[0]); + ASSERT_NE(fgate0, nullptr); + EXPECT_EQ(fgate0->kind, kGateCZ); + EXPECT_EQ(fgate0->time, 1); + EXPECT_EQ(fgate0->qubits.size(), 2); + EXPECT_EQ(fgate0->qubits[0], 0); + EXPECT_EQ(fgate0->qubits[1], 1); + EXPECT_EQ(fgate0->gates.size(), 3); + const auto* gate00 = OpGetAlternative(fgate0->gates[0]); + ASSERT_NE(gate00, nullptr); + EXPECT_EQ(gate00->kind, kGateHd); + EXPECT_EQ(gate00->time, 0); + EXPECT_EQ(gate00->qubits.size(), 1); + EXPECT_EQ(gate00->qubits[0], 0); + const auto* gate01 = OpGetAlternative(fgate0->gates[1]); + ASSERT_NE(gate01, nullptr); + EXPECT_EQ(gate01->kind, kGateHd); + EXPECT_EQ(gate01->time, 0); + EXPECT_EQ(gate01->qubits.size(), 1); + EXPECT_EQ(gate01->qubits[0], 1); + const auto* gate02 = OpGetAlternative(fgate0->gates[2]); + ASSERT_NE(gate02, nullptr); + EXPECT_EQ(gate02->kind, kGateCZ); + EXPECT_EQ(gate02->time, 1); + EXPECT_EQ(gate02->qubits.size(), 2); + EXPECT_EQ(gate02->qubits[0], 0); + EXPECT_EQ(gate02->qubits[1], 1); + + const auto* fgate1 = OpGetAlternative(fused_gates[1]); + ASSERT_NE(fgate1, nullptr); + EXPECT_EQ(fgate1->kind, kGateHd); + EXPECT_EQ(fgate1->time, 0); + EXPECT_EQ(fgate1->qubits.size(), 1); + EXPECT_EQ(fgate1->qubits[0], 2); + EXPECT_EQ(fgate1->gates.size(), 1); + const auto* gate10 = OpGetAlternative(fgate1->gates[0]); + ASSERT_NE(gate10, nullptr); + EXPECT_EQ(gate10->kind, kGateHd); + EXPECT_EQ(gate10->time, 0); + EXPECT_EQ(gate10->qubits.size(), 1); + EXPECT_EQ(gate10->qubits[0], 2); + + const auto* fgate2 = OpGetAlternative(fused_gates[2]); + ASSERT_NE(fgate2, nullptr); + EXPECT_EQ(fgate2->kind, kGateCZ); + EXPECT_EQ(fgate2->time, 3); + EXPECT_EQ(fgate2->qubits.size(), 2); + EXPECT_EQ(fgate2->qubits[0], 1); + EXPECT_EQ(fgate2->qubits[1], 2); + EXPECT_EQ(fgate2->gates.size(), 3); + const auto* gate20 = OpGetAlternative(fgate2->gates[0]); + ASSERT_NE(gate20, nullptr); + EXPECT_EQ(gate20->kind, kGateX); + EXPECT_EQ(gate20->time, 2); + EXPECT_EQ(gate20->qubits.size(), 1); + EXPECT_EQ(gate20->qubits[0], 1); + const auto* gate21 = OpGetAlternative(fgate2->gates[1]); + ASSERT_NE(gate21, nullptr); + EXPECT_EQ(gate21->kind, kGateY); + EXPECT_EQ(gate21->time, 2); + EXPECT_EQ(gate21->qubits.size(), 1); + EXPECT_EQ(gate21->qubits[0], 2); + const auto* gate22 = OpGetAlternative(fgate2->gates[2]); + ASSERT_NE(gate22, nullptr); + EXPECT_EQ(gate22->kind, kGateCZ); + EXPECT_EQ(gate22->time, 3); + EXPECT_EQ(gate22->qubits.size(), 2); + EXPECT_EQ(gate22->qubits[0], 1); + EXPECT_EQ(gate22->qubits[1], 2); + + const auto* fgate3 = OpGetAlternative(fused_gates[3]); + ASSERT_NE(fgate3, nullptr); + EXPECT_EQ(fgate3->kind, kGateT); + EXPECT_EQ(fgate3->time, 2); + EXPECT_EQ(fgate3->qubits.size(), 1); + EXPECT_EQ(fgate3->qubits[0], 0); + EXPECT_EQ(fgate3->gates.size(), 2); + const auto* gate30 = OpGetAlternative(fgate3->gates[0]); + ASSERT_NE(gate30, nullptr); + EXPECT_EQ(gate30->kind, kGateT); + EXPECT_EQ(gate30->time, 2); + EXPECT_EQ(gate30->qubits.size(), 1); + EXPECT_EQ(gate30->qubits[0], 0); + const auto* gate31 = OpGetAlternative(fgate3->gates[1]); + ASSERT_NE(gate31, nullptr); + EXPECT_EQ(gate31->kind, kGateX); + EXPECT_EQ(gate31->time, 4); + EXPECT_EQ(gate31->qubits.size(), 1); + EXPECT_EQ(gate31->qubits[0], 0); } -TEST(FuserBasicTest, UnfusibleSingleQubitGate) { +TEST(FuserBasicTest, DecomposedQubitGate) { + using Gate = qsim::Gate; + using Operation = qsim::Operation; + using FusedGate = qsim::FusedGate; + using DecomposedGate = qsim::DecomposedGate; + + using OperationD = detail::append_to_variant_t; + std::stringstream ss(circuit_string2); - Circuit> circuit; + Circuit circuit; EXPECT_TRUE(CircuitQsimParser::FromStream(2, provider, ss, circuit)); EXPECT_EQ(circuit.num_qubits, 3); - EXPECT_EQ(circuit.gates.size(), 7); + EXPECT_EQ(circuit.ops.size(), 7); + + Circuit circuitd; + circuitd.num_qubits = circuit.num_qubits; + circuitd.ops.resize(circuit.ops.size()); - circuit.gates[1].unfusible = true; - circuit.gates[2].unfusible = true; + circuitd.ops[0] = *OpGetAlternative(circuit.ops[0]); + circuitd.ops[1] = DecomposedGate{*OpGetAlternative(circuit.ops[1])}; + circuitd.ops[2] = DecomposedGate{*OpGetAlternative(circuit.ops[2])}; + circuitd.ops[3] = *OpGetAlternative(circuit.ops[3]); + circuitd.ops[4] = *OpGetAlternative(circuit.ops[4]); + circuitd.ops[5] = *OpGetAlternative(circuit.ops[5]); + circuitd.ops[6] = *OpGetAlternative(circuit.ops[6]); - using Fuser = BasicGateFuser>; + using Fuser = BasicGateFuser; Fuser::Parameter param; - auto fused_gates = Fuser::FuseGates(param, circuit.num_qubits, circuit.gates); + auto fused_gates = Fuser::FuseGates(param, circuitd.num_qubits, circuitd.ops); EXPECT_EQ(fused_gates.size(), 3); - EXPECT_EQ(fused_gates[0].kind, kGateHd); - EXPECT_EQ(fused_gates[0].time, 0); - EXPECT_EQ(fused_gates[0].qubits.size(), 1); - EXPECT_EQ(fused_gates[0].qubits[0], 1); - EXPECT_EQ(fused_gates[0].gates[0]->kind, kGateHd); - EXPECT_EQ(fused_gates[0].gates[0]->time, 0); - EXPECT_EQ(fused_gates[0].gates[0]->qubits.size(), 1); - EXPECT_EQ(fused_gates[0].gates[0]->qubits[0], 1); - - EXPECT_EQ(fused_gates[1].kind, kGateHd); - EXPECT_EQ(fused_gates[1].time, 0); - EXPECT_EQ(fused_gates[1].qubits.size(), 1); - EXPECT_EQ(fused_gates[1].qubits[0], 2); - EXPECT_EQ(fused_gates[1].gates.size(), 2); - EXPECT_EQ(fused_gates[1].gates[0]->kind, kGateHd); - EXPECT_EQ(fused_gates[1].gates[0]->time, 0); - EXPECT_EQ(fused_gates[1].gates[0]->qubits.size(), 1); - EXPECT_EQ(fused_gates[1].gates[0]->qubits[0], 2); - EXPECT_EQ(fused_gates[1].gates[1]->kind, kGateY); - EXPECT_EQ(fused_gates[1].gates[1]->time, 2); - EXPECT_EQ(fused_gates[1].gates[1]->qubits.size(), 1); - EXPECT_EQ(fused_gates[1].gates[1]->qubits[0], 2); - - EXPECT_EQ(fused_gates[2].kind, kGateCZ); - EXPECT_EQ(fused_gates[2].time, 1); - EXPECT_EQ(fused_gates[2].qubits.size(), 2); - EXPECT_EQ(fused_gates[2].qubits[0], 0); - EXPECT_EQ(fused_gates[2].qubits[1], 1); - EXPECT_EQ(fused_gates[2].gates.size(), 4); - EXPECT_EQ(fused_gates[2].gates[0]->kind, kGateHd); - EXPECT_EQ(fused_gates[2].gates[0]->time, 0); - EXPECT_EQ(fused_gates[2].gates[0]->qubits.size(), 1); - EXPECT_EQ(fused_gates[2].gates[0]->qubits[0], 0); - EXPECT_EQ(fused_gates[2].gates[1]->kind, kGateCZ); - EXPECT_EQ(fused_gates[2].gates[1]->time, 1); - EXPECT_EQ(fused_gates[2].gates[1]->qubits.size(), 2); - EXPECT_EQ(fused_gates[2].gates[1]->qubits[0], 0); - EXPECT_EQ(fused_gates[2].gates[1]->qubits[1], 1); - EXPECT_EQ(fused_gates[2].gates[2]->kind, kGateT); - EXPECT_EQ(fused_gates[2].gates[2]->time, 2); - EXPECT_EQ(fused_gates[2].gates[2]->qubits.size(), 1); - EXPECT_EQ(fused_gates[2].gates[2]->qubits[0], 0); - EXPECT_EQ(fused_gates[2].gates[3]->kind, kGateX); - EXPECT_EQ(fused_gates[2].gates[3]->time, 2); - EXPECT_EQ(fused_gates[2].gates[3]->qubits.size(), 1); - EXPECT_EQ(fused_gates[2].gates[3]->qubits[0], 1); + const auto* fgate0 = OpGetAlternative(fused_gates[0]); + ASSERT_NE(fgate0, nullptr); + EXPECT_EQ(fgate0->kind, kGateHd); + EXPECT_EQ(fgate0->time, 0); + EXPECT_EQ(fgate0->qubits.size(), 1); + EXPECT_EQ(fgate0->qubits[0], 1); + const auto* gate00 = OpGetAlternative(fgate0->gates[0]); + EXPECT_EQ(gate00->kind, kGateHd); + EXPECT_EQ(gate00->time, 0); + EXPECT_EQ(gate00->qubits.size(), 1); + EXPECT_EQ(gate00->qubits[0], 1); + + const auto* fgate1 = OpGetAlternative(fused_gates[1]); + ASSERT_NE(fgate1, nullptr); + EXPECT_EQ(fgate1->kind, kGateHd); + EXPECT_EQ(fgate1->time, 0); + EXPECT_EQ(fgate1->qubits.size(), 1); + EXPECT_EQ(fgate1->qubits[0], 2); + EXPECT_EQ(fgate1->gates.size(), 2); + const auto* gate10 = OpGetAlternative(fgate1->gates[0]); + EXPECT_EQ(gate10->kind, kGateHd); + EXPECT_EQ(gate10->time, 0); + EXPECT_EQ(gate10->qubits.size(), 1); + EXPECT_EQ(gate10->qubits[0], 2); + const auto* gate11 = OpGetAlternative(fgate1->gates[1]); + ASSERT_NE(gate11, nullptr); + EXPECT_EQ(gate11->kind, kGateY); + EXPECT_EQ(gate11->time, 2); + EXPECT_EQ(gate11->qubits.size(), 1); + EXPECT_EQ(gate11->qubits[0], 2); + + const auto* fgate2 = OpGetAlternative(fused_gates[2]); + ASSERT_NE(fgate2, nullptr); + EXPECT_EQ(fgate2->kind, kGateCZ); + EXPECT_EQ(fgate2->time, 1); + EXPECT_EQ(fgate2->qubits.size(), 2); + EXPECT_EQ(fgate2->qubits[0], 0); + EXPECT_EQ(fgate2->qubits[1], 1); + EXPECT_EQ(fgate2->gates.size(), 4); + const auto* gate20 = OpGetAlternative(fgate2->gates[0]); + ASSERT_NE(gate20, nullptr); + EXPECT_EQ(gate20->kind, kGateHd); + EXPECT_EQ(gate20->time, 0); + EXPECT_EQ(gate20->qubits.size(), 1); + EXPECT_EQ(gate20->qubits[0], 0); + const auto* gate21 = OpGetAlternative(fgate2->gates[1]); + ASSERT_NE(gate21, nullptr); + EXPECT_EQ(gate21->kind, kGateCZ); + EXPECT_EQ(gate21->time, 1); + EXPECT_EQ(gate21->qubits.size(), 2); + EXPECT_EQ(gate21->qubits[0], 0); + EXPECT_EQ(gate21->qubits[1], 1); + const auto* gate22 = OpGetAlternative(fgate2->gates[2]); + ASSERT_NE(gate22, nullptr); + EXPECT_EQ(gate22->kind, kGateT); + EXPECT_EQ(gate22->time, 2); + EXPECT_EQ(gate22->qubits.size(), 1); + EXPECT_EQ(gate22->qubits[0], 0); + const auto* gate23 = OpGetAlternative(fgate2->gates[3]); + ASSERT_NE(gate23, nullptr); + EXPECT_EQ(gate23->kind, kGateX); + EXPECT_EQ(gate23->time, 2); + EXPECT_EQ(gate23->qubits.size(), 1); + EXPECT_EQ(gate23->qubits[0], 1); } constexpr char circuit_string3[] = @@ -810,153 +1104,197 @@ R"(4 )"; TEST(FuserBasicTest, MeasurementGate) { + using Gate = qsim::Gate; + using Operation = qsim::Operation; + using FusedGate = qsim::FusedGate; + std::stringstream ss(circuit_string3); - Circuit> circuit; + Circuit circuit; EXPECT_TRUE(CircuitQsimParser::FromStream(99, provider, ss, circuit)); EXPECT_EQ(circuit.num_qubits, 4); - EXPECT_EQ(circuit.gates.size(), 17); + EXPECT_EQ(circuit.ops.size(), 17); // Vector of pointers to gates. - std::vector*> pgates; - pgates.reserve(circuit.gates.size()); + std::vector pgates; + pgates.reserve(circuit.ops.size()); - for (const auto& gate : circuit.gates) { + for (const auto& gate : circuit.ops) { pgates.push_back(&gate); } - using Fuser = BasicGateFuser*>; + using Fuser = BasicGateFuser; Fuser::Parameter param; - auto fused_gates = Fuser::FuseGates(param, circuit.num_qubits, pgates); + const auto fused_gates = Fuser::FuseGates(param, circuit.num_qubits, pgates); EXPECT_EQ(fused_gates.size(), 11); - EXPECT_EQ(fused_gates[0].kind, kGateCZ); - EXPECT_EQ(fused_gates[0].time, 1); - EXPECT_EQ(fused_gates[0].qubits.size(), 2); - EXPECT_EQ(fused_gates[0].qubits[0], 0); - EXPECT_EQ(fused_gates[0].qubits[1], 1); - EXPECT_EQ(fused_gates[0].gates.size(), 3); - EXPECT_EQ(fused_gates[0].gates[0]->kind, kGateHd); - EXPECT_EQ(fused_gates[0].gates[0]->time, 0); - EXPECT_EQ(fused_gates[0].gates[0]->qubits.size(), 1); - EXPECT_EQ(fused_gates[0].gates[0]->qubits[0], 0); - EXPECT_EQ(fused_gates[0].gates[1]->kind, kGateHd); - EXPECT_EQ(fused_gates[0].gates[1]->time, 0); - EXPECT_EQ(fused_gates[0].gates[1]->qubits.size(), 1); - EXPECT_EQ(fused_gates[0].gates[1]->qubits[0], 1); - EXPECT_EQ(fused_gates[0].gates[2]->kind, kGateCZ); - EXPECT_EQ(fused_gates[0].gates[2]->time, 1); - EXPECT_EQ(fused_gates[0].gates[2]->qubits.size(), 2); - EXPECT_EQ(fused_gates[0].gates[2]->qubits[0], 0); - EXPECT_EQ(fused_gates[0].gates[2]->qubits[1], 1); - - EXPECT_EQ(fused_gates[1].kind, kGateHd); - EXPECT_EQ(fused_gates[1].time, 0); - EXPECT_EQ(fused_gates[1].qubits.size(), 1); - EXPECT_EQ(fused_gates[1].qubits[0], 2); - EXPECT_EQ(fused_gates[1].gates.size(), 1); - EXPECT_EQ(fused_gates[1].gates[0]->kind, kGateHd); - EXPECT_EQ(fused_gates[1].gates[0]->time, 0); - EXPECT_EQ(fused_gates[1].gates[0]->qubits.size(), 1); - EXPECT_EQ(fused_gates[1].gates[0]->qubits[0], 2); - - EXPECT_EQ(fused_gates[2].kind, kGateHd); - EXPECT_EQ(fused_gates[2].time, 0); - EXPECT_EQ(fused_gates[2].qubits.size(), 1); - EXPECT_EQ(fused_gates[2].qubits[0], 3); - EXPECT_EQ(fused_gates[2].gates.size(), 1); - EXPECT_EQ(fused_gates[2].gates[0]->kind, kGateHd); - EXPECT_EQ(fused_gates[2].gates[0]->time, 0); - EXPECT_EQ(fused_gates[2].gates[0]->qubits.size(), 1); - EXPECT_EQ(fused_gates[2].gates[0]->qubits[0], 3); - - EXPECT_EQ(fused_gates[3].kind, kMeasurement); - EXPECT_EQ(fused_gates[3].time, 1); - EXPECT_EQ(fused_gates[3].qubits.size(), 2); - EXPECT_EQ(fused_gates[3].qubits[0], 2); - EXPECT_EQ(fused_gates[3].qubits[1], 3); - EXPECT_EQ(fused_gates[3].gates.size(), 2); - - EXPECT_EQ(fused_gates[4].kind, kGateIS); - EXPECT_EQ(fused_gates[4].time, 2); - EXPECT_EQ(fused_gates[4].qubits.size(), 2); - EXPECT_EQ(fused_gates[4].qubits[0], 2); - EXPECT_EQ(fused_gates[4].qubits[1], 3); - EXPECT_EQ(fused_gates[4].gates.size(), 1); - EXPECT_EQ(fused_gates[4].gates[0]->kind, kGateIS); - EXPECT_EQ(fused_gates[4].gates[0]->time, 2); - EXPECT_EQ(fused_gates[4].gates[0]->qubits.size(), 2); - EXPECT_EQ(fused_gates[4].gates[0]->qubits[0], 2); - EXPECT_EQ(fused_gates[4].gates[0]->qubits[1], 3); - - EXPECT_EQ(fused_gates[5].kind, kGateCZ); - EXPECT_EQ(fused_gates[5].time, 3); - EXPECT_EQ(fused_gates[5].qubits.size(), 2); - EXPECT_EQ(fused_gates[5].qubits[0], 0); - EXPECT_EQ(fused_gates[5].qubits[1], 1); - EXPECT_EQ(fused_gates[5].gates.size(), 3); - EXPECT_EQ(fused_gates[5].gates[0]->kind, kGateX); - EXPECT_EQ(fused_gates[5].gates[0]->time, 2); - EXPECT_EQ(fused_gates[5].gates[0]->qubits.size(), 1); - EXPECT_EQ(fused_gates[5].gates[0]->qubits[0], 0); - EXPECT_EQ(fused_gates[5].gates[1]->kind, kGateY); - EXPECT_EQ(fused_gates[5].gates[1]->time, 2); - EXPECT_EQ(fused_gates[5].gates[1]->qubits.size(), 1); - EXPECT_EQ(fused_gates[5].gates[1]->qubits[0], 1); - EXPECT_EQ(fused_gates[5].gates[2]->kind, kGateCZ); - EXPECT_EQ(fused_gates[5].gates[2]->time, 3); - EXPECT_EQ(fused_gates[5].gates[2]->qubits.size(), 2); - EXPECT_EQ(fused_gates[5].gates[2]->qubits[0], 0); - EXPECT_EQ(fused_gates[5].gates[2]->qubits[1], 1); - - EXPECT_EQ(fused_gates[6].kind, kMeasurement); - EXPECT_EQ(fused_gates[6].time, 3); - EXPECT_EQ(fused_gates[6].qubits.size(), 2); - EXPECT_EQ(fused_gates[6].qubits[0], 2); - EXPECT_EQ(fused_gates[6].qubits[1], 3); - EXPECT_EQ(fused_gates[6].gates.size(), 1); - - EXPECT_EQ(fused_gates[7].kind, kGateIS); - EXPECT_EQ(fused_gates[7].time, 4); - EXPECT_EQ(fused_gates[7].qubits.size(), 2); - EXPECT_EQ(fused_gates[7].qubits[0], 2); - EXPECT_EQ(fused_gates[7].qubits[1], 3); - EXPECT_EQ(fused_gates[7].gates.size(), 1); - EXPECT_EQ(fused_gates[7].gates[0]->kind, kGateIS); - EXPECT_EQ(fused_gates[7].gates[0]->time, 4); - EXPECT_EQ(fused_gates[7].gates[0]->qubits.size(), 2); - EXPECT_EQ(fused_gates[7].gates[0]->qubits[0], 2); - EXPECT_EQ(fused_gates[7].gates[0]->qubits[1], 3); - - EXPECT_EQ(fused_gates[8].kind, kGateX); - EXPECT_EQ(fused_gates[8].time, 4); - EXPECT_EQ(fused_gates[8].qubits.size(), 1); - EXPECT_EQ(fused_gates[8].qubits[0], 0); - EXPECT_EQ(fused_gates[8].gates.size(), 1); - EXPECT_EQ(fused_gates[8].gates[0]->kind, kGateX); - EXPECT_EQ(fused_gates[8].gates[0]->time, 4); - EXPECT_EQ(fused_gates[8].gates[0]->qubits.size(), 1); - EXPECT_EQ(fused_gates[8].gates[0]->qubits[0], 0); - - EXPECT_EQ(fused_gates[9].kind, kGateY); - EXPECT_EQ(fused_gates[9].time, 4); - EXPECT_EQ(fused_gates[9].qubits.size(), 1); - EXPECT_EQ(fused_gates[9].qubits[0], 1); - EXPECT_EQ(fused_gates[9].gates.size(), 1); - EXPECT_EQ(fused_gates[9].gates[0]->kind, kGateY); - EXPECT_EQ(fused_gates[9].gates[0]->time, 4); - EXPECT_EQ(fused_gates[9].gates[0]->qubits.size(), 1); - EXPECT_EQ(fused_gates[9].gates[0]->qubits[0], 1); - - EXPECT_EQ(fused_gates[10].kind, kMeasurement); - EXPECT_EQ(fused_gates[10].time, 5); - EXPECT_EQ(fused_gates[10].qubits.size(), 4); - EXPECT_EQ(fused_gates[10].qubits[0], 2); - EXPECT_EQ(fused_gates[10].qubits[1], 3); - EXPECT_EQ(fused_gates[10].qubits[2], 0); - EXPECT_EQ(fused_gates[10].qubits[3], 1); - EXPECT_EQ(fused_gates[10].gates.size(), 2); + const auto* fgate0 = OpGetAlternative(fused_gates[0]); + ASSERT_NE(fgate0, nullptr); + EXPECT_EQ(fgate0->kind, kGateCZ); + EXPECT_EQ(fgate0->time, 1); + EXPECT_EQ(fgate0->qubits.size(), 2); + EXPECT_EQ(fgate0->qubits[0], 0); + EXPECT_EQ(fgate0->qubits[1], 1); + EXPECT_EQ(fgate0->gates.size(), 3); + const auto* gate00 = OpGetAlternative(fgate0->gates[0]); + ASSERT_NE(gate00, nullptr); + EXPECT_EQ(gate00->kind, kGateHd); + EXPECT_EQ(gate00->time, 0); + EXPECT_EQ(gate00->qubits.size(), 1); + EXPECT_EQ(gate00->qubits[0], 0); + const auto* gate01 = OpGetAlternative(fgate0->gates[1]); + ASSERT_NE(gate01, nullptr); + EXPECT_EQ(gate01->kind, kGateHd); + EXPECT_EQ(gate01->time, 0); + EXPECT_EQ(gate01->qubits.size(), 1); + EXPECT_EQ(gate01->qubits[0], 1); + const auto* gate02 = OpGetAlternative(fgate0->gates[2]); + ASSERT_NE(gate02, nullptr); + EXPECT_EQ(gate02->kind, kGateCZ); + EXPECT_EQ(gate02->time, 1); + EXPECT_EQ(gate02->qubits.size(), 2); + EXPECT_EQ(gate02->qubits[0], 0); + EXPECT_EQ(gate02->qubits[1], 1); + + const auto* fgate1 = OpGetAlternative(fused_gates[1]); + ASSERT_NE(fgate1, nullptr); + EXPECT_EQ(fgate1->kind, kGateHd); + EXPECT_EQ(fgate1->time, 0); + EXPECT_EQ(fgate1->qubits.size(), 1); + EXPECT_EQ(fgate1->qubits[0], 2); + EXPECT_EQ(fgate1->gates.size(), 1); + const auto* gate10 = OpGetAlternative(fgate1->gates[0]); + ASSERT_NE(gate10, nullptr); + EXPECT_EQ(gate10->kind, kGateHd); + EXPECT_EQ(gate10->time, 0); + EXPECT_EQ(gate10->qubits.size(), 1); + EXPECT_EQ(gate10->qubits[0], 2); + + const auto* fgate2 = OpGetAlternative(fused_gates[2]); + ASSERT_NE(fgate2, nullptr); + EXPECT_EQ(fgate2->kind, kGateHd); + EXPECT_EQ(fgate2->time, 0); + EXPECT_EQ(fgate2->qubits.size(), 1); + EXPECT_EQ(fgate2->qubits[0], 3); + EXPECT_EQ(fgate2->gates.size(), 1); + const auto* gate20 = OpGetAlternative(fgate2->gates[0]); + ASSERT_NE(gate20, nullptr); + EXPECT_EQ(gate20->kind, kGateHd); + EXPECT_EQ(gate20->time, 0); + EXPECT_EQ(gate20->qubits.size(), 1); + EXPECT_EQ(gate20->qubits[0], 3); + + const auto* fgate3 = OpGetAlternative(fused_gates[3]); + EXPECT_EQ(fgate3->kind, kMeasurement); + EXPECT_EQ(fgate3->time, 1); + EXPECT_EQ(fgate3->qubits.size(), 2); + EXPECT_EQ(fgate3->qubits[0], 2); + EXPECT_EQ(fgate3->qubits[1], 3); + + const auto* fgate4 = OpGetAlternative(fused_gates[4]); + ASSERT_NE(fgate4, nullptr); + EXPECT_EQ(fgate4->kind, kGateIS); + EXPECT_EQ(fgate4->time, 2); + EXPECT_EQ(fgate4->qubits.size(), 2); + EXPECT_EQ(fgate4->qubits[0], 2); + EXPECT_EQ(fgate4->qubits[1], 3); + EXPECT_EQ(fgate4->gates.size(), 1); + const auto* gate40 = OpGetAlternative(fgate4->gates[0]); + ASSERT_NE(gate40, nullptr); + EXPECT_EQ(gate40->kind, kGateIS); + EXPECT_EQ(gate40->time, 2); + EXPECT_EQ(gate40->qubits.size(), 2); + EXPECT_EQ(gate40->qubits[0], 2); + EXPECT_EQ(gate40->qubits[1], 3); + + const auto* fgate5 = OpGetAlternative(fused_gates[5]); + ASSERT_NE(fgate5, nullptr); + EXPECT_EQ(fgate5->kind, kGateCZ); + EXPECT_EQ(fgate5->time, 3); + EXPECT_EQ(fgate5->qubits.size(), 2); + EXPECT_EQ(fgate5->qubits[0], 0); + EXPECT_EQ(fgate5->qubits[1], 1); + EXPECT_EQ(fgate5->gates.size(), 3); + const auto* gate50 = OpGetAlternative(fgate5->gates[0]); + ASSERT_NE(gate50, nullptr); + EXPECT_EQ(gate50->kind, kGateX); + EXPECT_EQ(gate50->time, 2); + EXPECT_EQ(gate50->qubits.size(), 1); + EXPECT_EQ(gate50->qubits[0], 0); + const auto* gate51 = OpGetAlternative(fgate5->gates[1]); + ASSERT_NE(gate51, nullptr); + EXPECT_EQ(gate51->kind, kGateY); + EXPECT_EQ(gate51->time, 2); + EXPECT_EQ(gate51->qubits.size(), 1); + EXPECT_EQ(gate51->qubits[0], 1); + const auto* gate52 = OpGetAlternative(fgate5->gates[2]); + ASSERT_NE(gate52, nullptr); + EXPECT_EQ(gate52->kind, kGateCZ); + EXPECT_EQ(gate52->time, 3); + EXPECT_EQ(gate52->qubits.size(), 2); + EXPECT_EQ(gate52->qubits[0], 0); + EXPECT_EQ(gate52->qubits[1], 1); + + const auto* fgate6 = OpGetAlternative(fused_gates[6]); + EXPECT_EQ(fgate6->kind, kMeasurement); + EXPECT_EQ(fgate6->time, 3); + EXPECT_EQ(fgate6->qubits.size(), 2); + EXPECT_EQ(fgate6->qubits[0], 2); + EXPECT_EQ(fgate6->qubits[1], 3); + + const auto* fgate7 = OpGetAlternative(fused_gates[7]); + ASSERT_NE(fgate7, nullptr); + EXPECT_EQ(fgate7->kind, kGateIS); + EXPECT_EQ(fgate7->time, 4); + EXPECT_EQ(fgate7->qubits.size(), 2); + EXPECT_EQ(fgate7->qubits[0], 2); + EXPECT_EQ(fgate7->qubits[1], 3); + EXPECT_EQ(fgate7->gates.size(), 1); + const auto* gate70 = OpGetAlternative(fgate7->gates[0]); + ASSERT_NE(gate70, nullptr); + EXPECT_EQ(gate70->kind, kGateIS); + EXPECT_EQ(gate70->time, 4); + EXPECT_EQ(gate70->qubits.size(), 2); + EXPECT_EQ(gate70->qubits[0], 2); + EXPECT_EQ(gate70->qubits[1], 3); + + const auto* fgate8 = OpGetAlternative(fused_gates[8]); + ASSERT_NE(fgate8, nullptr); + EXPECT_EQ(fgate8->kind, kGateX); + EXPECT_EQ(fgate8->time, 4); + EXPECT_EQ(fgate8->qubits.size(), 1); + EXPECT_EQ(fgate8->qubits[0], 0); + EXPECT_EQ(fgate8->gates.size(), 1); + const auto* gate80 = OpGetAlternative(fgate8->gates[0]); + ASSERT_NE(gate80, nullptr); + EXPECT_EQ(gate80->kind, kGateX); + EXPECT_EQ(gate80->time, 4); + EXPECT_EQ(gate80->qubits.size(), 1); + EXPECT_EQ(gate80->qubits[0], 0); + + const auto* fgate9 = OpGetAlternative(fused_gates[9]); + ASSERT_NE(fgate9, nullptr); + EXPECT_EQ(fgate9->kind, kGateY); + EXPECT_EQ(fgate9->time, 4); + EXPECT_EQ(fgate9->qubits.size(), 1); + EXPECT_EQ(fgate9->qubits[0], 1); + EXPECT_EQ(fgate9->gates.size(), 1); + const auto* gate90 = OpGetAlternative(fgate9->gates[0]); + ASSERT_NE(gate90, nullptr); + EXPECT_EQ(gate90->kind, kGateY); + EXPECT_EQ(gate90->time, 4); + EXPECT_EQ(gate90->qubits.size(), 1); + EXPECT_EQ(gate90->qubits[0], 1); + + const auto* fgate10 = OpGetAlternative(fused_gates[10]); + EXPECT_EQ(fgate10->kind, kMeasurement); + EXPECT_EQ(fgate10->time, 5); + EXPECT_EQ(fgate10->qubits.size(), 4); + EXPECT_EQ(fgate10->qubits[0], 2); + EXPECT_EQ(fgate10->qubits[1], 3); + EXPECT_EQ(fgate10->qubits[2], 0); + EXPECT_EQ(fgate10->qubits[3], 1); } constexpr char circuit_string4[] = @@ -977,198 +1315,180 @@ R"(5 )"; TEST(FuserBasicTest, ControlledGate) { + using Gate = qsim::Gate; + using Operation = qsim::Operation; + using ControlledGate = qsim::ControlledGate; + using FusedGate = qsim::FusedGate; + std::stringstream ss(circuit_string4); - Circuit> circuit; + Circuit circuit; EXPECT_TRUE(CircuitQsimParser::FromStream(99, provider, ss, circuit)); EXPECT_EQ(circuit.num_qubits, 5); - EXPECT_EQ(circuit.gates.size(), 13); + EXPECT_EQ(circuit.ops.size(), 13); // Vector of pointers to gates. - std::vector*> pgates; - pgates.reserve(circuit.gates.size()); + std::vector pgates; + pgates.reserve(circuit.ops.size()); - for (const auto& gate : circuit.gates) { + for (const auto& gate : circuit.ops) { pgates.push_back(&gate); } - using Fuser = BasicGateFuser*>; + using Fuser = BasicGateFuser; Fuser::Parameter param; - auto fused_gates = Fuser::FuseGates(param, circuit.num_qubits, pgates); + const auto fused_gates = Fuser::FuseGates(param, circuit.num_qubits, pgates); EXPECT_EQ(fused_gates.size(), 8); - EXPECT_EQ(fused_gates[0].kind, kGateCZ); - EXPECT_EQ(fused_gates[0].time, 1); - EXPECT_EQ(fused_gates[0].qubits.size(), 2); - EXPECT_EQ(fused_gates[0].qubits[0], 0); - EXPECT_EQ(fused_gates[0].qubits[1], 1); - EXPECT_EQ(fused_gates[0].gates.size(), 3); - EXPECT_EQ(fused_gates[0].gates[0]->kind, kGateHd); - EXPECT_EQ(fused_gates[0].gates[0]->time, 0); - EXPECT_EQ(fused_gates[0].gates[0]->qubits.size(), 1); - EXPECT_EQ(fused_gates[0].gates[0]->qubits[0], 0); - EXPECT_EQ(fused_gates[0].gates[1]->kind, kGateHd); - EXPECT_EQ(fused_gates[0].gates[1]->time, 0); - EXPECT_EQ(fused_gates[0].gates[1]->qubits.size(), 1); - EXPECT_EQ(fused_gates[0].gates[1]->qubits[0], 1); - EXPECT_EQ(fused_gates[0].gates[2]->kind, kGateCZ); - EXPECT_EQ(fused_gates[0].gates[2]->time, 1); - EXPECT_EQ(fused_gates[0].gates[2]->qubits.size(), 2); - EXPECT_EQ(fused_gates[0].gates[2]->qubits[0], 0); - EXPECT_EQ(fused_gates[0].gates[2]->qubits[1], 1); - - EXPECT_EQ(fused_gates[1].kind, kGateCZ); - EXPECT_EQ(fused_gates[1].time, 1); - EXPECT_EQ(fused_gates[1].qubits.size(), 2); - EXPECT_EQ(fused_gates[1].qubits[0], 2); - EXPECT_EQ(fused_gates[1].qubits[1], 3); - EXPECT_EQ(fused_gates[1].gates.size(), 4); - EXPECT_EQ(fused_gates[1].gates[0]->kind, kGateHd); - EXPECT_EQ(fused_gates[1].gates[0]->time, 0); - EXPECT_EQ(fused_gates[1].gates[0]->qubits.size(), 1); - EXPECT_EQ(fused_gates[1].gates[0]->qubits[0], 2); - EXPECT_EQ(fused_gates[1].gates[1]->kind, kGateHd); - EXPECT_EQ(fused_gates[1].gates[1]->time, 0); - EXPECT_EQ(fused_gates[1].gates[1]->qubits.size(), 1); - EXPECT_EQ(fused_gates[1].gates[1]->qubits[0], 3); - EXPECT_EQ(fused_gates[1].gates[2]->kind, kGateCZ); - EXPECT_EQ(fused_gates[1].gates[2]->time, 1); - EXPECT_EQ(fused_gates[1].gates[2]->qubits.size(), 2); - EXPECT_EQ(fused_gates[1].gates[2]->qubits[0], 2); - EXPECT_EQ(fused_gates[1].gates[2]->qubits[1], 3); - EXPECT_EQ(fused_gates[1].gates[3]->kind, kGateHd); - EXPECT_EQ(fused_gates[1].gates[3]->time, 3); - EXPECT_EQ(fused_gates[1].gates[3]->qubits.size(), 1); - EXPECT_EQ(fused_gates[1].gates[3]->qubits[0], 3); - - EXPECT_EQ(fused_gates[2].kind, kGateHd); - EXPECT_EQ(fused_gates[2].time, 0); - EXPECT_EQ(fused_gates[2].qubits.size(), 1); - EXPECT_EQ(fused_gates[2].qubits[0], 4); - EXPECT_EQ(fused_gates[2].gates.size(), 1); - EXPECT_EQ(fused_gates[2].gates[0]->kind, kGateHd); - EXPECT_EQ(fused_gates[2].gates[0]->time, 0); - EXPECT_EQ(fused_gates[2].gates[0]->qubits.size(), 1); - EXPECT_EQ(fused_gates[2].gates[0]->qubits[0], 4); - - EXPECT_EQ(fused_gates[3].kind, kGateHd); - EXPECT_EQ(fused_gates[3].time, 2); - EXPECT_EQ(fused_gates[3].qubits.size(), 1); - EXPECT_EQ(fused_gates[3].qubits[0], 2); - EXPECT_EQ(fused_gates[3].parent->controlled_by.size(), 3); - EXPECT_EQ(fused_gates[3].parent->controlled_by[0], 0); - EXPECT_EQ(fused_gates[3].parent->controlled_by[1], 1); - EXPECT_EQ(fused_gates[3].parent->controlled_by[2], 4); - EXPECT_EQ(fused_gates[3].parent->cmask, 7); - EXPECT_EQ(fused_gates[3].gates.size(), 1); - EXPECT_EQ(fused_gates[3].gates[0]->kind, kGateHd); - EXPECT_EQ(fused_gates[3].gates[0]->time, 2); - EXPECT_EQ(fused_gates[3].gates[0]->qubits.size(), 1); - EXPECT_EQ(fused_gates[3].gates[0]->qubits[0], 2); - - EXPECT_EQ(fused_gates[4].kind, kGateHd); - EXPECT_EQ(fused_gates[4].time, 3); - EXPECT_EQ(fused_gates[4].qubits.size(), 1); - EXPECT_EQ(fused_gates[4].qubits[0], 0); - EXPECT_EQ(fused_gates[4].gates.size(), 1); - EXPECT_EQ(fused_gates[4].gates[0]->kind, kGateHd); - EXPECT_EQ(fused_gates[4].gates[0]->time, 3); - EXPECT_EQ(fused_gates[4].gates[0]->qubits.size(), 1); - EXPECT_EQ(fused_gates[4].gates[0]->qubits[0], 0); - - EXPECT_EQ(fused_gates[5].kind, kGateHd); - EXPECT_EQ(fused_gates[5].time, 3); - EXPECT_EQ(fused_gates[5].qubits.size(), 1); - EXPECT_EQ(fused_gates[5].qubits[0], 1); - EXPECT_EQ(fused_gates[5].gates.size(), 1); - EXPECT_EQ(fused_gates[5].gates[0]->kind, kGateHd); - EXPECT_EQ(fused_gates[5].gates[0]->time, 3); - EXPECT_EQ(fused_gates[5].gates[0]->qubits.size(), 1); - EXPECT_EQ(fused_gates[5].gates[0]->qubits[0], 1); - - EXPECT_EQ(fused_gates[6].kind, kGateHd); - EXPECT_EQ(fused_gates[6].time, 3); - EXPECT_EQ(fused_gates[6].qubits.size(), 1); - EXPECT_EQ(fused_gates[6].qubits[0], 2); - EXPECT_EQ(fused_gates[6].gates.size(), 1); - EXPECT_EQ(fused_gates[6].gates[0]->kind, kGateHd); - EXPECT_EQ(fused_gates[6].gates[0]->time, 3); - EXPECT_EQ(fused_gates[6].gates[0]->qubits.size(), 1); - EXPECT_EQ(fused_gates[6].gates[0]->qubits[0], 2); - - EXPECT_EQ(fused_gates[7].kind, kGateHd); - EXPECT_EQ(fused_gates[7].time, 3); - EXPECT_EQ(fused_gates[7].qubits.size(), 1); - EXPECT_EQ(fused_gates[7].qubits[0], 4); - EXPECT_EQ(fused_gates[7].gates.size(), 1); - EXPECT_EQ(fused_gates[7].gates[0]->kind, kGateHd); - EXPECT_EQ(fused_gates[7].gates[0]->time, 3); - EXPECT_EQ(fused_gates[7].gates[0]->qubits.size(), 1); - EXPECT_EQ(fused_gates[7].gates[0]->qubits[0], 4); + const auto* fgate0 = OpGetAlternative(fused_gates[0]); + ASSERT_NE(fgate0, nullptr); + EXPECT_EQ(fgate0->kind, kGateCZ); + EXPECT_EQ(fgate0->time, 1); + EXPECT_EQ(fgate0->qubits.size(), 2); + EXPECT_EQ(fgate0->qubits[0], 0); + EXPECT_EQ(fgate0->qubits[1], 1); + EXPECT_EQ(fgate0->gates.size(), 3); + const auto* gate00 = OpGetAlternative(fgate0->gates[0]); + ASSERT_NE(gate00, nullptr); + EXPECT_EQ(gate00->kind, kGateHd); + EXPECT_EQ(gate00->time, 0); + EXPECT_EQ(gate00->qubits.size(), 1); + EXPECT_EQ(gate00->qubits[0], 0); + const auto* gate01 = OpGetAlternative(fgate0->gates[1]); + ASSERT_NE(gate01, nullptr); + EXPECT_EQ(gate01->kind, kGateHd); + EXPECT_EQ(gate01->time, 0); + EXPECT_EQ(gate01->qubits.size(), 1); + EXPECT_EQ(gate01->qubits[0], 1); + const auto* gate02 = OpGetAlternative(fgate0->gates[2]); + ASSERT_NE(gate02, nullptr); + EXPECT_EQ(gate02->kind, kGateCZ); + EXPECT_EQ(gate02->time, 1); + EXPECT_EQ(gate02->qubits.size(), 2); + EXPECT_EQ(gate02->qubits[0], 0); + EXPECT_EQ(gate02->qubits[1], 1); + + const auto* fgate1 = OpGetAlternative(fused_gates[1]); + ASSERT_NE(fgate1, nullptr); + EXPECT_EQ(fgate1->kind, kGateCZ); + EXPECT_EQ(fgate1->time, 1); + EXPECT_EQ(fgate1->qubits.size(), 2); + EXPECT_EQ(fgate1->qubits[0], 2); + EXPECT_EQ(fgate1->qubits[1], 3); + EXPECT_EQ(fgate1->gates.size(), 4); + const auto* gate10 = OpGetAlternative(fgate1->gates[0]); + ASSERT_NE(gate10, nullptr); + EXPECT_EQ(gate10->kind, kGateHd); + EXPECT_EQ(gate10->time, 0); + EXPECT_EQ(gate10->qubits.size(), 1); + EXPECT_EQ(gate10->qubits[0], 2); + const auto* gate11 = OpGetAlternative(fgate1->gates[1]); + ASSERT_NE(gate11, nullptr); + EXPECT_EQ(gate11->kind, kGateHd); + EXPECT_EQ(gate11->time, 0); + EXPECT_EQ(gate11->qubits.size(), 1); + EXPECT_EQ(gate11->qubits[0], 3); + const auto* gate12 = OpGetAlternative(fgate1->gates[2]); + ASSERT_NE(gate12, nullptr); + EXPECT_EQ(gate12->kind, kGateCZ); + EXPECT_EQ(gate12->time, 1); + EXPECT_EQ(gate12->qubits.size(), 2); + EXPECT_EQ(gate12->qubits[0], 2); + EXPECT_EQ(gate12->qubits[1], 3); + const auto* gate13 = OpGetAlternative(fgate1->gates[3]); + ASSERT_NE(gate13, nullptr); + EXPECT_EQ(gate13->kind, kGateHd); + EXPECT_EQ(gate13->time, 3); + EXPECT_EQ(gate13->qubits.size(), 1); + EXPECT_EQ(gate13->qubits[0], 3); + + const auto* fgate2 = OpGetAlternative(fused_gates[2]); + ASSERT_NE(fgate2, nullptr); + EXPECT_EQ(fgate2->kind, kGateHd); + EXPECT_EQ(fgate2->time, 0); + EXPECT_EQ(fgate2->qubits.size(), 1); + EXPECT_EQ(fgate2->qubits[0], 4); + EXPECT_EQ(fgate2->gates.size(), 1); + const auto* gate20 = OpGetAlternative(fgate2->gates[0]); + ASSERT_NE(gate20, nullptr); + EXPECT_EQ(gate20->kind, kGateHd); + EXPECT_EQ(gate20->time, 0); + EXPECT_EQ(gate20->qubits.size(), 1); + EXPECT_EQ(gate20->qubits[0], 4); + + const auto* fgate3 = OpGetAlternative(fused_gates[3]); + EXPECT_EQ(fgate3->kind, kGateHd); + EXPECT_EQ(fgate3->time, 2); + EXPECT_EQ(fgate3->qubits.size(), 1); + EXPECT_EQ(fgate3->qubits[0], 2); + EXPECT_EQ(fgate3->controlled_by.size(), 3); + EXPECT_EQ(fgate3->controlled_by[0], 0); + EXPECT_EQ(fgate3->controlled_by[1], 1); + EXPECT_EQ(fgate3->controlled_by[2], 4); + EXPECT_EQ(fgate3->cmask, 7); + + const auto* fgate4 = OpGetAlternative(fused_gates[4]); + ASSERT_NE(fgate4, nullptr); + EXPECT_EQ(fgate4->kind, kGateHd); + EXPECT_EQ(fgate4->time, 3); + EXPECT_EQ(fgate4->qubits.size(), 1); + EXPECT_EQ(fgate4->qubits[0], 0); + EXPECT_EQ(fgate4->gates.size(), 1); + const auto* gate40 = OpGetAlternative(fgate4->gates[0]); + ASSERT_NE(gate40, nullptr); + EXPECT_EQ(gate40->kind, kGateHd); + EXPECT_EQ(gate40->time, 3); + EXPECT_EQ(gate40->qubits.size(), 1); + EXPECT_EQ(gate40->qubits[0], 0); + + const auto* fgate5 = OpGetAlternative(fused_gates[5]); + ASSERT_NE(fgate5, nullptr); + EXPECT_EQ(fgate5->kind, kGateHd); + EXPECT_EQ(fgate5->time, 3); + EXPECT_EQ(fgate5->qubits.size(), 1); + EXPECT_EQ(fgate5->qubits[0], 1); + EXPECT_EQ(fgate5->gates.size(), 1); + const auto* gate50 = OpGetAlternative(fgate5->gates[0]); + ASSERT_NE(gate50, nullptr); + EXPECT_EQ(gate50->kind, kGateHd); + EXPECT_EQ(gate50->time, 3); + EXPECT_EQ(gate50->qubits.size(), 1); + EXPECT_EQ(gate50->qubits[0], 1); + + const auto* fgate6 = OpGetAlternative(fused_gates[6]); + ASSERT_NE(fgate6, nullptr); + EXPECT_EQ(fgate6->kind, kGateHd); + EXPECT_EQ(fgate6->time, 3); + EXPECT_EQ(fgate6->qubits.size(), 1); + EXPECT_EQ(fgate6->qubits[0], 2); + EXPECT_EQ(fgate6->gates.size(), 1); + const auto* gate60 = OpGetAlternative(fgate6->gates[0]); + ASSERT_NE(gate60, nullptr); + EXPECT_EQ(gate60->kind, kGateHd); + EXPECT_EQ(gate60->time, 3); + EXPECT_EQ(gate60->qubits.size(), 1); + EXPECT_EQ(gate60->qubits[0], 2); + + const auto* fgate7 = OpGetAlternative(fused_gates[7]); + ASSERT_NE(fgate7, nullptr); + EXPECT_EQ(fgate7->kind, kGateHd); + EXPECT_EQ(fgate7->time, 3); + EXPECT_EQ(fgate7->qubits.size(), 1); + EXPECT_EQ(fgate7->qubits[0], 4); + EXPECT_EQ(fgate7->gates.size(), 1); + const auto* gate70 = OpGetAlternative(fgate7->gates[0]); + ASSERT_NE(gate70, nullptr); + EXPECT_EQ(gate70->kind, kGateHd); + EXPECT_EQ(gate70->time, 3); + EXPECT_EQ(gate70->qubits.size(), 1); + EXPECT_EQ(gate70->qubits[0], 4); } -namespace { - -template -bool TestFusedGates(unsigned num_qubits, - const std::vector& gates, - const std::vector& fused_gates) { - std::vector times(num_qubits, 0); - std::vector gate_map(gates.size(), 0); - - // Test if gate times are ordered correctly. - for (auto g : fused_gates) { - if (g.parent->controlled_by.size() > 0 && g.gates.size() > 1) { - return false; - } - - for (auto p : g.gates) { - auto k = (std::size_t(p) - std::size_t(gates.data())) / sizeof(*p); - - if (k >= gate_map.size()) { - return false; - } - - ++gate_map[k]; - - if (p->kind == gate::kMeasurement) { - if (g.parent->kind != gate::kMeasurement || g.parent->time != p->time) { - return false; - } - } - - for (auto q : p->qubits) { - if (p->time < times[q]) { - return false; - } - times[q] = p->time; - } - - for (auto q : p->controlled_by) { - if (p->time < times[q]) { - return false; - } - times[q] = p->time; - } - } - } - - // Test if all gates are present only once. - for (auto m : gate_map) { - if (m != 1) { - return false; - } - } - - return true; -} - -} // namespace - TEST(FuserBasicTest, SmallCircuits) { - using Gate = GateQSim; - using Fuser = BasicGateFuser; + using Gate = qsim::Gate; + using Operation = qsim::Operation; + using Fuser = BasicGateFuser; Fuser::Parameter param; param.verbosity = 0; @@ -1179,7 +1499,7 @@ TEST(FuserBasicTest, SmallCircuits) { GateGPh::Create(0, -1, 0), }; - auto fused_gates = Fuser::FuseGates( + auto fused_gates = Fuser::FuseGates( param, num_qubits, circuit.begin(), circuit.end(), false); EXPECT_TRUE(TestFusedGates(num_qubits, circuit, fused_gates)); @@ -1193,7 +1513,7 @@ TEST(FuserBasicTest, SmallCircuits) { GateGPh::Create(0, -1, 0), }; - auto fused_gates = Fuser::FuseGates( + auto fused_gates = Fuser::FuseGates( param, num_qubits, circuit.begin(), circuit.end(), false); EXPECT_TRUE(TestFusedGates(num_qubits, circuit, fused_gates)); @@ -1207,7 +1527,7 @@ TEST(FuserBasicTest, SmallCircuits) { GateGPh::Create(0, -1, 0), }; - auto fused_gates = Fuser::FuseGates( + auto fused_gates = Fuser::FuseGates( param, num_qubits, circuit.begin(), circuit.end(), false); EXPECT_TRUE(TestFusedGates(num_qubits, circuit, fused_gates)); @@ -1216,24 +1536,29 @@ TEST(FuserBasicTest, SmallCircuits) { { unsigned num_qubits = 2; - std::vector circuit = { + std::vector circuit = { GateZ::Create(0, 0).ControlledBy({1}, {1}), GateCZ::Create(1, 0, 1), GateGPh::Create(1, -1, 0), }; - auto fused_gates = Fuser::FuseGates( + auto fused_gates = Fuser::FuseGates( param, num_qubits, circuit.begin(), circuit.end(), false); EXPECT_TRUE(TestFusedGates(num_qubits, circuit, fused_gates)); EXPECT_EQ(fused_gates.size(), 2); - EXPECT_EQ(fused_gates[1].gates[1], &circuit[2]); + + const auto* fgate1 = OpGetAlternative>(fused_gates[1]); + const auto* gate11 = OpGetAlternative(fgate1->gates[1]); + ASSERT_NE(gate11, nullptr); + EXPECT_EQ(gate11, OpGetAlternative(circuit[2])); } } TEST(FuserBasicTest, ValidTimeOrder) { - using Gate = GateQSim; - using Fuser = BasicGateFuser; + using Gate = qsim::Gate; + using Operation = qsim::Operation; + using Fuser = BasicGateFuser; Fuser::Parameter param; param.verbosity = 0; @@ -1243,16 +1568,16 @@ TEST(FuserBasicTest, ValidTimeOrder) { auto gate1 = GateZ::Create(1, 2); auto gate2 = GateZ::Create(2, 5); - std::vector circuit = { + std::vector circuit = { GateCZ::Create(0, 0, 1), GateCZ::Create(0, 2, 3), - MakeControlledGate({1}, gate1), + MakeControlledGate(gate1, {1}), GateCZ::Create(0, 4, 5), GateCZ::Create(2, 0, 1), GateCZ::Create(1, 3, 4), GateCZ::Create(2, 2, 3), GateCZ::Create(3, 1, 2), - MakeControlledGate({4}, gate2), + MakeControlledGate(gate2, {4}), GateCZ::Create(3, 3, 4), GateCZ::Create(5, 0, 1), GateCZ::Create(4, 2, 3), @@ -1260,7 +1585,7 @@ TEST(FuserBasicTest, ValidTimeOrder) { GateCZ::Create(4, 6, 7), }; - auto fused_gates = Fuser::FuseGates( + const auto fused_gates = Fuser::FuseGates( param, num_qubits, circuit.begin(), circuit.end()); EXPECT_EQ(fused_gates.size(), 14); @@ -1269,14 +1594,14 @@ TEST(FuserBasicTest, ValidTimeOrder) { { unsigned num_qubits = 6; - std::vector circuit = { + std::vector circuit = { GateCZ::Create(0, 0, 1), GateCZ::Create(0, 2, 3), GateCZ::Create(1, 1, 2), GateCZ::Create(0, 4, 5), GateCZ::Create(1, 3, 4), - gate::Measurement::Create(2, {0, 1, 2}), - gate::Measurement::Create(2, {4, 5}), + CreateMeasurement(2, {0, 1, 2}), + CreateMeasurement(2, {4, 5}), GateCZ::Create(3, 0, 1), GateCZ::Create(3, 2, 3), GateCZ::Create(4, 1, 2), @@ -1284,7 +1609,7 @@ TEST(FuserBasicTest, ValidTimeOrder) { GateCZ::Create(4, 3, 4), }; - auto fused_gates = Fuser::FuseGates( + const auto fused_gates = Fuser::FuseGates( param, num_qubits, circuit.begin(), circuit.end()); EXPECT_EQ(fused_gates.size(), 11); @@ -1302,7 +1627,7 @@ TEST(FuserBasicTest, ValidTimeOrder) { GateCZ::Create(1, 5, 0), }; - auto fused_gates = Fuser::FuseGates( + const auto fused_gates = Fuser::FuseGates( param, num_qubits, circuit.begin(), circuit.end()); EXPECT_EQ(fused_gates.size(), 6); @@ -1322,7 +1647,7 @@ TEST(FuserBasicTest, ValidTimeOrder) { GateCZ::Create(1, 7, 0), }; - auto fused_gates = Fuser::FuseGates( + const auto fused_gates = Fuser::FuseGates( param, num_qubits, circuit.begin(), circuit.end()); EXPECT_EQ(fused_gates.size(), 8); @@ -1343,7 +1668,7 @@ TEST(FuserBasicTest, ValidTimeOrder) { }; std::vector time_boundary = {3}; - auto fused_gates = Fuser::FuseGates( + const auto fused_gates = Fuser::FuseGates( param, num_qubits, circuit.begin(), circuit.end(), time_boundary); EXPECT_EQ(fused_gates.size(), 6); @@ -1352,18 +1677,18 @@ TEST(FuserBasicTest, ValidTimeOrder) { { unsigned num_qubits = 4; - std::vector circuit = { + std::vector circuit = { GateCZ::Create(0, 0, 1), GateCZ::Create(0, 2, 3), GateCZ::Create(2, 1, 2), GateCZ::Create(1, 0, 3), - gate::Measurement::Create(3, {1, 2}), + CreateMeasurement(3, {1, 2}), GateCZ::Create(3, 0, 3), GateCZ::Create(5, 1, 2), GateCZ::Create(4, 0, 3), }; - auto fused_gates = Fuser::FuseGates( + const auto fused_gates = Fuser::FuseGates( param, num_qubits, circuit.begin(), circuit.end()); EXPECT_EQ(fused_gates.size(), 7); @@ -1378,8 +1703,9 @@ TEST(FuserBasicTest, ValidTimeOrder) { }; std::vector time_boundary = {1}; - auto fused_gates = Fuser::FuseGates(param, num_qubits, circuit.begin(), - circuit.end(), time_boundary, false); + const auto fused_gates = Fuser::FuseGates( + param, num_qubits, circuit.begin(), circuit.end(), + time_boundary, false); EXPECT_EQ(fused_gates.size(), 2); EXPECT_TRUE(TestFusedGates(num_qubits, circuit, fused_gates)); @@ -1392,7 +1718,7 @@ TEST(FuserBasicTest, ValidTimeOrder) { GateGPh::Create(0, -1, 0), }; - auto fused_gates = Fuser::FuseGates( + const auto fused_gates = Fuser::FuseGates( param, num_qubits, circuit.begin(), circuit.end(), false); EXPECT_TRUE(TestFusedGates(num_qubits, circuit, fused_gates)); @@ -1401,8 +1727,9 @@ TEST(FuserBasicTest, ValidTimeOrder) { } TEST(FuserBasicTest, InvalidTimeOrder) { - using Gate = GateQSim; - using Fuser = BasicGateFuser; + using Gate = qsim::Gate; + using Operation = qsim::Operation; + using Fuser = BasicGateFuser; Fuser::Parameter param; param.verbosity = 0; @@ -1414,7 +1741,7 @@ TEST(FuserBasicTest, InvalidTimeOrder) { GateCZ::Create(0, 1, 2), }; - auto fused_gates = Fuser::FuseGates( + auto fused_gates = Fuser::FuseGates( param, num_qubits, circuit.begin(), circuit.end()); EXPECT_EQ(fused_gates.size(), 0); @@ -1429,7 +1756,7 @@ TEST(FuserBasicTest, InvalidTimeOrder) { GateCZ::Create(1, 0, 2), }; - auto fused_gates = Fuser::FuseGates( + auto fused_gates = Fuser::FuseGates( param, num_qubits, circuit.begin(), circuit.end()); EXPECT_EQ(fused_gates.size(), 0); @@ -1445,7 +1772,7 @@ TEST(FuserBasicTest, InvalidTimeOrder) { }; std::vector time_boundary = {1}; - auto fused_gates = Fuser::FuseGates( + auto fused_gates = Fuser::FuseGates( param, num_qubits, circuit.begin(), circuit.end(), time_boundary); EXPECT_EQ(fused_gates.size(), 0); @@ -1461,7 +1788,7 @@ TEST(FuserBasicTest, InvalidTimeOrder) { }; std::vector time_boundary = {2}; - auto fused_gates = Fuser::FuseGates( + auto fused_gates = Fuser::FuseGates( param, num_qubits, circuit.begin(), circuit.end(), time_boundary); EXPECT_EQ(fused_gates.size(), 0); @@ -1469,14 +1796,14 @@ TEST(FuserBasicTest, InvalidTimeOrder) { { unsigned num_qubits = 4; - std::vector circuit = { + std::vector circuit = { GateCZ::Create(0, 0, 1), GateCZ::Create(0, 2, 3), - gate::Measurement::Create(2, {0, 3}), + CreateMeasurement(2, {0, 3}), GateCZ::Create(1, 1, 2), }; - auto fused_gates = Fuser::FuseGates( + auto fused_gates = Fuser::FuseGates( param, num_qubits, circuit.begin(), circuit.end()); EXPECT_EQ(fused_gates.size(), 0); @@ -1484,14 +1811,14 @@ TEST(FuserBasicTest, InvalidTimeOrder) { { unsigned num_qubits = 4; - std::vector circuit = { + std::vector circuit = { GateCZ::Create(0, 0, 1), GateCZ::Create(0, 2, 3), GateCZ::Create(2, 0, 3), - gate::Measurement::Create(1, {1, 2}), + CreateMeasurement(1, {1, 2}), }; - auto fused_gates = Fuser::FuseGates( + auto fused_gates = Fuser::FuseGates( param, num_qubits, circuit.begin(), circuit.end()); EXPECT_EQ(fused_gates.size(), 0); @@ -1501,14 +1828,14 @@ TEST(FuserBasicTest, InvalidTimeOrder) { unsigned num_qubits = 4; auto gate = GateZ::Create(1, 1); - std::vector circuit = { + std::vector circuit = { GateCZ::Create(0, 0, 1), GateCZ::Create(0, 2, 3), GateCZ::Create(2, 0, 3), - MakeControlledGate({3}, gate), + MakeControlledGate(gate, {3}), }; - auto fused_gates = Fuser::FuseGates( + auto fused_gates = Fuser::FuseGates( param, num_qubits, circuit.begin(), circuit.end()); EXPECT_EQ(fused_gates.size(), 0); @@ -1518,14 +1845,14 @@ TEST(FuserBasicTest, InvalidTimeOrder) { unsigned num_qubits = 4; auto gate = GateZ::Create(2, 1); - std::vector circuit = { + std::vector circuit = { GateCZ::Create(0, 0, 1), GateCZ::Create(0, 2, 3), - MakeControlledGate({3}, gate), + MakeControlledGate(gate, {3}), GateCZ::Create(1, 0, 3), }; - auto fused_gates = Fuser::FuseGates( + auto fused_gates = Fuser::FuseGates( param, num_qubits, circuit.begin(), circuit.end()); EXPECT_EQ(fused_gates.size(), 0); @@ -1539,16 +1866,18 @@ TEST(FuserBasicTest, InvalidTimeOrder) { }; std::vector time_boundary = {1}; - auto fused_gates = Fuser::FuseGates(param, num_qubits, circuit.begin(), - circuit.end(), time_boundary, false); + auto fused_gates = Fuser::FuseGates( + param, num_qubits, circuit.begin(), circuit.end(), + time_boundary, false); EXPECT_EQ(fused_gates.size(), 0); } } TEST(FuserBasicTest, QubitsOutOfRange) { - using Gate = GateQSim; - using Fuser = BasicGateFuser; + using Gate = qsim::Gate; + using Operation = qsim::Operation; + using Fuser = BasicGateFuser; Fuser::Parameter param; param.verbosity = 0; @@ -1560,7 +1889,7 @@ TEST(FuserBasicTest, QubitsOutOfRange) { GateCZ::Create(0, 1, 2), }; - auto fused_gates = Fuser::FuseGates( + auto fused_gates = Fuser::FuseGates( param, num_qubits, circuit.begin(), circuit.end(), false); EXPECT_EQ(fused_gates.size(), 0); @@ -1569,12 +1898,13 @@ TEST(FuserBasicTest, QubitsOutOfRange) { { unsigned num_qubits = 3; auto gate = GateZ::Create(0, 2); - std::vector circuit = { + + std::vector circuit = { GateCZ::Create(0, 0, 1), - MakeControlledGate({3}, gate), + MakeControlledGate(gate, {3}), }; - auto fused_gates = Fuser::FuseGates( + auto fused_gates = Fuser::FuseGates( param, num_qubits, circuit.begin(), circuit.end(), false); EXPECT_EQ(fused_gates.size(), 0); diff --git a/tests/fuser_mqubit_test.cc b/tests/fuser_mqubit_test.cc index d6a42dc1f..c5060e75c 100644 --- a/tests/fuser_mqubit_test.cc +++ b/tests/fuser_mqubit_test.cc @@ -19,13 +19,17 @@ #include #include +#include "fuser_testfixture.h" + #include "gtest/gtest.h" #include "../lib/formux.h" +#include "../lib/fuser.h" #include "../lib/fuser_mqubit.h" #include "../lib/gate.h" #include "../lib/gate_appl.h" #include "../lib/matrix.h" +#include "../lib/operation.h" #include "../lib/simmux.h" namespace qsim { @@ -37,38 +41,15 @@ struct IO { namespace { -enum DummyGateKind { - kGateOther = 0, - kMeasurement = gate::kMeasurement, -}; - -struct DummyGate { - using GateKind = DummyGateKind; - using fp_type = float; - - GateKind kind; - unsigned time; - uint64_t cmask; - std::vector qubits; - std::vector controlled_by; - Matrix matrix; - bool unfusible; -}; - -DummyGate CreateDummyGate(unsigned time, std::vector&& qubits) { - return {kGateOther, time, 0, std::move(qubits), {}, {}, false}; +Gate CreateGate(unsigned time, std::vector&& qubits) { + return {0, time, std::move(qubits), {}, {}, false}; } -DummyGate CreateDummyMeasurementGate(unsigned time, - std::vector&& qubits) { - return {kMeasurement, time, 0, std::move(qubits), {}, {}, false}; -} - -DummyGate CreateDummyControlledGate(unsigned time, - std::vector&& qubits, - std::vector&& controlled_by) { - return {kGateOther, time, 0, std::move(qubits), std::move(controlled_by), {}, - false}; +ControlledGate CreateControlledGate( + unsigned time, std::vector&& qubits, + std::vector&& controlled_by) { + return Gate{0, time, std::move(qubits), {}, {}, false}.ControlledBy( + std::move(controlled_by)); } std::vector GenQubits(unsigned num_qubits, std::mt19937& rgen, @@ -95,34 +76,41 @@ constexpr double p6 = 0.02 + p5; constexpr double pc = 0.0075 + p6; constexpr double pm = 0.0075 + pc; -constexpr double pu = 0.002; +constexpr double pd = 0.002; +template void AddToCircuit(unsigned time, std::uniform_real_distribution& distr, std::mt19937& rgen, unsigned& n, std::vector& available, - std::vector& circuit) { + std::vector& circuit) { double r = distr(rgen); if (r < p0) { - circuit.push_back(CreateDummyGate(time, {})); + circuit.push_back(CreateGate(time, {})); } else if (r < p1) { - circuit.push_back(CreateDummyGate(time, GenQubits(1, rgen, n, available))); + circuit.push_back(CreateGate(time, GenQubits(1, rgen, n, available))); + + if (distr(rgen) < pd) { + using Gate = qsim::Gate; + using DecomposedGate = qsim::DecomposedGate; + circuit.back() = DecomposedGate{*OpGetAlternative(circuit.back())}; + } } else if (r < p2) { - circuit.push_back(CreateDummyGate(time, GenQubits(2, rgen, n, available))); + circuit.push_back(CreateGate(time, GenQubits(2, rgen, n, available))); } else if (r < p3) { - circuit.push_back(CreateDummyGate(time, GenQubits(3, rgen, n, available))); + circuit.push_back(CreateGate(time, GenQubits(3, rgen, n, available))); } else if (r < p4) { - circuit.push_back(CreateDummyGate(time, GenQubits(4, rgen, n, available))); + circuit.push_back(CreateGate(time, GenQubits(4, rgen, n, available))); } else if (r < p5) { - circuit.push_back(CreateDummyGate(time, GenQubits(5, rgen, n, available))); + circuit.push_back(CreateGate(time, GenQubits(5, rgen, n, available))); } else if (r < p6) { - circuit.push_back(CreateDummyGate(time, GenQubits(6, rgen, n, available))); + circuit.push_back(CreateGate(time, GenQubits(6, rgen, n, available))); } else if (r < pc) { auto qs = GenQubits(1 + rgen() % 3, rgen, n, available); auto cqs = GenQubits(1 + rgen() % 3, rgen, n, available); circuit.push_back( - CreateDummyControlledGate(time, std::move(qs), std::move(cqs))); + CreateControlledGate(time, std::move(qs), std::move(cqs))); } else if (r < pm) { unsigned num_mea_gates = 0; unsigned max_num_mea_gates = 1 + rgen() % 5; @@ -131,25 +119,25 @@ void AddToCircuit(unsigned time, unsigned k = 1 + rgen() % 12; if (k > n) k = n; circuit.push_back( - CreateDummyMeasurementGate(time, GenQubits(k, rgen, n, available))); + CreateMeasurement(time, GenQubits(k, rgen, n, available))); ++num_mea_gates; } } - if (r < p6 && distr(rgen) < pu) { - circuit.back().unfusible = true; - } + auto& op = circuit.back(); - auto& gate = circuit.back(); - - if (gate.kind != gate::kMeasurement) { - std::sort(gate.qubits.begin(), gate.qubits.end()); + if (!OpGetAlternative(op)) { + auto& base_op = OpBaseOperation(op); + std::sort(base_op.qubits.begin(), base_op.qubits.end()); } } -std::vector GenerateRandomCircuit1(unsigned num_qubits, - unsigned depth) { - std::vector circuit; +auto GenerateRandomCircuit1(unsigned num_qubits, unsigned depth) { + using DecomposedGate = qsim::DecomposedGate; + using Operation = qsim::Operation; + using OperationD = detail::append_to_variant_t; + + std::vector circuit; circuit.reserve(depth); std::mt19937 rgen(1); @@ -170,10 +158,13 @@ std::vector GenerateRandomCircuit1(unsigned num_qubits, return circuit; } -std::vector GenerateRandomCircuit2(unsigned num_qubits, - unsigned depth, - unsigned max_fused_gate_size) { - std::vector circuit; +auto GenerateRandomCircuit2(unsigned num_qubits, unsigned depth, + unsigned max_fused_gate_size) { + using DecomposedGate = qsim::DecomposedGate; + using Operation = qsim::Operation; + using OperationD = detail::append_to_variant_t; + + std::vector circuit; circuit.reserve(num_qubits * depth); std::mt19937 rgen(2); @@ -196,64 +187,10 @@ std::vector GenerateRandomCircuit2(unsigned num_qubits, return circuit; } -template -bool TestFusedGates(unsigned num_qubits, - const std::vector& gates, - const std::vector& fused_gates) { - std::vector times(num_qubits, 0); - std::vector gate_map(gates.size(), 0); - - // Test if gate times are ordered correctly. - for (auto g : fused_gates) { - if (g.parent->controlled_by.size() > 0 && g.gates.size() > 1) { - return false; - } - - for (auto p : g.gates) { - auto k = (std::size_t(p) - std::size_t(gates.data())) / sizeof(*p); - - if (k >= gate_map.size()) { - return false; - } - - ++gate_map[k]; - - if (p->kind == gate::kMeasurement) { - if (g.parent->kind != gate::kMeasurement || g.parent->time != p->time) { - return false; - } - } - - for (auto q : p->qubits) { - if (p->time < times[q]) { - return false; - } - times[q] = p->time; - } - - for (auto q : p->controlled_by) { - if (p->time < times[q]) { - return false; - } - times[q] = p->time; - } - } - } - - // Test if all gates are present only once. - for (auto m : gate_map) { - if (m != 1) { - return false; - } - } - - return true; -} - } // namespace TEST(FuserMultiQubitTest, RandomCircuit1) { - using Fuser = MultiQubitGateFuser; + using Fuser = MultiQubitGateFuser; unsigned num_qubits = 30; unsigned depth = 100000; @@ -264,9 +201,11 @@ TEST(FuserMultiQubitTest, RandomCircuit1) { Fuser::Parameter param; param.verbosity = 0; + using Operation = decltype(circuit)::value_type; + for (unsigned q = 2; q <= 6; ++q) { param.max_fused_size = q; - auto fused_gates = Fuser::FuseGates( + auto fused_gates = Fuser::FuseGates( param, num_qubits, circuit.begin(), circuit.end(), false); EXPECT_TRUE(TestFusedGates(num_qubits, circuit, fused_gates)); @@ -274,7 +213,7 @@ TEST(FuserMultiQubitTest, RandomCircuit1) { for (unsigned q = 2; q <= 6; ++q) { param.max_fused_size = q; - auto fused_gates = Fuser::FuseGates( + auto fused_gates = Fuser::FuseGates( param, num_qubits, circuit.begin(), circuit.end(), {5000, 7000, 25000, 37000}, false); @@ -283,7 +222,7 @@ TEST(FuserMultiQubitTest, RandomCircuit1) { } TEST(FuserMultiQubitTest, RandomCircuit2) { - using Fuser = MultiQubitGateFuser; + using Fuser = MultiQubitGateFuser; unsigned num_qubits = 40; unsigned depth = 6400; @@ -291,8 +230,10 @@ TEST(FuserMultiQubitTest, RandomCircuit2) { // Random circuit of approximately 100000 gates. auto circuit = GenerateRandomCircuit2(num_qubits, depth, 6); + using Operation = decltype(circuit)::value_type; + // Vector of pointers to gates. - std::vector pcircuit; + std::vector pcircuit; pcircuit.reserve(circuit.size()); for (const auto& gate : circuit) { @@ -304,7 +245,7 @@ TEST(FuserMultiQubitTest, RandomCircuit2) { for (unsigned q = 2; q <= 6; ++q) { param.max_fused_size = q; - auto fused_gates = Fuser::FuseGates( + auto fused_gates = Fuser::FuseGates( param, num_qubits, pcircuit.begin(), pcircuit.end(), false); EXPECT_TRUE(TestFusedGates(num_qubits, circuit, fused_gates)); @@ -312,7 +253,7 @@ TEST(FuserMultiQubitTest, RandomCircuit2) { for (unsigned q = 2; q <= 6; ++q) { param.max_fused_size = q; - auto fused_gates = Fuser::FuseGates( + auto fused_gates = Fuser::FuseGates( param, num_qubits, pcircuit.begin(), pcircuit.end(), {300, 700, 2400}, false); @@ -321,7 +262,11 @@ TEST(FuserMultiQubitTest, RandomCircuit2) { } TEST(FuserMultiQubitTest, Simulation) { - using Fuser = MultiQubitGateFuser; + using Gate = qsim::Gate; + using FusedGate = qsim::FusedGate; + using ControlledGate = ControlledGate; + using DecomposedGate = qsim::DecomposedGate; + using Fuser = MultiQubitGateFuser; unsigned num_qubits = 12; unsigned depth = 200; @@ -331,19 +276,35 @@ TEST(FuserMultiQubitTest, Simulation) { std::mt19937 rgen(1); std::uniform_real_distribution distr(0, 1); - for (auto& gate : circuit) { - if (gate.kind == kMeasurement) continue; + for (auto& op : circuit) { + if (OpGetAlternative(op)) continue; - if (gate.controlled_by.size() > 0) { - gate.cmask = (uint64_t{1} << gate.controlled_by.size()) - 1; - } + if (auto* pg = OpGetAlternative(op)) { + unsigned size = unsigned{1} << (2 * pg->qubits.size() + 1); + pg->matrix.reserve(size); - unsigned size = unsigned{1} << (2 * gate.qubits.size() + 1); - gate.matrix.reserve(size); + // Random gate matrices. + for (unsigned i = 0; i < size; ++i) { + pg->matrix.push_back(2 * distr(rgen) - 1); + } + } else if (auto* pg = OpGetAlternative(op)) { + unsigned size = unsigned{1} << (2 * pg->qubits.size() + 1); + pg->matrix.reserve(size); - // Random gate matrices. - for (unsigned i = 0; i < size; ++i) { - gate.matrix.push_back(2 * distr(rgen) - 1); + // Random gate matrices. + for (unsigned i = 0; i < size; ++i) { + pg->matrix.push_back(2 * distr(rgen) - 1); + } + } else if (auto* pg = OpGetAlternative(op)) { + unsigned size = unsigned{1} << (2 * pg->qubits.size() + 1); + pg->matrix.reserve(size); + + // Random gate matrices. + for (unsigned i = 0; i < size; ++i) { + pg->matrix.push_back(2 * distr(rgen) - 1); + } + + pg->cmask = (uint64_t{1} << pg->controlled_by.size()) - 1; } } @@ -370,15 +331,28 @@ TEST(FuserMultiQubitTest, Simulation) { for (unsigned q = 2; q <= 6; ++q) { state_space.SetStateZero(state1); + using Operation = decltype(circuit)::value_type; + param.max_fused_size = q; - auto fused_gates = Fuser::FuseGates( + auto fused_ops = Fuser::FuseGates( param, num_qubits, circuit.begin(), circuit.end()); - EXPECT_TRUE(TestFusedGates(num_qubits, circuit, fused_gates)); + EXPECT_TRUE(TestFusedGates(num_qubits, circuit, fused_ops)); // Simulate fused gates. - for (const auto& gate : fused_gates) { - ApplyFusedGate(simulator, gate, state1); + for (const auto& op : fused_ops) { + if (const auto* pg = OpGetAlternative(op)) { + if (!pg->ParentIsDecomposed()) { + ApplyGate(simulator, op, state1); + } else { + auto fgate = *pg; + CalculateFusedMatrix(fgate); + ApplyGate(simulator, fgate, state1); + } + } else { + ApplyGate(simulator, op, state1); + } + // Renormalize the state to prevent floating point overflow. state_space.Multiply(1.0 / std::sqrt(state_space.Norm(state1)), state1); } @@ -391,24 +365,27 @@ TEST(FuserMultiQubitTest, Simulation) { } TEST(FuserMultiQubitTest, SmallCircuits) { - using Fuser = MultiQubitGateFuser; + using Gate = qsim::Gate; + using FusedGate = qsim::FusedGate; + using Operation = qsim::Operation; + using Fuser = MultiQubitGateFuser; Fuser::Parameter param; param.verbosity = 0; { unsigned num_qubits = 4; - std::vector circuit = { - CreateDummyGate(0, {0, 1}), - CreateDummyGate(0, {2, 3}), - CreateDummyGate(1, {1, 2}), - CreateDummyGate(2, {0, 1}), - CreateDummyGate(2, {2, 3}), - CreateDummyGate(3, {1, 2}), + std::vector circuit = { + CreateGate(0, {0, 1}), + CreateGate(0, {2, 3}), + CreateGate(1, {1, 2}), + CreateGate(2, {0, 1}), + CreateGate(2, {2, 3}), + CreateGate(3, {1, 2}), }; param.max_fused_size = 4; - auto fused_gates = Fuser::FuseGates( + auto fused_gates = Fuser::FuseGates( param, num_qubits, circuit.begin(), circuit.end(), false); EXPECT_TRUE(TestFusedGates(num_qubits, circuit, fused_gates)); @@ -417,18 +394,18 @@ TEST(FuserMultiQubitTest, SmallCircuits) { { unsigned num_qubits = 4; - std::vector circuit = { - CreateDummyGate(0, {0, 1}), - CreateDummyGate(0, {2, 3}), - CreateDummyGate(1, {1, 2}), - CreateDummyControlledGate(1, {0}, {3}), - CreateDummyGate(3, {0, 1}), - CreateDummyGate(3, {2, 3}), - CreateDummyGate(4, {1, 2}), + std::vector circuit = { + CreateGate(0, {0, 1}), + CreateGate(0, {2, 3}), + CreateGate(1, {1, 2}), + CreateControlledGate(1, {0}, {3}), + CreateGate(3, {0, 1}), + CreateGate(3, {2, 3}), + CreateGate(4, {1, 2}), }; param.max_fused_size = 4; - auto fused_gates = Fuser::FuseGates( + auto fused_gates = Fuser::FuseGates( param, num_qubits, circuit.begin(), circuit.end(), false); EXPECT_TRUE(TestFusedGates(num_qubits, circuit, fused_gates)); @@ -437,21 +414,21 @@ TEST(FuserMultiQubitTest, SmallCircuits) { { unsigned num_qubits = 6; - std::vector circuit = { - CreateDummyGate(0, {0, 1}), - CreateDummyGate(0, {2, 3}), - CreateDummyGate(0, {4, 5}), - CreateDummyGate(1, {1, 2}), - CreateDummyGate(1, {3, 4}), - CreateDummyGate(2, {0, 1}), - CreateDummyGate(2, {2, 3}), - CreateDummyGate(2, {4, 5}), - CreateDummyGate(3, {1, 2}), - CreateDummyGate(3, {3, 4}), + std::vector circuit = { + CreateGate(0, {0, 1}), + CreateGate(0, {2, 3}), + CreateGate(0, {4, 5}), + CreateGate(1, {1, 2}), + CreateGate(1, {3, 4}), + CreateGate(2, {0, 1}), + CreateGate(2, {2, 3}), + CreateGate(2, {4, 5}), + CreateGate(3, {1, 2}), + CreateGate(3, {3, 4}), }; param.max_fused_size = 6; - auto fused_gates = Fuser::FuseGates( + auto fused_gates = Fuser::FuseGates( param, num_qubits, circuit.begin(), circuit.end(), false); EXPECT_TRUE(TestFusedGates(num_qubits, circuit, fused_gates)); @@ -460,23 +437,23 @@ TEST(FuserMultiQubitTest, SmallCircuits) { { unsigned num_qubits = 6; - std::vector circuit = { - CreateDummyGate(0, {0, 1}), - CreateDummyGate(0, {2, 3}), - CreateDummyGate(0, {4, 5}), - CreateDummyGate(1, {1, 2}), - CreateDummyGate(1, {3, 4}), - CreateDummyMeasurementGate(2, {0, 1, 2}), - CreateDummyMeasurementGate(2, {4, 5}), - CreateDummyGate(3, {0, 1}), - CreateDummyGate(3, {2, 3}), - CreateDummyGate(3, {4, 5}), - CreateDummyGate(4, {1, 2}), - CreateDummyGate(4, {3, 4}), + std::vector circuit = { + CreateGate(0, {0, 1}), + CreateGate(0, {2, 3}), + CreateGate(0, {4, 5}), + CreateGate(1, {1, 2}), + CreateGate(1, {3, 4}), + CreateMeasurement(2, {0, 1, 2}), + CreateMeasurement(2, {4, 5}), + CreateGate(3, {0, 1}), + CreateGate(3, {2, 3}), + CreateGate(3, {4, 5}), + CreateGate(4, {1, 2}), + CreateGate(4, {3, 4}), }; param.max_fused_size = 6; - auto fused_gates = Fuser::FuseGates( + auto fused_gates = Fuser::FuseGates( param, num_qubits, circuit.begin(), circuit.end(), false); EXPECT_TRUE(TestFusedGates(num_qubits, circuit, fused_gates)); @@ -485,14 +462,14 @@ TEST(FuserMultiQubitTest, SmallCircuits) { { unsigned num_qubits = 4; - std::vector circuit = { - CreateDummyGate(0, {0, 1, 2}), - CreateDummyGate(1, {2, 3}), - CreateDummyGate(2, {1, 2}), + std::vector circuit = { + CreateGate(0, {0, 1, 2}), + CreateGate(1, {2, 3}), + CreateGate(2, {1, 2}), }; param.max_fused_size = 3; - auto fused_gates = Fuser::FuseGates( + auto fused_gates = Fuser::FuseGates( param, num_qubits, circuit.begin(), circuit.end(), false); EXPECT_TRUE(TestFusedGates(num_qubits, circuit, fused_gates)); @@ -501,15 +478,15 @@ TEST(FuserMultiQubitTest, SmallCircuits) { { unsigned num_qubits = 4; - std::vector circuit = { - CreateDummyGate(0, {0, 1}), - CreateDummyGate(1, {1, 2}), - CreateDummyGate(2, {2, 3}), - CreateDummyGate(3, {0, 1, 2}), + std::vector circuit = { + CreateGate(0, {0, 1}), + CreateGate(1, {1, 2}), + CreateGate(2, {2, 3}), + CreateGate(3, {0, 1, 2}), }; param.max_fused_size = 3; - auto fused_gates = Fuser::FuseGates( + auto fused_gates = Fuser::FuseGates( param, num_qubits, circuit.begin(), circuit.end(), false); EXPECT_TRUE(TestFusedGates(num_qubits, circuit, fused_gates)); @@ -518,16 +495,16 @@ TEST(FuserMultiQubitTest, SmallCircuits) { { unsigned num_qubits = 5; - std::vector circuit = { - CreateDummyGate(0, {0, 1}), - CreateDummyGate(1, {1, 2}), - CreateDummyGate(2, {2, 3}), - CreateDummyGate(3, {3, 4}), - CreateDummyGate(4, {0, 1, 2, 3}), + std::vector circuit = { + CreateGate(0, {0, 1}), + CreateGate(1, {1, 2}), + CreateGate(2, {2, 3}), + CreateGate(3, {3, 4}), + CreateGate(4, {0, 1, 2, 3}), }; param.max_fused_size = 3; - auto fused_gates = Fuser::FuseGates( + auto fused_gates = Fuser::FuseGates( param, num_qubits, circuit.begin(), circuit.end(), false); EXPECT_TRUE(TestFusedGates(num_qubits, circuit, fused_gates)); @@ -536,17 +513,17 @@ TEST(FuserMultiQubitTest, SmallCircuits) { { unsigned num_qubits = 6; - std::vector circuit = { - CreateDummyGate(0, {0, 1}), - CreateDummyGate(0, {2, 3}), - CreateDummyGate(0, {4, 5}), - CreateDummyGate(1, {1, 2}), - CreateDummyGate(1, {3, 4}), - CreateDummyGate(1, {5, 0}), + std::vector circuit = { + CreateGate(0, {0, 1}), + CreateGate(0, {2, 3}), + CreateGate(0, {4, 5}), + CreateGate(1, {1, 2}), + CreateGate(1, {3, 4}), + CreateGate(1, {5, 0}), }; param.max_fused_size = 3; - auto fused_gates = Fuser::FuseGates( + auto fused_gates = Fuser::FuseGates( param, num_qubits, circuit.begin(), circuit.end(), false); EXPECT_TRUE(TestFusedGates(num_qubits, circuit, fused_gates)); @@ -555,19 +532,19 @@ TEST(FuserMultiQubitTest, SmallCircuits) { { unsigned num_qubits = 8; - std::vector circuit = { - CreateDummyGate(0, {0, 1}), - CreateDummyGate(0, {2, 3}), - CreateDummyGate(0, {4, 5}), - CreateDummyGate(0, {6, 7}), - CreateDummyGate(1, {1, 2}), - CreateDummyGate(1, {3, 4}), - CreateDummyGate(1, {5, 6}), - CreateDummyGate(1, {7, 0}), + std::vector circuit = { + CreateGate(0, {0, 1}), + CreateGate(0, {2, 3}), + CreateGate(0, {4, 5}), + CreateGate(0, {6, 7}), + CreateGate(1, {1, 2}), + CreateGate(1, {3, 4}), + CreateGate(1, {5, 6}), + CreateGate(1, {7, 0}), }; param.max_fused_size = 5; - auto fused_gates = Fuser::FuseGates( + auto fused_gates = Fuser::FuseGates( param, num_qubits, circuit.begin(), circuit.end(), false); EXPECT_TRUE(TestFusedGates(num_qubits, circuit, fused_gates)); @@ -576,14 +553,14 @@ TEST(FuserMultiQubitTest, SmallCircuits) { { unsigned num_qubits = 3; - std::vector circuit = { - CreateDummyGate(0, {1, 2}), - CreateDummyGate(1, {0, 1}), - CreateDummyGate(2, {1, 2}), + std::vector circuit = { + CreateGate(0, {1, 2}), + CreateGate(1, {0, 1}), + CreateGate(2, {1, 2}), }; param.max_fused_size = 3; - auto fused_gates = Fuser::FuseGates( + auto fused_gates = Fuser::FuseGates( param, num_qubits, circuit.begin(), circuit.end(), false); EXPECT_TRUE(TestFusedGates(num_qubits, circuit, fused_gates)); @@ -592,14 +569,14 @@ TEST(FuserMultiQubitTest, SmallCircuits) { { unsigned num_qubits = 3; - std::vector circuit = { - CreateDummyGate(0, {0, 1}), - CreateDummyGate(1, {0, 2}), - CreateDummyGate(2, {1, 2}), + std::vector circuit = { + CreateGate(0, {0, 1}), + CreateGate(1, {0, 2}), + CreateGate(2, {1, 2}), }; param.max_fused_size = 3; - auto fused_gates = Fuser::FuseGates( + auto fused_gates = Fuser::FuseGates( param, num_qubits, circuit.begin(), circuit.end(), false); EXPECT_TRUE(TestFusedGates(num_qubits, circuit, fused_gates)); @@ -608,16 +585,16 @@ TEST(FuserMultiQubitTest, SmallCircuits) { { unsigned num_qubits = 6; - std::vector circuit = { - CreateDummyGate(0, {0, 1}), - CreateDummyGate(1, {2, 3}), - CreateDummyGate(2, {4, 5}), - CreateDummyGate(3, {1, 2}), - CreateDummyGate(4, {2, 3, 4}), + std::vector circuit = { + CreateGate(0, {0, 1}), + CreateGate(1, {2, 3}), + CreateGate(2, {4, 5}), + CreateGate(3, {1, 2}), + CreateGate(4, {2, 3, 4}), }; param.max_fused_size = 3; - auto fused_gates = Fuser::FuseGates( + auto fused_gates = Fuser::FuseGates( param, num_qubits, circuit.begin(), circuit.end(), false); EXPECT_TRUE(TestFusedGates(num_qubits, circuit, fused_gates)); @@ -626,18 +603,18 @@ TEST(FuserMultiQubitTest, SmallCircuits) { { unsigned num_qubits = 7; - std::vector circuit = { - CreateDummyGate(0, {1, 6}), - CreateDummyGate(1, {0, 1}), - CreateDummyGate(1, {4, 5}), - CreateDummyGate(2, {1, 2, 3}), - CreateDummyGate(2, {5, 6}), - CreateDummyGate(3, {3, 4}), + std::vector circuit = { + CreateGate(0, {1, 6}), + CreateGate(1, {0, 1}), + CreateGate(1, {4, 5}), + CreateGate(2, {1, 2, 3}), + CreateGate(2, {5, 6}), + CreateGate(3, {3, 4}), }; { param.max_fused_size = 3; - auto fused_gates = Fuser::FuseGates( + auto fused_gates = Fuser::FuseGates( param, num_qubits, circuit.begin(), circuit.end(), false); EXPECT_TRUE(TestFusedGates(num_qubits, circuit, fused_gates)); @@ -646,7 +623,7 @@ TEST(FuserMultiQubitTest, SmallCircuits) { { param.max_fused_size = 5; - auto fused_gates = Fuser::FuseGates( + auto fused_gates = Fuser::FuseGates( param, num_qubits, circuit.begin(), circuit.end(), false); EXPECT_TRUE(TestFusedGates(num_qubits, circuit, fused_gates)); @@ -659,12 +636,12 @@ TEST(FuserMultiQubitTest, SmallCircuits) { { unsigned num_qubits = 2; - std::vector circuit = { - CreateDummyGate(0, {}), + std::vector circuit = { + CreateGate(0, {}), }; param.max_fused_size = 2; - auto fused_gates = Fuser::FuseGates( + auto fused_gates = Fuser::FuseGates( param, num_qubits, circuit.begin(), circuit.end(), false); EXPECT_TRUE(TestFusedGates(num_qubits, circuit, fused_gates)); @@ -673,13 +650,13 @@ TEST(FuserMultiQubitTest, SmallCircuits) { { unsigned num_qubits = 2; - std::vector circuit = { - CreateDummyGate(0, {}), - CreateDummyGate(0, {}), + std::vector circuit = { + CreateGate(0, {}), + CreateGate(0, {}), }; param.max_fused_size = 2; - auto fused_gates = Fuser::FuseGates( + auto fused_gates = Fuser::FuseGates( param, num_qubits, circuit.begin(), circuit.end(), false); EXPECT_TRUE(TestFusedGates(num_qubits, circuit, fused_gates)); @@ -688,13 +665,13 @@ TEST(FuserMultiQubitTest, SmallCircuits) { { unsigned num_qubits = 2; - std::vector circuit = { - CreateDummyGate(0, {0, 1}), - CreateDummyGate(0, {}), + std::vector circuit = { + CreateGate(0, {0, 1}), + CreateGate(0, {}), }; param.max_fused_size = 2; - auto fused_gates = Fuser::FuseGates( + auto fused_gates = Fuser::FuseGates( param, num_qubits, circuit.begin(), circuit.end(), false); EXPECT_TRUE(TestFusedGates(num_qubits, circuit, fused_gates)); @@ -703,49 +680,56 @@ TEST(FuserMultiQubitTest, SmallCircuits) { { unsigned num_qubits = 2; - std::vector circuit = { - CreateDummyControlledGate(0, {0}, {1}), - CreateDummyGate(1, {0, 1}), - CreateDummyGate(1, {}), + std::vector circuit = { + CreateControlledGate(0, {0}, {1}), + CreateGate(1, {0, 1}), + CreateGate(1, {}), }; param.max_fused_size = 2; - auto fused_gates = Fuser::FuseGates( + auto fused_gates = Fuser::FuseGates( param, num_qubits, circuit.begin(), circuit.end(), false); EXPECT_TRUE(TestFusedGates(num_qubits, circuit, fused_gates)); EXPECT_EQ(fused_gates.size(), 2); - EXPECT_EQ(fused_gates[1].gates[1], &circuit[2]); + + const auto* fgate1 = OpGetAlternative(fused_gates[1]); + ASSERT_NE(fgate1, nullptr); + const auto* gate11 = OpGetAlternative(fgate1->gates[1]); + ASSERT_NE(gate11, nullptr); + EXPECT_EQ(gate11, OpGetAlternative(circuit[2])); } } TEST(FuserMultiQubitTest, ValidTimeOrder) { - using Fuser = MultiQubitGateFuser; + using Gate = qsim::Gate; + using Operation = qsim::Operation; + using Fuser = MultiQubitGateFuser; Fuser::Parameter param; param.verbosity = 0; { unsigned num_qubits = 8; - std::vector circuit = { - CreateDummyGate(0, {0, 1}), - CreateDummyGate(0, {2, 3}), - CreateDummyControlledGate(1, {1}, {2}), - CreateDummyGate(0, {4, 5}), - CreateDummyGate(2, {0, 1}), - CreateDummyGate(1, {3, 4}), - CreateDummyGate(2, {2, 3}), - CreateDummyGate(3, {1, 2}), - CreateDummyControlledGate(2, {4}, {5}), - CreateDummyGate(3, {3, 4}), - CreateDummyGate(5, {0, 1}), - CreateDummyGate(4, {2, 3}), - CreateDummyGate(5, {4, 5}), - CreateDummyGate(4, {6, 7}), + std::vector circuit = { + CreateGate(0, {0, 1}), + CreateGate(0, {2, 3}), + CreateControlledGate(1, {1}, {2}), + CreateGate(0, {4, 5}), + CreateGate(2, {0, 1}), + CreateGate(1, {3, 4}), + CreateGate(2, {2, 3}), + CreateGate(3, {1, 2}), + CreateControlledGate(2, {4}, {5}), + CreateGate(3, {3, 4}), + CreateGate(5, {0, 1}), + CreateGate(4, {2, 3}), + CreateGate(5, {4, 5}), + CreateGate(4, {6, 7}), }; param.max_fused_size = 2; - auto fused_gates = Fuser::FuseGates( + auto fused_gates = Fuser::FuseGates( param, num_qubits, circuit.begin(), circuit.end(), false); EXPECT_EQ(fused_gates.size(), 14); @@ -754,23 +738,23 @@ TEST(FuserMultiQubitTest, ValidTimeOrder) { { unsigned num_qubits = 6; - std::vector circuit = { - CreateDummyGate(0, {0, 1}), - CreateDummyGate(0, {2, 3}), - CreateDummyGate(1, {1, 2}), - CreateDummyGate(0, {4, 5}), - CreateDummyGate(1, {3, 4}), - CreateDummyMeasurementGate(2, {0, 1, 2}), - CreateDummyMeasurementGate(2, {4, 5}), - CreateDummyGate(3, {0, 1}), - CreateDummyGate(3, {2, 3}), - CreateDummyGate(4, {1, 2}), - CreateDummyGate(3, {4, 5}), - CreateDummyGate(4, {3, 4}), + std::vector circuit = { + CreateGate(0, {0, 1}), + CreateGate(0, {2, 3}), + CreateGate(1, {1, 2}), + CreateGate(0, {4, 5}), + CreateGate(1, {3, 4}), + CreateMeasurement(2, {0, 1, 2}), + CreateMeasurement(2, {4, 5}), + CreateGate(3, {0, 1}), + CreateGate(3, {2, 3}), + CreateGate(4, {1, 2}), + CreateGate(3, {4, 5}), + CreateGate(4, {3, 4}), }; param.max_fused_size = 6; - auto fused_gates = Fuser::FuseGates( + auto fused_gates = Fuser::FuseGates( param, num_qubits, circuit.begin(), circuit.end(), false); EXPECT_EQ(fused_gates.size(), 3); @@ -779,17 +763,17 @@ TEST(FuserMultiQubitTest, ValidTimeOrder) { { unsigned num_qubits = 6; - std::vector circuit = { - CreateDummyGate(0, {0, 1}), - CreateDummyGate(0, {2, 3}), - CreateDummyGate(1, {1, 2}), - CreateDummyGate(0, {4, 5}), - CreateDummyGate(1, {3, 4}), - CreateDummyGate(1, {5, 0}), + std::vector circuit = { + CreateGate(0, {0, 1}), + CreateGate(0, {2, 3}), + CreateGate(1, {1, 2}), + CreateGate(0, {4, 5}), + CreateGate(1, {3, 4}), + CreateGate(1, {5, 0}), }; param.max_fused_size = 3; - auto fused_gates = Fuser::FuseGates( + auto fused_gates = Fuser::FuseGates( param, num_qubits, circuit.begin(), circuit.end(), false); EXPECT_EQ(fused_gates.size(), 4); @@ -798,19 +782,19 @@ TEST(FuserMultiQubitTest, ValidTimeOrder) { { unsigned num_qubits = 8; - std::vector circuit = { - CreateDummyGate(0, {0, 1}), - CreateDummyGate(0, {2, 3}), - CreateDummyGate(0, {4, 5}), - CreateDummyGate(1, {1, 2}), - CreateDummyGate(1, {3, 4}), - CreateDummyGate(0, {6, 7}), - CreateDummyGate(1, {5, 6}), - CreateDummyGate(1, {7, 0}), + std::vector circuit = { + CreateGate(0, {0, 1}), + CreateGate(0, {2, 3}), + CreateGate(0, {4, 5}), + CreateGate(1, {1, 2}), + CreateGate(1, {3, 4}), + CreateGate(0, {6, 7}), + CreateGate(1, {5, 6}), + CreateGate(1, {7, 0}), }; param.max_fused_size = 5; - auto fused_gates = Fuser::FuseGates( + auto fused_gates = Fuser::FuseGates( param, num_qubits, circuit.begin(), circuit.end(), false); EXPECT_EQ(fused_gates.size(), 3); @@ -819,16 +803,16 @@ TEST(FuserMultiQubitTest, ValidTimeOrder) { { unsigned num_qubits = 6; - std::vector circuit = { - CreateDummyGate(0, {0, 1}), - CreateDummyGate(1, {2, 3}), - CreateDummyGate(3, {1, 2}), - CreateDummyGate(2, {4, 5}), - CreateDummyGate(4, {2, 3, 4}), + std::vector circuit = { + CreateGate(0, {0, 1}), + CreateGate(1, {2, 3}), + CreateGate(3, {1, 2}), + CreateGate(2, {4, 5}), + CreateGate(4, {2, 3, 4}), }; param.max_fused_size = 3; - auto fused_gates = Fuser::FuseGates( + auto fused_gates = Fuser::FuseGates( param, num_qubits, circuit.begin(), circuit.end(), false); EXPECT_EQ(fused_gates.size(), 4); @@ -837,18 +821,18 @@ TEST(FuserMultiQubitTest, ValidTimeOrder) { { unsigned num_qubits = 7; - std::vector circuit = { - CreateDummyGate(0, {1, 6}), - CreateDummyGate(1, {0, 1}), - CreateDummyGate(2, {1, 2, 3}), - CreateDummyGate(1, {4, 5}), - CreateDummyGate(3, {3, 4}), - CreateDummyGate(2, {5, 6}), + std::vector circuit = { + CreateGate(0, {1, 6}), + CreateGate(1, {0, 1}), + CreateGate(2, {1, 2, 3}), + CreateGate(1, {4, 5}), + CreateGate(3, {3, 4}), + CreateGate(2, {5, 6}), }; { param.max_fused_size = 3; - auto fused_gates = Fuser::FuseGates( + auto fused_gates = Fuser::FuseGates( param, num_qubits, circuit.begin(), circuit.end(), false); EXPECT_TRUE(TestFusedGates(num_qubits, circuit, fused_gates)); @@ -856,7 +840,7 @@ TEST(FuserMultiQubitTest, ValidTimeOrder) { { param.max_fused_size = 5; - auto fused_gates = Fuser::FuseGates( + auto fused_gates = Fuser::FuseGates( param, num_qubits, circuit.begin(), circuit.end(), false); EXPECT_TRUE(TestFusedGates(num_qubits, circuit, fused_gates)); @@ -865,23 +849,24 @@ TEST(FuserMultiQubitTest, ValidTimeOrder) { { unsigned num_qubits = 4; - std::vector circuit = { - CreateDummyGate(0, {0, 1}), - CreateDummyGate(0, {2, 3}), - CreateDummyGate(2, {1, 2}), - CreateDummyGate(1, {0, 3}), - CreateDummyGate(3, {1, 2}), - CreateDummyGate(3, {0, 3}), - CreateDummyGate(4, {0, 1}), - CreateDummyGate(4, {2, 3}), - CreateDummyGate(6, {0, 3}), - CreateDummyGate(5, {1, 2}), + std::vector circuit = { + CreateGate(0, {0, 1}), + CreateGate(0, {2, 3}), + CreateGate(2, {1, 2}), + CreateGate(1, {0, 3}), + CreateGate(3, {1, 2}), + CreateGate(3, {0, 3}), + CreateGate(4, {0, 1}), + CreateGate(4, {2, 3}), + CreateGate(6, {0, 3}), + CreateGate(5, {1, 2}), }; param.max_fused_size = 4; std::vector time_boundary = {3}; - auto fused_gates = Fuser::FuseGates(param, num_qubits, circuit.begin(), - circuit.end(), time_boundary, false); + auto fused_gates = Fuser::FuseGates( + param, num_qubits, circuit.begin(), circuit.end(), + time_boundary, false); EXPECT_EQ(fused_gates.size(), 2); EXPECT_TRUE(TestFusedGates(num_qubits, circuit, fused_gates)); @@ -889,21 +874,21 @@ TEST(FuserMultiQubitTest, ValidTimeOrder) { { unsigned num_qubits = 4; - std::vector circuit = { - CreateDummyGate(0, {0, 1}), - CreateDummyGate(0, {2, 3}), - CreateDummyGate(2, {1, 2}), - CreateDummyGate(1, {0, 3}), - CreateDummyMeasurementGate(3, {1, 2}), - CreateDummyGate(3, {0, 3}), - CreateDummyGate(4, {0, 1}), - CreateDummyGate(4, {2, 3}), - CreateDummyGate(6, {0, 3}), - CreateDummyGate(5, {1, 2}), + std::vector circuit = { + CreateGate(0, {0, 1}), + CreateGate(0, {2, 3}), + CreateGate(2, {1, 2}), + CreateGate(1, {0, 3}), + CreateMeasurement(3, {1, 2}), + CreateGate(3, {0, 3}), + CreateGate(4, {0, 1}), + CreateGate(4, {2, 3}), + CreateGate(6, {0, 3}), + CreateGate(5, {1, 2}), }; param.max_fused_size = 4; - auto fused_gates = Fuser::FuseGates( + auto fused_gates = Fuser::FuseGates( param, num_qubits, circuit.begin(), circuit.end(), false); EXPECT_EQ(fused_gates.size(), 3); @@ -912,15 +897,16 @@ TEST(FuserMultiQubitTest, ValidTimeOrder) { { unsigned num_qubits = 2; - std::vector circuit = { - CreateDummyGate(1, {0, 1}), - CreateDummyGate(2, {}), + std::vector circuit = { + CreateGate(1, {0, 1}), + CreateGate(2, {}), }; param.max_fused_size = 2; std::vector time_boundary = {1}; - auto fused_gates = Fuser::FuseGates(param, num_qubits, circuit.begin(), - circuit.end(), time_boundary, false); + auto fused_gates = Fuser::FuseGates( + param, num_qubits, circuit.begin(), circuit.end(), + time_boundary, false); EXPECT_TRUE(TestFusedGates(num_qubits, circuit, fused_gates)); EXPECT_EQ(fused_gates.size(), 2); @@ -928,13 +914,13 @@ TEST(FuserMultiQubitTest, ValidTimeOrder) { { unsigned num_qubits = 2; - std::vector circuit = { - CreateDummyGate(1, {0, 1}), - CreateDummyGate(0, {}), + std::vector circuit = { + CreateGate(1, {0, 1}), + CreateGate(0, {}), }; param.max_fused_size = 2; - auto fused_gates = Fuser::FuseGates( + auto fused_gates = Fuser::FuseGates( param, num_qubits, circuit.begin(), circuit.end(), false); EXPECT_TRUE(TestFusedGates(num_qubits, circuit, fused_gates)); @@ -943,20 +929,22 @@ TEST(FuserMultiQubitTest, ValidTimeOrder) { } TEST(FuserMultiQubitTest, InvalidTimeOrder) { - using Fuser = MultiQubitGateFuser; + using Gate = qsim::Gate; + using Operation = qsim::Operation; + using Fuser = MultiQubitGateFuser; Fuser::Parameter param; param.verbosity = 0; { unsigned num_qubits = 3; - std::vector circuit = { - CreateDummyGate(0, {0, 1}), - CreateDummyGate(0, {1, 2}), + std::vector circuit = { + CreateGate(0, {0, 1}), + CreateGate(0, {1, 2}), }; param.max_fused_size = 3; - auto fused_gates = Fuser::FuseGates( + auto fused_gates = Fuser::FuseGates( param, num_qubits, circuit.begin(), circuit.end(), false); EXPECT_EQ(fused_gates.size(), 0); @@ -964,15 +952,15 @@ TEST(FuserMultiQubitTest, InvalidTimeOrder) { { unsigned num_qubits = 4; - std::vector circuit = { - CreateDummyGate(0, {0, 1}), - CreateDummyGate(0, {2, 3}), - CreateDummyGate(2, {1, 2}), - CreateDummyGate(1, {0, 2}), + std::vector circuit = { + CreateGate(0, {0, 1}), + CreateGate(0, {2, 3}), + CreateGate(2, {1, 2}), + CreateGate(1, {0, 2}), }; param.max_fused_size = 2; - auto fused_gates = Fuser::FuseGates( + auto fused_gates = Fuser::FuseGates( param, num_qubits, circuit.begin(), circuit.end(), false); EXPECT_EQ(fused_gates.size(), 0); @@ -980,49 +968,51 @@ TEST(FuserMultiQubitTest, InvalidTimeOrder) { { unsigned num_qubits = 4; - std::vector circuit = { - CreateDummyGate(0, {0, 1}), - CreateDummyGate(0, {2, 3}), - CreateDummyGate(2, {0, 3}), - CreateDummyGate(1, {1, 2}), + std::vector circuit = { + CreateGate(0, {0, 1}), + CreateGate(0, {2, 3}), + CreateGate(2, {0, 3}), + CreateGate(1, {1, 2}), }; param.max_fused_size = 2; std::vector time_boundary = {1}; - auto fused_gates = Fuser::FuseGates(param, num_qubits, circuit.begin(), - circuit.end(), time_boundary, false); + auto fused_gates = Fuser::FuseGates( + param, num_qubits, circuit.begin(), circuit.end(), + time_boundary, false); EXPECT_EQ(fused_gates.size(), 0); } { unsigned num_qubits = 4; - std::vector circuit = { - CreateDummyGate(0, {0, 1}), - CreateDummyGate(0, {2, 3}), - CreateDummyGate(2, {0, 3}), - CreateDummyGate(1, {1, 2}), + std::vector circuit = { + CreateGate(0, {0, 1}), + CreateGate(0, {2, 3}), + CreateGate(2, {0, 3}), + CreateGate(1, {1, 2}), }; param.max_fused_size = 2; std::vector time_boundary = {2}; - auto fused_gates = Fuser::FuseGates(param, num_qubits, circuit.begin(), - circuit.end(), time_boundary, false); + auto fused_gates = Fuser::FuseGates( + param, num_qubits, circuit.begin(), circuit.end(), + time_boundary, false); EXPECT_EQ(fused_gates.size(), 0); } { unsigned num_qubits = 4; - std::vector circuit = { - CreateDummyGate(0, {0, 1}), - CreateDummyGate(0, {2, 3}), - CreateDummyGate(2, {0, 3}), - CreateDummyMeasurementGate(1, {1, 2}), + std::vector circuit = { + CreateGate(0, {0, 1}), + CreateGate(0, {2, 3}), + CreateGate(2, {0, 3}), + CreateMeasurement(1, {1, 2}), }; param.max_fused_size = 2; - auto fused_gates = Fuser::FuseGates( + auto fused_gates = Fuser::FuseGates( param, num_qubits, circuit.begin(), circuit.end(), false); EXPECT_EQ(fused_gates.size(), 0); @@ -1030,15 +1020,15 @@ TEST(FuserMultiQubitTest, InvalidTimeOrder) { { unsigned num_qubits = 4; - std::vector circuit = { - CreateDummyGate(0, {0, 1}), - CreateDummyGate(0, {2, 3}), - CreateDummyMeasurementGate(2, {0, 3}), - CreateDummyGate(1, {1, 2}), + std::vector circuit = { + CreateGate(0, {0, 1}), + CreateGate(0, {2, 3}), + CreateMeasurement(2, {0, 3}), + CreateGate(1, {1, 2}), }; param.max_fused_size = 2; - auto fused_gates = Fuser::FuseGates( + auto fused_gates = Fuser::FuseGates( param, num_qubits, circuit.begin(), circuit.end(), false); EXPECT_EQ(fused_gates.size(), 0); @@ -1046,15 +1036,15 @@ TEST(FuserMultiQubitTest, InvalidTimeOrder) { { unsigned num_qubits = 4; - std::vector circuit = { - CreateDummyGate(0, {0, 1}), - CreateDummyGate(0, {2, 3}), - CreateDummyGate(2, {0, 3}), - CreateDummyControlledGate(1, {1}, {3}), + std::vector circuit = { + CreateGate(0, {0, 1}), + CreateGate(0, {2, 3}), + CreateGate(2, {0, 3}), + CreateControlledGate(1, {1}, {3}), }; param.max_fused_size = 2; - auto fused_gates = Fuser::FuseGates( + auto fused_gates = Fuser::FuseGates( param, num_qubits, circuit.begin(), circuit.end(), false); EXPECT_EQ(fused_gates.size(), 0); @@ -1062,15 +1052,15 @@ TEST(FuserMultiQubitTest, InvalidTimeOrder) { { unsigned num_qubits = 4; - std::vector circuit = { - CreateDummyGate(0, {0, 1}), - CreateDummyGate(0, {2, 3}), - CreateDummyControlledGate(2, {1}, {3}), - CreateDummyGate(1, {0, 3}), + std::vector circuit = { + CreateGate(0, {0, 1}), + CreateGate(0, {2, 3}), + CreateControlledGate(2, {1}, {3}), + CreateGate(1, {0, 3}), }; param.max_fused_size = 2; - auto fused_gates = Fuser::FuseGates( + auto fused_gates = Fuser::FuseGates( param, num_qubits, circuit.begin(), circuit.end(), false); EXPECT_EQ(fused_gates.size(), 0); @@ -1078,35 +1068,38 @@ TEST(FuserMultiQubitTest, InvalidTimeOrder) { { unsigned num_qubits = 2; - std::vector circuit = { - CreateDummyGate(1, {0, 1}), - CreateDummyGate(0, {}), + std::vector circuit = { + CreateGate(1, {0, 1}), + CreateGate(0, {}), }; param.max_fused_size = 2; std::vector time_boundary = {1}; - auto fused_gates = Fuser::FuseGates(param, num_qubits, circuit.begin(), - circuit.end(), time_boundary, false); + auto fused_gates = Fuser::FuseGates( + param, num_qubits, circuit.begin(), circuit.end(), + time_boundary, false); EXPECT_EQ(fused_gates.size(), 0); } } TEST(FuserMultiQubitTest, QubitsOutOfRange) { - using Fuser = MultiQubitGateFuser; + using Gate = qsim::Gate; + using Operation = qsim::Operation; + using Fuser = MultiQubitGateFuser; Fuser::Parameter param; param.verbosity = 0; { unsigned num_qubits = 3; - std::vector circuit = { - CreateDummyGate(0, {0, 3}), - CreateDummyGate(0, {1, 2}), + std::vector circuit = { + CreateGate(0, {0, 3}), + CreateGate(0, {1, 2}), }; param.max_fused_size = 2; - auto fused_gates = Fuser::FuseGates( + auto fused_gates = Fuser::FuseGates( param, num_qubits, circuit.begin(), circuit.end(), false); EXPECT_EQ(fused_gates.size(), 0); @@ -1114,13 +1107,13 @@ TEST(FuserMultiQubitTest, QubitsOutOfRange) { { unsigned num_qubits = 3; - std::vector circuit = { - CreateDummyGate(0, {0, 1}), - CreateDummyControlledGate(0, {2}, {3}), + std::vector circuit = { + CreateGate(0, {0, 1}), + CreateControlledGate(0, {2}, {3}), }; param.max_fused_size = 2; - auto fused_gates = Fuser::FuseGates( + auto fused_gates = Fuser::FuseGates( param, num_qubits, circuit.begin(), circuit.end(), false); EXPECT_EQ(fused_gates.size(), 0); @@ -1128,9 +1121,11 @@ TEST(FuserMultiQubitTest, QubitsOutOfRange) { } TEST(FuserMultiQubitTest, OrphanedGates) { - using Fuser = MultiQubitGateFuser; + using Gate = qsim::Gate; + using Operation = qsim::Operation; + using Fuser = MultiQubitGateFuser; - std::vector circuit; + std::vector circuit; circuit.reserve(6); Fuser::Parameter param; @@ -1140,12 +1135,12 @@ TEST(FuserMultiQubitTest, OrphanedGates) { circuit.resize(0); for (unsigned q = 0; q < num_qubits; ++q) { - circuit.push_back(CreateDummyGate(0, {q})); + circuit.push_back(CreateGate(0, {q})); } for (unsigned f = 2; f <= num_qubits; ++f) { param.max_fused_size = f; - auto fused_gates = Fuser::FuseGates( + auto fused_gates = Fuser::FuseGates( param, num_qubits, circuit.begin(), circuit.end(), false); EXPECT_TRUE(TestFusedGates(num_qubits, circuit, fused_gates)); @@ -1155,16 +1150,16 @@ TEST(FuserMultiQubitTest, OrphanedGates) { { unsigned num_qubits = 4; - std::vector circuit = { - CreateDummyGate(0, {0}), - CreateDummyGate(0, {1}), - CreateDummyGate(0, {2}), - CreateDummyGate(0, {3}), - CreateDummyGate(1, {0, 3}), + std::vector circuit = { + CreateGate(0, {0}), + CreateGate(0, {1}), + CreateGate(0, {2}), + CreateGate(0, {3}), + CreateGate(1, {0, 3}), }; param.max_fused_size = 2; - auto fused_gates = Fuser::FuseGates( + auto fused_gates = Fuser::FuseGates( param, num_qubits, circuit.begin(), circuit.end(), false); EXPECT_EQ(fused_gates.size(), 2); @@ -1172,16 +1167,16 @@ TEST(FuserMultiQubitTest, OrphanedGates) { { unsigned num_qubits = 4; - std::vector circuit = { - CreateDummyGate(0, {0, 3}), - CreateDummyGate(1, {0}), - CreateDummyGate(1, {1}), - CreateDummyGate(1, {2}), - CreateDummyGate(1, {3}), + std::vector circuit = { + CreateGate(0, {0, 3}), + CreateGate(1, {0}), + CreateGate(1, {1}), + CreateGate(1, {2}), + CreateGate(1, {3}), }; param.max_fused_size = 2; - auto fused_gates = Fuser::FuseGates( + auto fused_gates = Fuser::FuseGates( param, num_qubits, circuit.begin(), circuit.end(), false); EXPECT_EQ(fused_gates.size(), 2); @@ -1189,13 +1184,13 @@ TEST(FuserMultiQubitTest, OrphanedGates) { { unsigned num_qubits = 3; - std::vector circuit = { - CreateDummyGate(0, {0}), - CreateDummyControlledGate(0, {1}, {2}), + std::vector circuit = { + CreateGate(0, {0}), + CreateControlledGate(0, {1}, {2}), }; param.max_fused_size = 2; - auto fused_gates = Fuser::FuseGates( + auto fused_gates = Fuser::FuseGates( param, num_qubits, circuit.begin(), circuit.end(), false); EXPECT_EQ(fused_gates.size(), 2); @@ -1203,13 +1198,13 @@ TEST(FuserMultiQubitTest, OrphanedGates) { { unsigned num_qubits = 3; - std::vector circuit = { - CreateDummyGate(0, {0}), - CreateDummyMeasurementGate(0, {2}), + std::vector circuit = { + CreateGate(0, {0}), + CreateMeasurement(0, {2}), }; param.max_fused_size = 2; - auto fused_gates = Fuser::FuseGates( + auto fused_gates = Fuser::FuseGates( param, num_qubits, circuit.begin(), circuit.end(), false); EXPECT_EQ(fused_gates.size(), 2); diff --git a/tests/fuser_testfixture.h b/tests/fuser_testfixture.h new file mode 100644 index 000000000..d4c2fb47a --- /dev/null +++ b/tests/fuser_testfixture.h @@ -0,0 +1,191 @@ +// Copyright 2019 Google LLC. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef FUSER_TESTFIXTURE_H_ +#define FUSER_TESTFIXTURE_H_ + +#include +#include +#include +#include + +#include "gtest/gtest.h" + +#include "../lib/gate.h" +#include "../lib/operation.h" +#include "../lib/operation_base.h" + +namespace qsim { + +template +bool TestGate(const std::vector& ops, const Gate& gate, + std::vector& op_map) { + unsigned k = (std::size_t(&gate) - std::size_t(ops.data())) / sizeof(ops[0]); + + if (op_map[k] != 0) { + return false; + } else { + op_map[k] = 1; + return true; + } +} + +template +bool TestMeasurement(const std::vector& ops, + const Measurement& mea, std::vector& op_map, + const Map& mea_qubits, const Map& mea_at_time) { + std::vector qubits0; + qubits0.reserve(64); + + if (std::size_t(&mea) >= std::size_t(ops.data()) + && std::size_t(&mea) <= std::size_t(&ops.back())) { + unsigned k = (std::size_t(&mea) - std::size_t(ops.data())) / sizeof(ops[0]); + + if (op_map[k] != 0) { + return false; + } else { + op_map[k] = 1; + return true; + } + } + + auto it1 = mea_at_time.find(mea.time); + if (it1 == mea_at_time.end()) { + return false; + } + + const auto& indices = it1->second; + for (unsigned i : indices) { + if (op_map[i] != 0) { + return false; + } else { + op_map[i] = 1; + } + } + + std::vector qubits = mea.qubits; + std::sort(qubits.begin(), qubits.end()); + + auto it2 = mea_qubits.find(mea.time); + return it2 != mea_qubits.end() && qubits == it2->second; +} + +template +bool TestFusedGates(unsigned num_qubits, + const std::vector& ops, + const std::vector& fused_ops) { + using Gate = qsim::Gate; + using ControlledGate = qsim::ControlledGate; + using DecomposedGate = qsim::DecomposedGate; + using FusedGate = qsim::FusedGate; + + std::vector times(num_qubits, 0); + std::vector op_map(ops.size(), 0); + + std::map> mea_qubits; + std::map> mea_at_time; + + for (unsigned i = 0; i < ops.size(); ++i) { + const auto& op = ops[i]; + + if (const auto* pg = OpGetAlternative(op)) { + auto& qubits = mea_qubits[pg->time]; + if (qubits.size() == 0) { + qubits.reserve(8); + } + + for (auto q : pg->qubits) { + qubits.push_back(q); + } + + auto& indices = mea_at_time[pg->time]; + if (indices.size() == 0) { + indices.reserve(4); + } + + indices.push_back(i); + } + } + + for (auto it = mea_qubits.begin(); it != mea_qubits.end(); ++it) { + std::sort(it->second.begin(), it->second.end()); + } + + for (const auto& op : fused_ops) { + if (const auto* pg = OpGetAlternative(op)) { + for (const auto& pv : pg->gates) { + if (const auto* pg = OpGetAlternative(pv)) { + if (!TestGate(ops, *pg, op_map)) { + return false; + } + + for (auto q : pg->qubits) { + if (pg->time < times[q]) { + return false; + } + times[q] = pg->time; + } + } else if (const auto* pg = OpGetAlternative(pv)) { + if (!TestGate(ops, *pg, op_map)) { + return false; + } + + for (auto q : pg->qubits) { + if (pg->time < times[q]) { + return false; + } + times[q] = pg->time; + } + } + } + } else if (const auto* pg = OpGetAlternative(op)) { + // Measurements can be fused or unfused. + + if (!TestMeasurement(ops, *pg, op_map, mea_qubits, mea_at_time)) { + return false; + } + } else if (const auto* pg = OpGetAlternative(op)) { + if (!TestGate(ops, *pg, op_map)) { + return false; + } + + for (auto q : pg->qubits) { + if (pg->time < times[q]) { + return false; + } + times[q] = pg->time; + } + + for (auto q : pg->controlled_by) { + if (pg->time < times[q]) { + return false; + } + times[q] = pg->time; + } + } + } + + // Test if all gates are present only once. + for (auto m : op_map) { + if (m != 1) { + return false; + } + } + + return true; +} + +} // namespace qsim + +#endif // FUSER_TESTFIXTURE_H_ diff --git a/tests/gates_cirq_testfixture.h b/tests/gates_cirq_testfixture.h index 39e51873e..41a8430dc 100644 --- a/tests/gates_cirq_testfixture.h +++ b/tests/gates_cirq_testfixture.h @@ -21,148 +21,149 @@ #include "../lib/circuit.h" #include "../lib/gates_cirq.h" +#include "../lib/operation.h" namespace qsim { namespace CirqCircuit1 { template -Circuit> GetCircuit(bool qsim) { - Circuit> circuit{4, {}}; - circuit.gates.reserve(128); +Circuit> GetCircuit(bool qsim) { + Circuit> circuit{4, {}}; + circuit.ops.reserve(128); - circuit.gates.emplace_back(Cirq::H::Create(0, 0)); - circuit.gates.emplace_back(Cirq::H::Create(0, 1)); - circuit.gates.emplace_back(Cirq::H::Create(0, 2)); - circuit.gates.emplace_back(Cirq::H::Create(0, 3)); + circuit.ops.push_back(Cirq::H::Create(0, 0)); + circuit.ops.push_back(Cirq::H::Create(0, 1)); + circuit.ops.push_back(Cirq::H::Create(0, 2)); + circuit.ops.push_back(Cirq::H::Create(0, 3)); - circuit.gates.emplace_back(Cirq::T::Create(1, 0)); - circuit.gates.emplace_back(Cirq::T::Create(1, 1)); - circuit.gates.emplace_back(Cirq::T::Create(1, 2)); - circuit.gates.emplace_back(Cirq::T::Create(1, 3)); + circuit.ops.push_back(Cirq::T::Create(1, 0)); + circuit.ops.push_back(Cirq::T::Create(1, 1)); + circuit.ops.push_back(Cirq::T::Create(1, 2)); + circuit.ops.push_back(Cirq::T::Create(1, 3)); - circuit.gates.emplace_back( + circuit.ops.push_back( Cirq::CZPowGate::Create(2, 0, 1, 0.7, 0.2)); - circuit.gates.emplace_back( + circuit.ops.push_back( Cirq::CXPowGate::Create(2, 2, 3, 1.2, 0.4)); - circuit.gates.emplace_back(Cirq::XPowGate::Create(3, 0, 0.3, 1.1)); - circuit.gates.emplace_back(Cirq::YPowGate::Create(3, 1, 0.4, 1.0)); - circuit.gates.emplace_back(Cirq::ZPowGate::Create(3, 2, 0.5, 0.9)); - circuit.gates.emplace_back(Cirq::HPowGate::Create(3, 3, 0.6, 0.8)); + circuit.ops.push_back(Cirq::XPowGate::Create(3, 0, 0.3, 1.1)); + circuit.ops.push_back(Cirq::YPowGate::Create(3, 1, 0.4, 1.0)); + circuit.ops.push_back(Cirq::ZPowGate::Create(3, 2, 0.5, 0.9)); + circuit.ops.push_back(Cirq::HPowGate::Create(3, 3, 0.6, 0.8)); - circuit.gates.emplace_back(Cirq::CX::Create(4, 0, 2)); - circuit.gates.emplace_back(Cirq::CZ::Create(4, 1, 3)); + circuit.ops.push_back(Cirq::CX::Create(4, 0, 2)); + circuit.ops.push_back(Cirq::CZ::Create(4, 1, 3)); - circuit.gates.emplace_back(Cirq::X::Create(5, 0)); - circuit.gates.emplace_back(Cirq::Y::Create(5, 1)); - circuit.gates.emplace_back(Cirq::Z::Create(5, 2)); - circuit.gates.emplace_back(Cirq::S::Create(5, 3)); + circuit.ops.push_back(Cirq::X::Create(5, 0)); + circuit.ops.push_back(Cirq::Y::Create(5, 1)); + circuit.ops.push_back(Cirq::Z::Create(5, 2)); + circuit.ops.push_back(Cirq::S::Create(5, 3)); - circuit.gates.emplace_back( + circuit.ops.push_back( Cirq::XXPowGate::Create(6, 0, 1, 0.4, 0.7)); - circuit.gates.emplace_back( + circuit.ops.push_back( Cirq::YYPowGate::Create(6, 2, 3, 0.8, 0.5)); - circuit.gates.emplace_back(Cirq::I1::Create(7, 0)); - circuit.gates.emplace_back(Cirq::I1::Create(7, 1)); - circuit.gates.emplace_back(Cirq::I2::Create(7, 2, 3)); + circuit.ops.push_back(Cirq::I1::Create(7, 0)); + circuit.ops.push_back(Cirq::I1::Create(7, 1)); + circuit.ops.push_back(Cirq::I2::Create(7, 2, 3)); - circuit.gates.emplace_back(Cirq::rx::Create(8, 0, 0.7)); - circuit.gates.emplace_back(Cirq::ry::Create(8, 1, 0.2)); - circuit.gates.emplace_back(Cirq::rz::Create(8, 2, 0.4)); - circuit.gates.emplace_back( + circuit.ops.push_back(Cirq::rx::Create(8, 0, 0.7)); + circuit.ops.push_back(Cirq::ry::Create(8, 1, 0.2)); + circuit.ops.push_back(Cirq::rz::Create(8, 2, 0.4)); + circuit.ops.push_back( Cirq::PhasedXPowGate::Create(8, 3, 0.8, 0.6, 0.3)); - circuit.gates.emplace_back( + circuit.ops.push_back( Cirq::ZZPowGate::Create(9, 0, 2, 0.3, 1.3)); - circuit.gates.emplace_back( + circuit.ops.push_back( Cirq::ISwapPowGate::Create(9, 1, 3, 0.6, 1.2)); - circuit.gates.emplace_back(Cirq::XPowGate::Create(10, 0, 0.1, 0.9)); - circuit.gates.emplace_back(Cirq::YPowGate::Create(10, 1, 0.2, 1.0)); - circuit.gates.emplace_back(Cirq::ZPowGate::Create(10, 2, 0.3, 1.1)); - circuit.gates.emplace_back(Cirq::HPowGate::Create(10, 3, 0.4, 1.2)); + circuit.ops.push_back(Cirq::XPowGate::Create(10, 0, 0.1, 0.9)); + circuit.ops.push_back(Cirq::YPowGate::Create(10, 1, 0.2, 1.0)); + circuit.ops.push_back(Cirq::ZPowGate::Create(10, 2, 0.3, 1.1)); + circuit.ops.push_back(Cirq::HPowGate::Create(10, 3, 0.4, 1.2)); - circuit.gates.emplace_back( + circuit.ops.push_back( Cirq::SwapPowGate::Create(11, 0, 1, 0.2, 0.9)); - circuit.gates.emplace_back( + circuit.ops.push_back( Cirq::PhasedISwapPowGate::Create(11, 2, 3, 0.8, 0.6)); - circuit.gates.emplace_back( + circuit.ops.push_back( Cirq::PhasedXZGate::Create(12, 0, 0.2, 0.3, 1.4)); - circuit.gates.emplace_back(Cirq::T::Create(12, 1)); - circuit.gates.emplace_back(Cirq::H::Create(12, 2)); - circuit.gates.emplace_back(Cirq::S::Create(12, 3)); + circuit.ops.push_back(Cirq::T::Create(12, 1)); + circuit.ops.push_back(Cirq::H::Create(12, 2)); + circuit.ops.push_back(Cirq::S::Create(12, 3)); - circuit.gates.emplace_back(Cirq::SWAP::Create(13, 0, 2)); - circuit.gates.emplace_back(Cirq::XX::Create(13, 1, 3)); + circuit.ops.push_back(Cirq::SWAP::Create(13, 0, 2)); + circuit.ops.push_back(Cirq::XX::Create(13, 1, 3)); - circuit.gates.emplace_back(Cirq::rx::Create(14, 0, 0.8)); - circuit.gates.emplace_back(Cirq::ry::Create(14, 1, 0.9)); - circuit.gates.emplace_back(Cirq::rz::Create(14, 2, 1.2)); - circuit.gates.emplace_back(Cirq::T::Create(14, 3)); + circuit.ops.push_back(Cirq::rx::Create(14, 0, 0.8)); + circuit.ops.push_back(Cirq::ry::Create(14, 1, 0.9)); + circuit.ops.push_back(Cirq::rz::Create(14, 2, 1.2)); + circuit.ops.push_back(Cirq::T::Create(14, 3)); - circuit.gates.emplace_back(Cirq::YY::Create(15, 0, 1)); - circuit.gates.emplace_back(Cirq::ISWAP::Create(15, 2, 3)); + circuit.ops.push_back(Cirq::YY::Create(15, 0, 1)); + circuit.ops.push_back(Cirq::ISWAP::Create(15, 2, 3)); - circuit.gates.emplace_back(Cirq::T::Create(16, 0)); - circuit.gates.emplace_back(Cirq::Z::Create(16, 1)); - circuit.gates.emplace_back(Cirq::Y::Create(16, 2)); - circuit.gates.emplace_back(Cirq::X::Create(16, 3)); + circuit.ops.push_back(Cirq::T::Create(16, 0)); + circuit.ops.push_back(Cirq::Z::Create(16, 1)); + circuit.ops.push_back(Cirq::Y::Create(16, 2)); + circuit.ops.push_back(Cirq::X::Create(16, 3)); - circuit.gates.emplace_back( + circuit.ops.push_back( Cirq::FSimGate::Create(17, 0, 2, 0.3, 1.7)); - circuit.gates.emplace_back(Cirq::ZZ::Create(17, 1, 3)); + circuit.ops.push_back(Cirq::ZZ::Create(17, 1, 3)); if (qsim) { - circuit.gates.emplace_back(Cirq::ry::Create(18, 0, 1.3)); - circuit.gates.emplace_back(Cirq::rz::Create(18, 1, 0.4)); - circuit.gates.emplace_back(Cirq::rx::Create(18, 2, 0.7)); - circuit.gates.emplace_back(Cirq::S::Create(18, 3)); - circuit.gates.emplace_back( + circuit.ops.push_back(Cirq::ry::Create(18, 0, 1.3)); + circuit.ops.push_back(Cirq::rz::Create(18, 1, 0.4)); + circuit.ops.push_back(Cirq::rx::Create(18, 2, 0.7)); + circuit.ops.push_back(Cirq::S::Create(18, 3)); + circuit.ops.push_back( Cirq::GlobalPhaseGate::Create(18, -1, 0)); - circuit.gates.emplace_back(Cirq::I::Create(19, {0, 1, 2, 3})); + circuit.ops.push_back(Cirq::I::Create(19, {0, 1, 2, 3})); - circuit.gates.emplace_back( + circuit.ops.push_back( Cirq::CCZPowGate::Create(20, 2, 0, 1, 0.7, 0.3)); - circuit.gates.emplace_back(Cirq::CCXPowGate::Create( + circuit.ops.push_back(Cirq::CCXPowGate::Create( 21, 3, 1, 0, 0.4, 0.6).ControlledBy({2}, {0})); - circuit.gates.emplace_back(Cirq::rx::Create(22, 0, 0.3)); - circuit.gates.emplace_back(Cirq::ry::Create(22, 1, 0.5)); - circuit.gates.emplace_back(Cirq::rz::Create(22, 2, 0.7)); - circuit.gates.emplace_back(Cirq::rx::Create(22, 3, 0.9)); + circuit.ops.push_back(Cirq::rx::Create(22, 0, 0.3)); + circuit.ops.push_back(Cirq::ry::Create(22, 1, 0.5)); + circuit.ops.push_back(Cirq::rz::Create(22, 2, 0.7)); + circuit.ops.push_back(Cirq::rx::Create(22, 3, 0.9)); - circuit.gates.emplace_back( + circuit.ops.push_back( Cirq::TwoQubitDiagonalGate::Create(23, 0, 1, {0.1f, 0.2f, 0.3f, 0.4f})); - circuit.gates.emplace_back( + circuit.ops.push_back( Cirq::ThreeQubitDiagonalGate::Create( 24, 1, 2, 3, {0.5, 0.6, 0.7, 0.8, 0.9, 1, 1.2, 1.3})); - circuit.gates.emplace_back(Cirq::CSwapGate::Create(25, 0, 3, 1)); + circuit.ops.push_back(Cirq::CSwapGate::Create(25, 0, 3, 1)); - circuit.gates.emplace_back(Cirq::rz::Create(26, 0, 0.6)); - circuit.gates.emplace_back(Cirq::rx::Create(26, 1, 0.7)); - circuit.gates.emplace_back(Cirq::ry::Create(26, 2, 0.8)); - circuit.gates.emplace_back(Cirq::rz::Create(26, 3, 0.9)); + circuit.ops.push_back(Cirq::rz::Create(26, 0, 0.6)); + circuit.ops.push_back(Cirq::rx::Create(26, 1, 0.7)); + circuit.ops.push_back(Cirq::ry::Create(26, 2, 0.8)); + circuit.ops.push_back(Cirq::rz::Create(26, 3, 0.9)); - circuit.gates.emplace_back(Cirq::TOFFOLI::Create(27, 3, 2, 0)); + circuit.ops.push_back(Cirq::TOFFOLI::Create(27, 3, 2, 0)); - circuit.gates.emplace_back(Cirq::FREDKIN::Create(28, 1, 3, 2)); + circuit.ops.push_back(Cirq::FREDKIN::Create(28, 1, 3, 2)); Matrix m40 = {0, 0, -0.5, -0.5, -0.5, -0.5, 0, 0, 0.5, -0.5, 0, 0, 0, 0, -0.5, 0.5, 0.5, -0.5, 0, 0, 0, 0, 0.5, -0.5, 0, 0, -0.5, -0.5, 0.5, 0.5, 0, 0}; - circuit.gates.emplace_back( + circuit.ops.push_back( Cirq::MatrixGate2::Create(51, 0, 1, m40)); - circuit.gates.emplace_back( + circuit.ops.push_back( Cirq::MatrixGate::Create(51, {2, 3}, {0.5, -0.5, 0, 0, 0, 0, -0.5, 0.5, 0, 0, 0.5, -0.5, -0.5, 0.5, 0, 0, @@ -173,19 +174,19 @@ Circuit> GetCircuit(bool qsim) { Matrix m20 = {1, 0, 0, 0, 0, 0, 0, 1}; Matrix m21 = {0, 0, 0, -1, 0, 1, 0, 0}; Matrix m22 = {0, 0, 1, 0, 1, 0, 0, 0}; - circuit.gates.emplace_back(Cirq::MatrixGate1::Create(52, 0, m20)); - circuit.gates.emplace_back(Cirq::MatrixGate1::Create(52, 1, m21)); - circuit.gates.emplace_back(Cirq::MatrixGate1::Create(52, 2, m22)); - circuit.gates.emplace_back( + circuit.ops.push_back(Cirq::MatrixGate1::Create(52, 0, m20)); + circuit.ops.push_back(Cirq::MatrixGate1::Create(52, 1, m21)); + circuit.ops.push_back(Cirq::MatrixGate1::Create(52, 2, m22)); + circuit.ops.push_back( Cirq::MatrixGate1::Create(52, 3, {1, 0, 0, 0, 0, 0, -1, 0})); - circuit.gates.emplace_back(Cirq::riswap::Create(53, 0, 1, 0.7)); - circuit.gates.emplace_back(Cirq::givens::Create(53, 2, 3, 1.2)); + circuit.ops.push_back(Cirq::riswap::Create(53, 0, 1, 0.7)); + circuit.ops.push_back(Cirq::givens::Create(53, 2, 3, 1.2)); - circuit.gates.emplace_back(Cirq::H::Create(54, 0)); - circuit.gates.emplace_back(Cirq::H::Create(54, 1)); - circuit.gates.emplace_back(Cirq::H::Create(54, 2)); - circuit.gates.emplace_back(Cirq::H::Create(54, 3)); + circuit.ops.push_back(Cirq::H::Create(54, 0)); + circuit.ops.push_back(Cirq::H::Create(54, 1)); + circuit.ops.push_back(Cirq::H::Create(54, 2)); + circuit.ops.push_back(Cirq::H::Create(54, 3)); return circuit; } diff --git a/tests/gates_qsim_test.cc b/tests/gates_qsim_test.cc index e241ac94a..4b2be2ea0 100644 --- a/tests/gates_qsim_test.cc +++ b/tests/gates_qsim_test.cc @@ -16,6 +16,7 @@ #include "gtest/gtest.h" +#include "../lib/gate.h" #include "../lib/gates_qsim.h" namespace qsim { @@ -329,13 +330,13 @@ TEST(GatesQsimTest, GateCP) { TEST(GatesQsimTest, GateMeasurement) { unsigned time = 5; std::vector qubits = {3, 2, 4, 0, 7, 5, 1}; - auto gate = gate::Measurement>::Create(time, qubits); + auto mea = CreateMeasurement(time, qubits); - EXPECT_EQ(gate.time, time); - EXPECT_EQ(gate.qubits.size(), qubits.size()); + EXPECT_EQ(mea.time, time); + EXPECT_EQ(mea.qubits.size(), qubits.size()); for (std::size_t i = 0; i < qubits.size(); ++i) { - EXPECT_EQ(gate.qubits[i], qubits[i]); + EXPECT_EQ(mea.qubits[i], qubits[i]); } } diff --git a/tests/hybrid_testfixture.h b/tests/hybrid_testfixture.h index 66a580fb4..c3fd04eda 100644 --- a/tests/hybrid_testfixture.h +++ b/tests/hybrid_testfixture.h @@ -25,9 +25,9 @@ #include "../lib/circuit_qsim_parser.h" #include "../lib/formux.h" #include "../lib/fuser_basic.h" -#include "../lib/gates_qsim.h" #include "../lib/hybrid.h" #include "../lib/io.h" +#include "../lib/operation.h" namespace qsim { @@ -61,42 +61,43 @@ R"(2 14 sw 0 1 )"; + using fp_type = typename Factory::fp_type; + std::stringstream ss(circuit_string); - Circuit> circuit; + Circuit> circuit; EXPECT_TRUE(CircuitQsimParser::FromStream(99, provider, ss, circuit)); EXPECT_EQ(circuit.num_qubits, 2); - EXPECT_EQ(circuit.gates.size(), 23); + EXPECT_EQ(circuit.ops.size(), 23); - using HybridSimulator = HybridSimulator, BasicGateFuser, - For>; - using Fuser = HybridSimulator::Fuser; + using Fuser = BasicGateFuser; + using HybridSimulator = HybridSimulator; std::vector parts = {0, 1}; - HybridSimulator::HybridData hd; - EXPECT_TRUE(HybridSimulator::SplitLattice(parts, circuit.gates, hd)); + typename HybridSimulator::HybridData hd; + EXPECT_TRUE(HybridSimulator::SplitLattice(parts, circuit.ops, hd)); - EXPECT_EQ(hd.gates0.size(), 15); - EXPECT_EQ(hd.gates1.size(), 15); + EXPECT_EQ(hd.ops0.size(), 15); + EXPECT_EQ(hd.ops1.size(), 15); EXPECT_EQ(hd.gatexs.size(), 7); EXPECT_EQ(hd.qubit_map.size(), 2); EXPECT_EQ(hd.num_qubits0, 1); EXPECT_EQ(hd.num_qubits1, 1); EXPECT_EQ(hd.num_gatexs, 7); - HybridSimulator::Parameter param; + typename HybridSimulator::Parameter param; param.prefix = 1; param.num_prefix_gatexs = 0; param.num_root_gatexs = 0; param.num_threads = 1; param.verbosity = 0; - auto fgates0 = Fuser::FuseGates(param, hd.num_qubits0, hd.gates0); - auto fgates1 = Fuser::FuseGates(param, hd.num_qubits1, hd.gates1); + auto fops0 = Fuser::FuseGates(param, hd.num_qubits0, hd.ops0); + auto fops1 = Fuser::FuseGates(param, hd.num_qubits1, hd.ops1); - EXPECT_EQ(fgates0.size(), 7); - EXPECT_EQ(fgates1.size(), 7); + EXPECT_EQ(fops0.size(), 7); + EXPECT_EQ(fops1.size(), 7); std::vector bitstrings; bitstrings.reserve(4); @@ -104,12 +105,12 @@ R"(2 bitstrings.push_back(i); } - std::vector> results(4, 0); + std::vector> results(4, 0); std::complex zero(0, 0); EXPECT_TRUE(HybridSimulator(1).Run( - param, factory, hd, parts, fgates0, fgates1, bitstrings, results)); + param, factory, hd, parts, fops0, fops1, bitstrings, results)); EXPECT_NEAR(std::real(results[0]), -0.16006945, 1e-6); EXPECT_NEAR(std::imag(results[0]), -0.04964612, 1e-6); @@ -125,7 +126,7 @@ R"(2 param.num_root_gatexs = 1; EXPECT_TRUE(HybridSimulator(1).Run( - param, factory, hd, parts, fgates0, fgates1, bitstrings, results)); + param, factory, hd, parts, fops0, fops1, bitstrings, results)); EXPECT_NEAR(std::real(results[0]), -0.16006945, 1e-6); EXPECT_NEAR(std::imag(results[0]), -0.04964612, 1e-6); @@ -141,7 +142,7 @@ R"(2 param.num_root_gatexs = 2; EXPECT_TRUE(HybridSimulator(1).Run( - param, factory, hd, parts, fgates0, fgates1, bitstrings, results)); + param, factory, hd, parts, fops0, fops1, bitstrings, results)); EXPECT_NEAR(std::real(results[0]), -0.16006945, 1e-6); EXPECT_NEAR(std::imag(results[0]), -0.04964612, 1e-6); @@ -157,7 +158,7 @@ R"(2 param.num_root_gatexs = 5; EXPECT_TRUE(HybridSimulator(1).Run( - param, factory, hd, parts, fgates0, fgates1, bitstrings, results)); + param, factory, hd, parts, fops0, fops1, bitstrings, results)); EXPECT_NEAR(std::real(results[0]), -0.16006945, 1e-6); EXPECT_NEAR(std::imag(results[0]), -0.04964612, 1e-6); @@ -167,10 +168,6 @@ R"(2 EXPECT_NEAR(std::imag(results[2]), 0.56567556, 1e-6); EXPECT_NEAR(std::real(results[3]), 0.28935891, 1e-6); EXPECT_NEAR(std::imag(results[3]), 0.71751291, 1e-6); - - std::fill(results.begin(), results.end(), zero); - param.num_prefix_gatexs = 0; - param.num_root_gatexs = 6; } template @@ -243,42 +240,43 @@ R"(4 21 h 3 )"; + using fp_type = typename Factory::fp_type; + std::stringstream ss(circuit_string); - Circuit> circuit; + Circuit> circuit; EXPECT_TRUE(CircuitQsimParser::FromStream(99, provider, ss, circuit)); EXPECT_EQ(circuit.num_qubits, 4); - EXPECT_EQ(circuit.gates.size(), 63); + EXPECT_EQ(circuit.ops.size(), 63); - using HybridSimulator = HybridSimulator, BasicGateFuser, - For>; - using Fuser = HybridSimulator::Fuser; + using Fuser = BasicGateFuser; + using HybridSimulator = HybridSimulator; std::vector parts = {0, 0, 1, 1}; - HybridSimulator::HybridData hd; - EXPECT_TRUE(HybridSimulator::SplitLattice(parts, circuit.gates, hd)); + typename HybridSimulator::HybridData hd; + EXPECT_TRUE(HybridSimulator::SplitLattice(parts, circuit.ops, hd)); - EXPECT_EQ(hd.gates0.size(), 34); - EXPECT_EQ(hd.gates1.size(), 34); + EXPECT_EQ(hd.ops0.size(), 34); + EXPECT_EQ(hd.ops1.size(), 34); EXPECT_EQ(hd.gatexs.size(), 5); EXPECT_EQ(hd.qubit_map.size(), 4); EXPECT_EQ(hd.num_qubits0, 2); EXPECT_EQ(hd.num_qubits1, 2); EXPECT_EQ(hd.num_gatexs, 5); - HybridSimulator::Parameter param; + typename HybridSimulator::Parameter param; param.prefix = 1; param.num_prefix_gatexs = 2; param.num_root_gatexs = 1; param.num_threads = 1; param.verbosity = 0; - auto fgates0 = Fuser::FuseGates(param, hd.num_qubits0, hd.gates0); - auto fgates1 = Fuser::FuseGates(param, hd.num_qubits1, hd.gates1); + auto fops0 = Fuser::FuseGates(param, hd.num_qubits0, hd.ops0); + auto fops1 = Fuser::FuseGates(param, hd.num_qubits1, hd.ops1); - EXPECT_EQ(fgates0.size(), 10); - EXPECT_EQ(fgates1.size(), 10); + EXPECT_EQ(fops0.size(), 10); + EXPECT_EQ(fops1.size(), 10); std::vector bitstrings; bitstrings.reserve(8); @@ -289,7 +287,7 @@ R"(4 std::vector> results(8, 0); EXPECT_TRUE(HybridSimulator(1).Run( - param, factory, hd, parts, fgates0, fgates1, bitstrings, results)); + param, factory, hd, parts, fops0, fops1, bitstrings, results)); EXPECT_NEAR(std::real(results[0]), -0.02852439, 1e-6); EXPECT_NEAR(std::imag(results[0]), -0.05243438, 1e-6); diff --git a/tests/mps_simulator_test.cc b/tests/mps_simulator_test.cc index e2fc8351a..01f8d9fab 100644 --- a/tests/mps_simulator_test.cc +++ b/tests/mps_simulator_test.cc @@ -12,13 +12,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "../lib/mps_simulator.h" +#include + +#include "gtest/gtest.h" #include "../lib/formux.h" +#include "../lib/fuser.h" +#include "../lib/gate.h" #include "../lib/gate_appl.h" #include "../lib/gates_cirq.h" -#include "../lib/gates_qsim.h" -#include "gtest/gtest.h" +#include "../lib/mps_simulator.h" namespace qsim { @@ -818,22 +821,21 @@ TEST(MPSSimulator, ApplyFusedGateLeft) { using MPSStateSpace = MPSSimulator::MPSStateSpace_; auto ss = MPSStateSpace(1); - auto gate1 = GateCZ::Create(2, 0, 1); - auto gate2 = GateHd::Create(0, 0); - auto gate3 = GateHd::Create(0, 1); + auto gate1 = Cirq::CZ::Create(2, 0, 1); + auto gate2 = Cirq::H::Create(0, 0); + auto gate3 = Cirq::H::Create(0, 1); - GateFused> fgate1{kGateCZ, 2, {0, 1}, &gate1, - {&gate2, &gate3}, {}}; + FusedGate fgate1{Cirq::kCZ, 2, {0, 1}, &gate1, {&gate2, &gate3}, {}}; CalculateFusedMatrix(fgate1); auto mps = ss.Create(3, 4); ss.SetStateZero(mps); - ApplyFusedGate(sim, fgate1, mps); + ApplyGate(sim, fgate1, mps); float wf[32]; float ground_truth[] = {0.5, 0., 0., 0., 0.5, 0., 0., 0., 0.5, 0., 0., 0., 0.5, 0., 0., 0.}; ss.ToWaveFunction(mps, wf); - for (int i = 0; i < 16; i++) { + for (unsigned i = 0; i < 16; ++i) { EXPECT_NEAR(wf[i], ground_truth[i], 1e-4); } } @@ -844,7 +846,7 @@ TEST(MPSSimulator, ApplyFusedGateRight) { // | | | // | +-+-----+-+ // | |FusedGate| - // | +-+-----+-+ + // | +-+-----+-+ // | | | // +-+-+ +-+-+ +-+-+ // | 0 +-+ 1 +-+ 2 | @@ -853,22 +855,21 @@ TEST(MPSSimulator, ApplyFusedGateRight) { using MPSStateSpace = MPSSimulator::MPSStateSpace_; auto ss = MPSStateSpace(1); - auto gate1 = GateCZ::Create(2, 1, 2); - auto gate2 = GateHd::Create(0, 1); - auto gate3 = GateHd::Create(0, 2); + auto gate1 = Cirq::CZ::Create(2, 1, 2); + auto gate2 = Cirq::H::Create(0, 1); + auto gate3 = Cirq::H::Create(0, 2); - GateFused> fgate1{kGateCZ, 2, {1, 2}, &gate1, - {&gate2, &gate3}, {}}; + FusedGate fgate1{Cirq::kCZ, 2, {1, 2}, &gate1, {&gate2, &gate3}, {}}; CalculateFusedMatrix(fgate1); auto mps = ss.Create(3, 4); ss.SetStateZero(mps); - ApplyFusedGate(sim, fgate1, mps); + ApplyGate(sim, fgate1, mps); float wf[32]; float ground_truth[] = {0.5, 0., 0.5, 0., 0.5, 0., 0.5, 0., 0., 0., 0., 0., 0., 0., 0., 0.}; ss.ToWaveFunction(mps, wf); - for (int i = 0; i < 16; i++) { + for (unsigned i = 0; i < 16; ++i) { EXPECT_NEAR(wf[i], ground_truth[i], 1e-4); } } @@ -888,16 +889,15 @@ TEST(MPSSimulator, ApplyFusedGateMiddle) { using MPSStateSpace = MPSSimulator::MPSStateSpace_; auto ss = MPSStateSpace(1); - auto gate1 = GateCZ::Create(2, 1, 2); - auto gate2 = GateHd::Create(0, 1); - auto gate3 = GateHd::Create(0, 2); + auto gate1 = Cirq::CZ::Create(2, 1, 2); + auto gate2 = Cirq::H::Create(0, 1); + auto gate3 = Cirq::H::Create(0, 2); - GateFused> fgate1{kGateCZ, 2, {1, 2}, &gate1, - {&gate2, &gate3}, {}}; + FusedGate fgate1{Cirq::kCZ, 2, {1, 2}, &gate1, {&gate2, &gate3}, {}}; CalculateFusedMatrix(fgate1); auto mps = ss.Create(4, 4); ss.SetStateZero(mps); - ApplyFusedGate(sim, fgate1, mps); + ApplyGate(sim, fgate1, mps); float wf[64]; float ground_truth[] = {0.5, 0., 0., 0., 0.5, 0., 0., 0., @@ -905,7 +905,7 @@ TEST(MPSSimulator, ApplyFusedGateMiddle) { 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.}; ss.ToWaveFunction(mps, wf); - for (int i = 0; i < 32; i++) { + for (unsigned i = 0; i < 32; ++i) { EXPECT_NEAR(wf[i], ground_truth[i], 1e-4); } } diff --git a/tests/operation_test.cc b/tests/operation_test.cc new file mode 100644 index 000000000..b001fd7d0 --- /dev/null +++ b/tests/operation_test.cc @@ -0,0 +1,334 @@ +// Copyright 2026 Google LLC. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "gtest/gtest.h" + +#include "../lib/gate.h" +#include "../lib/operation.h" +#include "../lib/operation_base.h" + +namespace qsim { + +TEST(OperationTest, Test1) { + using Gate = qsim::Gate; + using ControlledGate = qsim::ControlledGate; + using Operation = qsim::Operation; + + { + Gate op1 = {0, 1, {2}}; + const Gate op2 = {0, 1, {2}}; + + Gate* pg1 = OpGetAlternative(op1); + ASSERT_NE(pg1, nullptr); + EXPECT_EQ(pg1->time, 1); + EXPECT_EQ(pg1->qubits.size(), 1); + + const Gate* pg2 = OpGetAlternative(&op2); + ASSERT_NE(pg2, nullptr); + EXPECT_EQ(pg2->time, 1); + EXPECT_EQ(pg2->qubits.size(), 1); + + unsigned time1 = OpTime(op1); + EXPECT_EQ(time1, 1); + + unsigned time2 = OpTime(op2); + EXPECT_EQ(time2, 1); + + const Qubits& qubits1 = OpQubits(op1); + EXPECT_EQ(qubits1.size(), 1); + + const Qubits& qubits2 = OpQubits(&op2); + EXPECT_EQ(qubits2.size(), 1); + + { + const BaseOperation& bop1 = OpBaseOperation(&op1); + EXPECT_EQ(bop1.time, 1); + EXPECT_EQ(bop1.qubits.size(), 1); + + const BaseOperation& bop2 = OpBaseOperation(&op2); + EXPECT_EQ(bop2.time, 1); + EXPECT_EQ(bop2.qubits.size(), 1); + } + + { + Gate* po1 = &op1; + const Gate* const po2 = &op2; + + BaseOperation& bop1 = OpBaseOperation(po1); + EXPECT_EQ(bop1.time, 1); + EXPECT_EQ(bop1.qubits.size(), 1); + + EXPECT_EQ(op1.time, 1); + bop1.time = 2; + EXPECT_EQ(op1.time, 2); + + const BaseOperation& bop2 = OpBaseOperation(po2); + EXPECT_EQ(bop2.time, 1); + EXPECT_EQ(bop2.qubits.size(), 1); + } + + ControlledGate* pg3 = OpGetAlternative(op1); + EXPECT_EQ(pg3, nullptr); + } + + { + Operation op1 = Gate{0, 1, {2}}; + const Operation op2 = Gate{0, 1, {2}}; + + Gate* pg1 = OpGetAlternative(op1); + ASSERT_NE(pg1, nullptr); + EXPECT_EQ(pg1->time, 1); + EXPECT_EQ(pg1->qubits.size(), 1); + + const Gate* pg2 = OpGetAlternative(op2); + ASSERT_NE(pg2, nullptr); + EXPECT_EQ(pg2->time, 1); + EXPECT_EQ(pg2->qubits.size(), 1); + + unsigned time1 = OpTime(op1); + EXPECT_EQ(time1, 1); + + unsigned time2 = OpTime(op2); + EXPECT_EQ(time2, 1); + + const Qubits& qubits1 = OpQubits(op1); + EXPECT_EQ(qubits1.size(), 1); + + const Qubits& qubits2 = OpQubits(op2); + EXPECT_EQ(qubits2.size(), 1); + + { + BaseOperation& bop1 = OpBaseOperation(op1); + EXPECT_EQ(bop1.time, 1); + EXPECT_EQ(bop1.qubits.size(), 1); + + EXPECT_EQ(std::get(op1).time, 1); + bop1.time = 2; + EXPECT_EQ(std::get(op1).time, 2); + + const BaseOperation& bop2 = OpBaseOperation(op2); + EXPECT_EQ(bop2.time, 1); + EXPECT_EQ(bop2.qubits.size(), 1); + } + + { + Operation* const po1 = &op1; + const Operation* const po2 = &op2; + + const BaseOperation& bop1 = OpBaseOperation(po1); + EXPECT_EQ(bop1.time, 2); + EXPECT_EQ(bop1.qubits.size(), 1); + + const BaseOperation& bop2 = OpBaseOperation(po2); + EXPECT_EQ(bop2.time, 1); + EXPECT_EQ(bop2.qubits.size(), 1); + } + + ControlledGate* pg3 = OpGetAlternative(op1); + EXPECT_EQ(pg3, nullptr); + } + + { + Operation op1 = ControlledGate{Gate{0, 1, {2}}, {0, 1}}; + const Operation op2 = ControlledGate{Gate{0, 1, {2}}, {0, 1}}; + Operation* po1 = &op1; + + ControlledGate* pg1 = OpGetAlternative(po1); + ASSERT_NE(pg1, nullptr); + EXPECT_EQ(pg1->time, 1); + EXPECT_EQ(pg1->qubits.size(), 1); + EXPECT_EQ(pg1->controlled_by.size(), 2); + + const ControlledGate* pg2 = OpGetAlternative(&op2); + ASSERT_NE(pg2, nullptr); + EXPECT_EQ(pg2->time, 1); + EXPECT_EQ(pg2->qubits.size(), 1); + EXPECT_EQ(pg2->controlled_by.size(), 2); + + unsigned time1 = OpTime(&op1); + EXPECT_EQ(time1, 1); + + unsigned time2 = OpTime(&op2); + EXPECT_EQ(time2, 1); + + const Qubits& qubits1 = OpQubits(op1); + EXPECT_EQ(qubits1.size(), 1); + + const Qubits& qubits2 = OpQubits(&op2); + EXPECT_EQ(qubits2.size(), 1); + + BaseOperation& bop1 = OpBaseOperation(op1); + EXPECT_EQ(bop1.time, 1); + EXPECT_EQ(bop1.qubits.size(), 1); + + const BaseOperation& bop2 = OpBaseOperation(&op2); + EXPECT_EQ(bop2.time, 1); + EXPECT_EQ(bop2.qubits.size(), 1); + + const Gate* pg3 = OpGetAlternative(&op1); + EXPECT_EQ(pg3, nullptr); + } +} + +TEST(OperationTest, Test2) { + using Gate = qsim::Gate; + using FusedGate = qsim::FusedGate; + using Operation = qsim::Operation; + using OperationF = std::variant; + + { + Operation op1 = Gate{0, 1, {2}}; + const OperationF opf1 = &op1; + + const Gate* pg1 = OpGetAlternative(opf1); + ASSERT_NE(pg1, nullptr); + EXPECT_EQ(pg1->time, 1); + EXPECT_EQ(pg1->qubits.size(), 1); + + unsigned time1 = OpTime(&opf1); + EXPECT_EQ(time1, 1); + + const Qubits& qubits1 = OpQubits(&opf1); + EXPECT_EQ(qubits1.size(), 1); + + const BaseOperation& bop1 = OpBaseOperation(opf1); + EXPECT_EQ(bop1.time, 1); + EXPECT_EQ(bop1.qubits.size(), 1); + + const Measurement* pg3 = OpGetAlternative(&opf1); + EXPECT_EQ(pg3, nullptr); + } + + { + OperationF opf1 = CreateMeasurement(1, {1}); + const OperationF opf2 = CreateMeasurement(1, {1}); + + EXPECT_EQ(std::get(opf1).time, 1); + + Measurement* pm1 = OpGetAlternative(opf1); + ASSERT_NE(pm1, nullptr); + EXPECT_EQ(pm1->time, 1); + EXPECT_EQ(pm1->qubits.size(), 1); + + pm1->time = 2; + EXPECT_EQ(std::get(opf1).time, 2); + + const Measurement* pm2 = OpGetAlternative(opf2); + ASSERT_NE(pm2, nullptr); + EXPECT_EQ(pm2->time, 1); + EXPECT_EQ(pm2->qubits.size(), 1); + + unsigned time1 = OpTime(&opf1); + EXPECT_EQ(time1, 2); + + unsigned time2 = OpTime(opf2); + EXPECT_EQ(time2, 1); + + const Qubits& qubits1 = OpQubits(opf1); + EXPECT_EQ(qubits1.size(), 1); + + const Qubits& qubits2 = OpQubits(&opf2); + EXPECT_EQ(qubits2.size(), 1); + + const BaseOperation& bop2 = OpBaseOperation(&opf2); + EXPECT_EQ(bop2.time, 1); + EXPECT_EQ(bop2.qubits.size(), 1); + + const Gate* pg3 = OpGetAlternative(opf2); + EXPECT_EQ(pg3, nullptr); + } +} + +TEST(OperationTest, Test3) { + using Gate = qsim::Gate; + using FusedGate = qsim::FusedGate; + using Operation = qsim::Operation; + using OperationF = std::variant; + + { + Operation op1 = Gate{0, 1, {2}}; + OperationF opf1 = &op1; + + const Gate* pg1 = OpGetAlternative(opf1); + ASSERT_NE(pg1, nullptr); + EXPECT_EQ(pg1->time, 1); + EXPECT_EQ(pg1->qubits.size(), 1); + + unsigned time1 = OpTime(&opf1); + EXPECT_EQ(time1, 1); + + const Qubits& qubits1 = OpQubits(&opf1); + EXPECT_EQ(qubits1.size(), 1); + + BaseOperation& bop1 = OpBaseOperation(opf1); + EXPECT_EQ(bop1.time, 1); + EXPECT_EQ(bop1.qubits.size(), 1); + + const Measurement* pg3 = OpGetAlternative(&opf1); + EXPECT_EQ(pg3, nullptr); + } + + { + OperationF opf1 = CreateMeasurement(1, {1}); + const OperationF opf2 = CreateMeasurement(1, {1}); + + EXPECT_EQ(std::get(opf1).time, 1); + + Measurement* pm1 = OpGetAlternative(opf1); + ASSERT_NE(pm1, nullptr); + EXPECT_EQ(pm1->time, 1); + EXPECT_EQ(pm1->qubits.size(), 1); + + pm1->time = 2; + EXPECT_EQ(std::get(opf1).time, 2); + + const Measurement* pm2 = OpGetAlternative(opf2); + ASSERT_NE(pm2, nullptr); + EXPECT_EQ(pm2->time, 1); + EXPECT_EQ(pm2->qubits.size(), 1); + + unsigned time1 = OpTime(&opf1); + EXPECT_EQ(time1, 2); + + unsigned time2 = OpTime(opf2); + EXPECT_EQ(time2, 1); + + const Qubits& qubits1 = OpQubits(opf1); + EXPECT_EQ(qubits1.size(), 1); + + const Qubits& qubits2 = OpQubits(&opf2); + EXPECT_EQ(qubits2.size(), 1); + + OperationF* pof1 = &opf1; + BaseOperation& bop1 = OpBaseOperation(pof1); + EXPECT_EQ(bop1.time, 2); + EXPECT_EQ(bop1.qubits.size(), 1); + + const BaseOperation& bop2 = OpBaseOperation(&opf2); + EXPECT_EQ(bop2.time, 1); + EXPECT_EQ(bop2.qubits.size(), 1); + + const Gate* pg3 = OpGetAlternative(opf2); + EXPECT_EQ(pg3, nullptr); + } +} + +} // namespace qsim + +int main(int argc, char** argv) { + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/tests/qtrajectory_avx_test.cc b/tests/qtrajectory_avx_test.cc index 983e899ff..e26670de6 100644 --- a/tests/qtrajectory_avx_test.cc +++ b/tests/qtrajectory_avx_test.cc @@ -41,43 +41,43 @@ struct Factory { }; TEST(QTrajectoryAVXTest, BitFlip) { - using Fuser = MultiQubitGateFuser*>; + using Fuser = MultiQubitGateFuser; using Runner = QSimRunner>; TestBitFlip(Factory()); } TEST(QTrajectoryAVXTest, GenDump) { - using Fuser = MultiQubitGateFuser*>; + using Fuser = MultiQubitGateFuser; using Runner = QSimRunner>; TestGenDump(Factory()); } TEST(QTrajectoryAVXTest, ReusingResults) { - using Fuser = MultiQubitGateFuser*>; + using Fuser = MultiQubitGateFuser; using Runner = QSimRunner>; TestReusingResults(Factory()); } TEST(QTrajectoryAVXTest, CollectKopStat) { - using Fuser = MultiQubitGateFuser*>; + using Fuser = MultiQubitGateFuser; using Runner = QSimRunner>; TestCollectKopStat(Factory()); } TEST(QTrajectoryAVXTest, CleanCircuit) { - using Fuser = MultiQubitGateFuser*>; + using Fuser = MultiQubitGateFuser; using Runner = QSimRunner>; TestCleanCircuit(Factory()); } TEST(QTrajectoryAVXTest, InitialState) { - using Fuser = MultiQubitGateFuser*>; + using Fuser = MultiQubitGateFuser; using Runner = QSimRunner>; TestInitialState(Factory()); } TEST(QTrajectoryAVXTest, UncomputeFinalState) { - using Fuser = MultiQubitGateFuser*>; + using Fuser = MultiQubitGateFuser; using Runner = QSimRunner>; TestUncomputeFinalState(Factory()); } diff --git a/tests/qtrajectory_cuda_test.cu b/tests/qtrajectory_cuda_test.cu index e113e0521..73c291a55 100644 --- a/tests/qtrajectory_cuda_test.cu +++ b/tests/qtrajectory_cuda_test.cu @@ -45,7 +45,7 @@ struct Factory { TEST(QTrajectoryCUDATest, BitFlip) { using Factory = qsim::Factory; - using Fuser = MultiQubitGateFuser*>; + using Fuser = MultiQubitGateFuser; using Runner = QSimRunner; Factory::StateSpace::Parameter param; Factory factory(param); @@ -54,7 +54,7 @@ TEST(QTrajectoryCUDATest, BitFlip) { TEST(QTrajectoryCUDATest, GenDump) { using Factory = qsim::Factory; - using Fuser = MultiQubitGateFuser*>; + using Fuser = MultiQubitGateFuser; using Runner = QSimRunner; Factory::StateSpace::Parameter param; Factory factory(param); @@ -63,7 +63,7 @@ TEST(QTrajectoryCUDATest, GenDump) { TEST(QTrajectoryCUDATest, ReusingResults) { using Factory = qsim::Factory; - using Fuser = MultiQubitGateFuser*>; + using Fuser = MultiQubitGateFuser; using Runner = QSimRunner; Factory::StateSpace::Parameter param; Factory factory(param); @@ -72,7 +72,7 @@ TEST(QTrajectoryCUDATest, ReusingResults) { TEST(QTrajectoryCUDATest, CollectKopStat) { using Factory = qsim::Factory; - using Fuser = MultiQubitGateFuser*>; + using Fuser = MultiQubitGateFuser; using Runner = QSimRunner; Factory::StateSpace::Parameter param; Factory factory(param); @@ -81,7 +81,7 @@ TEST(QTrajectoryCUDATest, CollectKopStat) { TEST(QTrajectoryCUDATest, CleanCircuit) { using Factory = qsim::Factory; - using Fuser = MultiQubitGateFuser*>; + using Fuser = MultiQubitGateFuser; using Runner = QSimRunner; Factory::StateSpace::Parameter param; Factory factory(param); @@ -90,7 +90,7 @@ TEST(QTrajectoryCUDATest, CleanCircuit) { TEST(QTrajectoryCUDATest, InitialState) { using Factory = qsim::Factory; - using Fuser = MultiQubitGateFuser*>; + using Fuser = MultiQubitGateFuser; using Runner = QSimRunner; Factory::StateSpace::Parameter param; Factory factory(param); @@ -99,7 +99,7 @@ TEST(QTrajectoryCUDATest, InitialState) { TEST(QTrajectoryCUDATest, UncomputeFinalState) { using Factory = qsim::Factory; - using Fuser = MultiQubitGateFuser*>; + using Fuser = MultiQubitGateFuser; using Runner = QSimRunner; Factory::StateSpace::Parameter param; Factory factory(param); diff --git a/tests/qtrajectory_custatevec_test.cu b/tests/qtrajectory_custatevec_test.cu index ee283bd6c..be6d65fe4 100644 --- a/tests/qtrajectory_custatevec_test.cu +++ b/tests/qtrajectory_custatevec_test.cu @@ -56,43 +56,43 @@ struct Factory { }; TEST(QTrajectoryCuStateVecTest, BitFlip) { - using Fuser = MultiQubitGateFuser*>; + using Fuser = MultiQubitGateFuser; using Runner = QSimRunner>; TestBitFlip(Factory()); } TEST(QTrajectoryCuStateVecTest, GenDump) { - using Fuser = MultiQubitGateFuser*>; + using Fuser = MultiQubitGateFuser; using Runner = QSimRunner>; TestGenDump(Factory()); } TEST(QTrajectoryCuStateVecTest, ReusingResults) { - using Fuser = MultiQubitGateFuser*>; + using Fuser = MultiQubitGateFuser; using Runner = QSimRunner>; TestReusingResults(Factory()); } TEST(QTrajectoryCuStateVecTest, CollectKopStat) { - using Fuser = MultiQubitGateFuser*>; + using Fuser = MultiQubitGateFuser; using Runner = QSimRunner>; TestCollectKopStat(Factory()); } TEST(QTrajectoryCuStateVecTest, CleanCircuit) { - using Fuser = MultiQubitGateFuser*>; + using Fuser = MultiQubitGateFuser; using Runner = QSimRunner>; TestCleanCircuit(Factory()); } TEST(QTrajectoryCuStateVecTest, InitialState) { - using Fuser = MultiQubitGateFuser*>; + using Fuser = MultiQubitGateFuser; using Runner = QSimRunner>; TestInitialState(Factory()); } TEST(QTrajectoryCuStateVecTest, UncomputeFinalState) { - using Fuser = MultiQubitGateFuser*>; + using Fuser = MultiQubitGateFuser; using Runner = QSimRunner>; TestUncomputeFinalState(Factory()); } diff --git a/tests/qtrajectory_testfixture.h b/tests/qtrajectory_testfixture.h index c0aa5bef4..bd2824ead 100644 --- a/tests/qtrajectory_testfixture.h +++ b/tests/qtrajectory_testfixture.h @@ -23,66 +23,76 @@ #include "../lib/channel.h" #include "../lib/channels_cirq.h" +#include "../lib/circuit.h" #include "../lib/circuit_noisy.h" #include "../lib/expect.h" #include "../lib/fuser_mqubit.h" +#include "../lib/gate.h" #include "../lib/gate_appl.h" #include "../lib/gates_cirq.h" #include "../lib/io.h" +#include "../lib/operation.h" +#include "../lib/operation_base.h" #include "../lib/qtrajectory.h" namespace qsim { -template -void AddBitFlipNoise1( - unsigned time, unsigned q, double p, NoisyCircuit& ncircuit) { - using fp_type = typename Gate::fp_type; - +template +void AddBitFlipNoise1(unsigned time, unsigned q, double p, + Circuit>& ncircuit) { double p1 = 1 - p; double p2 = p; - auto normal = KrausOperator::kNormal; - - ncircuit.channels.push_back( - {{normal, 1, p1, {Cirq::I1::Create(time, q)}}, - {normal, 1, p2, {Cirq::X::Create(time, q)}}}); + ncircuit.ops.push_back(Channel{ + {kChannel, time, {q}}, + { + {true, p1, {Cirq::I1::Create(time, q)}}, + {true, p2, {Cirq::X::Create(time, q)}}, + } + }); } -template -void AddBitFlipNoise2(unsigned time, double p, NoisyCircuit& ncircuit) { - using fp_type = typename Gate::fp_type; - +template +void AddBitFlipNoise2(unsigned time, double p, + Circuit>& ncircuit) { double p1 = 1 - p; double p2 = p; - auto normal = KrausOperator::kNormal; - - ncircuit.channels.push_back({ - {normal, 1, p1 * p1, {Cirq::I1::Create(time, 0), - Cirq::I1::Create(time, 1)}}, - {normal, 1, p1 * p2, {Cirq::I1::Create(time, 0), - Cirq::X::Create(time, 1)}}, - {normal, 1, p2 * p1, {Cirq::X::Create(time, 0), - Cirq::I1::Create(time, 1)}}, - {normal, 1, p2 * p2, {Cirq::X::Create(time, 0), - Cirq::X::Create(time, 1)}}, + ncircuit.ops.push_back(Channel{ + {kChannel, time, {0, 1}}, + { + {true, p1 * p1, {Cirq::I1::Create(time, 0), + Cirq::I1::Create(time, 1)}}, + {true, p1 * p2, {Cirq::I1::Create(time, 0), + Cirq::X::Create(time, 1)}}, + {true, p2 * p1, {Cirq::X::Create(time, 0), + Cirq::I1::Create(time, 1)}}, + {true, p2 * p2, {Cirq::X::Create(time, 0), + Cirq::X::Create(time, 1)}}, + } }); // This can also be imnplemented as the following. // -// ncircuit.channels.push_back( -// {{normal, 1, p1, {Cirq::I1::Create(time, 0)}}, -// {normal, 1, p2, {Cirq::X::Create(time, 0)}}}); -// ncircuit.channels.push_back( -// {{normal, 1, p1, {Cirq::I1::Create(time, 1)}}, -// {normal, 1, p2, {Cirq::X::Create(time, 1)}}}); +// ncircuit.ops.push_back(Channel{ +// {kChannel, time, {0}}, +// { +// {true, p1, {Cirq::I1::Create(time, 0)}}, +// {true, p2, {Cirq::X::Create(time, 0)}}, +// } +// }); +// ncircuit.ops.push_back(Channel{ +// {kChannel, time, {1}}, +// { +// {true, p1, {Cirq::I1::Create(time, 1)}}, +// {true, p2, {Cirq::X::Create(time, 1)}}, +// } +// }); } -template -void AddGenAmplDumpNoise1( - unsigned time, unsigned q, double g, NoisyCircuit& ncircuit) { - using fp_type = typename Gate::fp_type; - +template +void AddGenAmplDumpNoise1(unsigned time, unsigned q, double g, + Circuit>& ncircuit) { // Probability of exchanging energy with the environment. double p = 0.5; @@ -97,26 +107,28 @@ void AddGenAmplDumpNoise1( fp_type r2 = std::sqrt((1 - p) * (1 - g)); fp_type s2 = std::sqrt((1 - p) * g); - auto normal = KrausOperator::kNormal; - using M = Cirq::MatrixGate1; - ncircuit.channels.push_back( - {{normal, 0, p1, {M::Create(time, q, {t1, 0, 0, 0, 0, 0, r1, 0})}}, - {normal, 0, p2, {M::Create(time, q, {r2, 0, 0, 0, 0, 0, t2, 0})}}, - {normal, 0, p3, {M::Create(time, q, {0, 0, s1, 0, 0, 0, 0, 0})}}, - {normal, 0, p3, {M::Create(time, q, {0, 0, 0, 0, s2, 0, 0, 0})}}}); + Channel channel = { + {kChannel, time, {q}}, + { + {false, p1, {M::Create(time, q, {t1, 0, 0, 0, 0, 0, r1, 0})}}, + {false, p2, {M::Create(time, q, {r2, 0, 0, 0, 0, 0, t2, 0})}}, + {false, p3, {M::Create(time, q, {0, 0, s1, 0, 0, 0, 0, 0})}}, + {false, p3, {M::Create(time, q, {0, 0, 0, 0, s2, 0, 0, 0})}}, + } + }; - for (auto& kop : ncircuit.channels.back()) { + for (auto& kop : channel.kops) { kop.CalculateKdKMatrix(); } -} -template -void AddGenAmplDumpNoise2( - unsigned time, double g, NoisyCircuit& ncircuit) { - using fp_type = typename Gate::fp_type; + ncircuit.ops.push_back(std::move(channel)); +} +template +void AddGenAmplDumpNoise2(unsigned time, double g, + Circuit>& ncircuit) { // Probability of exchanging energy with the environment. double p = 0.5; @@ -131,37 +143,45 @@ void AddGenAmplDumpNoise2( fp_type r2 = std::sqrt((1 - p) * (1 - g)); fp_type s2 = std::sqrt((1 - p) * g); - auto normal = KrausOperator::kNormal; - using M = Cirq::MatrixGate1; - ncircuit.channels.push_back( - {{normal, 0, p1, {M::Create(time, 0, {t1, 0, 0, 0, 0, 0, r1, 0})}}, - {normal, 0, p2, {M::Create(time, 0, {r2, 0, 0, 0, 0, 0, t2, 0})}}, - {normal, 0, p3, {M::Create(time, 0, {0, 0, s1, 0, 0, 0, 0, 0})}}, - {normal, 0, p3, {M::Create(time, 0, {0, 0, 0, 0, s2, 0, 0, 0})}}}); + Channel channel1 = { + {kChannel, time, {0}}, + { + {false, p1, {M::Create(time, 0, {t1, 0, 0, 0, 0, 0, r1, 0})}}, + {false, p2, {M::Create(time, 0, {r2, 0, 0, 0, 0, 0, t2, 0})}}, + {false, p3, {M::Create(time, 0, {0, 0, s1, 0, 0, 0, 0, 0})}}, + {false, p3, {M::Create(time, 0, {0, 0, 0, 0, s2, 0, 0, 0})}}, + } + }; - for (auto& kop : ncircuit.channels.back()) { + for (auto& kop : channel1.kops) { kop.CalculateKdKMatrix(); } - ncircuit.channels.push_back( - {{normal, 0, p1, {M::Create(time, 1, {t1, 0, 0, 0, 0, 0, r1, 0})}}, - {normal, 0, p2, {M::Create(time, 1, {r2, 0, 0, 0, 0, 0, t2, 0})}}, - {normal, 0, p3, {M::Create(time, 1, {0, 0, s1, 0, 0, 0, 0, 0})}}, - {normal, 0, p3, {M::Create(time, 1, {0, 0, 0, 0, s2, 0, 0, 0})}}}); + ncircuit.ops.push_back(std::move(channel1)); - for (auto& kop : ncircuit.channels.back()) { + Channel channel2 = { + {kChannel, time, {1}}, + { + {false, p1, {M::Create(time, 1, {t1, 0, 0, 0, 0, 0, r1, 0})}}, + {false, p2, {M::Create(time, 1, {r2, 0, 0, 0, 0, 0, t2, 0})}}, + {false, p3, {M::Create(time, 1, {0, 0, s1, 0, 0, 0, 0, 0})}}, + {false, p3, {M::Create(time, 1, {0, 0, 0, 0, s2, 0, 0, 0})}}, + } + }; + + for (auto& kop : channel2.kops) { kop.CalculateKdKMatrix(); } + + ncircuit.ops.push_back(std::move(channel2)); } // Adds the same channel as in AddGenAmplDumpNoise2 above. -template -void AddGenAmplDumpNoise2Alt( - unsigned time, double g, NoisyCircuit& ncircuit) { - using fp_type = typename Gate::fp_type; - +template +void AddGenAmplDumpNoise2Alt(unsigned time, double g, + Circuit>& ncircuit) { // Probability of exchanging energy with the environment. double p = 0.5; @@ -176,178 +196,170 @@ void AddGenAmplDumpNoise2Alt( fp_type r2 = std::sqrt((1 - p) * (1 - g)); fp_type s2 = std::sqrt((1 - p) * g); - auto normal = KrausOperator::kNormal; - using M = Cirq::MatrixGate1; - ncircuit.channels.push_back( - {{normal, 0, p1 * p1, - {M::Create(time, 0, {t1, 0, 0, 0, 0, 0, r1, 0}), - M::Create(time, 1, {t1, 0, 0, 0, 0, 0, r1, 0})}}, - {normal, 0, p1 * p2, - {M::Create(time, 0, {t1, 0, 0, 0, 0, 0, r1, 0}), - M::Create(time, 1, {r2, 0, 0, 0, 0, 0, t2, 0})}}, - {normal, 0, p1 * p3, - {M::Create(time, 0, {t1, 0, 0, 0, 0, 0, r1, 0}), - M::Create(time, 1, {0, 0, s1, 0, 0, 0, 0, 0})}}, - {normal, 0, p1 * p3, - {M::Create(time, 0, {t1, 0, 0, 0, 0, 0, r1, 0}), - M::Create(time, 1, {0, 0, 0, 0, s2, 0, 0, 0})}}, - - {normal, 0, p2 * p1, - {M::Create(time, 0, {r2, 0, 0, 0, 0, 0, t2, 0}), - M::Create(time, 1, {t1, 0, 0, 0, 0, 0, r1, 0})}}, - {normal, 0, p2 * p2, - {M::Create(time, 0, {r2, 0, 0, 0, 0, 0, t2, 0}), - M::Create(time, 1, {r2, 0, 0, 0, 0, 0, t2, 0})}}, - {normal, 0, p2 * p3, - {M::Create(time, 0, {r2, 0, 0, 0, 0, 0, t2, 0}), - M::Create(time, 1, {0, 0, s1, 0, 0, 0, 0, 0})}}, - {normal, 0, p2 * p3, - {M::Create(time, 0, {r2, 0, 0, 0, 0, 0, t2, 0}), - M::Create(time, 1, {0, 0, 0, 0, s2, 0, 0, 0})}}, - - {normal, 0, p3 * p1, - {M::Create(time, 0, {0, 0, s1, 0, 0, 0, 0, 0}), - M::Create(time, 1, {t1, 0, 0, 0, 0, 0, r1, 0})}}, - {normal, 0, p3 * p2, - {M::Create(time, 0, {0, 0, s1, 0, 0, 0, 0, 0}), - M::Create(time, 1, {r2, 0, 0, 0, 0, 0, t2, 0})}}, - {normal, 0, p3 * p3, - {M::Create(time, 0, {0, 0, s1, 0, 0, 0, 0, 0}), - M::Create(time, 1, {0, 0, s1, 0, 0, 0, 0, 0})}}, - {normal, 0, p3 * p3, - {M::Create(time, 0, {0, 0, s1, 0, 0, 0, 0, 0}), - M::Create(time, 1, {0, 0, 0, 0, s2, 0, 0, 0})}}, - - {normal, 0, p3 * p1, - {M::Create(time, 0, {0, 0, 0, 0, s2, 0, 0, 0}), - M::Create(time, 1, {t1, 0, 0, 0, 0, 0, r1, 0})}}, - {normal, 0, p3 * p2, - {M::Create(time, 0, {0, 0, 0, 0, s2, 0, 0, 0}), - M::Create(time, 1, {r2, 0, 0, 0, 0, 0, t2, 0})}}, - {normal, 0, p3 * p3, - {M::Create(time, 0, {0, 0, 0, 0, s2, 0, 0, 0}), - M::Create(time, 1, {0, 0, s1, 0, 0, 0, 0, 0})}}, - {normal, 0, p3 * p3, - {M::Create(time, 0, {0, 0, 0, 0, s2, 0, 0, 0}), - M::Create(time, 1, {0, 0, 0, 0, s2, 0, 0, 0})}}, - }); - - for (auto& kop : ncircuit.channels.back()) { + Channel channel = { + {kChannel, time, {0, 1}}, + { + {false, p1 * p1, {M::Create(time, 0, {t1, 0, 0, 0, 0, 0, r1, 0}), + M::Create(time, 1, {t1, 0, 0, 0, 0, 0, r1, 0})}}, + {false, p1 * p2, {M::Create(time, 0, {t1, 0, 0, 0, 0, 0, r1, 0}), + M::Create(time, 1, {r2, 0, 0, 0, 0, 0, t2, 0})}}, + {false, p1 * p3, {M::Create(time, 0, {t1, 0, 0, 0, 0, 0, r1, 0}), + M::Create(time, 1, {0, 0, s1, 0, 0, 0, 0, 0})}}, + {false, p1 * p3, {M::Create(time, 0, {t1, 0, 0, 0, 0, 0, r1, 0}), + M::Create(time, 1, {0, 0, 0, 0, s2, 0, 0, 0})}}, + + {false, p2 * p1, {M::Create(time, 0, {r2, 0, 0, 0, 0, 0, t2, 0}), + M::Create(time, 1, {t1, 0, 0, 0, 0, 0, r1, 0})}}, + {false, p2 * p2, {M::Create(time, 0, {r2, 0, 0, 0, 0, 0, t2, 0}), + M::Create(time, 1, {r2, 0, 0, 0, 0, 0, t2, 0})}}, + {false, p2 * p3, {M::Create(time, 0, {r2, 0, 0, 0, 0, 0, t2, 0}), + M::Create(time, 1, {0, 0, s1, 0, 0, 0, 0, 0})}}, + {false, p2 * p3, {M::Create(time, 0, {r2, 0, 0, 0, 0, 0, t2, 0}), + M::Create(time, 1, {0, 0, 0, 0, s2, 0, 0, 0})}}, + + {false, p3 * p1, {M::Create(time, 0, {0, 0, s1, 0, 0, 0, 0, 0}), + M::Create(time, 1, {t1, 0, 0, 0, 0, 0, r1, 0})}}, + {false, p3 * p2, {M::Create(time, 0, {0, 0, s1, 0, 0, 0, 0, 0}), + M::Create(time, 1, {r2, 0, 0, 0, 0, 0, t2, 0})}}, + {false, p3 * p3, {M::Create(time, 0, {0, 0, s1, 0, 0, 0, 0, 0}), + M::Create(time, 1, {0, 0, s1, 0, 0, 0, 0, 0})}}, + {false, p3 * p3, {M::Create(time, 0, {0, 0, s1, 0, 0, 0, 0, 0}), + M::Create(time, 1, {0, 0, 0, 0, s2, 0, 0, 0})}}, + + {false, p3 * p1, {M::Create(time, 0, {0, 0, 0, 0, s2, 0, 0, 0}), + M::Create(time, 1, {t1, 0, 0, 0, 0, 0, r1, 0})}}, + {false, p3 * p2, {M::Create(time, 0, {0, 0, 0, 0, s2, 0, 0, 0}), + M::Create(time, 1, {r2, 0, 0, 0, 0, 0, t2, 0})}}, + {false, p3 * p3, {M::Create(time, 0, {0, 0, 0, 0, s2, 0, 0, 0}), + M::Create(time, 1, {0, 0, s1, 0, 0, 0, 0, 0})}}, + {false, p3 * p3, {M::Create(time, 0, {0, 0, 0, 0, s2, 0, 0, 0}), + M::Create(time, 1, {0, 0, 0, 0, s2, 0, 0, 0})}}, + } + }; + + for (auto& kop : channel.kops) { kop.CalculateKdKMatrix(); } -} -template -void AddAmplDumpNoise1( - unsigned time, unsigned q, double g, NoisyCircuit& ncircuit) { - using fp_type = typename Gate::fp_type; + ncircuit.ops.push_back(std::move(channel)); +} +template +void AddAmplDumpNoise1(unsigned time, unsigned q, double g, + Circuit>& ncircuit) { double p1 = 1 - g; double p2 = 0; fp_type r = std::sqrt(p1); fp_type s = std::sqrt(g); - auto normal = KrausOperator::kNormal; - using M = Cirq::MatrixGate1; - ncircuit.channels.push_back( - {{normal, 0, p1, {M::Create(time, q, {1, 0, 0, 0, 0, 0, r, 0})}}, - {normal, 0, p2, {M::Create(time, q, {0, 0, s, 0, 0, 0, 0, 0})}}}); + Channel channel = { + {kChannel, time, {q}}, + { + {false, p1, {M::Create(time, q, {1, 0, 0, 0, 0, 0, r, 0})}}, + {false, p2, {M::Create(time, q, {0, 0, s, 0, 0, 0, 0, 0})}}, + } + }; - for (auto& kop : ncircuit.channels.back()) { + for (auto& kop : channel.kops) { kop.CalculateKdKMatrix(); } -} -template -void AddAmplDumpNoise2( - unsigned time, double g, NoisyCircuit& ncircuit) { - using fp_type = typename Gate::fp_type; + ncircuit.ops.push_back(std::move(channel)); +} +template +void AddAmplDumpNoise2(unsigned time, double g, + Circuit>& ncircuit) { double p1 = 1 - g; double p2 = 0; fp_type r = std::sqrt(p1); fp_type s = std::sqrt(g); - auto normal = KrausOperator::kNormal; - using M = Cirq::MatrixGate1; - ncircuit.channels.push_back( - {{normal, 0, p1, {M::Create(time, 0, {1, 0, 0, 0, 0, 0, r, 0})}}, - {normal, 0, p2, {M::Create(time, 0, {0, 0, s, 0, 0, 0, 0, 0})}}}); + Channel channel1 = { + {kChannel, time, {0}}, + { + {false, p1, {M::Create(time, 0, {1, 0, 0, 0, 0, 0, r, 0})}}, + {false, p2, {M::Create(time, 0, {0, 0, s, 0, 0, 0, 0, 0})}}, + } + }; - for (auto& kop : ncircuit.channels.back()) { + for (auto& kop : channel1.kops) { kop.CalculateKdKMatrix(); } - ncircuit.channels.push_back( - {{normal, 0, p1, {M::Create(time, 1, {1, 0, 0, 0, 0, 0, r, 0})}}, - {normal, 0, p2, {M::Create(time, 1, {0, 0, s, 0, 0, 0, 0, 0})}}}); + ncircuit.ops.push_back(std::move(channel1)); + + Channel channel2 = { + {kChannel, time, {1}}, + { + {false, p1, {M::Create(time, 1, {1, 0, 0, 0, 0, 0, r, 0})}}, + {false, p2, {M::Create(time, 1, {0, 0, s, 0, 0, 0, 0, 0})}}, + } + }; - for (auto& kop : ncircuit.channels.back()) { + for (auto& kop : channel2.kops) { kop.CalculateKdKMatrix(); } + + ncircuit.ops.push_back(std::move(channel2)); } -template -NoisyCircuit GenerateNoisyCircuit( +template +Circuit> GenerateNoisyCircuit( double p, AddNoise1&& add_noise1, AddNoise2&& add_noise2, bool add_measurement = true) { - using fp_type = typename Gate::fp_type; - - NoisyCircuit ncircuit; + Circuit> ncircuit; ncircuit.num_qubits = 2; - ncircuit.channels.reserve(24); + ncircuit.ops.reserve(24); using Hd = Cirq::H; using IS = Cirq::ISWAP; using Rx = Cirq::rx; using Ry = Cirq::ry; - auto normal = KrausOperator::kNormal; - - ncircuit.channels.push_back({{normal, 1, 1.0, {Hd::Create(0, 0)}}}); + ncircuit.ops.push_back(Hd::Create(0, 0)); add_noise1(1, 0, p, ncircuit); - ncircuit.channels.push_back({{normal, 1, 1.0, {Hd::Create(0, 1)}}}); + ncircuit.ops.push_back(Hd::Create(0, 1)); add_noise1(1, 1, p, ncircuit); - ncircuit.channels.push_back({{normal, 1, 1.0, {IS::Create(2, 0, 1)}}}); + ncircuit.ops.push_back(IS::Create(2, 0, 1)); add_noise2(3, p, ncircuit); - ncircuit.channels.push_back({{normal, 1, 1.0, {Rx::Create(4, 0, 0.7)}}}); + ncircuit.ops.push_back(Rx::Create(4, 0, 0.7)); add_noise1(5, 0, p, ncircuit); - ncircuit.channels.push_back({{normal, 1, 1.0, {Ry::Create(4, 1, 0.1)}}}); + ncircuit.ops.push_back(Ry::Create(4, 1, 0.1)); add_noise1(5, 1, p, ncircuit); - ncircuit.channels.push_back({{normal, 1, 1.0, {IS::Create(6, 0, 1)}}}); + ncircuit.ops.push_back(IS::Create(6, 0, 1)); add_noise2(7, p, ncircuit); - ncircuit.channels.push_back({{normal, 1, 1.0, {Ry::Create(8, 0, 0.4)}}}); + ncircuit.ops.push_back(Ry::Create(8, 0, 0.4)); add_noise1(9, 0, p, ncircuit); - ncircuit.channels.push_back({{normal, 1, 1.0, {Rx::Create(8, 1, 0.7)}}}); + ncircuit.ops.push_back(Rx::Create(8, 1, 0.7)); add_noise1(9, 1, p, ncircuit); - ncircuit.channels.push_back({{normal, 1, 1.0, {IS::Create(10, 0, 1)}}}); + ncircuit.ops.push_back(IS::Create(10, 0, 1)); add_noise2(11, p, ncircuit); + if (add_measurement) { - ncircuit.channels.push_back( - {{KrausOperator::kMeasurement, 1, 1.0, - {gate::Measurement::Create(12, {0, 1})}}}); + ncircuit.ops.push_back(CreateMeasurement(12, {0, 1})); add_noise2(13, p, ncircuit); } return ncircuit; } -template -void RunBatch(const Factory& factory, const NoisyCircuit& ncircuit, +template +void RunBatch(const Factory& factory, const NoisyCircuit& ncircuit, const std::vector& expected_results) { using Simulator = typename Factory::Simulator; using StateSpace = typename Simulator::StateSpace; using State = typename StateSpace::State; - using QTSimulator = QuantumTrajectorySimulator; + using QTSimulator = QuantumTrajectorySimulator; unsigned num_qubits = 2; unsigned num_reps = 25000; @@ -375,14 +387,13 @@ void RunBatch(const Factory& factory, const NoisyCircuit& ncircuit, } } -template -void RunOnceRepeatedly(const Factory& factory, - const NoisyCircuit& ncircuit, +template +void RunOnceRepeatedly(const Factory& factory, const NoisyCircuit& ncircuit, const std::vector& expected_results) { using Simulator = typename Factory::Simulator; using StateSpace = typename Factory::StateSpace; using State = typename StateSpace::State; - using QTSimulator = QuantumTrajectorySimulator; + using QTSimulator = QuantumTrajectorySimulator; unsigned num_qubits = 2; unsigned num_reps = 25000; @@ -415,15 +426,15 @@ void RunOnceRepeatedly(const Factory& factory, } } -template +template std::vector> ExpValsRunBatch( - const Factory& factory, const NoisyCircuit& ncircuit, + const Factory& factory, const Circuit>& ncircuit, bool reuse_results) { using Simulator = typename Factory::Simulator; using StateSpace = typename Factory::StateSpace; using State = typename StateSpace::State; - using Fuser = MultiQubitGateFuser; - using QTSimulator = QuantumTrajectorySimulator; + using Fuser = MultiQubitGateFuser; + using QTSimulator = QuantumTrajectorySimulator; unsigned num_qubits = 2; unsigned num_reps = 25000; @@ -439,11 +450,11 @@ std::vector> ExpValsRunBatch( typename QTSimulator::Parameter param; param.apply_last_deferred_ops = !reuse_results; - using Observables = std::vector>>; + using Observables = std::vector>>; Observables observables; observables.reserve(num_qubits); - using rx = qsim::Cirq::rx; + using rx = qsim::Cirq::rx; for (unsigned q = 0; q < num_qubits; ++q) { observables.push_back({{{1.0, 0.0}, {rx::Create(0, q, 1.7 + 0.6 * q)}}}); @@ -504,15 +515,15 @@ std::vector> ExpValsRunBatch( return results; } -template +template std::vector> ExpValsRunOnceRepeatedly( - const Factory& factory, const NoisyCircuit& ncircuit, + const Factory& factory, const Circuit>& ncircuit, bool reuse_results) { using Simulator = typename Factory::Simulator; using StateSpace = typename Factory::StateSpace; using State = typename StateSpace::State; - using Fuser = MultiQubitGateFuser; - using QTSimulator = QuantumTrajectorySimulator; + using Fuser = MultiQubitGateFuser; + using QTSimulator = QuantumTrajectorySimulator; unsigned num_qubits = 2; unsigned num_reps = 25000; @@ -528,10 +539,10 @@ std::vector> ExpValsRunOnceRepeatedly( typename QTSimulator::Parameter param; param.apply_last_deferred_ops = true; - std::vector>> observables; + std::vector>> observables; observables.reserve(num_qubits); - using rx = qsim::Cirq::rx; + using rx = qsim::Cirq::rx; for (unsigned q = 0; q < num_qubits; ++q) { observables.push_back({{{1.0, 0.0}, {rx::Create(0, q, 1.7 + 0.6 * q)}}}); @@ -625,10 +636,10 @@ for key, val in sorted(res.histogram(key='m').items()): 0.389352, 0.242790, 0.081009, 0.286850, }; - using Gate = Cirq::GateCirq; + using fp_type = typename Factory::fp_type; - auto ncircuit = GenerateNoisyCircuit(0.01, AddBitFlipNoise1, - AddBitFlipNoise2); + auto ncircuit = GenerateNoisyCircuit( + 0.01, AddBitFlipNoise1, AddBitFlipNoise2); RunBatch(factory, ncircuit, expected_results); } @@ -683,32 +694,34 @@ for key, val in sorted(res.histogram(key='m').items()): 0.318501, 0.260538, 0.164616, 0.256345, }; - using Gate = Cirq::GateCirq; + using fp_type = typename Factory::fp_type; { - auto ncircuit = GenerateNoisyCircuit(0.1, AddGenAmplDumpNoise1, - AddGenAmplDumpNoise2); + auto ncircuit = GenerateNoisyCircuit( + 0.1, AddGenAmplDumpNoise1, AddGenAmplDumpNoise2); RunOnceRepeatedly(factory, ncircuit, expected_results); } { - auto ncircuit = GenerateNoisyCircuit(0.1, AddGenAmplDumpNoise1, - AddGenAmplDumpNoise2Alt); + auto ncircuit = GenerateNoisyCircuit( + 0.1, AddGenAmplDumpNoise1, AddGenAmplDumpNoise2Alt); RunOnceRepeatedly(factory, ncircuit, expected_results); } } template void TestReusingResults(const Factory& factory) { - using Gate = Cirq::GateCirq; + using fp_type = typename Factory::fp_type; - auto ncircuit = GenerateNoisyCircuit(0.02, AddAmplDumpNoise1, - AddAmplDumpNoise2, false); + auto ncircuit = GenerateNoisyCircuit( + 0.02, AddAmplDumpNoise1, AddAmplDumpNoise2, false); - auto results1 = ExpValsRunOnceRepeatedly(factory, ncircuit, false); - auto results2 = ExpValsRunOnceRepeatedly(factory, ncircuit, true); - auto results3 = ExpValsRunBatch(factory, ncircuit, false); - auto results4 = ExpValsRunBatch(factory, ncircuit, true); + auto results1 = + ExpValsRunOnceRepeatedly(factory, ncircuit, false); + auto results2 = + ExpValsRunOnceRepeatedly(factory, ncircuit, true); + auto results3 = ExpValsRunBatch(factory, ncircuit, false); + auto results4 = ExpValsRunBatch(factory, ncircuit, true); for (std::size_t k = 0; k < results1.size(); ++k) { EXPECT_NEAR(std::real(results1[k]), std::real(results2[k]), 1e-8); @@ -726,8 +739,7 @@ void TestCollectKopStat(const Factory& factory) { using StateSpace = typename Factory::StateSpace; using State = typename StateSpace::State; using fp_type = typename StateSpace::fp_type; - using GateCirq = Cirq::GateCirq; - using QTSimulator = QuantumTrajectorySimulator; + using QTSimulator = QuantumTrajectorySimulator; unsigned num_qubits = 4; unsigned num_reps = 20000; @@ -740,27 +752,41 @@ void TestCollectKopStat(const Factory& factory) { using I = Cirq::I1; using X = Cirq::X; - auto normal = KrausOperator::kNormal; - - NoisyCircuit ncircuit; + Circuit> ncircuit; ncircuit.num_qubits = num_qubits; - ncircuit.channels.reserve(8); + ncircuit.ops.reserve(8); - ncircuit.channels.push_back({{normal, 1, 1.0, {Hd::Create(0, 0)}}}); - ncircuit.channels.push_back({{normal, 1, 1.0, {Hd::Create(0, 1)}}}); - ncircuit.channels.push_back({{normal, 1, 1.0, {Hd::Create(0, 2)}}}); - ncircuit.channels.push_back({{normal, 1, 1.0, {Hd::Create(0, 3)}}}); + ncircuit.ops.push_back(Hd::Create(0, 0)); + ncircuit.ops.push_back(Hd::Create(0, 1)); + ncircuit.ops.push_back(Hd::Create(0, 2)); + ncircuit.ops.push_back(Hd::Create(0, 3)); + + Channel channel1 = { + {kChannel, 1, {0}}, + {{true, p1, {I::Create(1, 0)}}, {true, p2, {X::Create(1, 0)}}}, + }; + + Channel channel2 = { + {kChannel, 1, {1}}, + {{true, p1, {I::Create(1, 1)}}, {true, p2, {X::Create(1, 1)}}}, + }; + + Channel channel3 = { + {kChannel, 1, {2}}, + {{true, p1, {I::Create(1, 2)}}, {true, p2, {X::Create(1, 2)}}}, + }; + + Channel channel4 = { + {kChannel, 1, {3}}, + {{true, p1, {I::Create(1, 3)}}, {true, p2, {X::Create(1, 3)}}}, + }; // Add bit flip noise. - ncircuit.channels.push_back({{normal, 1, p1, {I::Create(1, 0)}}, - {normal, 1, p2, {X::Create(1, 0)}}}); - ncircuit.channels.push_back({{normal, 1, p1, {I::Create(1, 1)}}, - {normal, 1, p2, {X::Create(1, 1)}}}); - ncircuit.channels.push_back({{normal, 1, p1, {I::Create(1, 2)}}, - {normal, 1, p2, {X::Create(1, 2)}}}); - ncircuit.channels.push_back({{normal, 1, p1, {I::Create(1, 3)}}, - {normal, 1, p2, {X::Create(1, 3)}}}); + ncircuit.ops.push_back(std::move(channel1)); + ncircuit.ops.push_back(std::move(channel2)); + ncircuit.ops.push_back(std::move(channel3)); + ncircuit.ops.push_back(std::move(channel4)); auto measure = [](uint64_t r, const State& state, const typename QTSimulator::Stat& stat, @@ -800,7 +826,7 @@ void TestCleanCircuit(const Factory& factory) { using State = typename StateSpace::State; using fp_type = typename StateSpace::fp_type; using GateCirq = Cirq::GateCirq; - using QTSimulator = QuantumTrajectorySimulator; + using QTSimulator = QuantumTrajectorySimulator; unsigned num_qubits = 4; auto size = uint64_t{1} << num_qubits; @@ -834,15 +860,13 @@ void TestCleanCircuit(const Factory& factory) { circuit.push_back(Cirq::YPowGate::Create(5, 2, 0.9, 0.4)); circuit.push_back(Cirq::ZPowGate::Create(5, 3, 1.0, 0.5)); - NoisyCircuit ncircuit; + Circuit> ncircuit; ncircuit.num_qubits = num_qubits; - ncircuit.channels.reserve(circuit.size()); - - auto normal = KrausOperator::kNormal; + ncircuit.ops.reserve(circuit.size()); for (std::size_t i = 0; i < circuit.size(); ++i) { - ncircuit.channels.push_back({{normal, 1, 1.0, {circuit[i]}}}); + ncircuit.ops.push_back(MakeChannelFromGate(circuit[i])); } Simulator simulator = factory.CreateSimulator(); @@ -863,15 +887,14 @@ void TestCleanCircuit(const Factory& factory) { EXPECT_FALSE(state_space.IsNull(nstate)); typename QTSimulator::Stat stat; - typename QTSimulator::Parameter param; state_space.SetStateZero(nstate); // Run quantum trajectory simulator. - EXPECT_TRUE(QTSimulator::RunOnce(param, num_qubits, ncircuit.channels.begin(), - ncircuit.channels.end(), 0, state_space, - simulator, nstate, stat)); + EXPECT_TRUE(QTSimulator::template RunOnce>( + param, num_qubits, ncircuit.ops.begin(), ncircuit.ops.end(), + 0, state_space, simulator, nstate, stat)); EXPECT_EQ(stat.samples.size(), 0); @@ -890,24 +913,33 @@ void TestInitialState(const Factory& factory) { using StateSpace = typename Factory::StateSpace; using State = typename StateSpace::State; using fp_type = typename StateSpace::fp_type; - using GateCirq = Cirq::GateCirq; - using QTSimulator = QuantumTrajectorySimulator; + using QTSimulator = QuantumTrajectorySimulator; unsigned num_qubits = 3; - NoisyCircuit ncircuit; + Circuit> ncircuit; ncircuit.num_qubits = num_qubits; - ncircuit.channels.reserve(3); + ncircuit.ops.reserve(3); - auto normal = KrausOperator::kNormal; + Channel channel1 = { + {kChannel, 0, {0}}, + {{true, 1.0, {Cirq::X::Create(0, 0)}}}, + }; - ncircuit.channels.push_back( - {{normal, 1, 1.0, {Cirq::X::Create(0, 0)}}}); - ncircuit.channels.push_back( - {{normal, 1, 1.0, {Cirq::X::Create(0, 1)}}}); - ncircuit.channels.push_back( - {{normal, 1, 1.0, {Cirq::X::Create(0, 2)}}}); + Channel channel2 = { + {kChannel, 0, {1}}, + {{true, 1.0, {Cirq::X::Create(0, 1)}}}, + }; + + Channel channel3 = { + {kChannel, 0, {2}}, + {{true, 1.0, {Cirq::X::Create(0, 2)}}}, + }; + + ncircuit.ops.push_back(std::move(channel1)); + ncircuit.ops.push_back(std::move(channel2)); + ncircuit.ops.push_back(std::move(channel3)); Simulator simulator = factory.CreateSimulator(); StateSpace state_space = factory.CreateStateSpace(); @@ -938,12 +970,11 @@ void TestUncomputeFinalState(const Factory& factory) { using StateSpace = typename Factory::StateSpace; using State = typename StateSpace::State; using fp_type = typename StateSpace::fp_type; - using GateCirq = Cirq::GateCirq; - using QTSimulator = QuantumTrajectorySimulator; + using QTSimulator = QuantumTrajectorySimulator; unsigned num_qubits = 4; - std::vector circuit = { + std::vector> circuit = { Cirq::H::Create(0, 0), Cirq::H::Create(0, 1), Cirq::H::Create(0, 2), @@ -964,7 +995,7 @@ void TestUncomputeFinalState(const Factory& factory) { // Works only with mixtures. auto channel = Cirq::bit_flip(0.3); - auto ncircuit = MakeNoisy(num_qubits, circuit, channel); + auto ncircuit = MakeNoisy(num_qubits, circuit, channel); Simulator simulator = factory.CreateSimulator(); StateSpace state_space = factory.CreateStateSpace(); @@ -984,16 +1015,21 @@ void TestUncomputeFinalState(const Factory& factory) { EXPECT_TRUE(QTSimulator::RunOnce( param, ncircuit, 0, state_space, simulator, state, stat)); - EXPECT_EQ(ncircuit.channels.size(), stat.samples.size()); + EXPECT_EQ(ncircuit.ops.size(), stat.samples.size()); // Uncompute the final state back to |0000> (up to round-off errors). - for (std::size_t i = 0; i < ncircuit.channels.size(); ++i) { - auto k = ncircuit.channels.size() - 1 - i; + for (std::size_t i = 0; i < ncircuit.ops.size(); ++i) { + auto k = ncircuit.ops.size() - 1 - i; + const auto& op = ncircuit.ops[k]; - const auto& ops = ncircuit.channels[k][stat.samples[k]].ops; + if (const auto* pg = OpGetAlternative>(op)) { + const auto& ops = pg->kops[stat.samples[k]].ops; - for (auto it = ops.rbegin(); it != ops.rend(); ++it) { - ApplyGateDagger(simulator, *it, state); + for (auto it = ops.rbegin(); it != ops.rend(); ++it) { + ApplyGateDagger(simulator, *it, state); + } + } else { + ApplyGateDagger(simulator, op, state); } } diff --git a/tests/run_custatevecex_test.cu b/tests/run_custatevecex_test.cu index 823df7dbd..aa94284b4 100644 --- a/tests/run_custatevecex_test.cu +++ b/tests/run_custatevecex_test.cu @@ -24,7 +24,6 @@ #include "gtest/gtest.h" #include "../lib/circuit_qsim_parser.h" -#include "../lib/gates_qsim.h" #include "../lib/io.h" #include "../lib/multiprocess_custatevecex.h" #include "../lib/run_custatevecex.h" @@ -82,11 +81,11 @@ struct Factory { TEST(RunQSimTest, QSimRunner1) { std::stringstream ss(circuit_string); - Circuit> circuit; + Circuit> circuit; EXPECT_TRUE(CircuitQsimParser::FromStream(99, provider, ss, circuit)); EXPECT_EQ(circuit.num_qubits, 4); - EXPECT_EQ(circuit.gates.size(), 27); + EXPECT_EQ(circuit.ops.size(), 27); using Simulator = Factory::Simulator; using StateSpace = Simulator::StateSpace; @@ -120,11 +119,11 @@ TEST(RunQSimTest, QSimRunner1) { TEST(RunQSimTest, QSimRunner2) { std::stringstream ss(circuit_string); - Circuit> circuit; + Circuit> circuit; EXPECT_TRUE(CircuitQsimParser::FromStream(99, provider, ss, circuit)); EXPECT_EQ(circuit.num_qubits, 4); - EXPECT_EQ(circuit.gates.size(), 27); + EXPECT_EQ(circuit.ops.size(), 27); using Simulator = Factory::Simulator; using StateSpace = Simulator::StateSpace; @@ -176,11 +175,11 @@ R"(2 TEST(RunQSimTest, QSimSampler) { std::stringstream ss(sample_circuit_string); - Circuit> circuit; + Circuit> circuit; EXPECT_TRUE(CircuitQsimParser::FromStream(99, provider, ss, circuit)); EXPECT_EQ(circuit.num_qubits, 2); - EXPECT_EQ(circuit.gates.size(), 11); + EXPECT_EQ(circuit.ops.size(), 11); using Simulator = Factory::Simulator; using StateSpace = Simulator::StateSpace; diff --git a/tests/run_qsim_test.cc b/tests/run_qsim_test.cc index 89c826db0..a442b64c7 100644 --- a/tests/run_qsim_test.cc +++ b/tests/run_qsim_test.cc @@ -24,8 +24,8 @@ #include "../lib/circuit_qsim_parser.h" #include "../lib/formux.h" #include "../lib/fuser_basic.h" -#include "../lib/gates_qsim.h" #include "../lib/io.h" +#include "../lib/operation.h" #include "../lib/run_qsim.h" #include "../lib/simmux.h" @@ -79,16 +79,16 @@ struct Factory { TEST(RunQSimTest, QSimRunner1) { std::stringstream ss(circuit_string); - Circuit> circuit; + Circuit> circuit; EXPECT_TRUE(CircuitQsimParser::FromStream(99, provider, ss, circuit)); EXPECT_EQ(circuit.num_qubits, 4); - EXPECT_EQ(circuit.gates.size(), 27); + EXPECT_EQ(circuit.ops.size(), 27); using Simulator = Factory::Simulator; using StateSpace = Simulator::StateSpace; using State = StateSpace::State; - using Runner = QSimRunner>, Factory>; + using Runner = QSimRunner, Factory>; float entropy = 0; @@ -117,16 +117,16 @@ TEST(RunQSimTest, QSimRunner1) { TEST(RunQSimTest, QSimRunner2) { std::stringstream ss(circuit_string); - Circuit> circuit; + Circuit> circuit; EXPECT_TRUE(CircuitQsimParser::FromStream(99, provider, ss, circuit)); EXPECT_EQ(circuit.num_qubits, 4); - EXPECT_EQ(circuit.gates.size(), 27); + EXPECT_EQ(circuit.ops.size(), 27); using Simulator = Factory::Simulator; using StateSpace = Simulator::StateSpace; using State = StateSpace::State; - using Runner = QSimRunner>, Factory>; + using Runner = QSimRunner, Factory>; StateSpace state_space = Factory::CreateStateSpace(); State state = state_space.Create(circuit.num_qubits); @@ -172,17 +172,17 @@ R"(2 TEST(RunQSimTest, QSimSampler) { std::stringstream ss(sample_circuit_string); - Circuit> circuit; + Circuit> circuit; EXPECT_TRUE(CircuitQsimParser::FromStream(99, provider, ss, circuit)); EXPECT_EQ(circuit.num_qubits, 2); - EXPECT_EQ(circuit.gates.size(), 11); + EXPECT_EQ(circuit.ops.size(), 11); using Simulator = Factory::Simulator; using StateSpace = Simulator::StateSpace; using Result = StateSpace::MeasurementResult; using State = StateSpace::State; - using Runner = QSimRunner>, Factory>; + using Runner = QSimRunner, Factory>; StateSpace state_space = Factory::CreateStateSpace(); State state = state_space.Create(circuit.num_qubits); @@ -220,8 +220,7 @@ TEST(RunQSimTest, CirqGates) { using Simulator = Factory::Simulator; using StateSpace = Simulator::StateSpace; using State = StateSpace::State; - using Runner = QSimRunner>, - Factory>; + using Runner = QSimRunner, Factory>; StateSpace state_space = Factory::CreateStateSpace(); State state = state_space.Create(circuit.num_qubits); diff --git a/tests/run_qsimh_test.cc b/tests/run_qsimh_test.cc index f15db3c2d..784629e81 100644 --- a/tests/run_qsimh_test.cc +++ b/tests/run_qsimh_test.cc @@ -24,8 +24,8 @@ #include "../lib/circuit_qsim_parser.h" #include "../lib/formux.h" #include "../lib/fuser_basic.h" -#include "../lib/gates_qsim.h" #include "../lib/io.h" +#include "../lib/operation.h" #include "../lib/run_qsimh.h" #include "../lib/simulator_basic.h" @@ -116,15 +116,15 @@ struct Factory { TEST(RunQSimHTest, QSimHRunner) { std::stringstream ss(circuit_string); - Circuit> circuit; + Circuit> circuit; EXPECT_TRUE(CircuitQsimParser::FromStream(99, provider, ss, circuit)); EXPECT_EQ(circuit.num_qubits, 4); - EXPECT_EQ(circuit.gates.size(), 63); + EXPECT_EQ(circuit.ops.size(), 63); - using HybridSimulator = - HybridSimulator, BasicGateFuser, For>; - using Runner = QSimHRunner; + using Fuser = BasicGateFuser; + using HybridSimulator = HybridSimulator; + using Runner = QSimHRunner; Runner::Parameter param; param.prefix = 0; @@ -183,9 +183,9 @@ TEST(RunQSimHTest, CirqGates) { auto circuit = CirqCircuit1::GetCircuit(false); const auto& expected_results = CirqCircuit1::expected_results0; - using HybridSimulator = HybridSimulator, - BasicGateFuser, For>; - using Runner = QSimHRunner; + using Fuser = BasicGateFuser; + using HybridSimulator = HybridSimulator; + using Runner = QSimHRunner; Runner::Parameter param; param.prefix = 0; diff --git a/tests/simulator_testfixture.h b/tests/simulator_testfixture.h index bf46353a7..1a2e182ab 100644 --- a/tests/simulator_testfixture.h +++ b/tests/simulator_testfixture.h @@ -23,10 +23,12 @@ #include "gtest/gtest.h" #include "../lib/expect.h" +#include "../lib/fuser.h" #include "../lib/fuser_mqubit.h" #include "../lib/gate_appl.h" #include "../lib/gates_qsim.h" #include "../lib/io.h" +#include "../lib/operation.h" #include "../lib/util_cpu.h" namespace qsim { @@ -224,10 +226,10 @@ void TestApplyGate5(const Factory& factory) { auto gate25 = GateRX::Create(11, 4, 0.3); auto gate26 = GateGPh::Create(12, -1, 0); - GateFused> fgate1{kGateCZ, 2, {0, 1}, &gate11, + FusedGate fgate1{kGateCZ, 2, {0, 1}, &gate11, {&gate1, &gate2, &gate6, &gate7, &gate11, &gate12, &gate13}, {}}; CalculateFusedMatrix(fgate1); - ApplyFusedGate(simulator, fgate1, state); + ApplyGate(simulator, fgate1, state); EXPECT_NEAR(state_space.Norm(state), 1, 1e-6); @@ -246,10 +248,10 @@ void TestApplyGate5(const Factory& factory) { EXPECT_NEAR(std::imag(ampl3), 0.5, 1e-6); } - GateFused> fgate2{kGateIS, 4, {1, 2}, &gate14, + FusedGate fgate2{kGateIS, 4, {1, 2}, &gate14, {&gate3, &gate8, &gate14, &gate15, &gate16}, {}}; CalculateFusedMatrix(fgate2); - ApplyFusedGate(simulator, fgate2, state); + ApplyGate(simulator, fgate2, state); EXPECT_NEAR(state_space.Norm(state), 1, 1e-6); @@ -268,10 +270,10 @@ void TestApplyGate5(const Factory& factory) { EXPECT_NEAR(std::imag(ampl3), 0, 1e-6); } - GateFused> fgate3{kGateCNot, 6, {2, 3}, &gate17, + FusedGate fgate3{kGateCNot, 6, {2, 3}, &gate17, {&gate4, &gate9, &gate17, &gate18, &gate19},{}}; CalculateFusedMatrix(fgate3); - ApplyFusedGate(simulator, fgate3, state); + ApplyGate(simulator, fgate3, state); EXPECT_NEAR(state_space.Norm(state), 1, 1e-6); @@ -290,10 +292,10 @@ void TestApplyGate5(const Factory& factory) { EXPECT_NEAR(std::imag(ampl3), 0.00031570, 1e-6); } - GateFused> fgate4{kGateFS, 8, {3, 4}, &gate20, + FusedGate fgate4{kGateFS, 8, {3, 4}, &gate20, {&gate5, &gate10, &gate20, &gate21, &gate22}, {}}; CalculateFusedMatrix(fgate4); - ApplyFusedGate(simulator, fgate4, state); + ApplyGate(simulator, fgate4, state); EXPECT_NEAR(state_space.Norm(state), 1, 1e-6); @@ -312,10 +314,10 @@ void TestApplyGate5(const Factory& factory) { EXPECT_NEAR(std::imag(ampl3), -0.00987822, 1e-6); } - GateFused> fgate5{kGateCP, 10, {0, 1}, &gate23, + FusedGate fgate5{kGateCP, 10, {0, 1}, &gate23, {&gate23, &gate26}, {}}; CalculateFusedMatrix(fgate5); - ApplyFusedGate(simulator, fgate5, state); + ApplyGate(simulator, fgate5, state); EXPECT_NEAR(state_space.Norm(state), 1, 1e-6); @@ -360,12 +362,12 @@ void TestCircuitWithControlledGates(const Factory& factory) { using Simulator = typename Factory::Simulator; using StateSpace = typename Simulator::StateSpace; using fp_type = typename StateSpace::fp_type; - using Gate = GateQSim; + using Operation = qsim::Operation; unsigned num_qubits = 7; unsigned size = 1 << (num_qubits - 1); - std::vector gates; + std::vector gates; gates.reserve(128); gates.push_back(GateHd::Create(0, 0)); @@ -767,12 +769,12 @@ void TestCircuitWithControlledGatesDagger(const Factory& factory) { using Simulator = typename Factory::Simulator; using StateSpace = typename Simulator::StateSpace; using fp_type = typename StateSpace::fp_type; - using Gate = GateQSim; + using Operation = qsim::Operation; unsigned num_qubits = 7; unsigned size = 1 << (num_qubits - 1); - std::vector gates; + std::vector gates; gates.reserve(128); gates.push_back(GateHd::Create(0, 0)); @@ -1475,7 +1477,7 @@ void TestExpectationValue2(const Factory& factory) { using StateSpace = typename Simulator::StateSpace; using State = typename StateSpace::State; using fp_type = typename StateSpace::fp_type; - using Fuser = MultiQubitGateFuser>; + using Fuser = MultiQubitGateFuser; unsigned num_qubits = 16; unsigned depth = 16; @@ -1591,7 +1593,7 @@ for k in range(1, 7): }; for (unsigned k = 1; k <= 6; ++k) { - std::vector>> strings; + std::vector> strings; strings.reserve(num_qubits); for (unsigned i = 0; i <= num_qubits - k; ++i) { diff --git a/tests/statespace_testfixture.h b/tests/statespace_testfixture.h index a6df9a293..0bf9e4cd4 100644 --- a/tests/statespace_testfixture.h +++ b/tests/statespace_testfixture.h @@ -29,6 +29,7 @@ #include "../lib/fuser_basic.h" #include "../lib/gates_qsim.h" #include "../lib/io.h" +#include "../lib/operation.h" #include "../lib/run_qsim.h" namespace qsim { @@ -467,14 +468,14 @@ void TestNormAndInnerProduct(const Factory& factory) { using StateSpace = typename Simulator::StateSpace; using State = typename StateSpace::State; using fp_type = typename StateSpace::fp_type; - using Runner = QSimRunner>, Factory>; + using Runner = QSimRunner, Factory>; unsigned depth = 8; std::stringstream ss(circuit_string); - Circuit> circuit; + Circuit> circuit; EXPECT_TRUE(CircuitQsimParser::FromStream(depth, provider, ss, circuit)); - circuit.gates.emplace_back(GateT::Create(depth + 1, 0)); + circuit.ops.push_back(GateT::Create(depth + 1, 0)); StateSpace state_space = factory.CreateStateSpace(); State state0 = state_space.Create(circuit.num_qubits); @@ -562,13 +563,13 @@ void TestSamplingCrossEntropyDifference(const Factory& factory) { using StateSpace = typename Simulator::StateSpace; using State = typename StateSpace::State; using fp_type = typename StateSpace::fp_type; - using Runner = QSimRunner>, Factory>; + using Runner = QSimRunner, Factory>; unsigned depth = 30; uint64_t num_samples = 2000000; std::stringstream ss(circuit_string); - Circuit> circuit; + Circuit> circuit; EXPECT_TRUE(CircuitQsimParser::FromStream(depth, provider, ss, circuit)); StateSpace state_space = factory.CreateStateSpace(); @@ -778,12 +779,12 @@ void TestMeasurementLarge(const Factory& factory) { using StateSpace = typename Simulator::StateSpace; using State = typename StateSpace::State; using fp_type = typename StateSpace::fp_type; - using Runner = QSimRunner>, Factory>; + using Runner = QSimRunner, Factory>; unsigned depth = 20; std::stringstream ss(circuit_string); - Circuit> circuit; + Circuit> circuit; EXPECT_TRUE(CircuitQsimParser::FromStream(depth, provider, ss, circuit)); StateSpace state_space = factory.CreateStateSpace(); diff --git a/tests/unitary_calculator_testfixture.h b/tests/unitary_calculator_testfixture.h index cd77ca434..d9806fd9d 100644 --- a/tests/unitary_calculator_testfixture.h +++ b/tests/unitary_calculator_testfixture.h @@ -20,10 +20,12 @@ #include #include +#include "gtest/gtest.h" + #include "../lib/fuser.h" +#include "../lib/gate.h" #include "../lib/gate_appl.h" #include "../lib/gates_cirq.h" -#include "gtest/gtest.h" namespace qsim { @@ -425,11 +427,11 @@ void TestApplyFusedGate() { std::vector gates = {Cirq::H::Create(0, 0), Cirq::H::Create(1, 0)}; - GateFused fgate {Cirq::kH, 0, {0}, &gates[0], - {&gates[0], &gates[1]}, {}}; + FusedGate fgate {Cirq::kH, 0, {0}, &gates[0], + {&gates[0], &gates[1]}, {}}; CalculateFusedMatrix(fgate); - ApplyFusedGate(uc, fgate, u); + ApplyGate(uc, fgate, u); unsigned size = 1 << num_qubits; for (unsigned i = 0; i < size; ++i) { From dc48a21fb017e02551a62ecb85ae1d33c6b199f5 Mon Sep 17 00:00:00 2001 From: Sergei Isakov <54642992+sergeisakov@users.noreply.github.com> Date: Wed, 8 Apr 2026 17:24:02 +0200 Subject: [PATCH 2/8] Address lint checks. --- lib/BUILD | 2 +- qsimcirq/qsim_circuit.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/BUILD b/lib/BUILD index 08e7d85b4..ef5d43625 100644 --- a/lib/BUILD +++ b/lib/BUILD @@ -547,8 +547,8 @@ cc_library( ":circuit", ":gate", ":gate_appl", - ":util", ":operation_base", + ":util", ], ) diff --git a/qsimcirq/qsim_circuit.py b/qsimcirq/qsim_circuit.py index 642ac996d..ab58e0dd0 100644 --- a/qsimcirq/qsim_circuit.py +++ b/qsimcirq/qsim_circuit.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Dict, List, Sequence, Tuple, Union +from typing import Dict, List, Sequence, Tuple import cirq import numpy as np From e2a50f8e5bea681ba23001536c4d5799262552ce Mon Sep 17 00:00:00 2001 From: Sergei Isakov <54642992+sergeisakov@users.noreply.github.com> Date: Wed, 8 Apr 2026 17:53:11 +0200 Subject: [PATCH 3/8] Address gemini comments. --- lib/fuser_mqubit.h | 5 +++-- lib/operation_base.h | 35 ++++++++--------------------------- 2 files changed, 11 insertions(+), 29 deletions(-) diff --git a/lib/fuser_mqubit.h b/lib/fuser_mqubit.h index 2c1e55ec6..8bf973919 100644 --- a/lib/fuser_mqubit.h +++ b/lib/fuser_mqubit.h @@ -1219,7 +1219,7 @@ class MultiQubitGateFuser final : public Fuser { } if (gates_lat[q] != nullptr && bop.time <= OpTime(*gates_lat[q]->val->parent)) { - IO::errorf("fuser: gate at time %u is out of time order.\n", time); + IO::errorf("fuser: gate at time %u is out of time order.\n", bop.time); return false; } } @@ -1233,7 +1233,8 @@ class MultiQubitGateFuser final : public Fuser { } if (gates_lat[q] != nullptr && bop.time <= OpTime(*gates_lat[q]->val->parent)) { - IO::errorf("fuser: gate at time %u is out of time order.\n", time); + IO::errorf( + "fuser: gate at time %u is out of time order.\n", bop.time); return false; } } diff --git a/lib/operation_base.h b/lib/operation_base.h index 6803e6c7a..6aaf40c76 100644 --- a/lib/operation_base.h +++ b/lib/operation_base.h @@ -175,8 +175,7 @@ const T* OpGetAlternative(const Operation& op) { * or `std::variant`) to find the target value. * * Note: this function assumes that the input contains a type that is - * derived from `BaseOperation`. If an incompatible type is encountered, - * the program will terminate. + * derived from `BaseOperation`. * * @tparam Operation A type derived from `BaseOperation`, an `std::variant`, * or a pointer to one of these. @@ -198,10 +197,7 @@ inline unsigned OpTime(const Operation& op) { return std::visit(f, op); } else { - // This branch is reached if the input contains a type that is not derived - // from BaseOperation. - std::fprintf(stderr, "fatal error: OpTime encountered an invalid type.\n"); - std::terminate(); + static_assert(0, "OpBaseOperation encountered an invalid type"); } } @@ -214,8 +210,7 @@ inline unsigned OpTime(const Operation& op) { * or `std::variant`) to find the target value. * * Note: this function assumes that the input contains a type that is - * derived from `BaseOperation`. If an incompatible type is encountered, - * the program will terminate. + * derived from `BaseOperation`. * * @tparam Operation A type derived from `BaseOperation`, an `std::variant`, * or a pointer to one of these. @@ -237,11 +232,7 @@ inline const Qubits& OpQubits(const Operation& op) { return std::visit(f, op); } else { - // This branch is reached if the input contains a type that is not derived - // from BaseOperation. - std::fprintf( - stderr, "fatal error: OpQubits encountered an invalid type.\n"); - std::terminate(); + static_assert(0, "OpBaseOperation encountered an invalid type"); } } @@ -255,8 +246,7 @@ inline const Qubits& OpQubits(const Operation& op) { * find the target value. * * Note: this function assumes that the input contains a type that is - * derived from `BaseOperation`. If an incompatible type is encountered, - * the program will terminate. + * derived from `BaseOperation`. * * @tparam Operation A type derived from `BaseOperation`, an `std::variant`, * or a pointer to one of these. @@ -278,11 +268,7 @@ inline BaseOperation& OpBaseOperation(Operation& op) { return std::visit(f, op); } else { - // This branch is reached if the input contains a type that is not derived - // from BaseOperation. - std::fprintf( - stderr, "fatal error: OpBaseOperation encountered an invalid type.\n"); - std::terminate(); + static_assert(0, "OpBaseOperation encountered an invalid type"); } } @@ -295,8 +281,7 @@ inline BaseOperation& OpBaseOperation(Operation& op) { * or `std::variant`) to find the target value. * * Note: this function assumes that the input contains a type that is - * derived from `BaseOperation`. If an incompatible type is encountered, - * the program will terminate. + * derived from `BaseOperation`. * * @tparam Operation A type derived from `BaseOperation`, an `std::variant`, * or a pointer to one of these. @@ -318,11 +303,7 @@ inline const BaseOperation& OpBaseOperation(const Operation& op) { return std::visit(f, op); } else { - // This branch is reached if the input contains a type that is not derived - // from BaseOperation. - std::fprintf( - stderr, "fatal error: OpBaseOperation encountered an invalid type.\n"); - std::terminate(); + static_assert(0, "OpBaseOperation encountered an invalid type"); } } From 0624594777711c929b944348c6144145415358a4 Mon Sep 17 00:00:00 2001 From: Sergei Isakov <54642992+sergeisakov@users.noreply.github.com> Date: Wed, 8 Apr 2026 17:54:22 +0200 Subject: [PATCH 4/8] Address macos compilation failure. --- lib/run_qsimh.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/run_qsimh.h b/lib/run_qsimh.h index c3b2faded..82acb3001 100644 --- a/lib/run_qsimh.h +++ b/lib/run_qsimh.h @@ -28,7 +28,8 @@ namespace qsim { */ template struct QSimHRunner final { - using Parameter = typename HybridSimulator::Parameter; + using Parameter = + typename HybridSimulator::template Parameter; /** * Evaluates the amplitudes for a given circuit and set of output states. From 73ab7df010096174ed8b37610054a12f6d4a0445 Mon Sep 17 00:00:00 2001 From: Sergei Isakov <54642992+sergeisakov@users.noreply.github.com> Date: Wed, 8 Apr 2026 17:59:37 +0200 Subject: [PATCH 5/8] Address macos compilation failure. --- lib/run_qsimh.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/run_qsimh.h b/lib/run_qsimh.h index 82acb3001..6d22d160a 100644 --- a/lib/run_qsimh.h +++ b/lib/run_qsimh.h @@ -63,7 +63,7 @@ struct QSimHRunner final { using fp_type = OpFpType; - typename HybridSimulator::HybridData hd; + typename HybridSimulator::template HybridData hd; bool rc = HybridSimulator::SplitLattice(parts, ops, hd); if (!rc) { From 781928773364e9291f71f7d97181c54cc9522122 Mon Sep 17 00:00:00 2001 From: Sergei Isakov <54642992+sergeisakov@users.noreply.github.com> Date: Wed, 8 Apr 2026 18:23:59 +0200 Subject: [PATCH 6/8] Address compilation failures. --- lib/BUILD | 2 ++ lib/operation_base.h | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/BUILD b/lib/BUILD index ef5d43625..0546a13ae 100644 --- a/lib/BUILD +++ b/lib/BUILD @@ -798,6 +798,8 @@ cc_library( deps = [ ":gate", ":gate_appl", + ":gates_cirq", + ":gates_qsim", ], ) diff --git a/lib/operation_base.h b/lib/operation_base.h index 6aaf40c76..2fc2d9ff3 100644 --- a/lib/operation_base.h +++ b/lib/operation_base.h @@ -197,7 +197,7 @@ inline unsigned OpTime(const Operation& op) { return std::visit(f, op); } else { - static_assert(0, "OpBaseOperation encountered an invalid type"); + static_assert(0, "OpQubits encountered an invalid type"); } } @@ -232,7 +232,7 @@ inline const Qubits& OpQubits(const Operation& op) { return std::visit(f, op); } else { - static_assert(0, "OpBaseOperation encountered an invalid type"); + static_assert(0, "OpQubits encountered an invalid type"); } } From 310ba573d545231f9f6c6ef84d6875e94318a4e3 Mon Sep 17 00:00:00 2001 From: Sergei Isakov <54642992+sergeisakov@users.noreply.github.com> Date: Wed, 8 Apr 2026 21:17:47 +0200 Subject: [PATCH 7/8] Another attempt to address build failures. --- lib/BUILD | 2 ++ lib/operation_base.h | 15 +++++++++++---- pybind_interface/avx2/CMakeLists.txt | 2 ++ pybind_interface/avx512/CMakeLists.txt | 2 ++ pybind_interface/basic/CMakeLists.txt | 2 ++ pybind_interface/cuda/CMakeLists.txt | 2 ++ pybind_interface/custatevec/CMakeLists.txt | 2 ++ pybind_interface/custatevecex/CMakeLists.txt | 2 ++ pybind_interface/decide/CMakeLists.txt | 2 ++ pybind_interface/hip/CMakeLists.txt | 2 ++ pybind_interface/sse/CMakeLists.txt | 2 ++ 11 files changed, 31 insertions(+), 4 deletions(-) diff --git a/lib/BUILD b/lib/BUILD index 0546a13ae..f38e453e3 100644 --- a/lib/BUILD +++ b/lib/BUILD @@ -251,6 +251,7 @@ cc_library( name = "run_qsim_lib", hdrs = [ "bits.h", + "channel.h", "circuit.h", "circuit_qsim_parser.h", "expect.h", @@ -299,6 +300,7 @@ cc_library( name = "run_qsimh_lib", hdrs = [ "bits.h", + "channel.h", "circuit.h", "circuit_qsim_parser.h", "expect.h", diff --git a/lib/operation_base.h b/lib/operation_base.h index 2fc2d9ff3..93fef630c 100644 --- a/lib/operation_base.h +++ b/lib/operation_base.h @@ -67,6 +67,9 @@ struct is_variant> : std::true_type {}; template inline constexpr bool is_variant_v = is_variant::value; +template +inline constexpr bool always_false = false; + } // namespace detail /** @@ -197,7 +200,8 @@ inline unsigned OpTime(const Operation& op) { return std::visit(f, op); } else { - static_assert(0, "OpQubits encountered an invalid type"); + static_assert( + detail::always_false, "OpQubits encountered an invalid type"); } } @@ -232,7 +236,8 @@ inline const Qubits& OpQubits(const Operation& op) { return std::visit(f, op); } else { - static_assert(0, "OpQubits encountered an invalid type"); + static_assert( + detail::always_false, "OpQubits encountered an invalid type"); } } @@ -268,7 +273,8 @@ inline BaseOperation& OpBaseOperation(Operation& op) { return std::visit(f, op); } else { - static_assert(0, "OpBaseOperation encountered an invalid type"); + static_assert( + detail::always_false, "OpBaseOperation encountered an invalid type"); } } @@ -303,7 +309,8 @@ inline const BaseOperation& OpBaseOperation(const Operation& op) { return std::visit(f, op); } else { - static_assert(0, "OpBaseOperation encountered an invalid type"); + static_assert( + detail::always_false, "OpBaseOperation encountered an invalid type"); } } diff --git a/pybind_interface/avx2/CMakeLists.txt b/pybind_interface/avx2/CMakeLists.txt index f79bc0738..0a844150e 100644 --- a/pybind_interface/avx2/CMakeLists.txt +++ b/pybind_interface/avx2/CMakeLists.txt @@ -46,6 +46,8 @@ if(APPLE) ) endif() +set(CMAKE_CXX_STANDARD 17) + include(../GetPybind11.cmake) pybind11_add_module(qsim_avx2 pybind_main_avx2.cpp) diff --git a/pybind_interface/avx512/CMakeLists.txt b/pybind_interface/avx512/CMakeLists.txt index 2b4a000d3..de77a8ade 100644 --- a/pybind_interface/avx512/CMakeLists.txt +++ b/pybind_interface/avx512/CMakeLists.txt @@ -36,6 +36,8 @@ if(APPLE) ) endif() +set(CMAKE_CXX_STANDARD 17) + include(../GetPybind11.cmake) pybind11_add_module(qsim_avx512 pybind_main_avx512.cpp) diff --git a/pybind_interface/basic/CMakeLists.txt b/pybind_interface/basic/CMakeLists.txt index b83c1b67e..2913eca3f 100644 --- a/pybind_interface/basic/CMakeLists.txt +++ b/pybind_interface/basic/CMakeLists.txt @@ -36,6 +36,8 @@ if(APPLE) ) endif() +set(CMAKE_CXX_STANDARD 17) + include(../GetPybind11.cmake) pybind11_add_module(qsim_basic pybind_main_basic.cpp) diff --git a/pybind_interface/cuda/CMakeLists.txt b/pybind_interface/cuda/CMakeLists.txt index 85b9c37f5..cdf76a6b4 100644 --- a/pybind_interface/cuda/CMakeLists.txt +++ b/pybind_interface/cuda/CMakeLists.txt @@ -37,6 +37,8 @@ if(APPLE) ) endif() +set(CMAKE_CXX_STANDARD 17) + include(../GetPybind11.cmake) include(../GetCUDAARCHS.cmake) diff --git a/pybind_interface/custatevec/CMakeLists.txt b/pybind_interface/custatevec/CMakeLists.txt index 9afecb6bc..0bb9a55e4 100644 --- a/pybind_interface/custatevec/CMakeLists.txt +++ b/pybind_interface/custatevec/CMakeLists.txt @@ -36,6 +36,8 @@ if(APPLE) ) endif() +set(CMAKE_CXX_STANDARD 17) + include(../GetPybind11.cmake) find_package(Python3 3.10 REQUIRED) diff --git a/pybind_interface/custatevecex/CMakeLists.txt b/pybind_interface/custatevecex/CMakeLists.txt index 0b4d49439..3255dc79d 100644 --- a/pybind_interface/custatevecex/CMakeLists.txt +++ b/pybind_interface/custatevecex/CMakeLists.txt @@ -36,6 +36,8 @@ if(APPLE) ) endif() +set(CMAKE_CXX_STANDARD 17) + include(../GetPybind11.cmake) find_package(Python3 3.10 REQUIRED) diff --git a/pybind_interface/decide/CMakeLists.txt b/pybind_interface/decide/CMakeLists.txt index fef0dce98..6165267b3 100644 --- a/pybind_interface/decide/CMakeLists.txt +++ b/pybind_interface/decide/CMakeLists.txt @@ -39,6 +39,8 @@ if(APPLE) ) endif() +set(CMAKE_CXX_STANDARD 17) + include(../GetPybind11.cmake) # Configure based on the detected platform diff --git a/pybind_interface/hip/CMakeLists.txt b/pybind_interface/hip/CMakeLists.txt index f793f3302..5a3a7b50c 100644 --- a/pybind_interface/hip/CMakeLists.txt +++ b/pybind_interface/hip/CMakeLists.txt @@ -21,6 +21,8 @@ else() add_compile_options(-fno-lto) endif() +set(CMAKE_CXX_STANDARD 17) + include(../GetPybind11.cmake) find_package(PythonLibs 3.10 REQUIRED) diff --git a/pybind_interface/sse/CMakeLists.txt b/pybind_interface/sse/CMakeLists.txt index dd474504c..ff64c0098 100644 --- a/pybind_interface/sse/CMakeLists.txt +++ b/pybind_interface/sse/CMakeLists.txt @@ -36,6 +36,8 @@ if(APPLE) ) endif() +set(CMAKE_CXX_STANDARD 17) + include(../GetPybind11.cmake) pybind11_add_module(qsim_sse pybind_main_sse.cpp) From ae2c7617232d4a280a209f8573649bb34a288409 Mon Sep 17 00:00:00 2001 From: Sergei Isakov <54642992+sergeisakov@users.noreply.github.com> Date: Thu, 9 Apr 2026 16:27:27 +0200 Subject: [PATCH 8/8] Fix a typo. --- lib/operation_base.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/operation_base.h b/lib/operation_base.h index 93fef630c..6aa1f9cfc 100644 --- a/lib/operation_base.h +++ b/lib/operation_base.h @@ -201,7 +201,7 @@ inline unsigned OpTime(const Operation& op) { return std::visit(f, op); } else { static_assert( - detail::always_false, "OpQubits encountered an invalid type"); + detail::always_false, "OpTime encountered an invalid type"); } }