Contains functions which assist with various tasks allied to quantum computation and its scheme implementation

form-reg


    (form-reg state) -> register
        state: list of 0's and 1's indicating a state, length defines depth
        register: register
        

Creates a register with all coefficients 0 except for the one specified as parameter, whose coefficient is 1.0


	> (form-reg (list 0 1))
	(register (register (coeff 0) (coeff 1)) (register (coeff 0) (coeff 0)))
    

pump


    (pump list) -> reg
	list: coefficients of states listed in order
        reg: the output register
        

this is the opposite of flatten-reg, it returns a register created using the coefficients of all states given in the form of a list

Exceptions : If the number of elements in the list is not a power of 2


	> (pump (list 0.5 0.5))
	(register (coeff 0.5) (coeff 0.5))
	> (pump (list 0.5 0.6 1 0+0.8i))
	(register (register (coeff 0.5) (coeff 0.6)) (register (coeff 1) (coeff 0+0.8i)))
    

1-register


    (1-register no-of-qubits) -> register
        no-of-qubits: positive integer
        register: register
        

Creates a register with specified number of qubits that has all coefficients as 1, together with normalise function, this can be used to create a uniform register


    > (1-register 2)
    (register (register (coeff 1) (coeff 1)) (register (coeff 1) (coeff 1)))
    

0-register


    (0-register no-of-qubits) -> register
        no-of-qubits: positive integer
        register: register
        

Creates a register with specified number of qubits that has all coefficients as 0


	> (0-register 2)
	(register (register (coeff 0) (coeff 0)) (register (coeff 0) (coeff 0)))
    

normalise


    (normalise in-reg) -> out-reg
        in-reg: the input register
        out-reg: the output register
        

this function normalises a register, so that the sum of probabilities of occurence of all its states becomes 1


	> (normalise (pump (list 1 2 3 1+4i)))
	(register
	 (register (coeff 0.1796053020267749) (coeff 0.3592106040535498))
	 (register (coeff 0.5388159060803247) (coeff 0.1796053020267749+0.7184212081070996i)))
	

flatten-reg


    (flatten-reg reg) -> list
        reg: the input register
        list: coefficients listed in a row
    

returns the coefficients of all states in a register in the form of a list


	> (flatten-reg (register (register (coeff 0.5) (coeff 1)) (register (coeff 0.6) (coeff 0+0.8i))))
	'(0.5 1 0.6 0+0.8i)
    

display-reg


    (display-reg reg) -> bra-ket
        reg: the input register
        bra-ket: linear combination of pure states
        

brings the register into the coefficient-braket notation


	> (display-reg (pump (list 0 1 2 3)))
	"0|00> + 1|01> + 2|10> + 3|11>"
    

over-all


    (over-all-depth reg1 f) -> reg2
        reg1: input-register
        f: function to be applied over a register which is composed
        reg2: modified register
        

It returns the register after modifying all states according to function f. The function 'f' should return a coeff for proper functioning.


	>(define tr (register (register (coeff 0.6) (coeff 0)) (register (coeff 0) (coeff 0+0.8i))))
	> (over-all tr (λ(x) (coeff (+ x 1))))
	(register (register (coeff 1.6) (coeff 1)) (register (coeff 1) (coeff 1.0+0.8i)))
        

over-all-depth


    (over-all-depth reg1 f depth) -> reg2
        reg1: input-register
        f: function to be applied over a register which is composed, should return a register or a coeff as applicable
        depth: the length of the suffix which is common to all the states in the sub-register over which f is applied
        reg2: modified register
        

Applies the given function 'f' at a particular depth in the given register. This finds use in the 'on' function given in "execute.scm". To understand the concept of depth, consider the register to be in the form of a complete binary tree. Then all the states with a particular prefix state can be found as a subtree in the binary tree. f is applied over all such subtrees. The function 'f' should return a coeff for proper functioning.


	>(define tr (register (register (coeff 0.6) (coeff 0)) (register (coeff 0) (coeff 0+0.8i))))
	> (over-all-depth tr (λ(x) (normalise x )) 1)
	(register (register (coeff 1.0) (coeff 0)) (register (coeff 0) (coeff 0+1.0i)))
	;note how the subtrees have been independently normalized
        

