#!/usr/bin/python # -*- coding: utf-8 -*- """ Exposes a few basic QI operators And a circuit-model simulator """ from __future__ import absolute_import import numpy as np import itertools as it from fractions import Fraction from six.moves import range from six.moves import zip def hermitian_conjugate(u): """ Shortcut to the Hermitian conjugate """ return np.conjugate(np.transpose(u)) # Constants ir2 = 1 / np.sqrt(2) # Operators id = np.array(np.eye(2, dtype=complex)) px = np.array([[0, 1], [1, 0]], dtype=complex) py = np.array([[0, -1j], [1j, 0]], dtype=complex) pz = np.array([[1, 0], [0, -1]], dtype=complex) ha = hadamard = np.array([[1, 1], [1, -1]], dtype=complex) * ir2 ph = np.array([[1, 0], [0, 1j]], dtype=complex) t = np.array([[1, 0], [0, np.exp(1j * np.pi / 4)]], dtype=complex) sqx = np.array( [[1. + 0.j, -0. + 1.j], [-0. + 1.j, 1. - 0.j]], dtype=complex) * ir2 msqx = np.array( [[1. + 0.j, 0. - 1.j], [0. - 1.j, 1. - 0.j]], dtype=complex) * ir2 sqy = np.array( [[1. + 0.j, 1. + 0.j], [-1. - 0.j, 1. - 0.j]], dtype=complex) * ir2 msqy = np.array( [[1. + 0.j, -1. - 0.j], [1. + 0.j, 1. - 0.j]], dtype=complex) * ir2 sqz = np.array( [[1. + 1.j, 0. + 0.j], [0. + 0.j, 1. - 1.j]], dtype=complex) * ir2 msqz = np.array( [[1. - 1.j, 0. + 0.j], [0. + 0.j, 1. + 1.j]], dtype=complex) * ir2 # CZ gate cz = np.array(np.eye(4), dtype=complex) cz[3, 3] = -1 # States zero = np.array([[1], [0]], dtype=complex) one = np.array([[0], [1]], dtype=complex) plus = np.array([[1], [1]], dtype=complex) / np.sqrt(2) bond = cz.dot(np.kron(plus, plus)) nobond = np.kron(plus, plus) # Labelling stuff common_us = id, px, py, pz, ha, ph, sqz, msqz, sqy, msqy, sqx, msqx names = "identity", "px", "py", "pz", "hadamard", "phase", "sqz", "msqz", "sqy", "msqy", "sqx", "msqx" by_name = dict(list(zip(names, common_us))) paulis = px, py, pz operators = id, px, py, pz def normalize_global_phase(m): """ Normalize the global phase of a matrix """ v = next((x for x in m.flatten() if np.abs(x) > 0.001)) phase = np.arctan2(v.imag, v.real) % np.pi rot = np.exp(-1j * phase) return rot * m if rot * v > 0 else -rot * m class CircuitModel(object): def __init__(self, nqubits): self.nqubits = nqubits self.d = 2 ** nqubits self.state = np.zeros((self.d, 1), dtype=complex) self.state[0, 0] = 1 def act_cz(self, control, target): """ Act a CU somewhere. """ control = 1 << control target = 1 << target for i in range(self.d): if (i & control) and (i & target): self.state[i, 0] *= -1 def act_cnot(self, control, target): """ Act a CNOT. """ self.act_hadamard(target) self.act_cz(control, target) self.act_hadamard(target) def act_hadamard(self, qubit): """ Act a hadamard somewhere. """ where = 1 << qubit output = np.zeros((self.d, 1), dtype=complex) for i, v in enumerate(self.state): q = int(i & where > 0) output[i] += v * ha[q, q] output[i ^ where] += v * ha[int(not q), q] self.state = output def act_local_rotation(self, qubit, u): """ Act a local unitary somwhere. """ where = 1 << qubit output = np.zeros((self.d, 1), dtype=complex) for i, v in enumerate(self.state): q = int(i & where > 0) output[i] += v * u[q, q] output[i ^ where] += v * u[int(not q), q] self.state = output def act_circuit(self, circuit): """ Act a sequence of gates. """ for node, operation in circuit: if operation == "cz": self.act_cz(*node) else: self.act_local_rotation(node, operation) def __eq__(self, other): """ Check whether two quantum states are the same or not, up to a global phase. """ a = normalize_global_phase(self.state) b = normalize_global_phase(other.state) return np.allclose(a, b) def __setitem__(self, key, value): """ Set a matrix element """ self.state[key] = value def __getitem__(self, key): """ Get a matrix element """ return self.state[key] def __str__(self): s = "" for i in range(self.d): label = bin(i)[2:].rjust(self.nqubits, "0")[::-1] if abs(self.state[i, 0]) > 0.00001: term = self.state[i, 0] real_sign = " " if term.real>=0 else "-" real_frac = Fraction(str(term.real**2)).limit_denominator() imag_sign = "+" if term.imag>=0 else "-" imag_frac = Fraction(str(term.imag**2)).limit_denominator() s += "|{}❭: \t{}√{}\t{} i √{}\n".format( label, real_sign, real_frac, imag_sign, imag_frac) return s