123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213 |
- """An implementation of qubits and gates acting on them.
- Todo:
- * Update docstrings.
- * Update tests.
- * Implement apply using decompose.
- * Implement represent using decompose or something smarter. For this to
- work we first have to implement represent for SWAP.
- * Decide if we want upper index to be inclusive in the constructor.
- * Fix the printing of Rk gates in plotting.
- """
- from sympy.core.expr import Expr
- from sympy.core.numbers import (I, Integer, pi)
- from sympy.core.symbol import Symbol
- from sympy.functions.elementary.exponential import exp
- from sympy.matrices.dense import Matrix
- from sympy.functions import sqrt
- from sympy.physics.quantum.qapply import qapply
- from sympy.physics.quantum.qexpr import QuantumError, QExpr
- from sympy.matrices import eye
- from sympy.physics.quantum.tensorproduct import matrix_tensor_product
- from sympy.physics.quantum.gate import (
- Gate, HadamardGate, SwapGate, OneQubitGate, CGate, PhaseGate, TGate, ZGate
- )
- __all__ = [
- 'QFT',
- 'IQFT',
- 'RkGate',
- 'Rk'
- ]
- #-----------------------------------------------------------------------------
- # Fourier stuff
- #-----------------------------------------------------------------------------
- class RkGate(OneQubitGate):
- """This is the R_k gate of the QTF."""
- gate_name = 'Rk'
- gate_name_latex = 'R'
- def __new__(cls, *args):
- if len(args) != 2:
- raise QuantumError(
- 'Rk gates only take two arguments, got: %r' % args
- )
- # For small k, Rk gates simplify to other gates, using these
- # substitutions give us familiar results for the QFT for small numbers
- # of qubits.
- target = args[0]
- k = args[1]
- if k == 1:
- return ZGate(target)
- elif k == 2:
- return PhaseGate(target)
- elif k == 3:
- return TGate(target)
- args = cls._eval_args(args)
- inst = Expr.__new__(cls, *args)
- inst.hilbert_space = cls._eval_hilbert_space(args)
- return inst
- @classmethod
- def _eval_args(cls, args):
- # Fall back to this, because Gate._eval_args assumes that args is
- # all targets and can't contain duplicates.
- return QExpr._eval_args(args)
- @property
- def k(self):
- return self.label[1]
- @property
- def targets(self):
- return self.label[:1]
- @property
- def gate_name_plot(self):
- return r'$%s_%s$' % (self.gate_name_latex, str(self.k))
- def get_target_matrix(self, format='sympy'):
- if format == 'sympy':
- return Matrix([[1, 0], [0, exp(Integer(2)*pi*I/(Integer(2)**self.k))]])
- raise NotImplementedError(
- 'Invalid format for the R_k gate: %r' % format)
- Rk = RkGate
- class Fourier(Gate):
- """Superclass of Quantum Fourier and Inverse Quantum Fourier Gates."""
- @classmethod
- def _eval_args(self, args):
- if len(args) != 2:
- raise QuantumError(
- 'QFT/IQFT only takes two arguments, got: %r' % args
- )
- if args[0] >= args[1]:
- raise QuantumError("Start must be smaller than finish")
- return Gate._eval_args(args)
- def _represent_default_basis(self, **options):
- return self._represent_ZGate(None, **options)
- def _represent_ZGate(self, basis, **options):
- """
- Represents the (I)QFT In the Z Basis
- """
- nqubits = options.get('nqubits', 0)
- if nqubits == 0:
- raise QuantumError(
- 'The number of qubits must be given as nqubits.')
- if nqubits < self.min_qubits:
- raise QuantumError(
- 'The number of qubits %r is too small for the gate.' % nqubits
- )
- size = self.size
- omega = self.omega
- #Make a matrix that has the basic Fourier Transform Matrix
- arrayFT = [[omega**(
- i*j % size)/sqrt(size) for i in range(size)] for j in range(size)]
- matrixFT = Matrix(arrayFT)
- #Embed the FT Matrix in a higher space, if necessary
- if self.label[0] != 0:
- matrixFT = matrix_tensor_product(eye(2**self.label[0]), matrixFT)
- if self.min_qubits < nqubits:
- matrixFT = matrix_tensor_product(
- matrixFT, eye(2**(nqubits - self.min_qubits)))
- return matrixFT
- @property
- def targets(self):
- return range(self.label[0], self.label[1])
- @property
- def min_qubits(self):
- return self.label[1]
- @property
- def size(self):
- """Size is the size of the QFT matrix"""
- return 2**(self.label[1] - self.label[0])
- @property
- def omega(self):
- return Symbol('omega')
- class QFT(Fourier):
- """The forward quantum Fourier transform."""
- gate_name = 'QFT'
- gate_name_latex = 'QFT'
- def decompose(self):
- """Decomposes QFT into elementary gates."""
- start = self.label[0]
- finish = self.label[1]
- circuit = 1
- for level in reversed(range(start, finish)):
- circuit = HadamardGate(level)*circuit
- for i in range(level - start):
- circuit = CGate(level - i - 1, RkGate(level, i + 2))*circuit
- for i in range((finish - start)//2):
- circuit = SwapGate(i + start, finish - i - 1)*circuit
- return circuit
- def _apply_operator_Qubit(self, qubits, **options):
- return qapply(self.decompose()*qubits)
- def _eval_inverse(self):
- return IQFT(*self.args)
- @property
- def omega(self):
- return exp(2*pi*I/self.size)
- class IQFT(Fourier):
- """The inverse quantum Fourier transform."""
- gate_name = 'IQFT'
- gate_name_latex = '{QFT^{-1}}'
- def decompose(self):
- """Decomposes IQFT into elementary gates."""
- start = self.args[0]
- finish = self.args[1]
- circuit = 1
- for i in range((finish - start)//2):
- circuit = SwapGate(i + start, finish - i - 1)*circuit
- for level in range(start, finish):
- for i in reversed(range(level - start)):
- circuit = CGate(level - i - 1, RkGate(level, -i - 2))*circuit
- circuit = HadamardGate(level)*circuit
- return circuit
- def _eval_inverse(self):
- return QFT(*self.args)
- @property
- def omega(self):
- return exp(-2*pi*I/self.size)
|