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

Catch v4 up with v3 #1567

Merged
merged 2 commits into from
Apr 19, 2023
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
419 changes: 22 additions & 397 deletions CHANGELOG.md

Large diffs are not rendered by default.

1,409 changes: 691 additions & 718 deletions poetry.lock

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ packages = [{ include = "pyquil" }]

[tool.poetry.dependencies]
python = "^3.8,<3.12"
numpy = "^1.21"
numpy = "^1.22"
scipy = "^1.7.3"
lark = "^0.11.1"
rpcq = "^3.10.0"
Expand All @@ -47,7 +47,7 @@ black = "^22.8.0"
flake8 = "^3.8.1"
pytest = "^6.2.2"
pytest-cov = "^2.11.1"
mypy = "0.981"
mypy = "^1.2.0"
pytest-xdist = "^2.2.1"
pytest-rerunfailures = "^9.1.1"
pytest-timeout = "^1.4.2"
Expand Down
2 changes: 2 additions & 0 deletions pyquil/_parser/grammar.lark
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ frame_spec : _NEWLINE_TAB frame_attr ":" ( expression | string )
| "DIRECTION"
| "HARDWARE-OBJECT"
| "CENTER-FREQUENCY"
| "ENABLE-RAW-CAPTURE"
| "CHANNEL-DELAY"

def_waveform : "DEFWAVEFORM" waveform_name params ":" matrix

Expand Down
2 changes: 2 additions & 0 deletions pyquil/_parser/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,8 @@ def def_frame(self, frame, *specs):
"INITIAL-FREQUENCY": "initial_frequency",
"SAMPLE-RATE": "sample_rate",
"CENTER-FREQUENCY": "center_frequency",
"ENABLE-RAW-CAPTURE": "enable_raw_capture",
"CHANNEL-DELAY": "channel_delay",
}
options = {}

Expand Down
4 changes: 2 additions & 2 deletions pyquil/api/_qpu.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@ def alloc(spec: ParameterSpec) -> np.ndarray:
}
try:
return np.ndarray((num_shots, spec.length), dtype=dtype[spec.type])
except KeyError:
raise ValueError(f"Unexpected memory type {spec.type}.")
except KeyError as e:
raise ValueError(f"Unexpected memory type {spec.type}.") from e

regions: Dict[str, np.ndarray] = {}

Expand Down
13 changes: 7 additions & 6 deletions pyquil/api/_quantum_computer.py
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,8 @@ def run_symmetrized_readout(
f"The number of trials was modified from {trials} to "
f"{num_shots_per_prog * len(sym_programs)}. To be consistent with the "
f"number of trials required by the type of readout symmetrization "
f"chosen."
f"chosen.",
stacklevel=2,
)

