Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Construct multi-qubit Pauli terms and sums from strings #984

Merged
merged 21 commits into from
Sep 20, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
2d6e7f7
Added PauliTerm.matrix() and PauliSum.matrix() methods
jlbosse Mar 29, 2019
49da535
fixed formatting to be flake8 compliant
jlbosse Mar 29, 2019
6b81833
Updated matrix methods to make use of pyquil.unitary_tools.lifted_pauli
jlbosse May 30, 2019
7c0612f
fixed formatting
jlbosse May 30, 2019
553c452
Merge branch 'master' of https://github.com/rigetti/pyquil
jlbosse May 30, 2019
cad37a4
Added @msohaibalams suggestions.
jlbosse Jul 3, 2019
91ee70f
Merge branch 'master' of https://github.com/rigetti/pyquil
jlbosse Aug 31, 2019
9f27c7e
Added better string conversion to paulis.py
jlbosse Aug 31, 2019
5aa27eb
Added checks to PauliTerm.from_list_back
jlbosse Aug 31, 2019
3f5a7c6
Updated the changelog and docs
jlbosse Aug 31, 2019
2891407
fixed regex formatting
jlbosse Aug 31, 2019
5e7a47e
corrected PR number in Changelog.md
jlbosse Aug 31, 2019
917db7d
Fixed a docstring typo in paulis.py
jlbosse Sep 6, 2019
e761981
Merge remote-tracking branch 'rigetti/master' into from_compact_str
jlbosse Sep 18, 2019
f71a67c
Added more tests and assertions to catch wrong pauli strings.
jlbosse Sep 18, 2019
6e3c99b
Added more tests for PauliSum.from_compact_str()
jlbosse Sep 19, 2019
63641b6
added whitespace around operators
jlbosse Sep 19, 2019
6b0bd42
Update CHANGELOG.md
jlbosse Sep 20, 2019
e5ee352
Update CHANGELOG.md
jlbosse Sep 20, 2019
07d6882
Update CHANGELOG.md
jlbosse Sep 20, 2019
d91b12e
updated Changelog and moved the PR 984 to v2.12
jlbosse Sep 20, 2019
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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ Changelog
and a [Feature Request Template](.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md),
which contain sections to fill out when filing a bug or suggesting an enhancement
(@karalekas, gh-985, gh-986).
- `PauliSum` objects can now be constructed from strings via
`from_compact_str()` and `PauliTerm.from_compact_str()` supports multi-qubit
strings (@jlbosse, gh-984).

### Improvements and Changes

Expand Down
1 change: 1 addition & 0 deletions docs/source/apidocs/pauli.rst
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ Classes
~PauliSum.get_qubits
~PauliSum.simplify
~PauliSum.get_programs
~PauliSum.from_compact_str


.. autoclass:: pyquil.paulis.PauliTerm
Expand Down
57 changes: 47 additions & 10 deletions pyquil/paulis.py
Original file line number Diff line number Diff line change
Expand Up @@ -357,23 +357,38 @@ def from_list(cls, terms_list, coefficient=1.0):
def from_compact_str(cls, str_pauli_term):
"""Construct a PauliTerm from the result of str(pauli_term)
"""
coef_str, str_pauli_term = str_pauli_term.split('*')
# split into str_coef, str_op at first '*'' outside parenthesis
try:
coef = int(coef_str)
str_coef, str_op = re.split(r'\*(?![^(]*\))', str_pauli_term,
maxsplit=1)
except ValueError:
raise ValueError("Could not separate the pauli string into "
f"coefficient and operator. {str_pauli_term} does"
" not match <coefficient>*<operator>")

# parse the coefficient into either a float or complex
str_coef = str_coef.replace(' ', '')
try:
coef = float(str_coef)
except ValueError:
try:
coef = float(coef_str)
coef = complex(str_coef)
except ValueError:
coef = complex(coef_str)
raise ValueError("Could not parse the coefficient "
f"{str_coef}")