state-wise


    (state-wise func reg1 reg2) -> out-reg
        func: procedure that acts on two numbers and yields a new one
        reg1: first input register
	reg2: second input register
	out-reg: output register
        

applies func on the corresponding states of the two given registers and returns a single coeff for that state in the output register

Exceptions : if the two registers have different number of states


	> (state-wise + (pump (list 1 0)) (pump (list 2 3)))
	(register 3 3)
	> (state-wise + (pump (list 1 0)) (pump (list 2 3 4 5)))
	ERROR: Different length registers
	> (state-wise (lambda (x y) (* x y)) (pump (list 1 0)) (pump (list 2 3)))
	(register 2 0)
   

use


    (use f reg1 state) -> reg2
        f: function to be applied
        reg1: input-register
        state: the state on which f is applied
        reg2: modified register
        

The 'use' function applies the given function 'f' over one particular state in the given register


	>(define tr (register (register (coeff 0.6) (coeff 0)) (register (coeff 0) (coeff 0+0.8i))))
	>(use (λ (coeff-of-state) coeff-of-state) tr (list 1 1)))
	(coeff 0+0.8i)
        

mutate


    (mutate func in-reg state) -> out-reg
        func: a single input function
	in-reg: the input register
	state: a pure state expressed as a list
        out-reg: the output register
        

