Skip to content
This repository was archived by the owner on Oct 7, 2019. It is now read-only.
Merged
Show file tree
Hide file tree
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
40 changes: 39 additions & 1 deletion referenceqvm/tests/test_unitary_generator.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import pytest
import warnings
import numpy as np
from referenceqvm.unitary_generator import lifted_gate, apply_gate, tensor_gates
from referenceqvm.unitary_generator import (lifted_gate, apply_gate,
tensor_gates, tensor_up)
from referenceqvm.gates import gate_matrix, utility_gates
from pyquil.quil import Program
from pyquil.gates import H as Hgate
from pyquil.gates import RX as RXgate
from pyquil.gates import CNOT as CNOTgate
from pyquil.paulis import PauliTerm, PauliSum


def test_lifted_swap():
Expand Down Expand Up @@ -208,3 +211,38 @@ def test_tensor_gates_two_qubit():
test_unitary = tensor_gates(gate_matrix, {}, prog.actions[0][1], 4).toarray()
true_unitary = apply_gate(gate_matrix['CNOT'], [1, 3], 4).toarray()
assert np.allclose(test_unitary, true_unitary)


def test_tensor_up_error_catch():
"""Testing tensor up type checking"""
x_term = PauliTerm("X", 5)

# testing type rejection
with pytest.raises(TypeError):
tensor_up(x_term, 5)

# testing index rejection
with pytest.raises(IndexError):
tensor_up(PauliSum([x_term]), 3)


def test_tensor_up_correctness():
"""Check the correctness of the tensor up routine"""
xy_term = PauliSum([PauliTerm("X", 0)*PauliTerm("Y", 1)])

# test correctness
trial_matrix = tensor_up(xy_term, 2)
true_matrix = np.kron(gate_matrix['Y'], gate_matrix['X'])
np.testing.assert_allclose(trial_matrix, true_matrix)

x1_term = PauliSum([PauliTerm("X", 1)])
trial_matrix = tensor_up(x1_term, 2)
true_matrix = np.kron(gate_matrix['X'], gate_matrix['I'])
np.testing.assert_allclose(trial_matrix, true_matrix)

zpz_term = PauliTerm("Z", 0) + PauliTerm("Z", 1)
trial_matrix = tensor_up(zpz_term, 2)
true_matrix = np.zeros((4, 4))
true_matrix[0, 0] = 2
true_matrix[-1, -1] = -2
np.testing.assert_allclose(trial_matrix, true_matrix)
47 changes: 47 additions & 0 deletions referenceqvm/unitary_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@
Note: uses SciPy sparse diagonal (DIA) representation to increase space and
timeefficiency.
"""
import warnings
import scipy.sparse as sps
from referenceqvm.gates import gate_matrix
from pyquil.quilbase import *
from pyquil.paulis import PauliSum

"""
If True, only physically-implementable operations allowed!
Expand Down Expand Up @@ -355,6 +357,51 @@ def tensor_gates(gate_set, defgate_set, pyquil_gate, num_qubits):
return gate


def tensor_up(pauli_terms, num_qubits):
"""
Takes a PauliSum object along with a total number of
qubits and returns a matrix corresponding the tensor representation of the
object.

Useful for generating the full Hamiltonian after a particular fermion to
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't clarify the use case. If you say that then you should give an example

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yup.

pauli transformation. For example:

Converting a PauliSum X0Y1 + Y1X0 into the matrix

.. code-block:: python

[[ 0.+0.j, 0.+0.j, 0.+0.j, 0.-2.j],
[ 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],
[ 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],
[ 0.+2.j, 0.+0.j, 0.+0.j, 0.+0.j]]


:param pauli_terms: (PauliSum) object of PauliTerm
:param num_qubits: (int) number of qubits in the system
:returns: (numpy array) representation of the paui_terms operator
"""
if not isinstance(pauli_terms, PauliSum):
raise TypeError("can only tensor PauliSum")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You might wanna check for sanity if the operator gets too big. If you kron up 100 terms then the final matrix will be gargantuan and might kill you.
Otherwise you can always check for sparsity of the gate_matrices.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could do that. I believe in the qam.py there was a hard coded limit of 51 qubits (way beyond what could be represented with a numpy array on a typical machine). I advocated against putting a limit because the formal structure of the QAM doesn't have a limit.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can see that argument. Maybe we can find a middle ground and generate a warning (https://docs.python.org/3/library/warnings.html#module-warnings) beyond some semi-reasonable limit. This will not interrupt the code execution but warn the user that code with large number of qubits might have a performance issue

# 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 max(term._ops.keys()) >= num_qubits:
raise IndexError("pauli_terms has higher index than qubits")

big_hilbert = np.zeros((2 ** num_qubits, 2 ** num_qubits), dtype=complex)
# left kronecker product corresponds to the correct basis ordering
for term in pauli_terms.terms:

tmp_big_hilbert = np.array([1])
for index in range(num_qubits):
tmp_big_hilbert = np.kron(gate_matrix[term[index]], tmp_big_hilbert)

big_hilbert += tmp_big_hilbert * term.coefficient

return big_hilbert


def value_get(param_obj):
"""
Function that returns the raw number / string stored in certain pyQuil
Expand Down