00001 // QuCoSi - Quantum Computer Simulation 00002 // Copyright © 2009 Frank S. Thomas <frank@blue-dwarf.de> 00003 // 00004 // This program is free software: you can redistribute it and/or modify 00005 // it under the terms of the GNU General Public License as published by 00006 // the Free Software Foundation, either version 3 of the License, or 00007 // (at your option) any later version. 00008 // 00009 // This program is distributed in the hope that it will be useful, 00010 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00012 // GNU General Public License for more details. 00013 // 00014 // You should have received a copy of the GNU General Public License 00015 // along with this program. If not, see <http://www.gnu.org/licenses/>. 00016 00017 #ifndef QUCOSI_GATE_H 00018 #define QUCOSI_GATE_H 00019 00020 #include <bitset> 00021 #include <cassert> 00022 #include <cmath> 00023 #include <limits> 00024 #include <vector> 00025 00026 #include "Aux" 00027 #include "Vector" 00028 00029 namespace QuCoSi { 00030 00031 /** \class Gate 00032 * 00033 * \brief Single- and multi-qubit gate of varying complexity 00034 * 00035 * The Gate class is the base for all quantum circuits in QuCoSi. It 00036 * provides common quantum gates for one to three qubits, more complex gates 00037 * for an arbitrary number of qubits, and methods to combine or extend them. 00038 * Since gates are represented by unitary matrices, this class is derived 00039 * of Eigen's dynamic size matrix class that uses complex numbers. Therefore 00040 * composition of multiple gates is easily accomplished by simple matrix 00041 * multiplication of single gates. 00042 */ 00043 class Gate : public MatrixXc 00044 { 00045 public: 00046 /** \brief Constructs the 2 × 2 zero matrix 00047 */ 00048 inline Gate() : MatrixXc(2,2) {} 00049 00050 /** \brief Constructs the \p rows × \p cols zero matrix 00051 * 00052 * \param rows the number of rows of this gate 00053 * \param cols the number of columns of this gate 00054 */ 00055 inline Gate(const int rows, const int cols) : MatrixXc(rows,cols) {} 00056 00057 inline Gate& operator=(const MatrixXc& m) 00058 { 00059 MatrixXc::operator=(m); 00060 return *this; 00061 } 00062 00063 /** \brief Computes the tensor product of this gate with \p m 00064 * 00065 * The tensor product \f$\mathbf{A} \otimes \mathbf{B}\f$ of two gates 00066 * \f$\mathbf{A} \in K^{m \times n}\f$ and \f$\mathbf{B} \in K^{p \times 00067 * q}\f$ is defined as: 00068 * \f[ 00069 * \mathbf{A}\, \otimes\, \mathbf{B} = 00070 * \left(\begin{array}{ccc} 00071 * a_{11} \mathbf{B} & \cdots & a_{1n} \mathbf{B} \\ 00072 * \vdots & \ddots & \vdots \\ 00073 * a_{m1} \mathbf{B} & \cdots & a_{mn} \mathbf{B} 00074 * \end{array}\right) 00075 * = 00076 * \left(\begin{array}{cccccccccc} 00077 * a_{11} b_{11} & \cdots & a_{11} b_{1q} & \cdots & \cdots & 00078 * a_{1n} b_{11} & \cdots & a_{1n} b_{1q} \\ 00079 * \vdots & \ddots & \vdots & & & \vdots & \ddots & \vdots \\ 00080 * a_{11} b_{p1} & \cdots & a_{11} b_{pq} & \cdots & \cdots & 00081 * a_{1n} b_{p1} & \cdots & a_{1n} b_{pq} \\ 00082 * \vdots & & \vdots & \ddots & & \vdots & & \vdots \\ 00083 * \vdots & & \vdots & & \ddots & \vdots & & \vdots \\ 00084 * a_{m1} b_{11} & \cdots & a_{m1} b_{1q} & \cdots & \cdots & 00085 * a_{mn} b_{11} & \cdots & a_{mn} b_{1q} \\ 00086 * \vdots & \ddots & \vdots & & & \vdots & \ddots & \vdots \\ 00087 * a_{m1} b_{p1} & \cdots & a_{m1} b_{pq} & \cdots & \cdots & 00088 * a_{mn} b_{p1} & \cdots & a_{mn} b_{pq} 00089 * \end{array}\right) \in K^{mp \times nq} \ . 00090 * \f] 00091 * 00092 * \param m the right hand side operand of the tensor product 00093 * \return the tensor product of this gate with Gate \p m 00094 * \sa http://en.wikipedia.org/wiki/Kronecker_product 00095 */ 00096 inline Gate tensorDot(const Gate& m) const 00097 { 00098 const int r1 = rows(); 00099 const int c1 = cols(); 00100 const int r2 = m.rows(); 00101 const int c2 = m.cols(); 00102 Gate x(r1*r2, c1*c2); 00103 00104 for (int c = 0; c < c1; ++c) { 00105 for (int r = 0; r < r1; ++r) { 00106 x.block(r*r2, c*c2, r2, c2) = (*this)(r,c)*m; 00107 } 00108 } 00109 return x; 00110 } 00111 00112 /** \brief Sets the tensor product of this gate and \p m as this gate 00113 * 00114 * \param m the right hand side operand of the tensor product 00115 * \return a reference to \c *this 00116 * \sa tensorDot() 00117 */ 00118 inline Gate& tensorDotSet(const Gate& m) 00119 { 00120 *this = tensorDot(m); 00121 return *this; 00122 } 00123 00124 /** \brief Computes the <tt>n</tt>th tensor power of this gate 00125 * 00126 * The <tt>n</tt>th tensor power of a gate \f$\mathbf{A}\f$ is the 00127 * <tt>n</tt>-fold tensor product of \f$\mathbf{A}\f$ with itself: 00128 * \f[ 00129 * \mathbf{A}^{\otimes n} = \underbrace{\mathbf{A} \otimes \ldots 00130 * \otimes \mathbf{A}}_n \ . 00131 * \f] 00132 * 00133 * \param n the exponent of the tensor power 00134 * \return this gate raised to the <tt>n</tt>th power 00135 * \sa tensorDot() 00136 */ 00137 inline Gate tensorPow(const int n) const 00138 { 00139 Gate x = *this; 00140 for (int i = 1; i < n; ++i) { 00141 x.tensorDotSet(*this); 00142 } 00143 return x; 00144 } 00145 00146 /** \brief Sets the <tt>n</tt>th tensor power of this gate as this gate 00147 * 00148 * \param n the exponent of the tensor power 00149 * \return a reference to \c *this 00150 * \sa tensorPow() 00151 */ 00152 inline Gate& tensorPowSet(const int n) 00153 { 00154 *this = tensorPow(n); 00155 return *this; 00156 } 00157 00158 /** \brief Extends this gate to a <tt>n</tt>-qubits gate 00159 * 00160 * This method constructs a gate that acts on \p n qubits so that the 00161 * original gate acts on the qubit(s) at position \p j and all other 00162 * qubits are left unchanged. This is accomplished by tensor 00163 * multiplication of an appropriate number of identity gates from the 00164 * left and the right to the original gate. 00165 * 00166 * \param j the position of the qubit(s) the original gate acts on 00167 * \param n the number of qubits the returned gate acts on 00168 * \return the for \p n qubits extended gate 00169 * \sa tensorDot() 00170 */ 00171 inline Gate applyTo(const int j, const int n) const 00172 { 00173 const int k = n-j-log2(rows()); 00174 Gate id, x = *this; 00175 00176 if (j > 0) { 00177 id.resize(std::pow(2,j), std::pow(2,j)); 00178 id.setIdentity(); 00179 x = id.tensorDot(x); 00180 } 00181 if (j >= 0 && k > 0) { 00182 id.resize(std::pow(2,k), std::pow(2,k)); 00183 id.setIdentity(); 00184 x.tensorDotSet(id); 00185 } 00186 return x; 00187 } 00188 00189 /** \brief Sets the to <tt>n</tt>-qubits extended gate as this gate 00190 * 00191 * \param j the position of the qubit(s) the original gate acts on 00192 * \param n the number of qubits the extended gate acts on 00193 * \return a reference to \c *this 00194 * \sa applyTo() 00195 */ 00196 inline Gate& applyToSet(const int j, const int n) 00197 { 00198 *this = applyTo(j,n); 00199 return *this; 00200 } 00201 00202 /** \brief \b X gate (\f$\sigma_1\f$ Pauli matrix, NOT gate) 00203 * 00204 * \f[\mathbf{X} = 00205 * \left(\begin{array}{cc} 00206 * 0 & 1\\ 00207 * 1 & 0 00208 * \end{array}\right) 00209 * \f] 00210 * 00211 * \return a reference to \c *this 00212 */ 00213 inline Gate& X() 00214 { 00215 resize(2,2); 00216 *this << 0, 1, 00217 1, 0; 00218 return *this; 00219 } 00220 00221 /** \brief \b Y gate (\f$\sigma_2\f$ Pauli matrix) 00222 * 00223 * \f[\mathbf{Y} = 00224 * \left(\begin{array}{cc} 00225 * 0 & -i\\ 00226 * i & 0 00227 * \end{array}\right) 00228 * \f] 00229 * 00230 * \return a reference to \c *this 00231 */ 00232 inline Gate& Y() 00233 { 00234 resize(2,2); 00235 *this << 0, field(0,-1), 00236 field(0,1), 0; 00237 return *this; 00238 } 00239 00240 /** \brief \b Z gate (\f$\sigma_3\f$ Pauli matrix) 00241 * 00242 * \f[\mathbf{Z} = 00243 * \left(\begin{array}{cc} 00244 * 1 & 0\\ 00245 * 0 & -1 00246 * \end{array}\right) 00247 * = \mathbf{R}(2) 00248 * \f] 00249 * 00250 * \return a reference to \c *this 00251 * \sa R() 00252 */ 00253 inline Gate& Z() 00254 { 00255 resize(2,2); 00256 *this << 1, 0, 00257 0, -1; 00258 return *this; 00259 } 00260 00261 /** \brief \b H gate (Hadamard gate) 00262 * 00263 * \f[\mathbf{H} = \frac{1}{\sqrt{2}} 00264 * \left(\begin{array}{cc} 00265 * 1 & 1\\ 00266 * 1 & -1 00267 * \end{array}\right) 00268 * = \frac{1}{\sqrt{2}} (\mathbf{X} + \mathbf{Z}) 00269 * \f] 00270 * 00271 * \return a reference to \c *this 00272 */ 00273 inline Gate& H() 00274 { 00275 resize(2,2); 00276 *this << c_sqrt1_2, c_sqrt1_2, 00277 c_sqrt1_2, -c_sqrt1_2; 00278 return *this; 00279 } 00280 00281 /** \brief \b P gate (phase shift gate) 00282 * 00283 * \f[\mathbf{P} = 00284 * \left(\begin{array}{cc} 00285 * 1 & 0\\ 00286 * 0 & i 00287 * \end{array}\right) 00288 * = \mathbf{R}(4) 00289 * \f] 00290 * 00291 * \return a reference to \c *this 00292 * \sa R() 00293 */ 00294 inline Gate& P() 00295 { 00296 resize(2,2); 00297 *this << 1, 0, 00298 0, field(0,1); 00299 return *this; 00300 } 00301 00302 /** \brief \b T gate (\f$\pi/4\f$ phase shift gate) 00303 * 00304 * \f[\mathbf{T} = 00305 * \left(\begin{array}{cc} 00306 * 1 & 0\\ 00307 * 0 & e^{\pi i/4} 00308 * \end{array}\right) 00309 * = \mathbf{R}(8) 00310 * \f] 00311 * 00312 * \return a reference to \c *this 00313 * \sa R() 00314 */ 00315 inline Gate& T() 00316 { 00317 resize(2,2); 00318 *this << 1, 0, 00319 0, field(c_sqrt1_2,c_sqrt1_2); 00320 return *this; 00321 } 00322 00323 /** \brief <b>R</b>(\p k) gate (general phase shift gate) 00324 * 00325 * \f[\mathbf{R}(k) = 00326 * \left(\begin{array}{cc} 00327 * 1 & 0\\ 00328 * 0 & e^{2 \pi i/k} 00329 * \end{array}\right) 00330 * \f] 00331 * 00332 * \param k the phase shift of this gate 00333 * \return a reference to \c *this 00334 */ 00335 inline Gate& R(const fptype k) 00336 { 00337 resize(2,2); 00338 *this << 1, 0, 00339 0, std::exp(2*c_pi/k*field(0,1)); 00340 return *this; 00341 } 00342 00343 /** \brief \b I gate (identity gate) 00344 * 00345 * \f[\mathbf{I} = 00346 * \left(\begin{array}{cc} 00347 * 1 & 0\\ 00348 * 0 & 1 00349 * \end{array}\right) 00350 * = \mathbf{R}(1) 00351 * \f] 00352 * 00353 * \return a reference to \c *this 00354 */ 00355 inline Gate& I() 00356 { 00357 resize(2,2); 00358 setIdentity(); 00359 return *this; 00360 } 00361 00362 /** \brief <b>R</b><sub>x</sub>(\p theta) gate 00363 * 00364 * Rotates a qubit about the x-axis on the Bloch sphere by the angle 00365 * \p theta. 00366 * 00367 * \f[\mathbf{R_x}(\theta) = 00368 * \exp \left( -i \frac{\theta}{2} \mathbf{X} \right) = 00369 * \cos \frac{\theta}{2} \mathbf{I} - i \sin \frac{\theta}{2} 00370 * \mathbf{X} = 00371 * \left(\begin{array}{cc} 00372 * \cos \frac{\theta}{2} & -i \sin \frac{\theta}{2} \\ 00373 * -i \sin \frac{\theta}{2} & \cos \frac{\theta}{2} 00374 * \end{array}\right) 00375 * \f] 00376 * 00377 * \param theta the angle of rotation about the x-axis 00378 * \return a reference to \c *this 00379 */ 00380 inline Gate& Rx(const fptype theta) 00381 { 00382 resize(2,2); 00383 *this << std::cos(theta/2.), std::sin(theta/2.)*field(0,-1), 00384 std::sin(theta/2.)*field(0,-1), std::cos(theta/2.); 00385 return *this; 00386 } 00387 00388 /** \brief <b>R</b><sub>y</sub>(\p theta) gate 00389 * 00390 * Rotates a qubit about the y-axis on the Bloch sphere by the angle 00391 * \p theta. 00392 * 00393 * \f[\mathbf{R_y}(\theta) = 00394 * \exp \left( -i \frac{\theta}{2} \mathbf{Y} \right) = 00395 * \cos \frac{\theta}{2} \mathbf{I} - i \sin \frac{\theta}{2} 00396 * \mathbf{Y} = 00397 * \left(\begin{array}{cc} 00398 * \cos \frac{\theta}{2} & -\sin \frac{\theta}{2} \\ 00399 * \sin \frac{\theta}{2} & \cos \frac{\theta}{2} 00400 * \end{array}\right) 00401 * \f] 00402 * 00403 * \param theta the angle of rotation about the y-axis 00404 * \return a reference to \c *this 00405 */ 00406 inline Gate& Ry(const fptype theta) 00407 { 00408 resize(2,2); 00409 *this << std::cos(theta/2.), -std::sin(theta/2.), 00410 std::sin(theta/2.), std::cos(theta/2.); 00411 return *this; 00412 } 00413 00414 /** \brief <b>R</b><sub>z</sub>(\p theta) gate 00415 * 00416 * Rotates a qubit about the z-axis on the Bloch sphere by the angle 00417 * \p theta. 00418 * 00419 * \f[\mathbf{R_z}(\theta) = 00420 * \exp \left( -i \frac{\theta}{2} \mathbf{Z} \right) = 00421 * \cos \frac{\theta}{2} \mathbf{I} - i \sin \frac{\theta}{2} 00422 * \mathbf{Z} = 00423 * \left(\begin{array}{cc} 00424 * \exp \left(-i \frac{\theta}{2} \right) & 0 \\ 00425 * 0 & \exp \left(i \frac{\theta}{2} \right) 00426 * \end{array}\right) 00427 * \f] 00428 * 00429 * \param theta the angle of rotation about the z-axis 00430 * \return a reference to \c *this 00431 */ 00432 inline Gate& Rz(const fptype theta) 00433 { 00434 resize(2,2); 00435 *this << std::exp(theta/2.*field(0,-1)), 0, 00436 0, std::exp(theta/2.*field(0,1)); 00437 return *this; 00438 } 00439 00440 /** \brief \b CNOT gate (controlled NOT gate) 00441 * 00442 * \f[\mathbf{CNOT} = 00443 * \left(\begin{array}{cccc} 00444 * 1 & 0 & 0 & 0\\ 00445 * 0 & 1 & 0 & 0\\ 00446 * 0 & 0 & 0 & 1\\ 00447 * 0 & 0 & 1 & 0 00448 * \end{array}\right) 00449 * = \mathbf{C}_{102}(\mathbf{X}) 00450 * \f] 00451 * 00452 * \return a reference to \c *this 00453 * \sa C(), X() 00454 */ 00455 inline Gate& CNOT() 00456 { 00457 resize(4,4); 00458 *this << 1, 0, 0, 0, 00459 0, 1, 0, 0, 00460 0, 0, 0, 1, 00461 0, 0, 1, 0; 00462 return *this; 00463 } 00464 00465 /** \brief \b CCNOT gate (Toffoli gate, controlled \b CNOT gate) 00466 * 00467 * \f[\mathbf{CCNOT} = 00468 * \left(\begin{array}{cccccccc} 00469 * 1 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\ 00470 * 0 & 1 & 0 & 0 & 0 & 0 & 0 & 0\\ 00471 * 0 & 0 & 1 & 0 & 0 & 0 & 0 & 0\\ 00472 * 0 & 0 & 0 & 1 & 0 & 0 & 0 & 0\\ 00473 * 0 & 0 & 0 & 0 & 1 & 0 & 0 & 0\\ 00474 * 0 & 0 & 0 & 0 & 0 & 1 & 0 & 0\\ 00475 * 0 & 0 & 0 & 0 & 0 & 0 & 0 & 1\\ 00476 * 0 & 0 & 0 & 0 & 0 & 0 & 1 & 0 00477 * \end{array}\right) 00478 * = \mathbf{C}_{103}(\mathbf{CNOT}) 00479 * \f] 00480 * 00481 * \return a reference to \c *this 00482 * \sa C(), CNOT() 00483 */ 00484 inline Gate& CCNOT() 00485 { 00486 resize(8,8); 00487 setIdentity(); 00488 (*this)(6,6) = 0; 00489 (*this)(7,7) = 0; 00490 (*this)(7,6) = 1; 00491 (*this)(6,7) = 1; 00492 return *this; 00493 } 00494 00495 /** \brief \b CSWAP gate (Fredkin gate, controlled \b SWAP gate) 00496 * 00497 * \f[\mathbf{CSWAP} = 00498 * \left(\begin{array}{cccccccc} 00499 * 1 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\ 00500 * 0 & 1 & 0 & 0 & 0 & 0 & 0 & 0\\ 00501 * 0 & 0 & 1 & 0 & 0 & 0 & 0 & 0\\ 00502 * 0 & 0 & 0 & 1 & 0 & 0 & 0 & 0\\ 00503 * 0 & 0 & 0 & 0 & 1 & 0 & 0 & 0\\ 00504 * 0 & 0 & 0 & 0 & 0 & 0 & 1 & 0\\ 00505 * 0 & 0 & 0 & 0 & 0 & 1 & 0 & 0\\ 00506 * 0 & 0 & 0 & 0 & 0 & 0 & 0 & 1 00507 * \end{array}\right) 00508 * = \mathbf{C}_{103}(\mathbf{SWAP}) 00509 * \f] 00510 * 00511 * \return a reference to \c *this 00512 * \sa C(), SWAP() 00513 */ 00514 inline Gate& CSWAP() 00515 { 00516 resize(8,8); 00517 setIdentity(); 00518 (*this)(5,5) = 0; 00519 (*this)(6,6) = 0; 00520 (*this)(6,5) = 1; 00521 (*this)(5,6) = 1; 00522 return *this; 00523 } 00524 00525 /** \brief <b>C</b><sub>\p tcn</sub>(\p U) gate (controlled \p U gate) 00526 * 00527 * This method constructs a gate that acts on \p n qubits so that the 00528 * gate \p U is applied to the qubit(s) at position \p t (the target 00529 * qubit(s)) if the qubit at position \p c (the control qubit) is 1. 00530 * 00531 * \param t the position of the target qubit(s) 00532 * \param c the position of the control qubit 00533 * \param n the number of qubits this gate acts on 00534 * \param U the gate that acts on the target qubit and that is 00535 * controlled by the control qubit 00536 * \return a reference to \c *this 00537 */ 00538 inline Gate& C(const int t, const int c, const int n, const Gate& U) 00539 { 00540 assert(t < n || c < n || t == c); 00541 00542 // Construct the controlled U gate with the first qubit as control and 00543 // the second qubit as target. 00544 const int d = U.rows(); 00545 Gate cu(2*d,2*d); 00546 cu.setIdentity(); 00547 cu.block(d,d,d,d) = U; 00548 00549 // Extend the controlled U gate for n qubits if it does not already 00550 // operate on n qubits. 00551 if (std::pow(2,n) > 2*d) { 00552 cu = cu.applyTo(0,n); 00553 } 00554 00555 // Construct a cyclic permutation that displaces t to position 1. 00556 std::vector<int> sigma(n); 00557 for (int i = 0; i < n; ++i) { 00558 sigma[(n-t+i+1)%n] = i; 00559 } 00560 // Permute c to position 0. 00561 const int newc = (n-t+c+1)%n, tmp = sigma[0]; 00562 sigma[0] = sigma[newc]; 00563 sigma[newc] = tmp; 00564 00565 // Construct an S gate from the permutation sigma. 00566 Gate s; 00567 s.S(sigma); 00568 00569 // Now combine the S and the controlled U gate. 00570 *this = s.transpose()*cu*s; 00571 return *this; 00572 } 00573 00574 /** \brief \b SWAP gate 00575 * 00576 * \f[\mathbf{SWAP} = 00577 * \left(\begin{array}{cccc} 00578 * 1 & 0 & 0 & 0\\ 00579 * 0 & 0 & 1 & 0\\ 00580 * 0 & 1 & 0 & 0\\ 00581 * 0 & 0 & 0 & 1 00582 * \end{array}\right) 00583 * = \mathbf{S}_{102} = \mathbf{S}_{012} 00584 * \f] 00585 * 00586 * \return a reference to \c *this 00587 * \sa S() 00588 */ 00589 inline Gate& SWAP() 00590 { 00591 resize(4,4); 00592 *this << 1, 0, 0, 0, 00593 0, 0, 1, 0, 00594 0, 1, 0, 0, 00595 0, 0, 0, 1; 00596 return *this; 00597 } 00598 00599 /** \brief <b>S</b><sub>\p pqn</sub> gate 00600 * 00601 * This method constructs a \f$2^n \times 2^n\f$ tensor permutation 00602 * matrix that permutes the <tt>p</tt>th and <tt>q</tt>th qubits in a 00603 * tensor product of \p n qubits. 00604 * 00605 * \param p the new position of the <tt>q</tt>th qubit 00606 * \param q the new position of the <tt>p</tt>th qubit 00607 * \param n the number of qubits this gate acts on 00608 * \return a reference to \c *this 00609 */ 00610 inline Gate& S(const int p, const int q, const int n) 00611 { 00612 std::vector<int> sigma(n); 00613 for (int i = 0; i < n; ++i) { 00614 sigma[i] = i; 00615 } 00616 sigma[p] = q; 00617 sigma[q] = p; 00618 S(sigma); 00619 return *this; 00620 } 00621 00622 /** \brief <b>S</b>(\p sigma) gate 00623 * 00624 * This method constructs a tensor permutation matrix that permutes 00625 * qubits according to the permutation \p sigma. For example, this 00626 * matrix acts on the tensor product of the qubits \f$ q_0,\ q_1,\ 00627 * \ldots,\ q_k\f$ as follows: 00628 * \f[ 00629 * \mathbf{S}(\sigma) \left(q_0 \otimes q_1 \otimes \ldots \otimes 00630 * q_k\right) = q_{\sigma(0)} \otimes q_{\sigma(1)} \otimes \ldots 00631 * \otimes q_{\sigma(k)} \ . 00632 * \f] 00633 * 00634 * \note The implementation of this method is based on proposition 6.2 00635 * in the paper arXiv:math/0508053v2 by Rakotonirina Christian. It takes 00636 * advantage of the fact that the dimension of single qubits is 2 so 00637 * that the multiple row and column indices \f$i_1 \ldots i_k\f$ and 00638 * \f$j_1 \ldots j_k\f$ can be obtained from the row and column indices 00639 * of the permutation matrix with <tt>std::bitset</tt>s. 00640 * 00641 * \param sigma the permutation that will be applied to qubits 00642 * \return a reference to \c *this 00643 * \sa http://arxiv.org/abs/math/0508053 00644 */ 00645 inline Gate& S(const std::vector<int>& sigma) 00646 { 00647 const int n = sigma.size(); 00648 const int dim = std::pow(2,n); 00649 resize(dim,dim); 00650 setZero(); 00651 00652 // The first and last component of any tensor stays unchanged for all 00653 // tensor permutations, therefore we ignore the outermost rows and 00654 // columns. 00655 (*this)(0,0) = 1; 00656 (*this)(dim-1,dim-1) = 1; 00657 00658 for (int c = 1; c < dim-1; ++c) { 00659 for (int r = 1; r < dim-1; ++r) { 00660 // Create bitsets of the row and column index. Since the dimension 00661 // of single qubits is 2, these bitsets can be interpreted as the 00662 // multiple row and colum indices. 00663 std::bitset< std::numeric_limits<unsigned long>::digits > 00664 br(r), bc(c), bcp(0); 00665 00666 // Permute the bitset bc using the permutation sigma. 00667 for (int i = 0; i < n; ++i) { 00668 bcp[n-1-i] = bc[n-1-sigma[i]]; 00669 } 00670 00671 // If the bitset of the row index is equal to the permuted bitset of 00672 // the column index, set a 1 at this position. This equals the 00673 // multiple Kronecker deltas. 00674 if (br == bcp) { 00675 (*this)(r,c) = 1; 00676 break; 00677 } 00678 } 00679 } 00680 return *this; 00681 } 00682 00683 /** \brief <b>U</b><sub>f</sub> gate for one output qubit 00684 * 00685 * This method constructs a gate that can be associated with the function 00686 * \f$f:\,\{0,1,2,\ldots,2^n-1\} \rightarrow \{0,1\}\f$. It is defined so 00687 * that it acts on a tensor product of n + 1 qubits as 00688 * \f[ 00689 * \mathbf{U}_f |x\rangle_n |y\rangle_1 = 00690 * |x\rangle_n |y \oplus f(x)\rangle_1 \ , 00691 * \f] 00692 * where \f$\oplus\f$ denotes binary addition. 00693 * 00694 * \param f the function associated with this gate 00695 * \return a reference to \c *this 00696 */ 00697 inline Gate& U(const std::vector<int>& f) 00698 { 00699 const int s = f.size(); 00700 resize(2*s,2*s); 00701 setIdentity(); 00702 00703 for (int i = 0; i < s; ++i) { 00704 if (f.at(i) == 1) { 00705 (*this)(2*i,2*i) = 0; 00706 (*this)(2*i+1,2*i+1) = 0; 00707 (*this)(2*i+1,2*i) = 1; 00708 (*this)(2*i,2*i+1) = 1; 00709 } 00710 } 00711 return *this; 00712 } 00713 00714 /** \brief <b>U</b><sub>f</sub> gate for multiple output qubits 00715 * 00716 * This method constructs a gate that can be associated with the function 00717 * \f$f:\,\{0,1,2,\ldots,2^n-1\} \rightarrow \{0,1,2,\ldots,2^m-1\}\f$. 00718 * It is defined so that it acts on a tensor product of n + \p m qubits 00719 * as 00720 * \f[ 00721 * \mathbf{U}_f |x\rangle_n |y\rangle_m = 00722 * |x\rangle_n |y \oplus f(x)\rangle_m \ , 00723 * \f] 00724 * where \f$\oplus\f$ denotes bitwise binary addition. 00725 * 00726 * \param f the function associated with this gate 00727 * \param m the number of output qubits 00728 * \return a reference to \c *this 00729 */ 00730 inline Gate& U(const std::vector<int>& f, const int m) 00731 { 00732 const int sx = f.size(); 00733 const int sy = std::pow(2,m); 00734 resize(sx*sy,sx*sy); 00735 setZero(); 00736 00737 for (int i = 0, j = 0; i < sx; ++i) { 00738 for (int k = 0; k < sy; ++j, ++k) { 00739 (*this)(j-k+(k^f.at(i)),j) = 1; 00740 } 00741 } 00742 return *this; 00743 } 00744 00745 /** \brief <b>F</b><sub>\p n</sub> gate 00746 * (quantum Fourier transform (QFT) gate) 00747 * 00748 * This method constructs the quantum Fourier transform gate that acts on 00749 * \p n qubits. It is defined as: 00750 * \f[\mathbf{F}_n = 2^{-n/2} 00751 * \left(\begin{array}{cccccc} 00752 * 1 & 1 & 1 & 1 & \cdots & 1 \\ 00753 * 1 & \omega^1 & \omega^2 & \omega^3 & \cdots & \omega^{2^n-1}\\ 00754 * 1 & \omega^2 & \omega^4 & \omega^6 & \cdots & \omega^{2(2^n-1)}\\ 00755 * 1 & \omega^3 & \omega^6 & \omega^9 & \cdots & \omega^{3(2^n-1)}\\ 00756 * \vdots & \vdots & \vdots & \vdots & \ddots & \vdots \\ 00757 * 1 & \omega^{2^n-1} & \omega^{2(2^n-1)} & \omega^{3(2^n-1)} & 00758 * \cdots & \omega^{(2^n-1)^2} 00759 * \end{array}\right), 00760 * \quad \omega = \exp\left(2 \pi i/2^n\right) \ . 00761 * \f] 00762 * 00763 * \param n the number of qubits this gate acts on 00764 * \return a reference to \c *this 00765 */ 00766 inline Gate& F(const int n) 00767 { 00768 const int s = std::pow(2,n); 00769 resize(s,s); 00770 for (int x = 0; x < s; ++x) { 00771 for (int y = x; y < s; ++y) { 00772 (*this)(x,y) = std::exp(2*c_pi*x*y/s*field(0,1)); 00773 } 00774 } 00775 *this *= std::sqrt(1./s); 00776 00777 MatrixXc t = transpose(); 00778 t.diagonal().setZero(); 00779 *this += t; 00780 00781 return *this; 00782 } 00783 }; 00784 00785 } // namespace QuCoSi 00786 00787 #endif // QUCOSI_GATE_H 00788 00789 // vim: filetype=cpp shiftwidth=2 textwidth=78