#!/usr/bin/python # # Quantum Computing Python Library # Copyright (C) 2008 Robert Nowotniak # # $Id$ # # qclib is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # qclib is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with qclib; if not, write to the Free Software Foundation from numpy import * from random import random import copy # for floating point operations, comparisons etc. epsilon = 10e-6 class QRegister: '''Quantum register class''' def __init__(self, m = None): if m == None: return if isinstance(m, ndarray) or type(m) == type([]): m = matrix(m) if isinstance(m, matrix) and m.shape[0] == 1: m = transpose(m) if not isinstance(m, matrix) or m.shape[1] != 1: raise WrongSizeException self.matrix = m self.size = int(math.log(m.size, 2)) def __rmul__(self, arg1): # arg1 * self if type(arg1) not in [int, float, complex]: raise Exception() result = copy.deepcopy(self) result.matrix = arg1 * self.matrix return result def __add__(self, arg2): # self + arg2 result = copy.deepcopy(self) result.matrix = self.matrix + arg2.matrix return result def __sub__(self, arg2): # self - arg2 result = copy.deepcopy(self) result.matrix = self.matrix - arg2.matrix return result def __pow__(self, arg2): # self ** arg2 result = QRegister() result.matrix = kron(self.matrix, arg2.matrix) result.size = int(math.log(result.matrix.size, 2)) return result def __cmp__(self, other): m1 = self.matrix if isinstance(other, (matrix, ndarray)): m2 = other elif isinstance(other, QRegister): m2 = other.matrix else: return -1 try: if sum(abs(m1 - m2)) < epsilon: return 0 else: return 1 except Exception: raise WrongSizeException('Comparison of different size quantum registers') def __str__(self): return str(self.matrix) def reset(self, n = 0): for i in range(self.matrix.size): self.matrix[i] = 0 self.matrix[n] = 1 def normalize(self): l = sqrt(sum([abs(x)**2 for x in self.matrix])) self.matrix = self.matrix / l return self def measure(self, *qubits): if len(qubits) == 0: # measure all qubits in register qubits = range(int(math.log(self.matrix.size, 2))) qubits = list(qubits) qubits.sort() p = {} # results probabilities # number of possible measurement results nres = 2 ** len(qubits) # enumerate all posible results for i in range(nres): p[dec2bin(i, int(math.log(nres, 2)))[::-1]] = 0.0 for i in range(self.matrix.size): # reversed binary representation of base vector revbin = dec2bin(i, int(math.log(self.matrix.size, 2)))[::-1] # reversed binary representation of selected qubits revsel = ''.join([revbin[q] for q in qubits]) p[revsel] += float(abs(self.matrix[i]) ** 2) keys = p.keys() # accumulated probabilities last = p[keys[0]] for k in keys[1:]: p[k] += last last = p[k] p[keys[-1]] = 1.0 # get the measurement result according to probabilities r = random() for k in keys: if r <= p[k]: result = k break # selective reset of amplitudes for i in range(self.matrix.size): revbin = dec2bin(i, int(math.log(self.matrix.size, 2)))[::-1] revsel = ''.join([revbin[q] for q in qubits]) if revsel != result: self.matrix[i] = 0.0 # normalize final state self.normalize() return Ket(int(result[::-1], 2), len(qubits)) def dirac(self, reduce = True, binary = True): """Return state in Dirac (bra-ket) notation""" elems = [] if len(list(filter(lambda x: float(abs(x)) > 1 - epsilon, self.matrix))) == 1: single = True else: single = False for i in range(self.matrix.size): val = complex(real(self.matrix[i]), imag(self.matrix[i])) if reduce and abs(val) < epsilon: continue if abs(val) < epsilon: elem = '+0' elif imag(val) == 0: elem = '%+g' % abs(val) elif real(val) != 0: elem = '+%s' % str(val) else: # only imaginary part elem = '%+gj' % (imag(val)) if single and reduce: elem = '' if binary: elem += ('|%0'+str(math.log(self.matrix.size, 2))+'d>') % int(dec2bin(i)) else: elem += '|%s>' % i elems.append(elem) return ' '.join(elems) def outer(self, qreg): '''Compute an outer product with another register''' if self.matrix.size != transpose(qreg.matrix).size: raise WrongSizeException('Outer product of different size registers') result = Arbitrary(dot(self.matrix, transpose(qreg.matrix))) return result class Qubit(QRegister): '''Qubit class''' def __init__(self, val): if not isinstance(val, int): return QRegister.__init__(self, val) self.size = 1 if val == 0: self.matrix = transpose(matrix([[1, 0]])) elif val == 1: self.matrix = transpose(matrix([[0, 1]])) else: raise WrongSizeException def flip(self): tmp = self.matrix[0] self.matrix[0] = self.matrix[1] self.matrix[1] = tmp class QCircuit: '''Quantum circuit class''' def __init__(self, *stages): self.stages = stages def __call__(self, qreg): # Efficient algorithm could be implemented here instead. Reference: # Wissam A. Samad, Roy Ghandour, and Mohamad. # Memory efficient quantum circuit simulator based on linked list architecture result = copy.deepcopy(qreg) for s in self.stages: result = s(result) return result class QGate: '''Quantum gate class''' def __pow__(self, arg2): # parallel gates if not isinstance(arg2, QGate): raise Exception(repr(arg2)) result = Stage(self, arg2) return result def __str__(self): return str(self.matrix) def __mul__(self, arg2): # self * arg2 if isinstance(arg2, QRegister): # gate * reg result = QRegister() try: result.matrix = dot(self.matrix, arg2.matrix) except: raise WrongSizeException('Wrong size of input register for this gate') return result if self.matrix.shape != arg2.matrix.shape: raise Exception() # gate * gate result = QGate() # order changed! result.matrix = dot(arg2.matrix, self.matrix) return result def __rmul__(self, arg1): # arg1 * self if type(arg1) not in [int, float, complex]: raise Exception('Numerical coefficient expected') result = copy.deepcopy(self) result.matrix = arg1 * self.matrix return result def __add__(self, arg2): # self + arg2 result = copy.deepcopy(self) result.matrix = self.matrix + arg2.matrix return result def __sub__(self, arg2): # self - arg2 result = copy.deepcopy(self) result.matrix = self.matrix - arg2.matrix return result def __call__(self, qreg): return self.compute(qreg) def compute(self, qreg): if not isinstance(qreg, QRegister): raise Exception() return self * qreg def trace(self): return self.matrix.trace() def determinant(self): return linalg.det(self.matrix) def transpose(self): self.matrix = transpose(self.matrix) return self def inverse(self): self.matrix = linalg.inv(self.matrix) return self class Stage(QGate): '''Quantum computing stage -- a layer in circuit''' def __init__(self, *gates): self.gates = gates m = self.gates[0].matrix for g in self.gates[1:]: m = kron(m, g.matrix) self.matrix = m self.size = sum([g.size for g in gates]) # # Elementary quantum gates # class ElementaryQuantumGate(QGate): pass class Identity(ElementaryQuantumGate): def __init__(self, size = 1): self.matrix = eye(2 ** size) self.size = size class Hadamard(ElementaryQuantumGate): def __init__(self, size = 1): h = s2 * matrix([ [1, 1], [1, -1]]) m = h for i in range(size - 1): m = kron(m, h) self.matrix = m self.size = size class CNot(ElementaryQuantumGate): '''Controlled not gate''' def __init__(self, control = 1, target = 0): if control == target: # (it would impose non-unitary matrix) raise Exception('Control and target qubits cannot be equal') elif control == 1 and target == 0: self.matrix = matrix([ [1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0]]) self.size = 2 elif control == 0 and target == 1: self.matrix = matrix([ [1, 0, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0], [0, 1, 0, 0]]) self.size = 2 else: size = max(control, target) + 1 if size == 1: size = 2 dim = 2 ** size self.matrix = eye(dim) # find correct permutation of identity matrix columns for b in range(dim): bstr = dec2bin(b, size) if bstr[-(control+1)] == '1': bstr = list(bstr) if bstr[-(target+1)] == '0': bstr[-(target+1)] = '1' else: bstr[-(target+1)] = '0' bstr = ''.join(bstr) self.matrix[:,b] = eye(dim)[:,int(bstr, 2)] self.size = size class Not(ElementaryQuantumGate): '''Not gate''' def __init__(self): self.matrix = matrix([ [0, 1], [1, 0]]) self.size = 1 class PhaseShift(ElementaryQuantumGate): def __init__(self, angle = pi): self.angle = angle self.matrix = matrix([ [1, 0], [0, exp(angle * 1j)]]) self.size = 1 class Toffoli(ElementaryQuantumGate): '''Toffoli gate -- Controlled Controlled Not gate''' def __init__(self): self.matrix = matrix([ [ 1, 0, 0, 0, 0, 0, 0, 0], [ 0, 1, 0, 0, 0, 0, 0, 0], [ 0, 0, 1, 0, 0, 0, 0, 0], [ 0, 0, 0, 1, 0, 0, 0, 0], [ 0, 0, 0, 0, 1, 0, 0, 0], [ 0, 0, 0, 0, 0, 1, 0, 0], [ 0, 0, 0, 0, 0, 0, 0, 1], [ 0, 0, 0, 0, 0, 0, 1, 0]]) self.size = 3 class Fredkin(ElementaryQuantumGate): '''Fredkin gate -- Controlled Swap gate''' def __init__(self): self.matrix = matrix([ [ 1, 0, 0, 0, 0, 0, 0, 0], [ 0, 1, 0, 0, 0, 0, 0, 0], [ 0, 0, 1, 0, 0, 0, 0, 0], [ 0, 0, 0, 1, 0, 0, 0, 0], [ 0, 0, 0, 0, 1, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 1, 0], [ 0, 0, 0, 0, 0, 1, 0, 0], [ 0, 0, 0, 0, 0, 0, 0, 1]]) self.size = 3 class Swap(ElementaryQuantumGate): '''Qubits order swap gate''' def __init__(self): self.matrix = matrix([ [1, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1]]) self.size = 2 class Arbitrary(ElementaryQuantumGate): '''Quantum gate with arbitrary unitary matrix''' def __init__(self, m): m = matrix(m) if (m.H * m == eye(m.shape[0])).any() == False: pass # raise Exception, 'Not unitary matrix for quantum gate' self.matrix = m self.size = int(math.log(m.shape[0], 2)) class WrongSizeException(Exception): def __str__(self): return 'Wrong size of quantum computing object' def dec2bin(dec, length = None): """convert decimal value to binary string""" result = '' if dec < 0: raise ValueError("Must be a positive integer") if dec == 0: result = '0' if length != None: result = result.rjust(length, '0') return result while dec > 0: result = str(dec % 2) + result dec = dec >> 1 if length != None: result = result.rjust(length, '0') return result def Ket(n, size = None): if (n == 0 or n == 1) and size == None: return Qubit(n) ket = QRegister() if size == None: size = int(floor(math.log(n, 2)) + 1) ket.matrix = transpose(matrix([zeros(2 ** size)])) ket.matrix[n] = 1 return ket def epr(qreg = Ket(0) ** Ket(0)): """Generate an EPR-pair for |00> input state""" circ = (Hadamard() ** I) * CNot() return circ(qreg) ket0 = Ket(0) ket1 = Ket(1) s2 = sqrt(2) / 2 h = Hadamard() I = Identity() cnot = CNot() cnot2 = CNot(0, 1) T = Arbitrary(matrix([ [1, 0], [0, exp(1.0j*pi/4)]]))