Source code for run_circuit

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
This module provides a simple quantum circuit simulator.
"""
from sage.all import *

import numpy as np
import copy as cp

from vector_to_ket import *

field = SR
# field = UniversalCyclotomicField()
# e8 = field.gen(8)
# sqrt2 = e8 + conjugate(e8) # fix for UniversalCyclotomicField


[docs]def kronecker(a, b): r""" Computes the Kronecker product of ``a`` and ``b``. Example: >>> a = matrix([[1,0],[0,1]]) >>> b = matrix([[1,2],[3,4]]) >>> kronecker(a,b) [1 2 0 0] [3 4 0 0] [0 0 1 2] [0 0 3 4] :param matrix,vector a,b: Operands for the kronecker operator. :returns: matrix *or* vector -- The kronecker product of ``a`` and ``b`` (return type is the same a type of ``a``). """ a_matrix = matrix(a) b_matrix = matrix(b) ma, na = a_matrix.nrows(), a_matrix.ncols() mb, nb = b_matrix.nrows(), b_matrix.ncols() mr, nr = ma * mb, na * nb result = Matrix(field, mr, nr) for i in range(0, mr): for j in range(0, nr): result[i, j] = a_matrix[i // mb, j // nb] * b_matrix[i % mb, j % nb] if "Vector" in str(parent(a)): return vector(result) elif "Matrix" in str(parent(a)): return matrix(result) else: raise ValueError("Input type of first parameter must be Vector or " + "Matrix but " + str(parent(a)) + " was given")
[docs]def kronecker_power(a, n): r""" Computes the ``n`` `^{th}` Kronecker power of ``a``. Example: >>> a = matrix([[1,0],[0,2]]) >>> kronecker_power(a,2) [1 0 0 0] [0 2 0 0] [0 0 2 0] [0 0 0 4] :param matrix,vector a: The matrix (or vector) to be elevated to the ``n`` `^{th}` power. :param int n: The power ``a`` has to be elevated to. :returns: matrix *or* vector -- The ``n`` `^{th}` kronecker power of ``a`` (return type is the same a type of ``a``). """ result = a for _ in range(n-1): result = kronecker(result, a) if "Vector" in str(parent(a)): return vector(result) elif "Matrix" in str(parent(a)): return matrix(result) else: raise ValueError("Input type must be Vector or Matrix but " + str(parent(a)) + " was given")
[docs]def output_commant(command_name, command, output=False, output_to_file=False, w_file=None): r""" This function is used to print useful informations in various ways, see arguments details for more information. Example: >>> output_commant("test",vector(SR,[1,0,0,1]), output=True) \newcommand{\test}{1 \ket{00} + 1 \ket{11}} :param str command_name: The command name. :param any command: The command content (if type is ``str``, will be printed as such; if ``vector``, ``vector_to_ket`` will be called on it and if ``matrix``, ``latex`` method from SageMath will be called on it). :param bool output: disables or enables the output. :param bool output_to_file: Whether the output should be in the standard output or in a file. :param file w_file: The opened file to write the output to. :returns: None """ if output: if "Vector" in str(parent(command)): latex_command = vector_to_ket(command) elif "str" in str(parent(a)): latex_command = command else: latex_command = latex(command) newcommand = "\\newcommand{\\" + command_name + "}{" + str(latex_command) + "}\n" if output_to_file: w_file.write(newcommand) else: print(newcommand)
[docs]def run(matrix_layers, V_init, output=False, output_file=False, file=None, vector_name="V", matrix_name="M"): r""" Runs the algorithm specified by the matrix_layers. Example: >>> I2 = matrix.identity(2) >>> I4 = matrix.identity(4) >>> H = matrix([[1,1],[1,-1]])/sqrt(2) >>> swap = matrix([[1,0,0,0],[0,0,1,0],[0,1,0,0],[0,0,0,1]]) >>> v = vector([1, 0, 0, 0, 0, 0, 0, 0]) >>> layers = [[I4,H],[H,H,I2],[I2,swap]] >>> run(layers,v) ([(1, 0, 0, 0, 0, 0, 0, 0), (1/2*sqrt(2), 1/2*sqrt(2), 0, 0, 0, 0, 0, 0), (1/4*sqrt(2), 1/4*sqrt(2), 1/4*sqrt(2), 1/4*sqrt(2), 1/4*sqrt(2), 1/4*sqrt(2), 1/4*sqrt(2), 1/4*sqrt(2)), (1/4*sqrt(2), 1/4*sqrt(2), 1/4*sqrt(2), 1/4*sqrt(2), 1/4*sqrt(2), 1/4*sqrt(2), 1/4*sqrt(2), 1/4*sqrt(2))], [ [ 1/2*sqrt(2) 1/2*sqrt(2) 0 0 0 0 0 0] [ 1/2*sqrt(2) -1/2*sqrt(2) 0 0 0 0 0 0] [ 0 0 1/2*sqrt(2) 1/2*sqrt(2) 0 0 0 0] [ 0 0 1/2*sqrt(2) -1/2*sqrt(2) 0 0 0 0] [ 0 0 0 0 1/2*sqrt(2) 1/2*sqrt(2) 0 0] [ 0 0 0 0 1/2*sqrt(2) -1/2*sqrt(2) 0 0] [ 0 0 0 0 0 0 1/2*sqrt(2) 1/2*sqrt(2)] [ 0 0 0 0 0 0 1/2*sqrt(2) -1/2*sqrt(2)], [ 1/2 0 1/2 0 1/2 0 1/2 0] [1 0 0 0 0 0 0 0] [ 0 1/2 0 1/2 0 1/2 0 1/2] [0 0 1 0 0 0 0 0] [ 1/2 0 -1/2 0 1/2 0 -1/2 0] [0 1 0 0 0 0 0 0] [ 0 1/2 0 -1/2 0 1/2 0 -1/2] [0 0 0 1 0 0 0 0] [ 1/2 0 1/2 0 -1/2 0 -1/2 0] [0 0 0 0 1 0 0 0] [ 0 1/2 0 1/2 0 -1/2 0 -1/2] [0 0 0 0 0 0 1 0] [ 1/2 0 -1/2 0 -1/2 0 1/2 0] [0 0 0 0 0 1 0 0] [ 0 1/2 0 -1/2 0 -1/2 0 1/2], [0 0 0 0 0 0 0 1] ]) :param array[array[matrix]] matrix_layers: Algorithm described as it would be in a circuit. :param vector V_init: Initial input of the algorithm. :param bool output: If true, outputs are enabled. :param bool output_to_file: If true, trace is returned in file, else it is printed. :param file file: File to output the commands to. :param str vector_name: Base name used for the vectors commands. :param str matrix_name: Base name used for the matrices commands. :returns: vector -- The states along the execution of the algorithm as well as the matrix corresponding to each layer. """ V_running = cp.deepcopy(V_init) vectors_list = [V_running] output_commant(vector_name + int_name(0), V_running, output, output_file, file) matrices_list = [] for i in range(len(matrix_layers)): layerMatrix = Matrix(field, [[1]]) for running_matrix in matrix_layers[i]: layerMatrix = kronecker(layerMatrix, running_matrix) V_running = layerMatrix*V_running matrices_list.append(layerMatrix) output_commant(matrix_name + int_name(i+1), layerMatrix, output, output_file, file) vectors_list.append(V_running) output_commant(vector_name + int_name(i+1), V_running, output, output_file, file) return vectors_list, matrices_list
[docs]def int_name(num): r""" Converts a number to a string composed of the list of its digits in English. Example: >>> int_name(152) 'onefivetwo' :param int num: Number to be converted to a list of digits. :returns: str -- List of digits of ``num`` in base 10 concatenated. """ digit_str = ["zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"] if num == 0: return digit_str[0] k = 0 result = "" while num+1 > 10**k: k += 1 k -= 1 while k >= 0: result += digit_str[digit(num, k)] k -= 1 return result
[docs]def digit(n, k, base = 10): r""" Computes the digit ``k`` for the integer ``n`` in its representation in base ``base``. Example: >>> digit(152, 1) 5 >>> digit(152, 0) 2 :param int n: The integer for which the digit is needed. :param int k: The number of the digit. :param int base: The base used for the representation of ``n``. :returns: int -- The ``k`` `^{th}` digit of ``n`` in base ``base``. """ return floor(abs(n)/base**(k)) - floor(abs(n)/base**(k+1))*base
[docs]def layers_to_printable(layers): r"""layers is the same as matrix_layers but with matrix names embedded in them Example: >>> I2 = matrix.identity(2) >>> I4 = matrix.identity(4) >>> H = matrix([[1,1],[1,-1]])/sqrt(2) >>> swap = matrix([[1,0,0,0],[0,0,1,0],[0,1,0,0],[0,0,0,1]]) >>> layers = [[('I',I4),('H',H)],[('H',H),('H',H),('I',I2)],[('I',I2),('S',swap)]] [[('I',2),('H',1)],[('H',1),('H',1),('I',1)],[('I',1),('S',2)]] :param list[list[(str,matrix)]] layers: Circuit under the format described above. :returns: list[List[(str,int)]] -- Circuit under the ready-to-print format described above. """ return [[(gate[0], log(gate[1].nrows())/log(2)) for gate in layer] for layer in layers]
[docs]def layers_to_matrix(layers): r"""layers is the same as matrix_layers but with matrix names embedded in them Example: >>> I2 = matrix.identity(2) >>> I4 = matrix.identity(4) >>> H = matrix([[1,1],[1,-1]])/sqrt(2) >>> swap = matrix([[1,0,0,0],[0,0,1,0],[0,1,0,0],[0,0,0,1]]) >>> layers = [[('I',I4),('H',H)],[('H',H),('H',H),('I',I2)],[('I',I2),('S',swap)]] >>> layers_to_matrix(layers) [[I4,H],[H,H,I2],[I2,swap]] :param list[list[(str,matrix)]] layers: Circuit under the format described above. :returns: list[List[(str,int)]] -- Circuit under the ready-to-run format described above. """ return [[gate[1] for gate in layer] for layer in layers]