Skip to content

Commit 8b26630

Browse files
committed
Add global phase gate.
1 parent 849711e commit 8b26630

34 files changed

+679
-118
lines changed

apps/qsim_base_custatevec.cu

+1-1
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ int main(int argc, char* argv[]) {
133133
}
134134

135135
Simulator CreateSimulator() const {
136-
return Simulator(custatevec_handle);
136+
return Simulator(cublas_handle, custatevec_handle);
137137
}
138138

139139
cublasHandle_t cublas_handle;

lib/circuit_qsim_parser.h

+13-3
Original file line numberDiff line numberDiff line change
@@ -167,13 +167,19 @@ class CircuitQsimParser final {
167167
IO::errorf("invalid gate in %s in line %u.\n", provider.c_str(), line);
168168
}
169169

170+
/**
171+
* Checks formatting for a zero-qubit gate parsed from 'ss'.
172+
* @param ss Input stream containing the gate specification.
173+
*/
174+
static bool ValidateGate(std::stringstream& ss) {
175+
return ss && ss.peek() == std::stringstream::traits_type::eof();
176+
}
177+
170178
/**
171179
* Checks formatting for a single-qubit gate parsed from 'ss'.
172180
* @param ss Input stream containing the gate specification.
173181
* @param num_qubits Number of qubits, as defined at the start of the file.
174182
* @param q0 Index of the affected qubit.
175-
* @param provider Circuit source; only used for error reporting.
176-
* @param line Line number of the parsed gate; only used for error reporting.
177183
*/
178184
static bool ValidateGate(std::stringstream& ss,
179185
unsigned num_qubits, unsigned q0) {
@@ -261,7 +267,11 @@ class CircuitQsimParser final {
261267
unsigned q0, q1;
262268
fp_type phi, theta;
263269

264-
if (gate_name == "id1") {
270+
if (gate_name == "p") {
271+
ss >> phi;
272+
if (!ValidateGate(ss)) return false;
273+
gates.push_back(GateGPh<fp_type>::Create(time, phi));
274+
} else if (gate_name == "id1") {
265275
ss >> q0;
266276
if (!ValidateGate(ss, num_qubits, q0)) return false;
267277
gates.push_back(GateId1<fp_type>::Create(time, q0));

lib/fuser.h

+35-1
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,38 @@ class Fuser {
122122
return epochs;
123123
}
124124

125+
template <typename GateSeq0, typename Parent, typename GateFused>
126+
static void FuseZeroQubitGates(const GateSeq0& gate_seq0,
127+
Parent parent, std::size_t first,
128+
std::vector<GateFused>& fused_gates) {
129+
GateFused* fuse_to = nullptr;
130+
131+
for (std::size_t i = first; i < fused_gates.size(); ++i) {
132+
auto& fgate = fused_gates[i];
133+
134+
if (fgate.kind != gate::kMeasurement && fgate.kind != gate::kDecomp
135+
&& fgate.parent->controlled_by.size() == 0
136+
&& !fgate.parent->unfusible) {
137+
fuse_to = &fgate;
138+
break;
139+
}
140+
}
141+
142+
if (fuse_to != nullptr) {
143+
// Fuse zero-qubit gates with the first available fused gate.
144+
for (const auto& g : gate_seq0) {
145+
fuse_to->gates.push_back(parent(g));
146+
}
147+
} else {
148+
auto g0 = parent(gate_seq0[0]);
149+
fused_gates.push_back({g0->kind, g0->time, {}, g0, {g0}, {}});
150+
151+
for (std::size_t i = 1; i < gate_seq0.size(); ++i) {
152+
fused_gates.back().gates.push_back(parent(gate_seq0[i]));
153+
}
154+
}
155+
}
156+
125157
private:
126158
static bool AddBoundary(unsigned time, unsigned max_time,
127159
std::vector<unsigned>& boundaries) {
@@ -144,7 +176,9 @@ inline void CalculateFusedMatrix(FusedGate& gate) {
144176
MatrixIdentity(unsigned{1} << gate.qubits.size(), gate.matrix);
145177

146178
for (auto pgate : gate.gates) {
147-
if (gate.qubits.size() == pgate->qubits.size()) {
179+
if (pgate->qubits.size() == 0) {
180+
MatrixScalarMultiply(pgate->matrix[0], pgate->matrix[1], gate.matrix);
181+
} else if (gate.qubits.size() == pgate->qubits.size()) {
148182
MatrixMultiply(gate.qubits.size(), pgate->matrix, gate.matrix);
149183
} else {
150184
unsigned mask = 0;

lib/fuser_basic.h

+17
Original file line numberDiff line numberDiff line change
@@ -161,16 +161,24 @@ class BasicGateFuser final : public Fuser<IO, Gate> {
161161
// Sequence of top level gates the other gates get fused to.
162162
std::vector<const RGate*> gates_seq;
163163

164+
// Sequence of zero-qubit gates.
165+
std::vector<const RGate*> gates_seq0;
166+
164167
// Lattice of gates: qubits "hyperplane" and time direction.
165168
std::vector<std::vector<const RGate*>> gates_lat(max_qubit1);
166169

167170
// Current unfused gate.
168171
auto gate_it = gfirst;
169172

173+
std::size_t last_fused_gate_index = 0;
174+
170175
for (std::size_t l = 0; l < times.size(); ++l) {
171176
gates_seq.resize(0);
172177
gates_seq.reserve(num_gates);
173178

179+
gates_seq0.resize(0);
180+
gates_seq0.reserve(num_gates);
181+
174182
for (unsigned k = 0; k < max_qubit1; ++k) {
175183
gates_lat[k].resize(0);
176184
gates_lat[k].reserve(128);
@@ -212,6 +220,8 @@ class BasicGateFuser final : public Fuser<IO, Gate> {
212220
gates_lat[gate.qubits[0]].push_back(&gate);
213221
gates_lat[gate.qubits[1]].push_back(&gate);
214222
gates_seq.push_back(&gate);
223+
} else {
224+
gates_seq0.push_back(&gate);
215225
}
216226
}
217227

@@ -306,7 +316,14 @@ class BasicGateFuser final : public Fuser<IO, Gate> {
306316
gates_fused.push_back(std::move(gate_f));
307317
}
308318

319+
if (gates_seq0.size() != 0) {
320+
Base::FuseZeroQubitGates(gates_seq0, [](const RGate* g) { return g; },
321+
last_fused_gate_index, gates_fused);
322+
}
323+
309324
if (gate_it == glast) break;
325+
326+
last_fused_gate_index = gates_fused.size();
310327
}
311328

312329
if (fuse_matrix) {

lib/fuser_mqubit.h

+9
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,7 @@ class MultiQubitGateFuser final : public Fuser<IO, Gate> {
282282
unsigned max_fused_size = std::min(unsigned{6}, param.max_fused_size);
283283
max_fused_size = std::min(max_fused_size, max_qubit1);
284284

285+
std::size_t last_fused_gate_index = 0;
285286
auto gate_it = gfirst;
286287

287288
// Iterate over epochs.
@@ -432,6 +433,14 @@ class MultiQubitGateFuser final : public Fuser<IO, Gate> {
432433
FuseOrphanedGates(max_fused_size, stat, orphaned_gates, fused_gates);
433434
}
434435
}
436+
437+
if (fgates[0].size() != 0) {
438+
Base::FuseZeroQubitGates(fgates[0],
439+
[](const GateF* g) { return g->parent; },
440+
last_fused_gate_index, fused_gates);
441+
}
442+
443+
last_fused_gate_index = fused_gates.size();
435444
}
436445

437446
if (fuse_matrix) {

lib/gates_cirq.h

+26
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ enum GateKind {
7474
kMatrixGate1, // One-qubit matrix gate.
7575
kMatrixGate2, // Two-qubit matrix gate.
7676
kMatrixGate, // Multi-qubit matrix gate.
77+
kGlobalPhaseGate,
7778
kDecomp = gate::kDecomp,
7879
kMeasurement = gate::kMeasurement,
7980
};
@@ -85,6 +86,31 @@ constexpr double h_double = 0.5;
8586
constexpr double pi_double = 3.14159265358979323846264338327950288;
8687
constexpr double is2_double = 0.7071067811865475;
8788

89+
// Gates from cirq/ops/global_phase_op.py:
90+
91+
/**
92+
* The global phase gate.
93+
*/
94+
template <typename fp_type>
95+
struct GlobalPhaseGate {
96+
static constexpr GateKind kind = kGlobalPhaseGate;
97+
static constexpr char name[] = "GlobalPhaseGate";
98+
static constexpr unsigned num_qubits = 1;
99+
static constexpr bool symmetric = true;
100+
101+
static GateCirq<fp_type> Create(unsigned time, fp_type phi) {
102+
return Create(time, std::cos(phi), std::sin(phi));
103+
}
104+
105+
static GateCirq<fp_type> Create(unsigned time, fp_type cp, fp_type sp) {
106+
return CreateGate<GateCirq<fp_type>, GlobalPhaseGate>(
107+
time, {}, {cp, sp}, {cp, sp});
108+
}
109+
};
110+
111+
template <typename fp_type>
112+
using global_phase_operation = GlobalPhaseGate<fp_type>;
113+
88114
// Gates from cirq/ops/identity.py:
89115

90116
/**

lib/gates_qsim.h

+25-2
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,9 @@ enum GateKind {
4646
kGateIS, // iSwap
4747
kGateFS, // fSim
4848
kGateCP, // control phase
49-
kGateMatrix1, // One-qubit matrix gate.
50-
kGateMatrix2, // Two-qubit matrix gate.
49+
kGateMatrix1, // one-qubit matrix gate
50+
kGateMatrix2, // two-qubit matrix gate
51+
kGateGPh, // global phase gate
5152
kDecomp = gate::kDecomp,
5253
kMeasurement = gate::kMeasurement,
5354
};
@@ -59,6 +60,28 @@ using GateQSim = Gate<fp_type, GateKind>;
5960
constexpr double h_double = 0.5;
6061
constexpr double is2_double = 0.7071067811865475;
6162

63+
// Zero-qubit gates:
64+
65+
/**
66+
* The global phase gate.
67+
*/
68+
template <typename fp_type>
69+
struct GateGPh {
70+
static constexpr GateKind kind = kGateGPh;
71+
static constexpr char name[] = "p";
72+
static constexpr unsigned num_qubits = 1;
73+
static constexpr bool symmetric = true;
74+
75+
static GateQSim<fp_type> Create(unsigned time, fp_type phi) {
76+
return Create(time, std::cos(phi), std::sin(phi));
77+
}
78+
79+
static GateQSim<fp_type> Create(unsigned time, fp_type cp, fp_type sp) {
80+
return CreateGate<GateQSim<fp_type>, GateGPh>(
81+
time, {}, {cp, sp}, {cp, sp});
82+
}
83+
};
84+
6285
// One-qubit gates:
6386

6487
/**

lib/matrix.h

+18-1
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ inline void MatrixMultiply(unsigned mask1,
172172
}
173173

174174
/**
175-
* Multiply a matrix by a scalar value.
175+
* Multiply a matrix by a real scalar value.
176176
* @c Scalar value.
177177
* @m Input matrix to be multiplied. Output matrix.
178178
*/
@@ -183,6 +183,23 @@ inline void MatrixScalarMultiply(fp_type1 c, Matrix<fp_type2>& m) {
183183
}
184184
}
185185

186+
/**
187+
* Multiply a matrix by a complex scalar value.
188+
* @re Real part of scalar value.
189+
* @im Imaginary part of scalar value.
190+
* @m Input matrix to be multiplied. Output matrix.
191+
*/
192+
template <typename fp_type1, typename fp_type2>
193+
inline void MatrixScalarMultiply(
194+
fp_type1 re, fp_type1 im, Matrix<fp_type2>& m) {
195+
for (unsigned i = 0; i < m.size() / 2; ++i) {
196+
fp_type2 re0 = m[2 * i + 0];
197+
fp_type2 im0 = m[2 * i + 1];
198+
m[2 * i + 0] = re * re0 - im * im0;
199+
m[2 * i + 1] = re * im0 + im * re0;
200+
}
201+
}
202+
186203
/**
187204
* Daggers a matrix.
188205
* @n Number of matrix rows (columns).

lib/simulator.h

+19-14
Original file line numberDiff line numberDiff line change
@@ -41,22 +41,27 @@ class SimulatorBase {
4141
uint64_t* ms, uint64_t* xss) {
4242
constexpr unsigned hsize = 1 << H;
4343

44-
uint64_t xs[H];
45-
46-
xs[0] = uint64_t{1} << (qs[L] + 1);
47-
ms[0] = (uint64_t{1} << qs[L]) - 1;
48-
for (unsigned i = 1; i < H; ++i) {
49-
xs[i] = uint64_t{1} << (qs[L + i] + 1);
50-
ms[i] = ((uint64_t{1} << qs[L + i]) - 1) ^ (xs[i - 1] - 1);
51-
}
52-
ms[H] = ((uint64_t{1} << num_qubits) - 1) ^ (xs[H - 1] - 1);
44+
if (H == 0) {
45+
ms[0] = uint64_t(-1);
46+
xss[0] = 0;
47+
} else {
48+
uint64_t xs[H + 1];
49+
50+
xs[0] = uint64_t{1} << (qs[L] + 1);
51+
ms[0] = (uint64_t{1} << qs[L]) - 1;
52+
for (unsigned i = 1; i < H; ++i) {
53+
xs[i] = uint64_t{1} << (qs[L + i] + 1);
54+
ms[i] = ((uint64_t{1} << qs[L + i]) - 1) ^ (xs[i - 1] - 1);
55+
}
56+
ms[H] = ((uint64_t{1} << num_qubits) - 1) ^ (xs[H - 1] - 1);
5357

54-
for (unsigned i = 0; i < hsize; ++i) {
55-
uint64_t a = 0;
56-
for (uint64_t k = 0; k < H; ++k) {
57-
a += xs[k] * ((i >> k) & 1);
58+
for (unsigned i = 0; i < hsize; ++i) {
59+
uint64_t a = 0;
60+
for (uint64_t k = 0; k < H; ++k) {
61+
a += xs[k] * ((i >> k) & 1);
62+
}
63+
xss[i] = a;
5864
}
59-
xss[i] = a;
6065
}
6166
}
6267

lib/simulator_avx.h

+10-1
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@ class SimulatorAVX final : public SimulatorBase {
5151
// Assume qs[0] < qs[1] < qs[2] < ... .
5252

5353
switch (qs.size()) {
54+
case 0:
55+
ApplyGateH<0>(qs, matrix, state);
56+
break;
5457
case 1:
5558
if (qs[0] > 2) {
5659
ApplyGateH<1>(qs, matrix, state);
@@ -137,6 +140,13 @@ class SimulatorAVX final : public SimulatorBase {
137140
}
138141

139142
switch (qs.size()) {
143+
case 0:
144+
if (cqs[0] > 2) {
145+
ApplyControlledGateHH<0>(qs, cqs, cvals, matrix, state);
146+
} else {
147+
ApplyControlledGateHL<0>(qs, cqs, cvals, matrix, state);
148+
}
149+
break;
140150
case 1:
141151
if (qs[0] > 2) {
142152
if (cqs[0] > 2) {
@@ -322,7 +332,6 @@ class SimulatorAVX final : public SimulatorBase {
322332
}
323333

324334
private:
325-
326335
#ifdef __BMI2__
327336

328337
template <unsigned H>

lib/simulator_avx512.h

+10
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@ class SimulatorAVX512 final : public SimulatorBase {
5151
// Assume qs[0] < qs[1] < qs[2] < ... .
5252

5353
switch (qs.size()) {
54+
case 0:
55+
ApplyGateH<0>(qs, matrix, state);
56+
break;
5457
case 1:
5558
if (qs[0] > 3) {
5659
ApplyGateH<1>(qs, matrix, state);
@@ -143,6 +146,13 @@ class SimulatorAVX512 final : public SimulatorBase {
143146
}
144147

145148
switch (qs.size()) {
149+
case 0:
150+
if (cqs[0] > 3) {
151+
ApplyControlledGateHH<0>(qs, cqs, cvals, matrix, state);
152+
} else {
153+
ApplyControlledGateHL<0>(qs, cqs, cvals, matrix, state);
154+
}
155+
break;
146156
case 1:
147157
if (qs[0] > 3) {
148158
if (cqs[0] > 3) {

lib/simulator_basic.h

+6
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ class SimulatorBasic final : public SimulatorBase {
4949
// Assume qs[0] < qs[1] < qs[2] < ... .
5050

5151
switch (qs.size()) {
52+
case 0:
53+
ApplyGateH<0>(qs, matrix, state);
54+
break;
5255
case 1:
5356
ApplyGateH<1>(qs, matrix, state);
5457
break;
@@ -92,6 +95,9 @@ class SimulatorBasic final : public SimulatorBase {
9295
}
9396

9497
switch (qs.size()) {
98+
case 0:
99+
ApplyControlledGateH<0>(qs, cqs, cvals, matrix, state);
100+
break;
95101
case 1:
96102
ApplyControlledGateH<1>(qs, cqs, cvals, matrix, state);
97103
break;

0 commit comments

Comments
 (0)