Skip to content

Commit

Permalink
Merge ae81033 into e0066a2
Browse files Browse the repository at this point in the history
  • Loading branch information
damiansteiger committed May 4, 2017
2 parents e0066a2 + ae81033 commit 7136831
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 45 deletions.
4 changes: 2 additions & 2 deletions projectq/backends/_sim/_simulator_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ def test_simulator_expectation(sim):
expectation = sim.get_expectation_value(op_sum, qureg)
assert 0. == pytest.approx(expectation)

op_id = .4 * QubitOperator()
op_id = .4 * QubitOperator(())
expectation = sim.get_expectation_value(op_id, qureg)
assert .4 == pytest.approx(expectation)

Expand All @@ -261,7 +261,7 @@ def test_simulator_time_evolution(sim):
qubit_to_bit_map, init_wavefunction = copy.deepcopy(eng.backend.cheat())
Qop = QubitOperator
op = 0.3 * Qop("X0 Y1 Z2 Y3 X4")
op += 1.1 * Qop()
op += 1.1 * Qop(())
op += -1.4 * Qop("Y0 Z1 X3 Y5")
op += -1.1 * Qop("Y1 X2 X3 Y4")
TimeEvolution(time_to_evolve, op) | qureg
Expand Down
100 changes: 60 additions & 40 deletions projectq/ops/_qubit_operator.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,21 +67,22 @@ class QubitOperator(object):
hamiltonian = 0.5 * QubitOperator('X0 X5') + 0.3 * QubitOperator('Z0')
Attributes:
terms (dict): **key**: A term represented by a tuple of tuples. Each
tuple represents a local operator and is a Pauli
operator ('I', 'X', 'Y', or 'Z') which acts on one qubit
stored as a tuple. The first element is an integer
terms (dict): **key**: A term represented by a tuple containing all
non-trivial local Pauli operators ('X', 'Y', or 'Z').
A non-trivial local Pauli operator is specified by a
tuple with the first element being an integer
indicating the qubit on which a non-trivial local
operator acts and the second element is a string,
operator acts and the second element being a string,
either 'X', 'Y', or 'Z', indicating which non-trivial
Pauli operator acts on that qubit.
E.g. 'X1 Y5' is ((1, 'X'), (5, 'Y'))
The tuples are sorted according to the qubit number
they act on, starting from 0.
Pauli operator acts on that qubit. Examples:
((1, 'X'),) or ((1, 'X'), (4,'Z')) or the identity ().
The tuples representing the non-trivial local terms
are sorted according to the qubit number they act on,
starting from 0.
**value**: Coefficient of this term as a (complex) float
"""

def __init__(self, term=(), coefficient=1.):
def __init__(self, term=None, coefficient=1.):
"""
Inits a QubitOperator.
Expand All @@ -107,57 +108,76 @@ def __init__(self, term=(), coefficient=1.):
Args:
coefficient (complex float, optional): The coefficient of the
first term of this QubitOperator. Default is 1.0.
term (tuple of tuples, a string, or optional):
1) A sorted tuple of tuples. The first element of each tuple
term (optional, empy tuple, a tuple of tuples, or a string):
1) Default is None which means there are no terms in the
QubitOperator hence it is the "zero" Operator
2) An empty tuple means there are no non-trivial Pauli
operators acting on the qubits hence only identities
with a coefficient (which by default is 1.0).
3) A sorted tuple of tuples. The first element of each tuple
is an integer indicating the qubit on which a non-trivial
local operator acts, starting from zero. The second element
of each tuple is a string, either 'X', 'Y' or 'Z',
indicating which local operator acts on that qubit.
2) A string of the form 'X0 Z2 Y5', indicating an X on
4) A string of the form 'X0 Z2 Y5', indicating an X on
qubit 0, Z on qubit 2, and Y on qubit 5. The string should
be sorted by the qubit number. '' is the identity.
3) default will result in identity operations on all qubits,
which is just an empty tuple '()'
Raises:
QubitOperatorError: Invalid operators provided to QubitOperator.
"""
if not isinstance(term, (tuple, str)):
raise ValueError('term specified incorrectly.')
if not isinstance(coefficient, (int, float, complex)):
raise ValueError('Coefficient must be a numeric type.')

self.terms = {}

if isinstance(term, str):
if term is None:
return
elif isinstance(term, tuple):
if term is ():
self.terms[()] = coefficient
else:
# Test that input is a tuple of tuples and correct action
for local_operator in term:
if (not isinstance(local_operator, tuple) or
len(local_operator) != 2):
raise ValueError("term specified incorrectly.")
qubit_num, action = local_operator
if not isinstance(action, str) or action not in 'XYZ':
raise ValueError("Invalid action provided: must be "
"string 'X', 'Y', or 'Z'.")
if not (isinstance(qubit_num, int) and qubit_num >= 0):
raise QubitOperatorError("Invalid qubit number "
"provided to QubitTerm: "
"must be a non-negative "
"int.")
# Sort and add to self.terms:
term = list(term)
term.sort(key=lambda loc_operator: loc_operator[0])
self.terms[tuple(term)] = coefficient
elif isinstance(term, str):
list_ops = []
for el in term.split():
if len(el) < 2:
raise ValueError('term specified incorrectly.')
list_ops.append((int(el[1:]), el[0]))
list_ops.sort(key=lambda loc_operator: loc_operator[0])
self.terms[tuple(list_ops)] = coefficient
elif isinstance(term, tuple) and len(term) != 0:
term = list(term)
term.sort(key=lambda loc_operator: loc_operator[0])
self.terms[tuple(term)] = coefficient
else:
self.terms[()] = coefficient

# Test correctness
for term in self.terms:
for local_operator in term:
if (len(local_operator) != 2 or
not isinstance(local_operator, tuple)):
raise ValueError('term specified incorrectly.')
# Test that list_ops has correct format of tuples
for local_operator in list_ops:
if (not isinstance(local_operator, tuple) or
len(local_operator) != 2):
raise ValueError("term specified incorrectly.")
qubit_num, action = local_operator
if not isinstance(action, str) or action not in 'XYZ':
raise ValueError("Invalid action provided: must be string"
" 'X', 'Y', or 'Z'.")
raise ValueError("Invalid action provided: must be "
"string 'X', 'Y', or 'Z'.")
if not (isinstance(qubit_num, int) and qubit_num >= 0):
raise QubitOperatorError('Invalid qubit number provided '
'to QubitTerm: must be a'
' non-negative int.')
raise QubitOperatorError("Invalid qubit number "
"provided to QubitTerm: "
"must be a non-negative "
"int.")
# Sort and add to self.terms:
list_ops.sort(key=lambda loc_operator: loc_operator[0])
self.terms[tuple(list_ops)] = coefficient
else:
raise ValueError('term specified incorrectly.')

def compress(self, abs_tol=1e-12):
"""
Expand Down
3 changes: 1 addition & 2 deletions projectq/ops/_qubit_operator_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,7 @@ def test_pauli_operator_product_unchanged():

def test_init_defaults():
loc_op = qo.QubitOperator()
assert len(loc_op.terms) == 1
assert loc_op.terms[()] == 1.0
assert len(loc_op.terms) == 0


@pytest.mark.parametrize("coefficient", [0.5, 0.6j, numpy.float64(2.303),
Expand Down
1 change: 0 additions & 1 deletion projectq/ops/_time_evolution.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,6 @@ def __or__(self, qubits):
for i in range(len(non_trivial_qubits)):
new_index[non_trivial_qubits[i]] = i
new_hamiltonian = QubitOperator()
del new_hamiltonian.terms[()] # remove default identity
assert len(new_hamiltonian.terms) == 0
for term in self.hamiltonian.terms:
new_term = tuple([(new_index[index], action)
Expand Down

0 comments on commit 7136831

Please sign in to comment.