Skip to content

Commit

Permalink
Catch v4 up with v3 (#1567)
Browse files Browse the repository at this point in the history
* Merge `master` into `v4`

* update numpy and mypy, resolve type issues
  • Loading branch information
MarquessV authored Apr 19, 2023
1 parent 03ff2e2 commit c5e3443
Show file tree
Hide file tree
Showing 27 changed files with 1,483 additions and 1,172 deletions.
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

0 comments on commit c5e3443

Please sign in to comment.