Skip to content
Open
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
27 changes: 10 additions & 17 deletions src/braket/quantum_information/pauli_string.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@
}
_ID_OBS = I()
_PAULI_OBSERVABLES = {_PAULI_X: X(), _PAULI_Y: Y(), _PAULI_Z: Z()}
_SIGN_MAP = {"+": 1, "-": -1}
_SIGN_MAP = {"+": 1, "-": -1, "j": 1j, "J": -1j}
_PHASE_MAP = {v: k for k, v in _SIGN_MAP.items()}


class PauliString:
Expand Down Expand Up @@ -64,10 +65,10 @@ def __init__(self, pauli_string: str | PauliString):
raise TypeError(f"Pauli word {pauli_string} must be of type {PauliString} or {str}")

@property
def phase(self) -> int:
"""int: The phase of the Pauli string.
def phase(self) -> complex:
"""complex: The phase of the Pauli string.

Can be one of +/-1
Can be one of four roots of unity, +1, +1j, -1, and -1j.
"""
return self._phase

Expand Down Expand Up @@ -199,15 +200,12 @@ def dot(self, other: PauliString, inplace: bool = False) -> PauliString:
if i not in self._nontrivial:
pauli_result[i] = other._nontrivial[i]

# ignore complex global phase
out_phase = -1 if (phase_result.real < 0 or phase_result.imag < 0) else 1

# Bypass __init__ via __new__ to avoid serializing the computed dict
# back into a string just to have __init__ parse it again. The fields
# below fully define a valid PauliString, so direct assignment is both
# faster and avoids an O(qubit_count) dense-string round trip.
out_pauli_string = PauliString.__new__(PauliString)
out_pauli_string._phase = out_phase
out_pauli_string._phase = phase_result
out_pauli_string._qubit_count = self._qubit_count
out_pauli_string._nontrivial = pauli_result

Expand Down Expand Up @@ -334,11 +332,10 @@ def power(self, n: int, inplace: bool = False) -> PauliString:
# field assignment keeps the hot path allocation-light.
pauli_other = PauliString.__new__(PauliString)
pauli_other._qubit_count = self._qubit_count
pauli_other._phase = self._phase**n
if n % 2 == 0:
pauli_other._phase = 1
pauli_other._nontrivial = {}
else:
pauli_other._phase = self._phase
pauli_other._nontrivial = dict(self._nontrivial)

if inplace:
Expand Down Expand Up @@ -428,14 +425,14 @@ def __repr__(self):
factors = ["I"] * self._qubit_count
for i, p in self._nontrivial.items():
factors[i] = p
return f"{PauliString._phase_to_str(self._phase)}{''.join(factors)}"
return f"{_PHASE_MAP[self._phase]}{''.join(factors)}"

@staticmethod
def _split(pauli_word: str) -> tuple[int, str]:
index = 0
phase = 1
if pauli_word[index] in {"+", "-"}:
phase *= int(f"{pauli_word[index]}1")
if pauli_word[index] in _SIGN_MAP:
phase *= _SIGN_MAP[pauli_word[index]]
index += 1
unsigned = pauli_word[index:]
if not unsigned:
Expand All @@ -444,10 +441,6 @@ def _split(pauli_word: str) -> tuple[int, str]:
raise ValueError(f"{pauli_word} is not a valid Pauli string")
return phase, unsigned

@staticmethod
def _phase_to_str(phase: int) -> str:
return "+" if phase > 0 else "-"

def _generate_eigenstate_circuit(self, signs: tuple[int, ...]) -> Circuit:
circ = Circuit()
for qubit in range(len(signs)):
Expand Down
3 changes: 1 addition & 2 deletions src/braket/quantum_information/pauli_sum.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,8 +208,7 @@ def _pad_pauli_string(pauli_string: PauliString, qubit_count: int) -> PauliStrin
factors = ["I"] * qubit_count
for qubit in range(pauli_string.qubit_count):
factors[qubit] = "IXYZ"[pauli_string[qubit]]
sign = "-" if pauli_string.phase < 0 else "+"
return PauliString(f"{sign}{''.join(factors)}")
return PauliString("".join(factors))

@staticmethod
def _term_from_observable(observable: Observable) -> tuple[numbers.Number, str]:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,8 @@ def test_eigenstate_invalid_signs(sign):
("XYXZY", "+XYXZY", "IIIII"),
("XYZ", "ZYX", "YIY"),
("YZ", "ZX", "-XY"),
("-Z", "Y", "X"),
("Z", "Y", "-X"),
("-Z", "Y", "jX"),
("Z", "Y", "JX"),
],
)
def test_dot(circ_arg_1, circ_arg_2, circ_res):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,20 +35,20 @@ def test_addition_subtraction_and_scalar_multiplication():
def test_multiplication_by_pauli_string():
pauli_sum = PauliStringSum([(2.0, "XY"), (3.0, "ZZ")])

assert (pauli_sum * PauliString("YZ")).to_list() == [(-2.0, "ZX"), (-3.0, "XI")]
assert (pauli_sum * PauliString("YZ")).to_list() == [(-2.0 + 0j, "ZX"), (-3j, "XI")]


def test_multiplication_by_pauli_string_pads_mixed_width_terms():
pauli_sum = PauliStringSum([(1.0, "X"), (2.0, "IZ")])

assert (pauli_sum * PauliString("ZZ")).to_list() == [(-1.0, "YZ"), (2.0, "ZI")]
assert (pauli_sum * PauliString("ZZ")).to_list() == [(-1j, "YZ"), (2.0, "ZI")]


def test_left_multiplication_by_pauli_string_preserves_order():
pauli_sum = PauliStringSum([(2.0, "X")])

assert (pauli_sum * PauliString("Z")).to_list() == [(-2.0, "Y")]
assert (PauliString("Z") * pauli_sum).to_list() == [(2.0, "Y")]
assert (pauli_sum * PauliString("Z")).to_list() == [(-2j, "Y")]
assert (PauliString("Z") * pauli_sum).to_list() == [(2j, "Y")]


def test_indexing_and_membership():
Expand Down
Loading