From af64df56456f7302255465cb9bac7fbff93e644a Mon Sep 17 00:00:00 2001 From: Thom Dixon Date: Sat, 7 Oct 2017 13:19:10 -0700 Subject: [PATCH 1/3] Clean up unitary_generator a bit 1) Use Python's abstract base classes to remove strict type assertions where they're not necessary 2) Use Python's support for a more mathematical (and therefore verifiable) syntax for relations (e.g., x < y < z) 3) Be more Pythonic in general. --- referenceqvm/unitary_generator.py | 49 +++++++++++++++++-------------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/referenceqvm/unitary_generator.py b/referenceqvm/unitary_generator.py index 15a7fe0..415e5b4 100644 --- a/referenceqvm/unitary_generator.py +++ b/referenceqvm/unitary_generator.py @@ -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 @@ -74,7 +78,7 @@ def lifted_gate(i, matrix, num_qubits): 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 @@ -124,7 +128,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) @@ -187,16 +191,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") - else: + # Don't permit NoneType or empty sequences, but allow 0 + if args is None or (isinstance(args, Sequence) and not args): + raise ValueError("Need at least one qubit index to perform" + "permutation") + + if not isinstance(args, Sequence): 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 @@ -273,7 +278,7 @@ 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 " @@ -288,7 +293,7 @@ def apply_gate(matrix, args, num_qubits): 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]") @@ -300,15 +305,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) @@ -330,10 +335,10 @@ 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: @@ -341,10 +346,10 @@ def tensor_gates(gate_set, defgate_set, pyquil_gate, num_qubits): "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, @@ -385,7 +390,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") From f1a1f43ed7b81cd008aee628c99bcd60469db60e Mon Sep 17 00:00:00 2001 From: Thom Dixon Date: Sat, 7 Oct 2017 14:37:22 -0700 Subject: [PATCH 2/3] Don't use divmod or calculate log2 twice --- referenceqvm/unitary_generator.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/referenceqvm/unitary_generator.py b/referenceqvm/unitary_generator.py index 415e5b4..01d18c1 100644 --- a/referenceqvm/unitary_generator.py +++ b/referenceqvm/unitary_generator.py @@ -71,8 +71,7 @@ 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: @@ -285,8 +284,7 @@ def apply_gate(matrix, args, num_qubits): "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: From 6808ebae75f876f34b4c960e8ba812da7551d8ef Mon Sep 17 00:00:00 2001 From: Thom Dixon Date: Mon, 9 Oct 2017 17:50:15 -0700 Subject: [PATCH 3/3] Code review comments --- referenceqvm/unitary_generator.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/referenceqvm/unitary_generator.py b/referenceqvm/unitary_generator.py index 01d18c1..1344fc8 100644 --- a/referenceqvm/unitary_generator.py +++ b/referenceqvm/unitary_generator.py @@ -179,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: @@ -191,11 +191,11 @@ def permutation_arbitrary(args, num_qubits): :rtype: tuple (sparse_array, np.array, int) """ # Don't permit NoneType or empty sequences, but allow 0 - if args is None or (isinstance(args, Sequence) and not args): - raise ValueError("Need at least one qubit index to perform" - "permutation") - - if not isinstance(args, Sequence): + 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 args])