results = _measure_bitstrings(self, sym_programs, meas_qubits, num_shots_per_prog)
Expand Down Expand Up @@ -944,14 +945,14 @@ def local_forest_runtime(
# with 127.0.0.1 to use a valid IP when checking if the port is in use.
if _port_used(host if host != "0.0.0.0" else "127.0.0.1", qvm_port):
warning_msg = ("Unable to start qvm server, since the specified port {} is in use.").format(qvm_port)
warnings.warn(RuntimeWarning(warning_msg))
warnings.warn(RuntimeWarning(warning_msg), stacklevel=2)
else:
qvm_cmd = ["qvm", "-S", "--host", host, "-p", str(qvm_port)]
qvm = subprocess.Popen(qvm_cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)

if _port_used(host if host != "0.0.0.0" else "127.0.0.1", quilc_port):
warning_msg = ("Unable to start quilc server, since the specified port {} is in use.").format(quilc_port)
warnings.warn(RuntimeWarning(warning_msg))
warnings.warn(RuntimeWarning(warning_msg), stacklevel=2)
else:
quilc_cmd = ["quilc", "--host", host, "-p", str(quilc_port), "-R"]

Expand Down Expand Up @@ -1036,7 +1037,7 @@ def _symmetrization(
elif symm_type == -1:
# exhaustive = all possible binary strings
flip_matrix = np.asarray(list(itertools.product([0, 1], repeat=len(meas_qubits))))
elif symm_type >= 0:
else:
flip_matrix = _construct_orthogonal_array(len(meas_qubits), symm_type)

# The next part is not rigorous in the sense that we simply truncate to the desired
Expand Down Expand Up @@ -1137,7 +1138,7 @@ def _construct_orthogonal_array(num_qubits: int, strength: int = 3) -> np.ndarra
flip_matrix = np.concatenate((zero_array, one_array), axis=0).astype(int)
elif strength == 2:
flip_matrix = _construct_strength_two_orthogonal_array(num_qubits)
elif strength == 3:
else: # strength == 3
flip_matrix = _construct_strength_three_orthogonal_array(num_qubits)

return flip_matrix
Expand Down Expand Up @@ -1291,5 +1292,5 @@ def _f(x: int) -> int:

if trials < min_num_trials:
trials = min_num_trials
warnings.warn(f"Number of trials was too low, it is now {trials}.")
warnings.warn(f"Number of trials was too low, it is now {trials}.", stacklevel=2)
return trials
128 changes: 127 additions & 1 deletion pyquil/gates.py
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,23 @@ def RZ(angle: ParameterDesignator, qubit: QubitDesignator) -> Gate:
return Gate(name="RZ", params=[angle], qubits=[unpack_qubit(qubit)])


def U(theta: ParameterDesignator, phi: ParameterDesignator, lam: ParameterDesignator, qubit: QubitDesignator) -> Gate:
"""Produces a generic single-qubit rotation::

U(theta, phi, lam) = [[ cos(theta / 2), -1 * exp(1j*lam) * sin(theta / 2)]
[exp(1j*phi) * sin(theta / 2), exp(1j*(phi+lam)) * cos(theta / 2)]]

Single qubit rotation with 3 Euler angles.

:param theta: The theta Euler angle.
:param phi: The phi Euler angle.
:param lam: The lambda Euler angle.
:param qubit: The qubit to apply the gate to.
:returns: A Gate object.
"""
return Gate(name="U", params=[theta, phi, lam], qubits=[unpack_qubit(qubit)])


def PHASE(angle: ParameterDesignator, qubit: QubitDesignator) -> Gate:
"""Produces the PHASE gate::

Expand Down Expand Up @@ -506,7 +523,7 @@ def XY(angle: ParameterDesignator, q1: QubitDesignator, q2: QubitDesignator) ->
XY(phi) = [[1, 0, 0, 0],
[0, cos(phi/2), 1j * sin(phi/2), 0],
[0, 1j * sin(phi/2), cos(phi/2), 0],
[0, 0, 0, 1]
[0, 0, 0, 1]]

:param angle: The angle of the rotation to apply to the population 1 subspace.
:param q1: Qubit 1.
Expand All @@ -516,6 +533,115 @@ def XY(angle: ParameterDesignator, q1: QubitDesignator, q2: QubitDesignator) ->
return Gate(name="XY", params=[angle], qubits=[unpack_qubit(q) for q in (q1, q2)])


def SQISW(q1: QubitDesignator, q2: QubitDesignator) -> Gate:
"""Produces a SQISW gate::

SQiSW = [[1, 0, 0, 0],
[0, 1 / sqrt(2), 1j / sqrt(2), 0],
[0, 1j / sqrt(2), 1 / sqrt(2), 0],
[0, 0, 0, 1]]

:param q1: Qubit 1.
:param q2: Qubit 2.
:returns: A Gate object.
"""
return Gate(name="SQISW", params=[], qubits=[unpack_qubit(q) for q in (q1, q2)])


def FSIM(theta: ParameterDesignator, phi: ParameterDesignator, q1: QubitDesignator, q2: QubitDesignator) -> Gate:
"""Produces an fsim (Fermionic simulation) gate:

FSIM(theta, phi) = [[1, 0, 0, 0],
[0, cos(theta/2), 1j * sin(theta/2), 0],
[0, 1j * sin(theta/2), cos(theta/2), 0],
[0, 0, 0, exp(1j*phi)]]

:param theta: The angle for the XX + YY rotation.
:param phi: The angle for the ZZ rotation.
:param q1: Qubit 1.
:param q2: Qubit 2.
:returns: A Gate object.
"""
return Gate(name="FSIM", params=[theta, phi], qubits=[unpack_qubit(q) for q in (q1, q2)])


def PHASEDFSIM(
theta: ParameterDesignator,
zeta: ParameterDesignator,
chi: ParameterDesignator,
gamma: ParameterDesignator,
phi: ParameterDesignator,
q1: QubitDesignator,
q2: QubitDesignator,
) -> Gate:
"""Produces an phasedfsim (Fermionic simulation) gate:

PHASEDFSIM(theta, zeta, chi, gamma, phi) = [
[1, 0, 0, 0],
[0, exp(-1j*(gamma+zeta)) * cos(theta/2), 1j* exp(-1j*(gamma-chi)) * sin(theta/2), 0],
[0, 1j* exp(-1j*(gamma+chi)) * sin(theta/2), exp(-1j*(gamma-zeta)) * cos(theta/2), 0],
[0, 0, 0, exp(1j*phi - 2j*gamma)]]

:param theta: The angle for the XX + YY rotation.
:param zeta: Zeta phase.
:param chi: Chi phase.
:param gamma: Gamma phase.
:param phi: The angle for the ZZ rotation.
:param q1: Qubit 1.
:param q2: Qubit 2.
:returns: A Gate object.
"""
return Gate(name="PHASEDFSIM", params=[theta, zeta, chi, gamma, phi], qubits=[unpack_qubit(q) for q in (q1, q2)])


def RZZ(phi: ParameterDesignator, q1: QubitDesignator, q2: QubitDesignator) -> Gate:
"""Produces a RZZ(phi) gate:

RZZ(phi) = [[ exp(-1j*phi/2), 0, 0, 0],
[ 0, exp(1j*phi/2), 0, 0],
[ 0, 0, exp(1j*phi/2), 0],
[ 0, 0, 0, exp(-1j*phi/2)]]

:param phi: The angle for the ZZ rotation.
:param q1: Qubit 1.
:param q2: Qubit 2.
:returns: A Gate object.
"""
return Gate(name="RZZ", params=[phi], qubits=[unpack_qubit(q) for q in (q1, q2)])


def RXX(phi: ParameterDesignator, q1: QubitDesignator, q2: QubitDesignator) -> Gate:
"""Produces a RXX(phi) gate:

RXX(phi) = [[ cos(phi/2), 0, 0, -1j*sin(phi/2)],
[ 0, cos(phi/2), -1j*sin(phi/2), 0],
[ 0, -1j*sin(phi/2), cos(phi/2), 0],
[ -1j*sin(phi/2), 0, 0, cos(phi/2)]]

:param phi: The angle for the XX rotation.
:param q1: Qubit 1.
:param q2: Qubit 2.
:returns: A Gate object.
"""
return Gate(name="RXX", params=[phi], qubits=[unpack_qubit(q) for q in (q1, q2)])


def RYY(phi: ParameterDesignator, q1: QubitDesignator, q2: QubitDesignator) -> Gate:
"""Produces a RYY(phi) gate:

RYY(phi) = [[ cos(phi/2), 0, 0, 1j*sin(phi/2)],
[ 0, cos(phi/2), -1j*sin(phi/2), 0],
[ 0, -1j*sin(phi/2), cos(phi/2), 0],
[ 1j*sin(phi/2), 0, 0, cos(phi/2)]]

:param phi: The angle for the YY rotation.
:param q1: Qubit 1.
:param q2: Qubit 2.
:returns: A Gate object.
"""
return Gate(name="RYY", params=[phi], qubits=[unpack_qubit(q) for q in (q1, q2)])


WAIT = Wait()
"""
This instruction tells the quantum computation to halt. Typically these is used while classical
Expand Down
6 changes: 3 additions & 3 deletions pyquil/latex/_diagram.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,6 @@ def TIKZ_GATE_GROUP(qubits: Sequence[int], width: int, label: str) -> str:
"CNOT": (TIKZ_CONTROL, TIKZ_CNOT_TARGET),
"SWAP": (TIKZ_SWAP, TIKZ_SWAP_TARGET),
"CZ": (TIKZ_CONTROL, lambda: TIKZ_GATE("Z")),
"CPHASE": (TIKZ_CONTROL, TIKZ_CPHASE_TARGET),
}


Expand Down Expand Up @@ -498,8 +497,9 @@ def _build_generic_unitary(self) -> None:
self.diagram.extend_lines_to_common_edge(qubits)

control_qubits = qubits[:controls]
target_qubits = qubits[controls:]
if not self.diagram.is_interval(sorted(target_qubits)):
# sort the target qubit list because the first qubit indicates wire placement on the diagram
target_qubits = sorted(qubits[controls:])
if not self.diagram.is_interval(target_qubits):
raise ValueError(f"Unable to render instruction {instr} which targets non-adjacent qubits.")

for q in control_qubits:
Expand Down
12 changes: 6 additions & 6 deletions pyquil/noise.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,14 @@ def unpack_kraus_matrix(m: Union[List[Any], np.ndarray]) -> np.ndarray:
(as nested lists) representing the element-wise real and imaginary part of m.
:return: A complex square numpy array representing the Kraus operator.
"""
m = np.asarray(m, dtype=complex)
if m.ndim == 3:
m = m[0] + 1j * m[1]
if not m.ndim == 2: # pragma no coverage
matrix = np.asarray(m, dtype=complex)
if matrix.ndim == 3:
matrix = matrix[0] + 1j * matrix[1]
if not matrix.ndim == 2: # pragma no coverage
raise ValueError("Need 2d array.")
if not m.shape[0] == m.shape[1]: # pragma no coverage
if not matrix.shape[0] == matrix.shape[1]: # pragma no coverage
raise ValueError("Need square matrix.")
return m
return matrix

def to_dict(self) -> Dict[str, Any]:
"""
Expand Down
5 changes: 3 additions & 2 deletions pyquil/paulis.py
Original file line number Diff line number Diff line change
Expand Up @@ -677,8 +677,10 @@ def __add__(self, other: Union[PauliDesignator, ExpressionDesignator]) -> "Pauli
other_sum = PauliSum([other])
elif isinstance(other, (Expression, Number, complex)):
other_sum = PauliSum([other * ID()])
else:
elif isinstance(other, PauliSum):
other_sum = other
else:
raise TypeError(f"{type(other)} is not a valid PauliDesignator or ExpressionDesignator")
new_terms = [term.copy() for term in self.terms]
new_terms.extend(other_sum.terms)
new_sum = PauliSum(new_terms)
Expand Down Expand Up @@ -840,7 +842,6 @@ def commuting_sets(pauli_terms: PauliSum) -> List[List[PauliTerm]]:
isAssigned_bool = False
for p in range(m_s): # check if it commutes with each group
if isAssigned_bool is False:

if check_commutation(groups[p], pauli_terms.terms[j]):
isAssigned_bool = True
groups[p].append(pauli_terms.terms[j])
Expand Down
Loading