op = sI() * coef
if str_pauli_term == 'I':
if str_op == 'I':
return op
ma = re.fullmatch(r'(([XYZ])(\d+))+', str_pauli_term)
if ma is None:
raise ValueError(f"Could not parse pauli string {str_pauli_term}")
for ma in re.finditer(r'([XYZ])(\d+)', str_pauli_term):
op *= cls(ma.group(1), int(ma.group(2)))

# parse the operator
str_op = re.sub(r'\*', '', str_op)
if not re.match(r'^(([XYZ])(\d+))+$', str_op):
raise ValueError(f"Could not parse operator string {str_op}. "
r"It should match ^(([XYZ])(\d+))+$")

for factor in re.finditer(r'([XYZ])(\d+)', str_op):
op *= cls(factor.group(1), int(factor.group(2)))

return op

Expand Down Expand Up @@ -677,6 +692,28 @@ def get_programs(self):
coefficients = np.array([term.coefficient for term in self.terms])
return programs, coefficients

def compact_str(self):
"""A string representation of the PauliSum that is more compact than ``str(pauli_sum)``

>>> pauli_sum = 2.0 * sX(1)* sZ(2) + 1.5 * sY(2)
>>> str(pauli_sum)
>>> '2.0*X1*X2 + 1.5*Y2'
>>> pauli_sum.compact_str()
>>> '2.0*X1X2+1.5*Y2'
"""
return "+".join([term.compact_str() for term in self.terms])

@classmethod
def from_compact_str(cls, str_pauli_sum):
"""Construct a PauliSum from the result of str(pauli_sum)
"""
# split str_pauli_sum only at "+" outside of parenthesis to allow
# e.g. "0.5*X0 + (0.5+0j)*Z2"
str_terms = re.split(r'\+(?![^(]*\))', str_pauli_sum)
str_terms = [s.strip() for s in str_terms]
terms = [PauliTerm.from_compact_str(term) for term in str_terms]
return cls(terms).simplify()


def simplify_pauli_sum(pauli_sum):
"""Simplify the sum of Pauli operators according to Pauli algebra rules."""
Expand Down
38 changes: 38 additions & 0 deletions pyquil/tests/test_paulis.py
Original file line number Diff line number Diff line change
Expand Up @@ -676,3 +676,41 @@ def test_identity_no_qubit():
def test_qubit_validation():
with pytest.raises(ValueError):
op = sX(None)


def test_pauli_term_from_str():
# tests that should _not_ fail are in test_pauli_sum_from_str
with pytest.raises(ValueError):
PauliTerm.from_compact_str("X0")
with pytest.raises(ValueError):
PauliTerm.from_compact_str("10")
with pytest.raises(ValueError):
PauliTerm.from_compact_str("1.0X0")
with pytest.raises(ValueError):
PauliTerm.from_compact_str("(1.0+9i)*X0")
with pytest.raises(ValueError):
PauliTerm.from_compact_str("(1.0+0j)*A0")


def test_pauli_sum_from_str():
# this also tests PauliTerm.from_compact_str() since it gets called
Sum = (1.5 + .5j) * sX(0) * sZ(2) + 0.7 * sZ(1)
another_str = "(1.5 + 0.5j)*X0*Z2+.7*Z1"
assert PauliSum.from_compact_str(str(Sum)) == Sum
assert PauliSum.from_compact_str(Sum.compact_str()) == Sum
assert PauliSum.from_compact_str(another_str) == Sum

# test sums of length one
Sum = PauliSum([1 * sY(0) * sY(1)])
the_str = "1*Y0*Y1"
assert PauliSum.from_compact_str(the_str) == Sum

# test sums containing the identity
Sum = (1.5 + .5j) * sX(0) * sZ(2) + 0.7 * sI(1)
the_str = "(1.5 + 0.5j)*X0*Z2+.7*I"
assert PauliSum.from_compact_str(the_str) == Sum

# test the simplification (both in sums and products)
Sum = PauliSum([2 * sY(1)])
the_str = "1*Y0*X0 + (0+1j)*Z0 + 2*Y1"
assert PauliSum.from_compact_str(the_str) == Sum