Qrack  10.0
General classical-emulating-quantum development framework
qcircuit.hpp
Go to the documentation of this file.
1 //
3 // (C) Daniel Strano and the Qrack contributors 2017-2023. All rights reserved.
4 //
5 // This is a multithreaded, universal quantum register simulation, allowing
6 // (nonphysical) register cloning and direct measurement of probability and
7 // phase, to leverage what advantages classical emulation of qubits can have.
8 //
9 // Licensed under the GNU Lesser General Public License V3.
10 // See LICENSE.md in the project root or https://www.gnu.org/licenses/lgpl-3.0.en.html
11 // for details.
12 
13 #pragma once
14 
15 #include "qinterface.hpp"
16 
17 #include <algorithm>
18 #include <iostream>
19 #include <iterator>
20 #include <list>
21 
22 #define amp_leq_0(x) (norm(x) <= FP_NORM_EPSILON)
23 #define __IS_REAL_1(r) (abs(ONE_R1 - r) <= FP_NORM_EPSILON)
24 #define __IS_SAME(a, b) (norm((a) - (b)) <= FP_NORM_EPSILON)
25 #define __IS_CTRLED_CLIFFORD(top, bottom) \
26  ((__IS_REAL_1(std::real(top)) || __IS_REAL_1(std::imag(bottom))) && \
27  (__IS_SAME(top, bottom) || __IS_SAME(top, -bottom)))
28 #define __IS_CLIFFORD_PHASE_INVERT(top, bottom) \
29  (__IS_SAME(top, bottom) || __IS_SAME(top, -bottom) || __IS_SAME(top, I_CMPLX * bottom) || \
30  __IS_SAME(top, -I_CMPLX * bottom))
31 #define __IS_CLIFFORD(mtrx) \
32  ((__IS_PHASE(mtrx) && __IS_CLIFFORD_PHASE_INVERT(mtrx[0], mtrx[3])) || \
33  (__IS_INVERT(mtrx) && __IS_CLIFFORD_PHASE_INVERT(mtrx[1], mtrx[2])) || \
34  ((__IS_SAME(mtrx[0U], mtrx[1U]) || __IS_SAME(mtrx[0U], -mtrx[1U]) || \
35  __IS_SAME(mtrx[0U], I_CMPLX * mtrx[1U]) || __IS_SAME(mtrx[0U], -I_CMPLX * mtrx[1U])) && \
36  (__IS_SAME(mtrx[0U], mtrx[2U]) || __IS_SAME(mtrx[0U], -mtrx[2U]) || \
37  __IS_SAME(mtrx[0U], I_CMPLX * mtrx[2U]) || __IS_SAME(mtrx[0U], -I_CMPLX * mtrx[2U])) && \
38  (__IS_SAME(mtrx[0U], mtrx[3U]) || __IS_SAME(mtrx[0U], -mtrx[3U]) || \
39  __IS_SAME(mtrx[0U], I_CMPLX * mtrx[3U]) || IS_SAME(mtrx[0U], -I_CMPLX * mtrx[3U]))))
40 #define __IS_PHASE(mtrx) (IS_NORM_0(mtrx[1U]) && IS_NORM_0(mtrx[2U]))
41 #define __IS_INVERT(mtrx) (IS_NORM_0(mtrx[0U]) && IS_NORM_0(mtrx[3U]))
42 
43 namespace Qrack {
44 
48 struct QCircuitGate;
49 typedef std::shared_ptr<QCircuitGate> QCircuitGatePtr;
50 
51 struct QCircuitGate {
53  std::map<bitCapInt, std::shared_ptr<complex>> payloads;
54  std::set<bitLenInt> controls;
55 
60  : target(0U)
61  , payloads()
62  , controls()
63 
64  {
65  Clear();
66  }
67 
72  : target(q1)
73  , payloads()
74  , controls({ q2 })
75 
76  {
77  // Swap gate constructor.
78  }
79 
83  QCircuitGate(bitLenInt trgt, const complex matrix[])
84  : target(trgt)
85  {
86  payloads[ZERO_BCI] = std::shared_ptr<complex>(new complex[4U], std::default_delete<complex[]>());
87  std::copy(matrix, matrix + 4U, payloads[ZERO_BCI].get());
88  }
89 
93  QCircuitGate(bitLenInt trgt, const complex matrix[], const std::set<bitLenInt>& ctrls, const bitCapInt& perm)
94  : target(trgt)
95  , controls(ctrls)
96  {
97  const std::shared_ptr<complex>& p = payloads[perm] =
98  std::shared_ptr<complex>(new complex[4U], std::default_delete<complex[]>());
99  std::copy(matrix, matrix + 4U, p.get());
100  }
101 
106  bitLenInt trgt, const std::map<bitCapInt, std::shared_ptr<complex>>& pylds, const std::set<bitLenInt>& ctrls)
107  : target(trgt)
108  , controls(ctrls)
109  {
110  for (const auto& payload : pylds) {
111  payloads[payload.first] = std::shared_ptr<complex>(new complex[4U], std::default_delete<complex[]>());
112  std::copy(payload.second.get(), payload.second.get() + 4U, payloads[payload.first].get());
113  }
114  }
115 
116  QCircuitGatePtr Clone() { return std::make_shared<QCircuitGate>(target, payloads, controls); }
117 
121  bool CanCombine(QCircuitGatePtr other, bool clifford = false)
122  {
123  if (target != other->target) {
124  return false;
125  }
126 
127  if (clifford) {
128  if (controls.empty() && other->controls.empty()) {
129  return true;
130  }
131 
132  const bool mc = IsClifford();
133  const bool oc = other->IsClifford();
134 
135  if (mc != oc) {
136  return false;
137  }
138 
139  if (mc) {
140  return controls.empty() || other->controls.empty() ||
141  (*(controls.begin()) == *(other->controls.begin()));
142  }
143  }
144 
145  if (controls.empty() || other->controls.empty()) {
146  return true;
147  }
148 
149  return std::includes(other->controls.begin(), other->controls.end(), controls.begin(), controls.end()) ||
150  std::includes(controls.begin(), controls.end(), other->controls.begin(), other->controls.end());
151  }
152 
156  void Clear()
157  {
158  controls.clear();
159  payloads.clear();
160 
161  payloads[ZERO_BCI] = std::shared_ptr<complex>(new complex[4U], std::default_delete<complex[]>());
162  complex* p = payloads[ZERO_BCI].get();
163  p[0U] = ONE_CMPLX;
164  p[1U] = ZERO_CMPLX;
165  p[2U] = ZERO_CMPLX;
166  p[3U] = ONE_CMPLX;
167  }
168 
173  {
174  if (controls.find(c) != controls.end()) {
175  return;
176  }
177 
178  controls.insert(c);
179 
180  const size_t cpos = std::distance(controls.begin(), controls.find(c));
181  const bitCapInt midPow = pow2(cpos);
182  bitCapInt lowMask = midPow;
183  bi_decrement(&lowMask, 1U);
184  const bitCapInt highMask = ~lowMask;
185 
186  std::map<bitCapInt, std::shared_ptr<complex>> nPayloads;
187  for (const auto& payload : payloads) {
188  bitCapInt nKey = (payload.first & lowMask) | ((payload.first & highMask) << 1U);
189 
190  nPayloads.emplace(nKey, payload.second);
191 
192  std::shared_ptr<complex> np = std::shared_ptr<complex>(new complex[4U], std::default_delete<complex[]>());
193  std::copy(payload.second.get(), payload.second.get() + 4U, np.get());
194  bi_or_ip(&nKey, midPow);
195  nPayloads.emplace(nKey, np);
196  }
197 
198  payloads = nPayloads;
199  }
200 
205  {
206  const size_t cpos = std::distance(controls.begin(), controls.find(c));
207  const bitCapInt midPow = pow2(cpos);
208 
209  for (const auto& payload : payloads) {
210  bitCapInt nKey = ~midPow & payload.first;
211 
212  if (bi_compare(nKey, payload.first) == 0) {
213  if (payloads.find(nKey | midPow) == payloads.end()) {
214  return false;
215  }
216  } else {
217  if (payloads.find(nKey) == payloads.end()) {
218  return false;
219  }
220  }
221 
222  const complex* l = payloads[nKey].get();
223  bi_or_ip(&nKey, midPow);
224  const complex* h = payloads[nKey].get();
225  if (amp_leq_0(l[0U] - h[0U]) && amp_leq_0(l[1U] - h[1U]) && amp_leq_0(l[2U] - h[2U]) &&
226  amp_leq_0(l[3U] - h[3U])) {
227  continue;
228  }
229 
230  return false;
231  }
232 
233  return true;
234  }
235 
240  {
241  const size_t cpos = std::distance(controls.begin(), controls.find(c));
242  const bitCapInt midPow = pow2(cpos);
243  bitCapInt lowMask = midPow;
244  bi_decrement(&lowMask, 1U);
245  const bitCapInt highMask = ~(lowMask | midPow);
246 
247  std::map<bitCapInt, std::shared_ptr<complex>> nPayloads;
248  for (const auto& payload : payloads) {
249  nPayloads.emplace((payload.first & lowMask) | ((payload.first & highMask) >> 1U), payload.second);
250  }
251 
252  payloads = nPayloads;
253  controls.erase(c);
254  }
255 
260  {
261  if (!CanRemoveControl(c)) {
262  return false;
263  }
264  RemoveControl(c);
265 
266  return true;
267  }
268 
273  {
274  std::set<bitLenInt> ctrlsToTest;
275  std::set_intersection(controls.begin(), controls.end(), other->controls.begin(), other->controls.end(),
276  std::inserter(ctrlsToTest, ctrlsToTest.begin()));
277 
278  if (controls.size() < other->controls.size()) {
279  for (const bitLenInt& oc : other->controls) {
280  AddControl(oc);
281  }
282  } else if (controls.size() > other->controls.size()) {
283  for (const bitLenInt& c : controls) {
284  other->AddControl(c);
285  }
286  }
287 
288  for (const auto& payload : other->payloads) {
289  const auto& pit = payloads.find(payload.first);
290  if (pit == payloads.end()) {
291  const std::shared_ptr<complex>& p = payloads[payload.first] =
292  std::shared_ptr<complex>(new complex[4U], std::default_delete<complex[]>());
293  std::copy(payload.second.get(), payload.second.get() + 4U, p.get());
294 
295  continue;
296  }
297 
298  complex* p = pit->second.get();
299  complex out[4];
300  mul2x2(payload.second.get(), p, out);
301 
302  if (amp_leq_0(out[1U]) && amp_leq_0(out[2U]) && amp_leq_0(ONE_CMPLX - out[0U]) &&
303  amp_leq_0(ONE_CMPLX - out[3U])) {
304  payloads.erase(pit);
305 
306  continue;
307  }
308 
309  std::copy(out, out + 4U, p);
310  }
311 
312  if (payloads.empty()) {
313  return Clear();
314  }
315 
316  for (const bitLenInt& c : ctrlsToTest) {
317  TryRemoveControl(c);
318  }
319  }
320 
324  bool TryCombine(QCircuitGatePtr other, bool clifford = false)
325  {
326  if (!CanCombine(other, clifford)) {
327  return false;
328  }
329  Combine(other);
330 
331  return true;
332  }
333 
337  bool IsIdentity()
338  {
339  const bitCapInt controlPow = pow2(controls.size());
340  if (payloads.size() == controlPow) {
341  const complex* refP = payloads.begin()->second.get();
342  if (norm(refP[0U] - refP[3U]) > FP_NORM_EPSILON) {
343  return false;
344  }
345 
346  const complex phaseFac = refP[0U];
347  for (const auto& payload : payloads) {
348  complex* p = payload.second.get();
349  if ((norm(p[1U]) > FP_NORM_EPSILON) || (norm(p[2U]) > FP_NORM_EPSILON) ||
350  (norm(phaseFac - p[0U]) > FP_NORM_EPSILON) || (norm(phaseFac - p[3U]) > FP_NORM_EPSILON)) {
351  return false;
352  }
353  }
354 
355  return true;
356  }
357 
358  for (const auto& payload : payloads) {
359  complex* p = payload.second.get();
360  if ((norm(p[1U]) > FP_NORM_EPSILON) || (norm(p[2U]) > FP_NORM_EPSILON) ||
361  (norm(ONE_CMPLX - p[0U]) > FP_NORM_EPSILON) || (norm(ONE_CMPLX - p[3U]) > FP_NORM_EPSILON)) {
362  return false;
363  }
364  }
365 
366  return true;
367  }
368 
372  bool IsPhase()
373  {
374  for (const auto& payload : payloads) {
375  complex* p = payload.second.get();
376  if ((norm(p[1U]) > FP_NORM_EPSILON) || (norm(p[2U]) > FP_NORM_EPSILON)) {
377  return false;
378  }
379  }
380 
381  return true;
382  }
383 
387  bool IsInvert()
388  {
389  for (const auto& payload : payloads) {
390  complex* p = payload.second.get();
391  if ((norm(p[0U]) > FP_NORM_EPSILON) || (norm(p[3U]) > FP_NORM_EPSILON)) {
392  return false;
393  }
394  }
395 
396  return true;
397  }
398 
403  {
404  for (const auto& payload : payloads) {
405  complex* p = payload.second.get();
406  if (((norm(p[0]) > FP_NORM_EPSILON) || (norm(p[3]) > FP_NORM_EPSILON)) &&
407  ((norm(p[1]) > FP_NORM_EPSILON) || (norm(p[2]) > FP_NORM_EPSILON))) {
408  return false;
409  }
410  }
411 
412  return true;
413  }
414 
418  bool IsCnot()
419  {
420  if ((controls.size() != 1U) || (payloads.size() != 1U) || (payloads.find(ONE_BCI) == payloads.end())) {
421  return false;
422  }
423 
424  complex* p = payloads[ONE_BCI].get();
425  if ((norm(p[0]) > FP_NORM_EPSILON) || (norm(p[3]) > FP_NORM_EPSILON) ||
426  (norm(ONE_CMPLX - p[1]) > FP_NORM_EPSILON) || (norm(ONE_CMPLX - p[2]) > FP_NORM_EPSILON)) {
427  return false;
428  }
429 
430  return true;
431  }
432 
436  bool IsAntiCnot()
437  {
438  if ((controls.size() != 1U) || (payloads.size() != 1U) || (payloads.find(ZERO_BCI) == payloads.end())) {
439  return false;
440  }
441 
442  complex* p = payloads[ZERO_BCI].get();
443  if ((norm(p[0]) > FP_NORM_EPSILON) || (norm(p[3]) > FP_NORM_EPSILON) ||
444  (norm(ONE_CMPLX - p[1]) > FP_NORM_EPSILON) || (norm(ONE_CMPLX - p[2]) > FP_NORM_EPSILON)) {
445  return false;
446  }
447 
448  return true;
449  }
450 
454  bool IsClifford()
455  {
456  if (payloads.empty()) {
457  // Swap gate is Clifford
458  return true;
459  }
460 
461  if (controls.size() > 1U) {
462  return false;
463  }
464 
465  if (controls.empty()) {
466  return __IS_CLIFFORD(payloads[ZERO_BCI].get());
467  }
468 
469  for (const auto& kvPair : payloads) {
470  const complex* p = kvPair.second.get();
471  if ((norm(p[1U]) <= FP_NORM_EPSILON) && (norm(p[2U]) <= FP_NORM_EPSILON)) {
472  // Phase payload
473  if (!__IS_CLIFFORD_PHASE_INVERT(p[0U], p[3U])) {
474  return false;
475  }
476  } else if ((norm(p[0U]) <= FP_NORM_EPSILON) && (norm(p[3U]) <= FP_NORM_EPSILON)) {
477  // Negation payload
478  if (!__IS_CLIFFORD_PHASE_INVERT(p[1U], p[2U])) {
479  return false;
480  }
481  } else {
482  return false;
483  }
484  }
485 
486  return true;
487  }
488 
493  {
494  const std::set<bitLenInt>::iterator c = other->controls.find(target);
495  if (c == other->controls.end()) {
496  if (controls.find(other->target) != controls.end()) {
497  return other->IsPhase();
498  }
499 
500  return (target != other->target) || (IsPhase() && other->IsPhase());
501  }
502 
503  if (controls.find(other->target) != controls.end()) {
504  return IsPhase() && other->IsPhase();
505  }
506 
507  if (IsPhase()) {
508  return true;
509  }
510 
511  if (!IsPhaseInvert() ||
512  !std::includes(other->controls.begin(), other->controls.end(), controls.begin(), controls.end())) {
513  return false;
514  }
515 
516  std::vector<bitCapInt> opfPows;
517  opfPows.reserve(controls.size());
518  for (const bitLenInt& ctrl : controls) {
519  opfPows.emplace_back(pow2(std::distance(other->controls.begin(), other->controls.find(ctrl))));
520  }
521  const bitCapInt p = pow2(std::distance(other->controls.begin(), c));
522  std::map<bitCapInt, std::shared_ptr<complex>> nPayloads;
523  for (const auto& payload : other->payloads) {
524  bitCapInt pf = ZERO_BCI;
525  for (size_t i = 0U; i < opfPows.size(); ++i) {
526  if (bi_compare_0(payload.first & opfPows[i]) != 0) {
527  bi_or_ip(&pf, pow2(i));
528  }
529  }
530  const auto& poi = payloads.find(pf);
531  if ((poi == payloads.end()) || (norm(poi->second.get()[0U]) > FP_NORM_EPSILON)) {
532  nPayloads[payload.first] = payload.second;
533  } else {
534  nPayloads[payload.first ^ p] = payload.second;
535  }
536  }
537  other->payloads = nPayloads;
538 
539  return true;
540  }
541 
545  std::unique_ptr<complex[]> MakeUniformlyControlledPayload()
546  {
547  const bitCapIntOcl maxQPower = pow2Ocl(controls.size());
548  std::unique_ptr<complex[]> toRet(new complex[maxQPower << 2U]);
550  for (bitCapIntOcl i = 0U; i < maxQPower; ++i) {
551  complex* mtrx = toRet.get() + (i << 2U);
552  const auto& p = payloads.find(i);
553  if (p == payloads.end()) {
554  std::copy(identity, identity + 4U, mtrx);
555  continue;
556  }
557 
558  const complex* oMtrx = p->second.get();
559  std::copy(oMtrx, oMtrx + 4U, mtrx);
560  }
561 
562  return toRet;
563  }
564 
568  std::vector<bitLenInt> GetControlsVector() { return std::vector<bitLenInt>(controls.begin(), controls.end()); }
569 
573  void PostSelectControl(bitLenInt c, bool eigen)
574  {
575  const auto controlIt = controls.find(c);
576  if (controlIt == controls.end()) {
577  return;
578  }
579 
580  const size_t cpos = std::distance(controls.begin(), controlIt);
581  const bitCapInt midPow = pow2(cpos);
582  bitCapInt lowMask = midPow;
583  bi_decrement(&lowMask, 1U);
584  const bitCapInt highMask = ~(lowMask | midPow);
585  const bitCapInt qubitPow = pow2(cpos);
586  const bitCapInt eigenPow = eigen ? qubitPow : ZERO_BCI;
587 
588  std::map<bitCapInt, std::shared_ptr<complex>> nPayloads;
589  for (const auto& payload : payloads) {
590  if (bi_compare(payload.first & qubitPow, eigenPow) != 0) {
591  continue;
592  }
593  nPayloads.emplace((payload.first & lowMask) | ((payload.first & highMask) >> 1U), payload.second);
594  }
595 
596  payloads = nPayloads;
597  controls.erase(c);
598  }
599 };
600 
601 std::ostream& operator<<(std::ostream& os, const QCircuitGatePtr g);
602 std::istream& operator>>(std::istream& os, QCircuitGatePtr& g);
603 
607 class QCircuit;
608 typedef std::shared_ptr<QCircuit> QCircuitPtr;
609 
610 class QCircuit {
611 protected:
615  std::list<QCircuitGatePtr> gates;
616 
617  // Method provided by Google search AI:
618  bool setsIntersect(const std::set<bitLenInt>& set1, const std::set<bitLenInt>& set2)
619  {
620  auto it1 = set1.begin();
621  auto it2 = set2.begin();
622 
623  while (it1 != set1.end() && it2 != set2.end()) {
624  if (*it1 == *it2) {
625  return true; // Found a common element, so they intersect
626  } else if (*it1 < *it2) {
627  ++it1; // Element in set1 is smaller, advance set1 iterator
628  } else {
629  ++it2; // Element in set2 is smaller, advance set2 iterator
630  }
631  }
632 
633  return false; // Reached the end of one set without finding common elements
634  }
635 
636 public:
640  QCircuit(bool collapse = true, bool clifford = false)
641  : isCollapsed(collapse)
642  , isNearClifford(clifford)
643  , qubitCount(0U)
644  , gates()
645  {
646  // Intentionally left blank
647  }
648 
652  QCircuit(bitLenInt qbCount, const std::list<QCircuitGatePtr>& g, bool collapse = true, bool clifford = false)
653  : isCollapsed(collapse)
654  , isNearClifford(clifford)
655  , qubitCount(qbCount)
656  {
657  for (auto gIt = g.begin(); gIt != g.end(); ++gIt) {
658  gates.push_back((*gIt)->Clone());
659  }
660  }
661 
662  QCircuitPtr Clone() { return std::make_shared<QCircuit>(qubitCount, gates, isCollapsed, isNearClifford); }
663 
665  {
666  QCircuitPtr clone = Clone();
667  for (auto gIt = clone->gates.begin(); gIt != clone->gates.end(); ++gIt) {
668  for (auto& p : (*gIt)->payloads) {
669  const complex* m = p.second.get();
670  complex inv[4U]{ conj(m[0U]), conj(m[2U]), conj(m[1U]), conj(m[3U]) };
671  std::copy(inv, inv + 4U, p.second.get());
672  }
673  }
674  clone->gates.reverse();
675 
676  return clone;
677  }
678 
683 
688 
692  std::list<QCircuitGatePtr> GetGateList() { return gates; }
693 
697  void SetGateList(std::list<QCircuitGatePtr> gl) { gates = gl; }
698 
702  void Swap(bitLenInt q1, bitLenInt q2)
703  {
704  if (q1 == q2) {
705  return;
706  }
707 
708  // If all swap gates are constructed in the same order, between high and low qubits, then the chances of
709  // combining them might be higher.
710  if (q1 > q2) {
711  std::swap(q1, q2);
712  }
713 
715  const std::set<bitLenInt> s1{ q1 };
716  const std::set<bitLenInt> s2{ q2 };
717  AppendGate(std::make_shared<QCircuitGate>(q1, m, s2, ONE_BCI));
718  AppendGate(std::make_shared<QCircuitGate>(q2, m, s1, ONE_BCI));
719  AppendGate(std::make_shared<QCircuitGate>(q1, m, s2, ONE_BCI));
720  }
721 
725  void Append(QCircuitPtr circuit)
726  {
727  if (circuit->qubitCount > qubitCount) {
728  qubitCount = circuit->qubitCount;
729  }
730  gates.insert(gates.end(), circuit->gates.begin(), circuit->gates.end());
731  }
732 
737  void Combine(QCircuitPtr circuit)
738  {
739  if (circuit->qubitCount > qubitCount) {
740  qubitCount = circuit->qubitCount;
741  }
742  for (auto gIt = circuit->gates.begin(); gIt != circuit->gates.end(); ++gIt) {
743  AppendGate(*gIt);
744  }
745  }
746 
750  bool AppendGate(QCircuitGatePtr nGate);
754  void Run(QInterfacePtr qsim);
755 
760  {
761  for (const QCircuitGatePtr& gate : gates) {
762  if (gate->target != qubit) {
763  continue;
764  }
765  if (gate->IsInvert() && gate->controls.empty()) {
766  continue;
767  }
768  if (!(gate->IsPhase())) {
769  return true;
770  }
771  }
772 
773  return false;
774  }
775 
779  bool IsNonClassicalTarget(bitLenInt qubit, bool* eigen)
780  {
781  for (const QCircuitGatePtr& gate : gates) {
782  if (gate->target != qubit) {
783  continue;
784  }
785  if (gate->IsInvert() && gate->controls.empty()) {
786  *eigen = !*eigen;
787  continue;
788  }
789  if (!(gate->IsPhase())) {
790  return true;
791  }
792  }
793 
794  return false;
795  }
796 
800  bool DeleteClassicalTarget(bitLenInt qubit, bool eigen)
801  {
802  std::list<QCircuitGatePtr> nGates;
803  gates.reverse();
804  for (const QCircuitGatePtr& gate : gates) {
805  if (gate->target == qubit) {
806  if (gate->IsInvert()) {
807  eigen = !eigen;
808  QCircuitGatePtr nGate = gate->Clone();
809  nGates.insert(nGates.begin(), nGate);
810  }
811  continue;
812  }
813  QCircuitGatePtr nGate = gate->Clone();
814  nGate->PostSelectControl(qubit, eigen);
815  nGates.insert(nGates.begin(), nGate);
816  }
817  gates = nGates;
818  return eigen;
819  }
820 
824  QCircuitPtr PastLightCone(std::set<bitLenInt>& qubits)
825  {
826  std::list<QCircuitGatePtr> nGates;
827  // We're working from latest gate to earliest gate.
828  for (auto gIt = gates.rbegin(); gIt != gates.rend(); ++gIt) {
829  QCircuitGatePtr& gate = *gIt;
830 
831  std::set<bitLenInt> toCheck = gate->controls;
832  toCheck.insert(gate->target);
833 
834  if (!setsIntersect(qubits, toCheck)) {
835  // This gate is not on the past light cone.
836  continue;
837  }
838 
839  // This gate is on the past light cone.
840  nGates.insert(nGates.begin(), gate->Clone());
841 
842  // Every qubit involved in this gate is now considered to be part of the past light cone.
843  qubits.insert(gate->target);
844  qubits.insert(gate->controls.begin(), gate->controls.end());
845  }
846 
847  // Restore the original order of this QCircuit's gates.
848  gates.reverse();
849 
850  return std::make_shared<QCircuit>(qubitCount, nGates, isCollapsed, isNearClifford);
851  }
852 
857  QCircuitPtr RemovePastLightCone(std::set<bitLenInt>& qubits)
858  {
859  std::list<QCircuitGatePtr> oGates;
860  std::list<QCircuitGatePtr> nGates;
861  // We're working from latest gate to earliest gate.
862  for (auto gIt = gates.rbegin(); gIt != gates.rend(); ++gIt) {
863  QCircuitGatePtr& gate = *gIt;
864 
865  std::set<bitLenInt> toCheck = gate->controls;
866  toCheck.insert(gate->target);
867 
868  if (!setsIntersect(qubits, toCheck)) {
869  // This gate is not on the past light cone.
870  oGates.insert(oGates.begin(), gate);
871  continue;
872  }
873 
874  // This gate is on the past light cone.
875  nGates.insert(nGates.begin(), gate);
876  // Every qubit involved in this gate is now considered to be part of the past light cone.
877  qubits.insert(gate->target);
878  qubits.insert(gate->controls.begin(), gate->controls.end());
879  }
880 
881  // Internally retain just the gates off the light cone.
882  gates = oGates;
883 
884  return std::make_shared<QCircuit>(qubitCount, nGates, isCollapsed, isNearClifford);
885  }
886 
887 #if ENABLE_ALU
889  void INC(const bitCapInt& toAdd, bitLenInt start, bitLenInt length);
890 #endif
891 };
892 
893 std::ostream& operator<<(std::ostream& os, const QCircuitPtr g);
894 std::istream& operator>>(std::istream& os, QCircuitPtr& g);
895 } // namespace Qrack
void bi_or_ip(BigInteger *left, const BigInteger &right)
Definition: big_integer.hpp:444
void bi_decrement(BigInteger *pBigInt, const BIG_INTEGER_WORD &value)
Definition: big_integer.hpp:237
int bi_compare_0(const BigInteger &left)
Definition: big_integer.hpp:140
int bi_compare(const BigInteger &left, const BigInteger &right)
Definition: big_integer.hpp:125
Definition: qcircuit.hpp:610
std::list< QCircuitGatePtr > GetGateList()
Return the raw list of gates.
Definition: qcircuit.hpp:692
bool isNearClifford
Definition: qcircuit.hpp:613
bitLenInt GetQubitCount()
Get the (automatically calculated) count of qubits in this circuit, so far.
Definition: qcircuit.hpp:682
bool DeleteClassicalTarget(bitLenInt qubit, bool eigen)
(If the qubit is not a target of a non-classical gate...) Delete this qubits' controls and phase targ...
Definition: qcircuit.hpp:800
void SetGateList(std::list< QCircuitGatePtr > gl)
Set the raw list of gates.
Definition: qcircuit.hpp:697
bitLenInt qubitCount
Definition: qcircuit.hpp:614
bool IsNonClassicalTarget(bitLenInt qubit, bool *eigen)
Check if an index is any target qubit of a nonclassical gate in this circuit.
Definition: qcircuit.hpp:779
bool setsIntersect(const std::set< bitLenInt > &set1, const std::set< bitLenInt > &set2)
Definition: qcircuit.hpp:618
void Append(QCircuitPtr circuit)
Append circuit (with identical qubit index mappings) at the end of this circuit.
Definition: qcircuit.hpp:725
bool IsNonClassicalTarget(bitLenInt qubit)
Check if an index is any target qubit of a nonclassical gate in this circuit.
Definition: qcircuit.hpp:759
void INC(const bitCapInt &toAdd, bitLenInt start, bitLenInt length)
Add integer (without sign)
Definition: arithmetic_qcircuit.cpp:23
QCircuitPtr RemovePastLightCone(std::set< bitLenInt > &qubits)
Return (as a new QCircuit) just the gates on the past light cone of a set of qubit indices,...
Definition: qcircuit.hpp:857
QCircuit(bitLenInt qbCount, const std::list< QCircuitGatePtr > &g, bool collapse=true, bool clifford=false)
Manual constructor.
Definition: qcircuit.hpp:652
QCircuitPtr Inverse()
Definition: qcircuit.hpp:664
void Combine(QCircuitPtr circuit)
Combine circuit (with identical qubit index mappings) at the end of this circuit, by acting all addit...
Definition: qcircuit.hpp:737
QCircuitPtr PastLightCone(std::set< bitLenInt > &qubits)
Return (as a new QCircuit) just the gates on the past light cone of a set of qubit indices.
Definition: qcircuit.hpp:824
QCircuit(bool collapse=true, bool clifford=false)
Default constructor.
Definition: qcircuit.hpp:640
bool AppendGate(QCircuitGatePtr nGate)
Add a gate to the gate sequence.
Definition: qcircuit.cpp:101
QCircuitPtr Clone()
Definition: qcircuit.hpp:662
void SetQubitCount(bitLenInt n)
Set the count of qubits in this circuit, so far.
Definition: qcircuit.hpp:687
void Run(QInterfacePtr qsim)
Run this circuit.
Definition: qcircuit.cpp:173
void Swap(bitLenInt q1, bitLenInt q2)
Add a Swap gate to the gate sequence.
Definition: qcircuit.hpp:702
std::list< QCircuitGatePtr > gates
Definition: qcircuit.hpp:615
bool isCollapsed
Definition: qcircuit.hpp:612
GLOSSARY: bitLenInt - "bit-length integer" - unsigned integer ID of qubit position in register bitCap...
Definition: complex16x2simd.hpp:25
std::shared_ptr< QInterface > QInterfacePtr
Definition: qinterface.hpp:29
void mul2x2(const complex *left, const complex *right, complex *out)
Definition: functions.cpp:113
void U(quid sid, bitLenInt q, real1_f theta, real1_f phi, real1_f lambda)
(External API) 3-parameter unitary gate
Definition: wasm_api.cpp:1199
std::istream & operator>>(std::istream &os, QCircuitGatePtr &g)
Definition: qcircuit.cpp:38
std::complex< real1 > complex
Definition: qrack_types.hpp:136
QRACK_CONST real1 FP_NORM_EPSILON
Definition: qrack_types.hpp:268
bitCapInt pow2(const bitLenInt &p)
Definition: qrack_functions.hpp:143
double norm(const complex2 &c)
Definition: complex16x2simd.hpp:122
QRACK_CONST complex ONE_CMPLX
Definition: qrack_types.hpp:262
std::shared_ptr< QCircuitGate > QCircuitGatePtr
Definition: qcircuit.hpp:48
std::shared_ptr< QCircuit > QCircuitPtr
Definition: qcircuit.hpp:607
std::ostream & operator<<(std::ostream &os, const QCircuitGatePtr g)
Definition: qcircuit.cpp:17
QRACK_CONST complex ZERO_CMPLX
Definition: qrack_types.hpp:263
const bitCapInt ONE_BCI
Definition: qrack_types.hpp:137
const bitCapInt ZERO_BCI
Definition: qrack_types.hpp:138
bitCapIntOcl pow2Ocl(const bitLenInt &p)
Definition: qrack_functions.hpp:144
#define amp_leq_0(x)
Definition: qcircuit.hpp:22
#define __IS_CLIFFORD_PHASE_INVERT(top, bottom)
Definition: qcircuit.hpp:28
#define __IS_CLIFFORD(mtrx)
Definition: qcircuit.hpp:31
#define QRACK_CONST
Definition: qrack_types.hpp:182
#define bitLenInt
Definition: qrack_types.hpp:42
#define bitCapInt
Definition: qrack_types.hpp:66
#define bitCapIntOcl
Definition: qrack_types.hpp:54
Definition: qcircuit.hpp:51
std::unique_ptr< complex[]> MakeUniformlyControlledPayload()
To run as a uniformly controlled gate, generate my payload array.
Definition: qcircuit.hpp:545
std::set< bitLenInt > controls
Definition: qcircuit.hpp:54
QCircuitGate(bitLenInt trgt, const complex matrix[], const std::set< bitLenInt > &ctrls, const bitCapInt &perm)
Controlled gate constructor.
Definition: qcircuit.hpp:93
QCircuitGate(bitLenInt trgt, const std::map< bitCapInt, std::shared_ptr< complex >> &pylds, const std::set< bitLenInt > &ctrls)
Uniformly controlled gate constructor (that only accepts control qubits is ascending order)
Definition: qcircuit.hpp:105
bool TryRemoveControl(bitLenInt c)
Check if I can remove control, and do so, if possible.
Definition: qcircuit.hpp:259
std::map< bitCapInt, std::shared_ptr< complex > > payloads
Definition: qcircuit.hpp:53
bool CanCombine(QCircuitGatePtr other, bool clifford=false)
Can I combine myself with gate other?
Definition: qcircuit.hpp:121
bool IsCnot()
Am I a CNOT gate?
Definition: qcircuit.hpp:418
bool TryCombine(QCircuitGatePtr other, bool clifford=false)
Check if I can combine with gate other, and do so, if possible.
Definition: qcircuit.hpp:324
QCircuitGate()
Identity gate constructor.
Definition: qcircuit.hpp:59
std::vector< bitLenInt > GetControlsVector()
Convert my set of qubit indices to a vector.
Definition: qcircuit.hpp:568
void Clear()
Set this gate to the identity operator.
Definition: qcircuit.hpp:156
void RemoveControl(bitLenInt c)
Remove control qubit.
Definition: qcircuit.hpp:239
bool CanPass(QCircuitGatePtr other)
Do I commute with gate other?
Definition: qcircuit.hpp:492
QCircuitGate(bitLenInt trgt, const complex matrix[])
Single-qubit gate constructor.
Definition: qcircuit.hpp:83
void Combine(QCircuitGatePtr other)
Combine myself with gate other
Definition: qcircuit.hpp:272
QCircuitGatePtr Clone()
Definition: qcircuit.hpp:116
bool IsAntiCnot()
Am I a CNOT gate?
Definition: qcircuit.hpp:436
bool IsPhaseInvert()
Am I a combination of "phase" and "invert" payloads?
Definition: qcircuit.hpp:402
void AddControl(bitLenInt c)
Add control qubit.
Definition: qcircuit.hpp:172
bool CanRemoveControl(bitLenInt c)
Check if a control qubit can be removed.
Definition: qcircuit.hpp:204
bool IsClifford()
Am I a Clifford gate?
Definition: qcircuit.hpp:454
bitLenInt target
Definition: qcircuit.hpp:52
bool IsPhase()
Am I a phase gate?
Definition: qcircuit.hpp:372
bool IsIdentity()
Am I an identity gate?
Definition: qcircuit.hpp:337
bool IsInvert()
Am I a Pauli X plus a phase gate?
Definition: qcircuit.hpp:387
QCircuitGate(bitLenInt q1, bitLenInt q2)
Swap gate constructor
Definition: qcircuit.hpp:71
void PostSelectControl(bitLenInt c, bool eigen)
Erase a control index, if it exists, (via post selection).
Definition: qcircuit.hpp:573