XACC
reversible.h
Go to the documentation of this file.
1 /*
2  * This file is part of Quantum++.
3  *
4  * MIT License
5  *
6  * Copyright (c) 2013 - 2020 Vlad Gheorghiu.
7  *
8  * Permission is hereby granted, free of charge, to any person obtaining a copy
9  * of this software and associated documentation files (the "Software"), to deal
10  * in the Software without restriction, including without limitation the rights
11  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12  * copies of the Software, and to permit persons to whom the Software is
13  * furnished to do so, subject to the following conditions:
14  *
15  * The above copyright notice and this permission notice shall be included in
16  * all copies or substantial portions of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24  * SOFTWARE.
25  */
26 
32 #ifndef CLASSES_REVERSIBLE_H
33 #define CLASSES_REVERSIBLE_H
34 
35 namespace qpp {
42 class Dynamic_bitset : public IDisplay {
43  public:
44  using value_type = unsigned int;
45  using storage_type = std::vector<value_type>;
46  protected:
47  idx storage_size_;
48  idx n_;
49  std::vector<value_type> v_;
50 
57  idx index_(idx pos) const { return pos / (sizeof(value_type) * CHAR_BIT); }
58 
67  idx offset_(idx pos) const { return pos % (sizeof(value_type) * CHAR_BIT); }
68 
69  public:
75  explicit Dynamic_bitset(idx n)
76  : storage_size_{n / (sizeof(value_type) * CHAR_BIT) + 1}, n_{n},
77  v_(storage_size_) {}
78 
82  virtual ~Dynamic_bitset() = default;
83 
84  /* getters */
85 
91  const storage_type& data() const { return v_; }
92 
98  idx size() const noexcept { return n_; }
99 
106  idx storage_size() const noexcept { return storage_size_; }
107 
113  idx count() const noexcept {
114  idx result = 0;
115  idx bitset_size = size();
116  for (idx i = 0; i < bitset_size; ++i) {
117  if (get(i))
118  ++result;
119  }
120 
121  return result;
122  }
123 
130  bool get(idx pos) const noexcept {
131  return 1 & (v_[index_(pos)] >> offset_(pos));
132  }
133 
139  bool none() const noexcept {
140  bool result = true;
141  idx bitset_storage_size = storage_size();
142  for (idx i = 0; i < bitset_storage_size; ++i) {
143  if (v_[i]) {
144  return false;
145  }
146  }
147 
148  return result;
149  }
150 
156  bool all() const noexcept {
157  bool result = true;
158  idx bitset_storage_size = storage_size();
159  for (idx i = 0; i < bitset_storage_size; ++i) {
160  if (~v_[i]) {
161  return false;
162  }
163  }
164 
165  return result;
166  }
167 
173  bool any() const noexcept { return !(none()); }
174 
175  /* setters */
183  Dynamic_bitset& set(idx pos, bool value = true) {
184  value ? v_[index_(pos)] |= (1 << offset_(pos))
185  : v_[index_(pos)] &= ~(1 << offset_(pos));
186 
187  // v_[index_(pos)] &= ~(!value << offset_(pos));
188 
189  return *this;
190  }
191 
197  Dynamic_bitset& set() noexcept {
198  idx bitset_storage_size = storage_size();
199  for (idx i = 0; i < bitset_storage_size; ++i) {
200  v_[i] = ~0;
201  }
202 
203  return *this;
204  }
205 
214  Dynamic_bitset& rand(idx pos, double p = 0.5) {
215  std::random_device rd;
216  std::mt19937 gen{rd()};
217  std::bernoulli_distribution d{p};
218 
219  set(pos, d(gen));
220 
221  return *this;
222  }
223 
230  Dynamic_bitset& rand(double p = 0.5) {
231  idx bitset_size = size();
232  for (idx i = 0; i < bitset_size; ++i) {
233  this->rand(i, p);
234  }
235 
236  return *this;
237  }
238 
245  Dynamic_bitset& reset(idx pos) {
246  v_[index_(pos)] &= ~(1 << offset_(pos));
247 
248  return *this;
249  }
250 
256  Dynamic_bitset& reset() noexcept {
257  idx bitset_storage_size = storage_size();
258  for (idx i = 0; i < bitset_storage_size; ++i) {
259  v_[i] = 0;
260  }
261 
262  return *this;
263  }
264 
271  Dynamic_bitset& flip(idx pos) {
272  v_[index_(pos)] ^= 1 << (offset_(pos));
273 
274  return *this;
275  }
276 
282  Dynamic_bitset& flip() noexcept {
283  idx bitset_storage_size = storage_size();
284  for (idx i = 0; i < bitset_storage_size; ++i) {
285  v_[i] = ~v_[i];
286  }
287 
288  return *this;
289  }
290 
291  /* operators */
298  bool operator==(const Dynamic_bitset& rhs) const noexcept {
299  assert(size() == rhs.size());
300  bool result = true;
301  idx n = std::min(storage_size(), rhs.storage_size());
302  for (idx i = 0; i < n; ++i) {
303  if (v_[i] != rhs.v_[i]) {
304  return false;
305  }
306  }
307 
308  return result;
309  }
310 
317  bool operator!=(const Dynamic_bitset& rhs) const noexcept {
318  return !(*this == rhs);
319  }
320 
328  idx operator-(const Dynamic_bitset& rhs) const noexcept {
329  idx result = 0;
330  idx bitset_size = size();
331  for (idx i = 0; i < bitset_size; ++i) {
332  if (get(i) != rhs.get(i))
333  ++result;
334  }
335 
336  return result;
337  }
338 
339  /* input/output */
350  template <class CharT = char, class Traits = std::char_traits<CharT>,
351  class Allocator = std::allocator<CharT>>
352  std::basic_string<CharT, Traits, Allocator>
353  to_string(CharT zero = CharT('0'), CharT one = CharT('1')) const {
354  std::basic_string<CharT, Traits, Allocator> result;
355  idx bitset_size = size();
356  result.resize(bitset_size);
357 
358  for (idx i = bitset_size; i-- > 0;) {
359  if (!get(i)) {
360  result[bitset_size - i - 1] = zero;
361  } else {
362  result[bitset_size - i - 1] = one;
363  }
364  }
365 
366  return result;
367  }
368 
369  protected:
379  std::ostream& display(std::ostream& os) const override {
380  idx bitset_size = size();
381  for (idx i = bitset_size; i-- > 0;) {
382  os << get(i);
383  }
384 
385  return os;
386  }
387 }; /* class Dynamic_bitset */
388 
393 class Bit_circuit : public Dynamic_bitset, public IJSON {
394  std::unordered_map<std::string, idx> count_{};
395  std::unordered_map<std::string, idx> depth_{};
396  Dynamic_bitset bNOT_, bCNOT_, bSWAP_, bTOF_, bFRED_,
397  btotal_;
398 
399  public:
405  explicit Bit_circuit(idx n)
406  : Dynamic_bitset{n}, bNOT_{n}, bCNOT_{n}, bSWAP_{n}, bTOF_{n},
407  bFRED_{n}, btotal_{n} {}
414  explicit Bit_circuit(const Dynamic_bitset& dynamic_bitset)
415  : Dynamic_bitset{dynamic_bitset}, bNOT_{size()}, bCNOT_{size()},
416  bSWAP_{size()}, bTOF_{size()}, bFRED_{size()}, btotal_{size()} {}
417 
425  Bit_circuit& X(idx i) {
426  NOT(i);
427 
428  return *this;
429  }
430 
434  virtual ~Bit_circuit() = default;
435 
443  Bit_circuit& NOT(idx i) {
444  flip(i);
445  ++count_["NOT"];
446  ++count_[__FILE__ "__total__"];
447 
448  // compute the depth
449  if (count_["NOT"] == 1)
450  depth_["NOT"] = 1;
451  // apply the gate
452  bNOT_.flip(i);
453  // check whether gates overlap
454  if (bNOT_.get(i) == false) {
455  // reset the b bitset
456  bNOT_ = Dynamic_bitset{n_};
457  // set to true the locations of the last gate
458  bNOT_.set(i);
459  ++depth_["NOT"];
460  }
461 
462  // compute the total depth
463  if (count_[__FILE__ "__total__"] == 1)
464  depth_[__FILE__ "__total__"] = 1;
465  // apply the gate
466  btotal_.flip(i);
467  // check whether gates overlap
468  if (btotal_.get(i) == false) {
469  // reset the b bitset
470  btotal_ = Dynamic_bitset{n_};
471  // set to true the locations of the last gate
472  btotal_.set(i);
473  ++depth_[__FILE__ "__total__"];
474  }
475 
476  return *this;
477  }
478 
486  Bit_circuit& CNOT(idx ctrl, idx target) {
487  v_[index_(target)] ^= (1 & (v_[index_(ctrl)] >> offset_(ctrl)))
488  << offset_(target);
489  ++count_["CNOT"];
490  ++count_[__FILE__ "__total__"];
491 
492  // compute the depth
493  if (count_["CNOT"] == 1)
494  depth_["CNOT"] = 1;
495  // apply the gate
496  bCNOT_.flip(ctrl).flip(target);
497  // check whether gates overlap
498  if (bCNOT_.get(ctrl) == false || bCNOT_.get(target) == false) {
499  // reset the b bitset
500  bCNOT_ = Dynamic_bitset{n_};
501  // set to true the locations of the last gate
502  bCNOT_.set(ctrl).set(target);
503  ++depth_["CNOT"];
504  }
505 
506  // compute the total depth
507  if (count_[__FILE__ "__total__"] == 1)
508  depth_[__FILE__ "__total__"] = 1;
509  // apply the gate
510  btotal_.flip(ctrl).flip(target);
511  // check whether gates overlap
512  if (btotal_.get(ctrl) == false || btotal_.get(target) == false) {
513  // reset the b bitset
514  btotal_ = Dynamic_bitset{n_};
515  // set to true the locations of the last gate
516  btotal_.set(ctrl).set(target);
517  ++depth_[__FILE__ "__total__"];
518  }
519 
520  return *this;
521  }
522 
531  Bit_circuit& TOF(idx i, idx j, idx k) {
532  v_[index_(k)] ^= ((1 & (v_[index_(j)] >> offset_(j))) &
533  (1 & (v_[index_(i)] >> offset_(i))))
534  << offset_(k);
535  ++count_["TOF"];
536  ++count_[__FILE__ "__total__"];
537 
538  // compute the depth
539  if (count_["TOF"] == 1)
540  depth_["TOF"] = 1;
541  // apply the gate
542  bTOF_.flip(i).flip(j).flip(k);
543  // check whether gates overlap
544  if (bTOF_.get(i) == false || bTOF_.get(j) == false ||
545  bTOF_.get(k) == false) {
546  // reset the b bitset
547  bTOF_ = Dynamic_bitset{n_};
548  // set to true the locations of the last gate
549  bTOF_.set(i).set(j).set(k);
550  ++depth_["TOF"];
551  }
552 
553  // compute the total depth
554  if (count_[__FILE__ "__total__"] == 1)
555  depth_[__FILE__ "__total__"] = 1;
556  // apply the gate
557  btotal_.flip(i).flip(j).flip(k);
558  // check whether gates overlap
559  if (btotal_.get(i) == false || btotal_.get(j) == false ||
560  btotal_.get(k) == false) {
561  // reset the b bitset
562  btotal_ = Dynamic_bitset{n_};
563  // set to true the locations of the last gate
564  btotal_.set(i).set(j).set(k);
565  ++depth_[__FILE__ "__total__"];
566  }
567 
568  return *this;
569  }
570 
578  Bit_circuit& SWAP(idx i, idx j) {
579  if (get(i) != get(j)) {
580  X(i);
581  X(j);
582  }
583  ++count_["SWAP"];
584  ++count_[__FILE__ "__total__"];
585 
586  // compute the depth
587  if (count_["SWAP"] == 1)
588  depth_["SWAP"] = 1;
589  // apply the gate
590  bSWAP_.flip(i).flip(j);
591  // check whether gates overlap
592  if (bSWAP_.get(i) == false || bSWAP_.get(j) == false) {
593  // reset the b bitset
594  bSWAP_ = Dynamic_bitset{n_};
595  // set to true the locations of the last gate
596  bSWAP_.set(i).set(j);
597  ++depth_["SWAP"];
598  }
599 
600  // compute the total depth
601  if (count_[__FILE__ "__total__"] == 1)
602  depth_[__FILE__ "__total__"] = 1;
603  // apply the gate
604  btotal_.flip(i).flip(j);
605  // check whether gates overlap
606  if (btotal_.get(i) == false || btotal_.get(j) == false) {
607  // reset the b bitset
608  btotal_ = Dynamic_bitset{n_};
609  // set to true the locations of the last gate
610  btotal_.set(i).set(j);
611  ++depth_[__FILE__ "__total__"];
612  }
613 
614  return *this;
615  }
616 
625  Bit_circuit& FRED(idx i, idx j, idx k) {
626  if (get(i)) {
627  SWAP(j, k);
628  }
629  ++count_["FRED"];
630  ++count_[__FILE__ "__total__"];
631 
632  // compute the depth
633  if (count_["FRED"] == 1)
634  depth_["FRED"] = 1;
635  // apply the gate
636  bFRED_.flip(i).flip(j).flip(k);
637  // check whether gates overlap
638  if (bFRED_.get(i) == false || bFRED_.get(j) == false ||
639  bFRED_.get(k) == false) {
640  // reset the b bitset
641  bFRED_ = Dynamic_bitset{n_};
642  // set to true the locations of the last gate
643  bFRED_.set(i).set(j).set(k);
644  ++depth_["FRED"];
645  }
646 
647  // compute the total depth
648  if (count_[__FILE__ "__total__"] == 1)
649  depth_[__FILE__ "__total__"] = 1;
650  // apply the gate
651  btotal_.flip(i).flip(j).flip(k);
652  // check whether gates overlap
653  if (btotal_.get(i) == false || btotal_.get(j) == false ||
654  btotal_.get(k) == false) {
655  // reset the b bitset
656  btotal_ = Dynamic_bitset{n_};
657  // set to true the locations of the last gate
658  btotal_.set(i).set(j).set(k);
659  ++depth_[__FILE__ "__total__"];
660  }
661 
662  return *this;
663  }
664 
670  Bit_circuit& reset() noexcept {
671  count_ = {};
672  depth_ = {};
673  Dynamic_bitset::reset();
674 
675  return *this;
676  }
677 
678  // getters
685  idx get_gate_count(const std::string& name) const {
686  idx result = 0;
687 
688  // EXCEPTION CHECKS
689 
690  try {
691  if (name == "X")
692  result = count_.at("NOT");
693  else
694  result = count_.at(name);
695  } catch (...) {
696  return 0;
697  }
698  // END EXCEPTION CHECKS
699 
700  return result;
701  }
702 
708  idx get_gate_count() const {
709  idx result = 0;
710 
711  // EXCEPTION CHECKS
712 
713  try {
714  result = count_.at(__FILE__ "__total__");
715  } catch (...) {
716  return 0;
717  }
718  // END EXCEPTION CHECKS
719 
720  return result;
721  }
722 
729  idx get_gate_depth(const std::string& name) const {
730  idx result = 0;
731 
732  // EXCEPTION CHECKS
733 
734  try {
735  if (name == "X")
736  result = depth_.at("NOT");
737  else
738  result = depth_.at(name);
739  } catch (...) {
740  return 0;
741  }
742  // END EXCEPTION CHECKS
743 
744  return result;
745  }
746 
752  idx get_gate_depth() const {
753  idx result = 0;
754 
755  // EXCEPTION CHECKS
756 
757  try {
758  result = depth_.at(__FILE__ "__total__");
759  } catch (...) {
760  return 0;
761  }
762  // END EXCEPTION CHECKS
763 
764  return result;
765  }
766  // end getters
767 
778  std::string to_JSON(bool enclosed_in_curly_brackets = true) const override {
779  std::string result;
780 
781  if (enclosed_in_curly_brackets)
782  result += "{";
783 
784  result += "\"n\" : " + std::to_string(n_);
785  result +=
786  ", \"total gate count\" : " + std::to_string(get_gate_count());
787  result +=
788  ", \"total gate depth\" : " + std::to_string(get_gate_depth());
789  result += ", \"bit state\" : \"" + this->to_string() + '\"';
790  result += ", \"Hamming weight\" : " + std::to_string(count());
791 
792  if (enclosed_in_curly_brackets)
793  result += "}";
794 
795  return result;
796  }
797 
798  private:
807  std::ostream& display(std::ostream& os) const override {
808  os << "n = " << n_ << '\n';
809  os << "total gate count: " << get_gate_count() << '\n';
810  os << "total gate depth: " << get_gate_depth() << '\n';
811  os << "bit state: " << this->to_string() << '\n';
812  os << "Hamming weight: " << count();
813 
814  return os;
815  }
816 
817 }; /* class Bit_circuit */
818 
819 } /* namespace qpp */
820 
821 #endif /* CLASSES_REVERSIBLE_H */
Quantum++ main namespace.
Definition: circuits.h:35