This function returns the entire register after modifying the given state according to function f


	> (mutate (lambda (x) (expt x 2)) (register (register (coeff 0.6) (coeff 0.8)) (register (coeff 0+0.6i) (coeff 0.8))) '(1 0))
	(register (register (coeff 0.6) (coeff 0.8)) (register (coeff -0.36) (coeff 0.8)))
    

tensor


    (tensor reg1 reg2 ...) -> out-reg
        reg1: first input register
	reg2: second input register
	...: further optional input registers
        out-reg: tensor product, expressed as a register
        

gives the tensor product of any number of registers provided as arguments. The tensor product produces an entanglement of states.


	> (tensor (pump (list 0.6 0 0 0+0.8i)) (normalise (1-register 1)))
	(register
	 (register (register (coeff 0.42426406871192845) (coeff 0.42426406871192845)) (register (coeff 0) (coeff 0)))
	 (register (register (coeff 0) (coeff 0)) (register (coeff 0+0.565685424949238i) (coeff 0+0.565685424949238i))))
    

register-dot-product


    (register-dot-product reg1 reg2) -> real
        reg1: first input register
        reg2: second input register
        real: scalar
        

This function gives the dot-product of two registers, similar to the dot product of two wave-functions, wherein the orthogonal states multiply to give zero.


	> (register-dot-product (pump (list 0.8 0.6)) (pump (list 0.6 0.8)))
	0.96
    

generate-all-states


    (generate-all-states num) -> list
	num: positive integer
	list: all possible states listed
		

returns a list of all the states in a register of given number of qubits


	> (generate-all-states 3)
	'((0 0 0) (0 0 1) (0 1 0) (0 1 1) (1 0 0) (1 0 1) (1 1 0) (1 1 1))
    

no-of-qubits


    (no-of-qubits in-register) -> int
        in-register: the input register
        int: positive integer
        

this returns the number of qubits in a register. The number of states is equal to (expt 2 no-of-qubits)


	> (no-of-qubits (register (register (coeff 0.5) (coeff 1)) (register (coeff 0.5) (coeff 1))))
	2
    

get-coeff


    (get-coeff reg1 state) -> coeff
        reg1: input-register
        state: the state for which coeff has to be returned
        coeff: the coefficient printed as an imaginary number
        

	>(define tr (register (register (coeff 0.6) (coeff 0)) (register (coeff 0) (coeff 0+0.8i))))
	(get-coeff tr '(1 1))
	(coeff 0+0.8i)
        

list-pos


    (list-pos list elem) -> position
        list: list of elements
        elem: element to look for
	position: integer indicating index in list
        

returns the position of an element in a given list, false if the element is absent


	> (list-pos (list 10 5 7 1) 6)
	#f
	> (list-pos (list 10 5 7 1) 7)
	2
    

prob


    (prob coeff) -> real-number
        coeff: The imaginary coeff value to be converted into a real value
        real-number: its probability if the register was normalized
        

Given the magnitude of the coefficient of a state, this function returns the rounded off value of its square, representative of the state's probability of occurence


	> (prob (coeff 0.25+0.25i))
	0.12500000000000003
    

prob-sum


    (prob-sum reg) -> real-number
        reg: the input register
        real-number: its total probability
        

Returns the sum of probabilities of occurence of all states in a given register


	> (prob-sum (1-register 2))
	4
    

my-roundoff


    (my-roundoff real) -> round
        real: a real number
        round: another real number
        

rounds off the number, essentially a workaround for making things like (expt (/ 1 (sqrt 2)) 2) turn into 0.5 and not 0.49999999, for ease of normalisation check later


	> (my-roundoff 0.678999999999)
	0.679
    

Functions handle execution tasks namely application of a gate on a particular set of qubits in a quantum register along with debugging functionality

on


    (on qubits gate) -> procedure
        qubits: ordered list of qubit indices to be used
        gate: gate to be applied
      

This function takes a gate and transforms it into a function which can be applied on a quantum register on the qubits mentioned in the list. The returned procedure can be applied on a quantum register

Exceptions : If the number of qubits taken by the gate is not the same as the elements in list


	> ((on (list 0) hadamard) (1-register 2))
	(register (register (coeff 0.7071067811865475) (coeff 0.7071067811865475)) (register (coeff 0) (coeff 0)))

	> ((on (list 1 2) CNOT) (1-register 3))
	(register
	 (register
	  (register (coeff 0.35355339059327373) (coeff 0.35355339059327373))
	  (register (coeff 0.35355339059327373) (coeff 0.35355339059327373)))
	 (register
	  (register (coeff 0.35355339059327373) (coeff 0.35355339059327373))
	  (register (coeff 0.35355339059327373) (coeff 0.35355339059327373))))

	> ((on (list 0 1) hadamard) (1-register 2))
	. . gates.scm:41:22: The   gate requires a register with 1 qubits
      

debug-callback


    (debug-callback) -> boolean
      

Turns on debugging

add-break-point


        (add-break-point position)
      

Adds a break-point at the given position

go


        (go)
      

This function runs-till the last but one gate before breakpoint, shows the result and steps forward

step


        (step)
      

This function applies the next gate and shows the result

Using debug tool


	>(debug-callback)
	#t
	
	>(define debug-object1 ((on (list 0) hadamard) ((on (list 0) hadamard) ((on (list 0) hadamard) ((on (list 0) hadamard) tr)))))
	0.6|00> + 0|01> + 0|10> + 0+0.8i|11>

	>(define debug-object2 ((on (list 0) hadamard) ((on (list 0) hadamard) ((on (list 0) Pauli-X) ((on (list 0) hadamard) tr)))))
	0.6|00> + 0|01> + 0|10> + 0+0.8i|11>

	>(send debug-object1 add-break-point 2)
	>(send debug-object2 add-break-point 2)
	
	>(send debug-object1 go)
	0.5999999999999999|00> + 0+0.0i|01> + 0.0|10> + 0+0.7999999999999999i|11>
	
	>(send debug-object2 go)
	0.42426406871192845|00> + 0-0.565685424949238i|01> + 0.42426406871192845|10> + 0+0.565685424949238i|11>

	>(send debug-object1 step)
	0.4242640687119284|00> + 0+0.5656854249492379i|01> + 0.4242640687119284|10> + 0-0.5656854249492379i|11>

	>(send debug-object2 step)
	0.5999999999999999|00> + 0+0.0i|01> + 0.0|10> + 0-0.7999999999999999i|11>

	>(send debug-object1 step)
	0.5999999999999998|00> + 0+0.0i|01> + 0.0|10> + 0+0.7999999999999998i|11>

	>(send debug-object2 step)
	0.4242640687119284|00> + 0-0.5656854249492379i|01> + 0.4242640687119284|10> + 0+0.5656854249492379i|11>

	>(send debug-object1 step)
	done

	>(send debug-object2 step)
	done

      

Quantum Computing specific syntax which allows hassle-free implementation of custom gates and measurements

Quantum register (ket notation)


        (! qreg >)
      

This construct creates a quantum register from another existing one or applies form-reg on the given list. This is equivalent to representing the wavefunction.


	(! (list 1 0) >)
	(register (register (coeff 0) (coeff 0)) (register (coeff 1) (coeff 0)))
      

Bra-ket notation


        (*< qreg1 ! qreg2 >)
      

This construct implements a dot product of qreg1 and qreg2. For more information check out this wiki link.


	> (define tr2 (form-reg (list 0 1)))

	> (*< tr2 ! tr2 >)
	1

	> (*< tr2 ! ((on (list 0) hadamard) tr2) >)
	0.7071067811865475
      

Quantum Functions


        (qs ! qreg > -> f(qreg) )
      

This construct creates a lambda function, which given qreg returns f(qreg). The operators allowed in f are addition(+), subtraction(-), division(/), multiplication(*) along with assignments. Also unlike other prefix operators in scheme these operators have to be used in infix format, in order to maintain conformity with scientific notation
NB - The string qs is part of the macro and cannot be replaced by anything else. Also other functions named qs can cause resolution errors


	> (define tr2 (form-reg (list 0 1)))

	> ((qs ! x > -> (! (list 1 1) >) - (! x >)) tr2)
	(register (register (coeff 0) (coeff -1)) (register (coeff 0) (coeff 1)))

	> ((qs ! x > -> ((on (list 0) hadamard) (! x >))) tr2)
	(register (register (coeff 0) (coeff 0.7071067811865475)) (register (coeff 0) (coeff 0.7071067811865475)))		

	> ((qs ! y > -> ((qs ! x > -> (*< y ! x >)) tr2)) (! (list 1 0) >))
	0

      

contains basic and commonly used gates in the form of functions which can be applied on the registers. Single-qubit gates can be related to actions on the Bloch sphere.

Pauli-X


    (Pauli-X in-register) -> out-register
        in-register: register
        out-register: register
        

The Pauli-X gate acts on a single qubit. It is the quantum equivalent of a NOT gate. It equates to a rotation of the Bloch Sphere around the X-axis by π radians. It maps |0> to |1> and vice-versa.

For further info: Pauli-X_wiki

Exceptions : If the number of qubits in the in-register is not 1


	> (Pauli-X (register (coeff 0.6) (coeff 0.8)))
	(register (coeff 0.8) (coeff 0.6))
	

Pauli-Y


    (Pauli-Y in-register) -> out-register
        in-register: register
        out-register: register
        

The Pauli-Y gate acts on a single qubit. It equates to a rotation around the Y-axis of the Bloch Sphere by π radians. It maps |0> to i|1> and |1> to -i|0>.

For further info: Pauli-Y_wiki

Exceptions : If the number of qubits in the in-register is not 1


	> (Pauli-Y (register (coeff 0.6) (coeff 0.8)))
	(register (coeff 0-0.8i) (coeff 0+0.6i))
	

Pauli-Z


    (Pauli-Z in-register) -> out-register
        in-register: register
        out-register: register
        

The Pauli-Z gate acts on a single qubit. It equates to a rotation around the Z-axis of the Bloch Sphere by π radians. Thus, it is a special case of a phase shift gate (next) with θ=π. It leaves the basis state |0> unchanged and maps |1> to -|1>.

For further info: Pauli-Z_wiki

Exceptions : If the number of qubits in the in-register is not 1


	> (Pauli-Z (register (coeff 0.6) (coeff 0.8)))
	(register (coeff 0.6) (coeff -0.8))
	

phase-shift


    (phase-shift θ) -> procedure
        θ: real number
        procedure: quantum gate
        

This is a family of single-qubit gates that leave the basis state |0> unchanged and map |1> to e|1>. The probability of measuring a |0> or |1> is unchanged after applying this gate, however it modifies the phase of the quantum state. This is equivalent to tracing a horizontal circle (a line of latitude) on the Bloch Sphere by θ radians.

For further info: Phase_shift_wiki


	> ((phase-shift (/ pi 2)) (register (coeff 0.6) (coeff 0.8)))
	(register (coeff 0.6) (coeff 4.898587196589413e-017+0.8i))
	

hadamard


    (hadamard in-register) -> out-register
        in-register: register
        out-register: register
        

The Hadamard gate acts on a single qubit. It maps the basis state |0> to (|0>+|1>)/sqrt(2) and |1> to (|0>-|1>)/sqrt(2) and represents a rotation of π about the axis that bisects the x-z plane.

For further info: Hadamard_wiki

Exceptions : If the number of qubits in the in-register is not 1


	> (hadamard (register (coeff 0.6) (coeff 0.8)))
	(register (coeff 0.9899494936611664) (coeff -0.14142135623730953))
	

swap


    (swap in-register) -> out-register
        in-register: register
        out-register: register
        

This is a 2-qubit gate that swaps the qubits, hence exchanging the coefficients of the |01> and |10> states.

For further info: Swap_wiki

Exceptions : If the number of qubits in the in-register is not 2


	> (swap (register (register (coeff 1) (coeff 2)) (register (coeff 3) (coeff 4))))
	(register (register (coeff 1) (coeff 3)) (register (coeff 2) (coeff 4)))
	

control


    (control control-qubits gate) -> procedure
        control-qubits: positive integer
        gate: any quantum mechanical gate
	procedure: a new gate
        

takes number of control qubits and the basic/complex gate to form a complex gate (procedure) that can then be applied on a register. If the positive integer entered is n, then the first n qubits are taken to be the control qubits, i.e., the gate is applied on the later qubits only if the first n are all 1s.

For further info: Controlled_gates_wiki


	> (define  tr2 (register (register (coeff 1) (coeff 2)) (register (coeff 3) (coeff 4))))
	> ((control 1 Pauli-X) tr2)
	(register (register (coeff 1) (coeff 2)) (register (coeff 4) (coeff 3)))
	> ((control 1 swap) (register tr2 tr2))
	(register (register (register (coeff 1) (coeff 2)) (register (coeff 3) (coeff 4))) (register (register (coeff 1) (coeff 3)) (register (coeff 2) (coeff 4))))
	

fredkin


    (fredkin in-register) -> out-register
        in-register: register
        out-register: register
        

The Fredkin gate (also CSWAP gate) is a 3-bit gate that performs a controlled swap, using the first qubit as control. So, the |110> and |101> states exchange coefficients.

For further info: Fredkin_gate_wiki

Exceptions : If the number of qubits in the in-register is not 3


	> (define tree3 (register (register (register (coeff 1) (coeff 2)) (register (coeff 3) (coeff 4))) 
				 (register (register (coeff 5) (coeff 6)) (register (coeff 7) (coeff 8)))))
	> (fredkin tree3)
	(register (register (register (coeff 1) (coeff 2)) (register (coeff 3) (coeff 4))) 
		  (register (register (coeff 5) (coeff 7)) (register (coeff 6) (coeff 8))))
	> (fredkin (register tree3 tree3))
	ERROR: The gate requires a register with 3 qubits
	

CNOT


    (CNOT in-register) -> out-register
        in-register: register
        out-register: register
        

This swaps the 2nd qubit in a 2-qubit register, i.e., exchanges the coefficients of |10> and |11> states.

Exceptions : If the number of qubits in the in-register is not 2


	> (CNOT (register (register (coeff 1) (coeff 2)) (register (coeff 3) (coeff 4))))
	(register (register (coeff 1) (coeff 2)) (register (coeff 4) (coeff 3)))
	

toffoli


    (toffoli in-register) -> out-register
        in-register: register
        out-register: register
        

The Toffoli gate, also CCNOT gate, is a 3-bit gate, which is universal for classical computation. The quantum Toffoli gate is the same gate, defined for 3 qubits. If the first two bits are in the state |1>, it applies a Pauli-X on the third bit, else it does nothing. It is an example of a controlled gate.

For further info: Toffoli_wiki

Exceptions : If the number of qubits in the in-register is not 3


	> (define tree3 (register (register (register (coeff 1) (coeff 2)) (register (coeff 3) (coeff 4))) 
				 (register (register (coeff 5) (coeff 6)) (register (coeff 7) (coeff 8)))))
	> (toffoli tree3)
	(register (register (register (coeff 1) (coeff 2)) (register (coeff 3) (coeff 4))) 
		  (register (register (coeff 5) (coeff 6)) (register (coeff 8) (coeff 7))))
	

check-gate


    (check-gate gate number1 number2) -> bool
        gate: any quantum mechanical gate
	number1: positive integer
	number2: positive integer or 0
	bool: #t (true) or error message
        

checks if the no.of inputs given matches with that of given basic/complex gate, gives required input qubits other-wise. number1 represents the qubits on which the gate will act, number2 is the number of control qubits. So, if Pauli-X is supplied 2 qubits and 1 more is used for control, the function reports that we need 2 qubits (1 for control and another for the gate to act on).


	> (check-gate Pauli-X 2 0)
	ERROR: The gate requires a register with 1 qubits
	> (check-gate Pauli-X 2 1)
	ERROR: The innermost gate requires a register with 2 qubits	
	> (check-gate Pauli-X 1 0)
	#t
	> (check-gate Pauli-X 1 1)
	#t
	

contains functions that convert matrices into equivalent gates and vice-versa. Also provided is the choice of applying a matrix on a register. Here we treat rows as list of elements while columns are seen as lists of single-element lists.

firstcol


    (firstcol matrix) -> vector
        matrix: m*n dimensional matrix expressed in row-major format
        vector: m-dimensional vector
        

returns the first column of a matrix which has been expressed as a list of rows


	> (firstcol (list (list 1 0) (list 5+2i 1) (list 10 2)))
	'((1) (5+2i) (10))
	

lastcols


    (lastcols matrix) -> sub-matrix
        matrix: m*n dimensional matrix expressed in row-major format
        sub-matrix: m*(n-1) dimensional matrix expressed in row-major format
        

returns all but the first column of a matrix which has been expressed as a list of rows


	> (lastcols (list (list 1 0 2) (list 5 -1 8) (list 10 2 3+2i)))
	'((0 2) (-1 8) (2 3+2i))
	

dot-product


    (dot-product row column) -> scalar
        row: n-dimensional row vector
        column: n-dimensional column vector
	scalar: complex number
        

gives the scalar product of a row and a column vector


	> (dot-product (list 1 2) (list (list 3) (list 0+4i)))
	3+8i
	

vmatmult


    (vmatmult row matrix) -> list
        row: m-dimensional row vector
        matrix: m*n dimensional matrix, expressed in row-major format 
	list: n-dimensional row vector
        

returns the dot product of a row with all columns of a matrix in the form of a list, i.e., the ith element in the list will be the dot product of the row with the ith column of the matrix.


	> (vmatmult (list 1 2) (list (list 1 -5 10) (list 0 2 4)))
	'(1 -1 18)
	

matmult


    (matmult matrix1 matrix2) -> matrix3
        matrix1: an m*n dimensional matrix
        matrix2: an n*p dimensional matrix 
	matrix3: an m*p dimensional matrix
        

gives the product of two matrices, each of them expressed in the row-major format.


	> (matmult (list (list 1 2 0) (list 1 0 1)) (list (list 1 5 4) (list 2 6 9) (list 3 -1 0)))
	'((5 17 22) (4 4 4))
	

cols


    (cols matrix) -> list
        matrix: an m*n dimensional matrix
        list: a list of n elements, each of which is a list of m elements 
        

returns the matrix in the form of a list of columns


	> (cols (list (list 1 2 3) (list 4 5 6)))
	'(((1) (4)) ((2) (5)) ((3) (6)))	
	

transpose


    (transpose matrix1) -> matrix2
        matrix1: an m*n dimensional matrix
        matrix2: an n*m dimensional matrix 
        

produces the transpose of a given matrix, i.e. replaces the ith row by the ith column


	> (transpose (list (list 1 2 3) (list 4 5 6)))
	'((1 4) (2 5) (3 6))
 	

unitary-check


    (unitary-check matrix) -> bool
        matrix: an m*n dimensional matrix
        bool: #f (false) or #t (true) 
        

checks whether a given matrix is unitary or not, i.e. whether or not its complex-conjugate is its own inverse. All matrices applied on registers in quantum computing must necessarily be unitary. This is one of the major reasons for the reversibility of all quantum gates


	> (unitary-check (list (list 1 0 0) (list 0 0 1) (list 0 1 0)))
	#t
	> (unitary-check (list (list 1 2) (list 4 5)))
	#f	
	

convert-to-matrix


    (convert-to-matrix gate no-of-qubits) -> matrix
        gate: any of the quantum gates defined in Gates or a user-defined one 
        no-of-qubits: positive integer
        matrix: a square matrix of dimension 2no-of-qubits
		

converts a gate (which is actually a λ (reg) ...) into a matrix, that when applied on the register, gives the same output as the gate


	> (convert-to-matrix (λ (in-register)  (register (register-0s in-register) (coeff (* (/ 1 (sqrt 2)) (get-coeff in-register '(1)))))) 1)
	'((1 0) (0 0.7071067811865475))
	> (convert-to-matrix Pauli-X 1)
	'((0 1) (1 0))
	> (convert-to-matrix swap 2)
	'((1 0 0 0) (0 0 1 0) (0 1 0 0) (0 0 0 1))
	

matrix-to-gate


    (matrix-to-gate matrix) -> gate
        matrix: a square matrix of dimension 2x
	gate: corresponding matrix for application on an x-qubit register
		

being the opposite of 'convert-to-matrix', it converts a matrix into a gate (which is actually a λ (reg) ...) that can be applied on the register


	> (matrix-to-gate (list (list 1 0) (list 0 1)))
	#procedure
	> (define tree (register (register (coeff 0.5) (coeff 0+0.5i)) (register (coeff -0.5) (coeff 0-0.5i))))
	> ((matrix-to-gate '((-1 0 0 0) (0 0+1i 0 0) (0 0 0 -1) (0 0 1 0))) tree)
	(register (register (coeff -0.5) (coeff -0.5)) (register (coeff 0+0.5i) (coeff -0.5)))
	

generate-identity


    (generate-identity number) -> matrix
        number: a positive integer
	matrix: a square matrix
		

generates the identity matrix, given its dimension


	> (generate-identity 5)
	'((1 0 0 0 0) (0 1 0 0 0) (0 0 1 0 0) (0 0 0 1 0) (0 0 0 0 1))
	

zeroes


    (zeroes number) -> list
        number: a positive integer
	list: a list of elements
		

yields a list containing the specified number of zeroes


	> (zeroes 4)
	'(0 0 0 0)
	

apply-matrix


    (apply-matrix matrix qubits in-register) -> out-register 
        matrix: a unitary matrix of appropriate dimensions
	qubits : list of elements
	in-register: register
	out-register: register
		

treats the matrix as a gate and applies on the register given as parameter, specifically acting on the qubits mentioned in the list. It will throw an exception if the matrix is not unitary, i.e., it does not represent a quantum mechanical operator.


	> (define tr-1 (register (register (coeff 0.6) (coeff 0)) (register (coeff 0) (coeff 0+0.8i))))
	> (apply-matrix '((-1 0) (0 -1)) '(1) tr-1)
	(register (register (coeff -0.6) (coeff 0)) (register (coeff 0) (coeff 0-0.8i)))
	
	> (define tr-2 (register (register (register (coeff 1) (coeff 2)) (register (coeff 3) (coeff 4)))
					(register (register (coeff 5) (coeff 6)) (register (coeff 7) (coeff 8)))))

	> (apply-matrix '((-1 0 0 0) (0 -1 0 0) (0 0 0 1) (0 0 1 0)) '(2 1) tr-2)
	The given register is not normalised, hence the gate has been applied on its normalized form and the output is 
	(register
	 (register (register (coeff -0.07001400420140048) (coeff 0.28005601680560194)) (register (coeff -0.21004201260420147) (coeff 0.14002800840280097)))
	 (register (register (coeff -0.3500700210070024) (coeff 0.5601120336112039)) (register (coeff -0.4900980294098034) (coeff 0.42008402520840293))))	

	 > (apply-matrix '((-1 0) (2 -1)) '(1) tr-2)
	"In quantum computation, all matrices need to be unitary. Please re-check the given matrix."
	 

In quantum measurement, a wave-function collapses once it is measured and the measurement is probabilistic. We too have implemented these and provided functions measure! as well as partial-measure! that give the output state as well as cause a collapse or partial-collapse of the initial register.

measure


    (measure in-register) -> state
        in-register: register
        state: list of qubits
        

given the register, i.e., coefficients of each of the 2n states for the n qubits, it yields a probabilistic measurement. In the example here, the state |01> is output more frequently due to its higher coefficient.


	> (define tree (register (register (coeff 0) (coeff 1)) (register (coeff 0.8) (coeff 0+0.6i))))
	> (measure tree)
	'(0 1)
	> (measure tree)
	'(1 1)
	> (measure tree)
	'(0 1)
	

measure!


    (measure! in-register) -> state
        in-register: register
        state: list of qubits
        

given the register, i.e., coefficients of each of the 2n states for the n qubits, it yields a probabilistic measurement and also causes a collapse of the register to that single state, so all subsequent measurements yield the same result


	> (define tree (register (register (coeff 0) (coeff 1)) (register (coeff 0.8) (coeff 0+0.6i))))
	> (measure! tree)
	'(1 0)
	> (measure! tree)
	'(1 0)
	> (measure! tree)
	'(1 0)
	

partial-measure!


    (partial-measure! number in-register) -> state
        number: positive integer
        in-register: register
		state: 0 or 1
        

given the register, i.e., coefficients of each of the 2n states for the n qubits, it yields a probabilistic measurement on the qubit mentioned as parameter and causes a collapse of the register to just that state-space. In the example, the |00> and |01> qubits have higher coefficients, thus a partial measurement on the 1st qubit (indexed by 0) causes collapse to these two states.


	> (define tree (register (register (coeff 2) (coeff 2)) (register (coeff 0.8) (coeff 0+0.6i))))
	> (partial-measure! '(0) tree)
	The given register is not normalised, hence the gate has been applied on its normalized form and the output is 
	'(0)
	> tree
	(register (register (coeff 0.7071067811865475) (coeff 0.7071067811865475)) (register (coeff 0) (coeff 0)))
	

coeff-prob-sum


    (coeff-prob-sum in-register) -> real
		in-register: register
		real: a positive real number
		

returns the sum of probabilities associated with each coefficient in a register, used later for normalization


	> (coeff-prob-sum (register (coeff 0.5) (coeff 1+2i)))
	(coeff 5.250000000000001)