Skip to content

Commit

Permalink
Merge pull request #232 from quantumlib/fuser-params
Browse files Browse the repository at this point in the history
Add options for gate fusion.
  • Loading branch information
sergeisakov authored Nov 3, 2020
2 parents beb9b20 + 73f2b7d commit e348faa
Show file tree
Hide file tree
Showing 6 changed files with 65 additions and 43 deletions.
28 changes: 20 additions & 8 deletions lib/fuser_basic.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,26 +31,35 @@ template <typename IO, typename Gate>
struct BasicGateFuser final {
using GateFused = qsim::GateFused<Gate>;

/**
* 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<GateFused> FuseGates(
unsigned num_qubits, const std::vector<Gate>& gates) {
return FuseGates(num_qubits, gates.cbegin(), gates.cend(), {});
static std::vector<GateFused> FuseGates(const Parameter& param,
unsigned num_qubits,
const std::vector<Gate>& 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
Expand All @@ -60,10 +69,11 @@ struct BasicGateFuser final {
* acting on a specific pair of qubits which can be applied as a group.
*/
static std::vector<GateFused> FuseGates(
const Parameter& param,
unsigned num_qubits, const std::vector<Gate>& gates,
const std::vector<unsigned>& 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);
}

/**
Expand All @@ -72,23 +82,25 @@ 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.
* @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<GateFused> FuseGates(
unsigned num_qubits,
const Parameter& param, unsigned num_qubits,
typename std::vector<Gate>::const_iterator gfirst,
typename std::vector<Gate>::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.
Expand All @@ -99,7 +111,7 @@ struct BasicGateFuser final {
* acting on a specific pair of qubits which can be applied as a group.
*/
static std::vector<GateFused> FuseGates(
unsigned num_qubits,
const Parameter& param, unsigned num_qubits,
typename std::vector<Gate>::const_iterator gfirst,
typename std::vector<Gate>::const_iterator glast,
const std::vector<unsigned>& times_to_split_at) {
Expand Down
4 changes: 2 additions & 2 deletions lib/hybrid.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
20 changes: 11 additions & 9 deletions lib/run_qsim.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,15 @@ namespace qsim {
template <typename IO, typename Fuser, typename Simulator,
typename RGen = std::mt19937>
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.
*/
Expand All @@ -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).
Expand All @@ -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
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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
Expand All @@ -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;
}
Expand Down Expand Up @@ -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
Expand Down
8 changes: 4 additions & 4 deletions lib/run_qsimh.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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;
}
Expand Down
24 changes: 16 additions & 8 deletions tests/fuser_basic_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ TEST(FuserBasicTest, NoTimesToSplitAt) {
EXPECT_EQ(circuit.gates.size(), 27);

using Fuser = BasicGateFuser<IO, GateQSim<float>>;
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);

Expand Down Expand Up @@ -233,8 +234,9 @@ TEST(FuserBasicTest, TimesToSplitAt1) {
std::vector<unsigned> times_to_split_at{3, 8, 10};

using Fuser = BasicGateFuser<IO, GateQSim<float>>;
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);

Expand Down Expand Up @@ -408,8 +410,9 @@ TEST(FuserBasicTest, TimesToSplitAt2) {
std::vector<unsigned> times_to_split_at{2, 10};

using Fuser = BasicGateFuser<IO, GateQSim<float>>;
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);

Expand Down Expand Up @@ -586,7 +589,8 @@ TEST(FuserBasicTest, OrphanedQubits1) {
EXPECT_EQ(circuit.gates.size(), 7);

using Fuser = BasicGateFuser<IO, GateQSim<float>>;
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);

Expand Down Expand Up @@ -644,8 +648,9 @@ TEST(FuserBasicTest, OrphanedQubits2) {
std::vector<unsigned> times_to_split_at{1, 4};

using Fuser = BasicGateFuser<IO, GateQSim<float>>;
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);

Expand Down Expand Up @@ -726,7 +731,8 @@ TEST(FuserBasicTest, UnfusibleSingleQubitGate) {
circuit.gates[2].unfusible = true;

using Fuser = BasicGateFuser<IO, GateQSim<float>>;
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);

Expand Down Expand Up @@ -808,7 +814,8 @@ TEST(FuserBasicTest, MeasurementGate) {
EXPECT_EQ(circuit.gates.size(), 17);

using Fuser = BasicGateFuser<IO, GateQSim<float>>;
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);

Expand Down Expand Up @@ -966,7 +973,8 @@ TEST(FuserBasicTest, ControlledGate) {
EXPECT_EQ(circuit.gates.size(), 13);

using Fuser = BasicGateFuser<IO, GateQSim<float>>;
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);

Expand Down
24 changes: 12 additions & 12 deletions tests/hybrid_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -84,19 +84,19 @@ 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;
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);

EXPECT_EQ(fgates0.size(), 7);
EXPECT_EQ(fgates1.size(), 7);

std::vector<uint64_t> bitstrings;
bitstrings.reserve(4);
for (std::size_t i = 0; i < 4; ++i) {
Expand Down Expand Up @@ -265,19 +265,19 @@ 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;
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);

EXPECT_EQ(fgates0.size(), 10);
EXPECT_EQ(fgates1.size(), 10);

std::vector<uint64_t> bitstrings;
bitstrings.reserve(8);
for (std::size_t i = 0; i < 8; ++i) {
Expand Down

0 comments on commit e348faa

Please sign in to comment.