Skip to content
This repository was archived by the owner on Oct 7, 2019. It is now read-only.
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 30 additions & 27 deletions referenceqvm/unitary_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,16 @@
Note: uses SciPy sparse diagonal (DIA) representation to increase space and
timeefficiency.
"""
from collections import Sequence
from numbers import Integral
import warnings

import scipy.sparse as sps
from referenceqvm.gates import gate_matrix
from pyquil.quilbase import *
from pyquil.paulis import PauliSum

from referenceqvm.gates import gate_matrix

"""
If True, only physically-implementable operations allowed!
i.e. local SWAPS only (topology of QPU is periodic with nearest-neighbor gate
Expand Down Expand Up @@ -67,14 +71,13 @@ def lifted_gate(i, matrix, num_qubits):
"""
# input is checked in parent function apply_gate()
# Find gate size (number of qubits operated on)
quot, rem = divmod(np.log2(matrix.shape[0]), 1)
if rem > 0:
if (matrix.shape[0] & matrix.shape[0] - 1) != 0:
raise TypeError("Invalid gate size. Must be power of 2! "
"Received {} size".format(matrix.shape))
else:
gate_size = np.log2(matrix.shape[0])
# Is starting gate index out of range?
if i < 0 or i >= num_qubits + 1 - gate_size:
if not (0 <= i < num_qubits + 1 - gate_size):
raise ValueError("Gate index out of range!")

# Outer-product to lift gate to complete Hilbert space
Expand Down Expand Up @@ -124,7 +127,7 @@ def two_swap_helper(j, k, num_qubits, qubit_map):
and the new qubit_map, after permutation is made
:rtype: tuple (np.array, np.array)
"""
if j >= num_qubits or k >= num_qubits or j < 0 or k < 0:
if not (0 <= j < num_qubits and 0 <= k < num_qubits):
raise ValueError("Permutation SWAP index not valid")

perm = sps.eye(2 ** num_qubits).astype(np.complex128)
Expand Down Expand Up @@ -176,8 +179,8 @@ def permutation_arbitrary(args, num_qubits):
Done in preparation for arbitrary gate application on
adjacent qubits.

:param tuple args: (int) Qubit indices in the order the gate is
applied to.
:param Sequence args: (int) Qubit indices in the order the gate is
applied to.
:param int num_qubits: Number of qubits in system

:return:
Expand All @@ -187,16 +190,17 @@ def permutation_arbitrary(args, num_qubits):
start_i - starting index to lift gate from
:rtype: tuple (sparse_array, np.array, int)
"""
# Check input
if type(args) is tuple or type(args) is list:
if len(args) is 0:
raise TypeError("Need at least one qubit index to perform"
"permutation")
# Don't permit NoneType or empty sequences, but allow 0
if isinstance(args, Sequence):
if not args:
raise ValueError("Need at least one qubit index to perform"
"permutation")
else:
args = [args]
inds = np.array([value_get(x) for x in list(args)])

inds = np.array([value_get(x) for x in args])
for ind in inds:
if ind >= num_qubits or ind < 0:
if not (0 <= ind < num_qubits):
raise ValueError("Permutation SWAP index not valid")

# Begin construction of permutation
Expand Down Expand Up @@ -273,22 +277,21 @@ def apply_gate(matrix, args, num_qubits):
:return: transformed gate that acts on the specified qubits
:rtype: np.array
"""
if num_qubits < 1 or type(num_qubits) is not int:
if not isinstance(num_qubits, Integral) or num_qubits < 1:
raise ValueError("Improper number of qubits passed.")
if len(matrix.shape) != 2 or matrix.shape[0] != matrix.shape[1]:
raise TypeError("Gate array must be two-dimensional and "
"square matrix.")

# Find gate size (number of qubits operated on)
quot, rem = divmod(np.log2(matrix.shape[0]), 1)
if rem > 0:
if (matrix.shape[0] & matrix.shape[0] - 1) != 0:
raise TypeError("Invalid gate size. Must be power of 2! "
"Received {} size".format(matrix.shape))
else:
gate_size = int(np.log2(matrix.shape[0]))

# Is gate size proper?
if gate_size > num_qubits or gate_size < 1:
if not (1 <= gate_size <= num_qubits):
raise TypeError("Invalid gate size. k-qubit gates supported, for "
"k in [1, num_qubits]")

Expand All @@ -300,15 +303,15 @@ def apply_gate(matrix, args, num_qubits):
raise NotImplementedError("Arbitrary SWAPs not yet implemented")

# Transform qubit indices into ints
if type(args) == tuple or type(args) == list:
args = tuple([value_get(x) for x in args])
if isinstance(args, Sequence):
args = tuple(value_get(x) for x in args)
else:
args = value_get(args)

if start_i != 0:
if start_i:
assert np.allclose(final_map[- gate_size - start_i: - start_i],
np.array(args))
elif start_i == 0:
else:
assert np.allclose(final_map[- gate_size - start_i:], np.array(args))

v_matrix = lifted_gate(start_i, matrix, num_qubits)
Expand All @@ -330,21 +333,21 @@ def tensor_gates(gate_set, defgate_set, pyquil_gate, num_qubits):
:return: input gate lifted to full Hilbert space and applied
:rtype: np.array
"""
if pyquil_gate.operator_name in gate_set.keys():
if pyquil_gate.operator_name in gate_set:
# Input gate set. Assumed to be standard gate set.
dict_check = gate_set
elif pyquil_gate.operator_name in defgate_set.keys():
elif pyquil_gate.operator_name in defgate_set:
# defined_gates
dict_check = defgate_set
else:
raise ValueError("Instruction (presumed a Gate or DefGate) is not "
"found in standard gate set or defined "
"gate set of program!")

args = tuple([value_get(x) for x in pyquil_gate.arguments]) \
args = tuple(value_get(x) for x in pyquil_gate.arguments) \
if dict_check == gate_matrix else tuple(pyquil_gate.arguments)

if len(pyquil_gate.parameters) != 0:
if pyquil_gate.parameters:
gate = apply_gate(dict_check[pyquil_gate.operator_name]
(*[value_get(p) for p in pyquil_gate.parameters]),
args,
Expand Down Expand Up @@ -385,7 +388,7 @@ def tensor_up(pauli_terms, num_qubits):

# check if operator is valid w.r.t the input number of qubits
for term in pauli_terms.terms:
if len(term._ops.keys()) > 0:
if term._ops.keys():
if max(term._ops.keys()) >= num_qubits:
raise IndexError("pauli_terms has higher index than qubits")

Expand Down