Skip to content

Commit e18c2f4

Browse files
authored
Merge pull request #459 from quantumlib/split-fusion-and-simulation
Split gate fusion and simulation.
2 parents 0e7fb7c + 8fcd3fa commit e18c2f4

13 files changed

+258
-148
lines changed

docs/usage.md

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,23 @@ Sample circuits are provided in
2222
|`-d maxtime` | maximum time |
2323
|`-t num_threads` | number of threads to use|
2424
|`-f max_fused_size` | maximum fused gate size|
25-
|`-v verbosity` | verbosity level (0,1,>1)|
25+
|`-v verbosity` | verbosity level (0,1,2,3,4,5)|
2626
|`-z` | set flush-to-zero and denormals-are-zeros MXCSR control flags|
2727

2828
qsim_base computes all the amplitudes and just prints the first eight of them
2929
(or a smaller number for 1- or 2-qubit circuits).
3030

31+
Verbosity levels are described in the following table.
32+
33+
| Verbosity level | Description |
34+
|-----------------|-------------|
35+
| 0 | no additional information|
36+
| 1 | add total simulation runtime|
37+
| 2 | add initialization runtime and fuser runtime|
38+
| 3 | add basic fuser statistics|
39+
| 4 | add simulation runtime for each fused gate|
40+
| 5 | additional fuser information (qubit indices for each fused gate)|
41+
3142
Example:
3243
```
3344
./qsim_base.x -c ../circuits/circuit_q24 -d 16 -t 8 -v 1
@@ -46,7 +57,7 @@ Example:
4657
|`-d maxtime` | maximum time |
4758
|`-t num_threads` | number of threads to use|
4859
|`-f max_fused_size` | maximum fused gate size|
49-
|`-v verbosity` | verbosity level (0,1,>1)|
60+
|`-v verbosity` | verbosity level (0,1,2,3,4,5)|
5061
|`-z` | set flush-to-zero and denormals-are-zeros MXCSR control flags|
5162

5263
qsim_von_neumann computes all the amplitudes and calculates the von Neumann
@@ -77,7 +88,7 @@ Example:
7788
|`-o output_files` | comma-separated list of amplitude output files|
7889
|`-t num_threads` | number of threads to use|
7990
|`-f max_fused_size` | maximum fused gate size|
80-
|`-v verbosity` | verbosity level (0,1,>1)|
91+
|`-v verbosity` | verbosity level (0,1,2,3,4,5)|
8192
|`-z` | set flush-to-zero and denormals-are-zeros MXCSR control flags|
8293

8394
qsim_amplitudes reads input files of bitstrings, computes the corresponding
@@ -112,7 +123,7 @@ Example:
112123
|`-t traj0` | starting trajectory |
113124
|`-n num_trajectories ` | number of trajectories to run starting with `traj0` |
114125
|`-f max_fused_size` | maximum fused gate size|
115-
|`-v verbosity` | verbosity level (0,1)|
126+
|`-v verbosity` | verbosity level (0,1,2,3,4,5)|
116127

117128
qsim_qtrajectory_cuda runs on GPUs. qsim_qtrajectory_cuda performs quantum
118129
trajactory simulations with amplitude damping and phase damping noise channels.
@@ -145,7 +156,7 @@ Example:
145156
|`-p num_prefix_gates` | number of prefix gates|
146157
|`-r num_root_gates` | number of root gates|
147158
|`-t num_threads` | number of threads to use|
148-
|`-v verbosity` | verbosity level (0,>0)|
159+
|`-v verbosity` | verbosity level (0,1,4,5)|
149160
|`-z` | set flush-to-zero and denormals-are-zeros MXCSR control flags|
150161

151162
qsimh_base just computes and just prints the first eight amplitudes. The hybrid
@@ -226,7 +237,7 @@ maximum "time".
226237
|`-i input_file` | bitstring input file|
227238
|`-o output_file` | amplitude output file|
228239
|`-t num_threads` | number of threads to use|
229-
|`-v verbosity` | verbosity level (0,>0)|
240+
|`-v verbosity` | verbosity level (0,1,4,5)|
230241
|`-z` | set flush-to-zero and denormals-are-zeros MXCSR control flags|
231242

232243
qsimh_amplitudes reads the input file of bitstrings, computes the corresponding

lib/expect.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,8 @@ std::complex<double> ExpectationValue(
123123
break;
124124
}
125125

126-
auto matrix = CalculateFusedMatrix<typename Simulator::fp_type>(fgate);
127-
auto r = simulator.ExpectationValue(fgate.qubits, matrix.data(), state);
126+
auto r = simulator.ExpectationValue(
127+
fgate.qubits, fgate.matrix.data(), state);
128128
eval += str.weight * r;
129129
}
130130
}

lib/fuser.h

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ struct GateFused {
5050
* Ordered list of component gates.
5151
*/
5252
std::vector<const Gate*> gates;
53+
/**
54+
* Fused gate matrix.
55+
*/
56+
Matrix<typename Gate::fp_type> matrix;
5357
};
5458

5559
/**
@@ -134,16 +138,14 @@ class Fuser {
134138
/**
135139
* Multiplies component gate matrices of a fused gate.
136140
* @param gate Fused gate.
137-
* @return Matrix product of component matrices.
138141
*/
139-
template <typename fp_type, typename FusedGate>
140-
inline Matrix<fp_type> CalculateFusedMatrix(const FusedGate& gate) {
141-
Matrix<fp_type> matrix;
142-
MatrixIdentity(unsigned{1} << gate.qubits.size(), matrix);
142+
template <typename FusedGate>
143+
inline void CalculateFusedMatrix(FusedGate& gate) {
144+
MatrixIdentity(unsigned{1} << gate.qubits.size(), gate.matrix);
143145

144146
for (auto pgate : gate.gates) {
145147
if (gate.qubits.size() == pgate->qubits.size()) {
146-
MatrixMultiply(gate.qubits.size(), pgate->matrix, matrix);
148+
MatrixMultiply(gate.qubits.size(), pgate->matrix, gate.matrix);
147149
} else {
148150
unsigned mask = 0;
149151

@@ -157,11 +159,31 @@ inline Matrix<fp_type> CalculateFusedMatrix(const FusedGate& gate) {
157159
}
158160

159161
MatrixMultiply(mask, pgate->qubits.size(), pgate->matrix,
160-
gate.qubits.size(), matrix);
162+
gate.qubits.size(), gate.matrix);
161163
}
162164
}
165+
}
163166

164-
return matrix;
167+
/**
168+
* Multiplies component gate matrices for a range of fused gates.
169+
* @param gbeg, gend The iterator range [gbeg, gend) of fused gates.
170+
*/
171+
template <typename Iterator>
172+
inline void CalculateFusedMatrices(Iterator gbeg, Iterator gend) {
173+
for (auto g = gbeg; g != gend; ++g) {
174+
if (g->kind != gate::kMeasurement) {
175+
CalculateFusedMatrix(*g);
176+
}
177+
}
178+
}
179+
180+
/**
181+
* Multiplies component gate matrices for a vector of fused gates.
182+
* @param gates The vector of fused gates.
183+
*/
184+
template <typename FusedGate>
185+
inline void CalculateFusedMatrices(std::vector<FusedGate>& gates) {
186+
CalculateFusedMatrices(gates.begin(), gates.end());
165187
}
166188

167189
} // namespace qsim

lib/fuser_basic.h

Lines changed: 36 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -53,29 +53,29 @@ class BasicGateFuser final : public Fuser<IO, Gate> {
5353

5454
/**
5555
* Stores sets of gates that can be applied together. Only one- and
56-
* two-qubit gates will get fused. Gates fused with this method are not
57-
* multiplied together until ApplyFusedGate is called on the output.
58-
* To respect specific time boundaries while fusing gates, use the other
59-
* version of this method below.
56+
* two-qubit gates will get fused. To respect specific time boundaries while
57+
* fusing gates, use the other version of this method below.
6058
* @param param Options for gate fusion.
6159
* @param num_qubits The number of qubits acted on by 'gates'.
6260
* @param gates The gates (or pointers to the gates) to be fused.
6361
* Gate times of the gates that act on the same qubits should be ordered.
6462
* Gates that are out of time order should not cross the time boundaries
6563
* set by measurement gates.
64+
* @param fuse_matrix If true, multiply gate matrices together.
6665
* @return A vector of fused gate objects. Each element is a set of gates
6766
* acting on a specific pair of qubits which can be applied as a group.
6867
*/
6968
static std::vector<GateFused> FuseGates(const Parameter& param,
7069
unsigned num_qubits,
71-
const std::vector<Gate>& gates) {
72-
return FuseGates(param, num_qubits, gates.cbegin(), gates.cend(), {});
70+
const std::vector<Gate>& gates,
71+
bool fuse_matrix = true) {
72+
return FuseGates(
73+
param, num_qubits, gates.cbegin(), gates.cend(), {}, fuse_matrix);
7374
}
7475

7576
/**
7677
* Stores sets of gates that can be applied together. Only one- and
77-
* two-qubit gates will get fused. Gates fused with this method are not
78-
* multiplied together until ApplyFusedGate is called on the output.
78+
* two-qubit gates will get fused.
7979
* @param param Options for gate fusion.
8080
* @param num_qubits The number of qubits acted on by 'gates'.
8181
* @param gates The gates (or pointers to the gates) to be fused.
@@ -85,43 +85,44 @@ class BasicGateFuser final : public Fuser<IO, Gate> {
8585
* @param times_to_split_at Ordered list of time steps (boundaries) at which
8686
* to separate fused gates. Each element of the output will contain gates
8787
* from a single 'window' in this list.
88+
* @param fuse_matrix If true, multiply gate matrices together.
8889
* @return A vector of fused gate objects. Each element is a set of gates
8990
* acting on a specific pair of qubits which can be applied as a group.
9091
*/
9192
static std::vector<GateFused> FuseGates(
9293
const Parameter& param,
9394
unsigned num_qubits, const std::vector<Gate>& gates,
94-
const std::vector<unsigned>& times_to_split_at) {
95+
const std::vector<unsigned>& times_to_split_at,
96+
bool fuse_matrix = true) {
9597
return FuseGates(param, num_qubits, gates.cbegin(), gates.cend(),
96-
times_to_split_at);
98+
times_to_split_at, fuse_matrix);
9799
}
98100

99101
/**
100102
* Stores sets of gates that can be applied together. Only one- and
101-
* two-qubit gates will get fused. Gates fused with this method are not
102-
* multiplied together until ApplyFusedGate is called on the output.
103-
* To respect specific time boundaries while fusing gates, use the other
104-
* version of this method below.
103+
* two-qubit gates will get fused. To respect specific time boundaries while
104+
* fusing gates, use the other version of this method below.
105105
* @param param Options for gate fusion.
106106
* @param num_qubits The number of qubits acted on by gates.
107107
* @param gfirst, glast The iterator range [gfirst, glast) to fuse gates
108108
* (or pointers to gates) in. Gate times of the gates that act on the same
109109
* qubits should be ordered. Gates that are out of time order should not
110110
* cross the time boundaries set by measurement gates.
111+
* @param fuse_matrix If true, multiply gate matrices together.
111112
* @return A vector of fused gate objects. Each element is a set of gates
112113
* acting on a specific pair of qubits which can be applied as a group.
113114
*/
114115
static std::vector<GateFused> FuseGates(
115116
const Parameter& param, unsigned num_qubits,
116117
typename std::vector<Gate>::const_iterator gfirst,
117-
typename std::vector<Gate>::const_iterator glast) {
118-
return FuseGates(param, num_qubits, gfirst, glast, {});
118+
typename std::vector<Gate>::const_iterator glast,
119+
bool fuse_matrix = true) {
120+
return FuseGates(param, num_qubits, gfirst, glast, {}, fuse_matrix);
119121
}
120122

121123
/**
122124
* Stores sets of gates that can be applied together. Only one- and
123-
* two-qubit gates will get fused. Gates fused with this method are not
124-
* multiplied together until ApplyFusedGate is called on the output.
125+
* two-qubit gates will get fused.
125126
* @param param Options for gate fusion.
126127
* @param num_qubits The number of qubits acted on by gates.
127128
* @param gfirst, glast The iterator range [gfirst, glast) to fuse gates
@@ -132,14 +133,16 @@ class BasicGateFuser final : public Fuser<IO, Gate> {
132133
* @param times_to_split_at Ordered list of time steps (boundaries) at which
133134
* to separate fused gates. Each element of the output will contain gates
134135
* from a single 'window' in this list.
136+
* @param fuse_matrix If true, multiply gate matrices together.
135137
* @return A vector of fused gate objects. Each element is a set of gates
136138
* acting on a specific pair of qubits which can be applied as a group.
137139
*/
138140
static std::vector<GateFused> FuseGates(
139141
const Parameter& param, unsigned num_qubits,
140142
typename std::vector<Gate>::const_iterator gfirst,
141143
typename std::vector<Gate>::const_iterator glast,
142-
const std::vector<unsigned>& times_to_split_at) {
144+
const std::vector<unsigned>& times_to_split_at,
145+
bool fuse_matrix = true) {
143146
std::vector<GateFused> gates_fused;
144147

145148
if (gfirst >= glast) return gates_fused;
@@ -243,11 +246,11 @@ class BasicGateFuser final : public Fuser<IO, Gate> {
243246
}
244247

245248
gates_fused.push_back({pgate->kind, pgate->time, pgate->qubits,
246-
pgate, {pgate}});
249+
pgate, {pgate}, {}});
247250
} else if (pgate->qubits.size() == 1) {
248251
unsigned q0 = pgate->qubits[0];
249252

250-
GateFused gate_f = {pgate->kind, pgate->time, {q0}, pgate, {}};
253+
GateFused gate_f = {pgate->kind, pgate->time, {q0}, pgate, {}, {}};
251254

252255
last[q0] = Advance(last[q0], gates_lat[q0], gate_f.gates);
253256
gate_f.gates.push_back(gates_lat[q0][last[q0]]);
@@ -260,7 +263,8 @@ class BasicGateFuser final : public Fuser<IO, Gate> {
260263

261264
if (Done(last[q0], pgate->time, gates_lat[q0])) continue;
262265

263-
GateFused gate_f = {pgate->kind, pgate->time, {q0, q1}, pgate, {}};
266+
GateFused gate_f =
267+
{pgate->kind, pgate->time, {q0, q1}, pgate, {}, {}};
264268

265269
do {
266270
last[q0] = Advance(last[q0], gates_lat[q0], gate_f.gates);
@@ -290,7 +294,7 @@ class BasicGateFuser final : public Fuser<IO, Gate> {
290294

291295
const auto& mea_gates_at_time = measurement_gates[pgate->time];
292296

293-
GateFused gate_f = {pgate->kind, pgate->time, {}, pgate, {}};
297+
GateFused gate_f = {pgate->kind, pgate->time, {}, pgate, {}, {}};
294298
gate_f.gates.reserve(mea_gates_at_time.size());
295299

296300
// Fuse measurement gates with equal times.
@@ -307,6 +311,14 @@ class BasicGateFuser final : public Fuser<IO, Gate> {
307311
if (gate_it == glast) break;
308312
}
309313

314+
if (fuse_matrix) {
315+
for (auto& gate_f : gates_fused) {
316+
if (gate_f.kind != gate::kMeasurement && gate_f.kind != gate::kDecomp) {
317+
CalculateFusedMatrix(gate_f);
318+
}
319+
}
320+
}
321+
310322
return gates_fused;
311323
}
312324

@@ -338,7 +350,7 @@ class BasicGateFuser final : public Fuser<IO, Gate> {
338350
std::vector<GateFused>& gates_fused) {
339351
auto pgate = gates_lat[q][k];
340352

341-
GateFused gate_f = {pgate->kind, pgate->time, {q}, pgate, {}};
353+
GateFused gate_f = {pgate->kind, pgate->time, {q}, pgate, {}, {}};
342354
gate_f.gates.push_back(pgate);
343355

344356
k = Advance(k + 1, gates_lat[q], gate_f.gates);

0 commit comments

Comments
 (0)