diff --git a/lib/fuser_basic.h b/lib/fuser_basic.h index 8a1d73cd..f36f08a5 100644 --- a/lib/fuser_basic.h +++ b/lib/fuser_basic.h @@ -31,26 +31,35 @@ template struct BasicGateFuser final { using GateFused = qsim::GateFused; + /** + * User-specified parameters for gate fusion. + * BasicGateFuser does not use any parameters. + */ + struct Parameter {}; + /** * Stores ordered sets of gates, each acting on two qubits, that can be * applied together. Note that gates fused with this method are not * multiplied together until ApplyFusedGate is called on the output. * To respect specific time boundaries while fusing gates, use the other * version of this method below. + * @param param Options for gate fusion. * @param num_qubits The number of qubits acted on by 'gates'. * @param gates The gates to be fused. * @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. */ - static std::vector FuseGates( - unsigned num_qubits, const std::vector& gates) { - return FuseGates(num_qubits, gates.cbegin(), gates.cend(), {}); + static std::vector FuseGates(const Parameter& param, + unsigned num_qubits, + const std::vector& gates) { + return FuseGates(param, num_qubits, gates.cbegin(), gates.cend(), {}); } /** * Stores ordered sets of gates, each acting on two qubits, that can be * applied together. Note that gates fused with this method are not * multiplied together until ApplyFusedGate is called on the output. + * @param param Options for gate fusion. * @param num_qubits The number of qubits acted on by 'gates'. * @param gates The gates to be fused. Gate times should be ordered. * @param times_to_split_at Ordered list of time steps at which to separate @@ -60,10 +69,11 @@ struct BasicGateFuser final { * acting on a specific pair of qubits which can be applied as a group. */ static std::vector FuseGates( + const Parameter& param, unsigned num_qubits, const std::vector& gates, const std::vector& times_to_split_at) { - return - FuseGates(num_qubits, gates.cbegin(), gates.cend(), times_to_split_at); + return FuseGates(param, num_qubits, gates.cbegin(), gates.cend(), + times_to_split_at); } /** @@ -72,6 +82,7 @@ struct BasicGateFuser final { * multiplied together until ApplyFusedGate is called on the output. * To respect specific time boundaries while fusing gates, use the other * version of this method below. + * @param param Options for gate fusion. * @param num_qubits The number of qubits acted on by gates. * @param gfirst, glast The iterator range [gfirst, glast) to fuse gates in. * Gate times should be ordered. @@ -79,16 +90,17 @@ struct BasicGateFuser final { * acting on a specific pair of qubits which can be applied as a group. */ static std::vector FuseGates( - unsigned num_qubits, + const Parameter& param, unsigned num_qubits, typename std::vector::const_iterator gfirst, typename std::vector::const_iterator glast) { - return FuseGates(num_qubits, gfirst, glast, {}); + return FuseGates(param, num_qubits, gfirst, glast, {}); } /** * Stores ordered sets of gates, each acting on two qubits, that can be * applied together. Note that gates fused with this method are not * multiplied together until ApplyFusedGate is called on the output. + * @param param Options for gate fusion. * @param num_qubits The number of qubits acted on by gates. * @param gfirst, glast The iterator range [gfirst, glast) to fuse gates in. * Gate times should be ordered. @@ -99,7 +111,7 @@ struct BasicGateFuser final { * acting on a specific pair of qubits which can be applied as a group. */ static std::vector FuseGates( - unsigned num_qubits, + const Parameter& param, unsigned num_qubits, typename std::vector::const_iterator gfirst, typename std::vector::const_iterator glast, const std::vector& times_to_split_at) { diff --git a/lib/hybrid.h b/lib/hybrid.h index bab77c00..d5054e9c 100644 --- a/lib/hybrid.h +++ b/lib/hybrid.h @@ -106,9 +106,9 @@ struct HybridSimulator final { }; /** - * User-specified parameters for hybrid simulation. + * User-specified parameters for gate fusion and hybrid simulation. */ - struct Parameter { + struct Parameter : public Fuser::Parameter { /** * Fixed bitstring indicating values to assign to Schmidt decomposition * indices of prefix gates. diff --git a/lib/run_qsim.h b/lib/run_qsim.h index 78c810b9..1b160493 100644 --- a/lib/run_qsim.h +++ b/lib/run_qsim.h @@ -31,14 +31,15 @@ namespace qsim { template struct QSimRunner final { + public: using StateSpace = typename Simulator::StateSpace; using State = typename StateSpace::State; using MeasurementResult = typename StateSpace::MeasurementResult; /** - * User-specified parameters for simulation. + * User-specified parameters for gate fusion and simulation. */ - struct Parameter { + struct Parameter : public Fuser::Parameter { /** * Random number generator seed to apply measurement gates. */ @@ -49,7 +50,7 @@ struct QSimRunner final { /** * Runs the given circuit, only measuring at the end. - * @param param Options for parallelism and logging. + * @param param Options for gate fusion, parallelism and logging. * @param circuit The circuit to be simulated. * @param measure Function that performs measurements (in the sense of * computing expectation values, etc). @@ -63,7 +64,7 @@ struct QSimRunner final { /** * Runs the given circuit, measuring at user-specified times. - * @param param Options for parallelism and logging. + * @param param Options for gate fusion, parallelism and logging. * @param times_to_measure_at Time steps at which to perform measurements. * @param circuit The circuit to be simulated. * @param measure Function that performs measurements (in the sense of @@ -94,8 +95,8 @@ struct QSimRunner final { state_space.SetStateZero(state); Simulator simulator(param.num_threads); - auto fused_gates = Fuser::FuseGates(circuit.num_qubits, circuit.gates, - times_to_measure_at); + auto fused_gates = Fuser::FuseGates(param, circuit.num_qubits, + circuit.gates, times_to_measure_at); if (fused_gates.size() == 0 && circuit.gates.size() > 0) { return false; } @@ -139,7 +140,7 @@ struct QSimRunner 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 parallelism and logging. + * @param param Options for gate fusion, parallelism and logging. * @param circuit The circuit to be simulated. * @param state As an input parameter, this should contain the initial state * of the system. After a successful run, it will be populated with the @@ -166,7 +167,8 @@ struct QSimRunner final { Simulator simulator(param.num_threads); - auto fused_gates = Fuser::FuseGates(circuit.num_qubits, circuit.gates); + auto fused_gates = Fuser::FuseGates(param, circuit.num_qubits, + circuit.gates); if (fused_gates.size() == 0 && circuit.gates.size() > 0) { return false; } @@ -201,7 +203,7 @@ struct QSimRunner 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 parallelism and logging. + * @param param Options for gate fusion, parallelism and logging. * @param circuit The circuit to be simulated. * @param state As an input parameter, this should contain the initial state * of the system. After a successful run, it will be populated with the diff --git a/lib/run_qsimh.h b/lib/run_qsimh.h index d7945bab..de27065e 100644 --- a/lib/run_qsimh.h +++ b/lib/run_qsimh.h @@ -37,8 +37,8 @@ struct QSimHRunner final { /** * Evaluates the amplitudes for a given circuit and set of output states. - * @param param Options for parallelism and logging. Also specifies the size - * of the 'prefix' and 'root' sections of the lattice. + * @param param Options for gate fusion, parallelism and logging. Also + * specifies the size of the 'prefix' and 'root' sections of the lattice. * @param circuit The circuit to be simulated. * @param parts Lattice sections to be simulated. * @param bitstrings List of output states to simulate, as bitstrings. @@ -81,12 +81,12 @@ struct QSimHRunner final { PrintInfo(param, hd); } - auto fgates0 = Fuser::FuseGates(hd.num_qubits0, hd.gates0); + auto fgates0 = Fuser::FuseGates(param, hd.num_qubits0, hd.gates0); if (fgates0.size() == 0 && hd.gates0.size() > 0) { return false; } - auto fgates1 = Fuser::FuseGates(hd.num_qubits1, hd.gates1); + auto fgates1 = Fuser::FuseGates(param, hd.num_qubits1, hd.gates1); if (fgates1.size() == 0 && hd.gates1.size() > 0) { return false; } diff --git a/tests/fuser_basic_test.cc b/tests/fuser_basic_test.cc index 103ea2cf..9fc7fbe4 100644 --- a/tests/fuser_basic_test.cc +++ b/tests/fuser_basic_test.cc @@ -66,7 +66,8 @@ TEST(FuserBasicTest, NoTimesToSplitAt) { EXPECT_EQ(circuit.gates.size(), 27); using Fuser = BasicGateFuser>; - auto fused_gates = Fuser::FuseGates(circuit.num_qubits, circuit.gates); + Fuser::Parameter param; + auto fused_gates = Fuser::FuseGates(param, circuit.num_qubits, circuit.gates); EXPECT_EQ(fused_gates.size(), 5); @@ -233,8 +234,9 @@ TEST(FuserBasicTest, TimesToSplitAt1) { std::vector times_to_split_at{3, 8, 10}; using Fuser = BasicGateFuser>; + Fuser::Parameter param; auto fused_gates = Fuser::FuseGates( - circuit.num_qubits, circuit.gates, times_to_split_at); + param, circuit.num_qubits, circuit.gates, times_to_split_at); EXPECT_EQ(fused_gates.size(), 6); @@ -408,8 +410,9 @@ TEST(FuserBasicTest, TimesToSplitAt2) { std::vector times_to_split_at{2, 10}; using Fuser = BasicGateFuser>; + Fuser::Parameter param; auto fused_gates = Fuser::FuseGates( - circuit.num_qubits, circuit.gates, times_to_split_at); + param, circuit.num_qubits, circuit.gates, times_to_split_at); EXPECT_EQ(fused_gates.size(), 5); @@ -586,7 +589,8 @@ TEST(FuserBasicTest, OrphanedQubits1) { EXPECT_EQ(circuit.gates.size(), 7); using Fuser = BasicGateFuser>; - auto fused_gates = Fuser::FuseGates(circuit.num_qubits, circuit.gates); + Fuser::Parameter param; + auto fused_gates = Fuser::FuseGates(param, circuit.num_qubits, circuit.gates); EXPECT_EQ(fused_gates.size(), 2); @@ -644,8 +648,9 @@ TEST(FuserBasicTest, OrphanedQubits2) { std::vector times_to_split_at{1, 4}; using Fuser = BasicGateFuser>; + Fuser::Parameter param; auto fused_gates = Fuser::FuseGates( - circuit.num_qubits, circuit.gates, times_to_split_at); + param, circuit.num_qubits, circuit.gates, times_to_split_at); EXPECT_EQ(fused_gates.size(), 4); @@ -726,7 +731,8 @@ TEST(FuserBasicTest, UnfusibleSingleQubitGate) { circuit.gates[2].unfusible = true; using Fuser = BasicGateFuser>; - auto fused_gates = Fuser::FuseGates(circuit.num_qubits, circuit.gates); + Fuser::Parameter param; + auto fused_gates = Fuser::FuseGates(param, circuit.num_qubits, circuit.gates); EXPECT_EQ(fused_gates.size(), 3); @@ -808,7 +814,8 @@ TEST(FuserBasicTest, MeasurementGate) { EXPECT_EQ(circuit.gates.size(), 17); using Fuser = BasicGateFuser>; - auto fused_gates = Fuser::FuseGates(circuit.num_qubits, circuit.gates); + Fuser::Parameter param; + auto fused_gates = Fuser::FuseGates(param, circuit.num_qubits, circuit.gates); EXPECT_EQ(fused_gates.size(), 11); @@ -966,7 +973,8 @@ TEST(FuserBasicTest, ControlledGate) { EXPECT_EQ(circuit.gates.size(), 13); using Fuser = BasicGateFuser>; - auto fused_gates = Fuser::FuseGates(circuit.num_qubits, circuit.gates); + Fuser::Parameter param; + auto fused_gates = Fuser::FuseGates(param, circuit.num_qubits, circuit.gates); EXPECT_EQ(fused_gates.size(), 8); diff --git a/tests/hybrid_test.cc b/tests/hybrid_test.cc index 889aebaa..abcb14f0 100644 --- a/tests/hybrid_test.cc +++ b/tests/hybrid_test.cc @@ -84,12 +84,6 @@ R"(2 EXPECT_EQ(hd.num_qubits1, 1); EXPECT_EQ(hd.num_gatexs, 7); - auto fgates0 = Fuser::FuseGates(hd.num_qubits0, hd.gates0); - auto fgates1 = Fuser::FuseGates(hd.num_qubits1, hd.gates1); - - EXPECT_EQ(fgates0.size(), 7); - EXPECT_EQ(fgates1.size(), 7); - HybridSimulator::Parameter param; param.prefix = 1; param.num_prefix_gatexs = 0; @@ -97,6 +91,12 @@ R"(2 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); + + EXPECT_EQ(fgates0.size(), 7); + EXPECT_EQ(fgates1.size(), 7); + std::vector bitstrings; bitstrings.reserve(4); for (std::size_t i = 0; i < 4; ++i) { @@ -265,12 +265,6 @@ R"(4 EXPECT_EQ(hd.num_qubits1, 2); EXPECT_EQ(hd.num_gatexs, 5); - auto fgates0 = Fuser::FuseGates(hd.num_qubits0, hd.gates0); - auto fgates1 = Fuser::FuseGates(hd.num_qubits1, hd.gates1); - - EXPECT_EQ(fgates0.size(), 10); - EXPECT_EQ(fgates1.size(), 10); - HybridSimulator::Parameter param; param.prefix = 1; param.num_prefix_gatexs = 2; @@ -278,6 +272,12 @@ R"(4 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); + + EXPECT_EQ(fgates0.size(), 10); + EXPECT_EQ(fgates1.size(), 10); + std::vector bitstrings; bitstrings.reserve(8); for (std::size_t i = 0; i < 8; ++i) {