From 0832596e605dac248aadf47f56f0d9aaf887be77 Mon Sep 17 00:00:00 2001 From: Peter Karalekas Date: Mon, 23 Dec 2019 13:57:01 -0800 Subject: [PATCH 1/9] Ignore as few of the flake8 rules as is possible --- .flake8 | 13 ++++++-- CONTRIBUTING.md | 19 ++++++----- examples/1.3_vqe_demo.py | 3 +- examples/qaoa_ansatz.py | 1 - examples/website-script.py | 2 +- pyproject.toml | 6 ++-- pyquil/api/_base_connection.py | 1 - pyquil/api/_compiler.py | 2 +- pyquil/api/_qpu.py | 2 +- pyquil/api/_qvm.py | 1 - pyquil/api/tests/test_config.py | 1 - pyquil/experiment/_memory.py | 1 - pyquil/experiment/_setting.py | 4 --- pyquil/gates.py | 6 +--- pyquil/latex/_diagram.py | 1 - pyquil/latex/tests/test_latex.py | 2 +- pyquil/noise.py | 1 - pyquil/numpy_simulator.py | 1 - pyquil/pyqvm.py | 12 +------ pyquil/quil.py | 4 +-- pyquil/tests/test_gate_matrices.py | 25 ++++++--------- pyquil/tests/test_magic.py | 10 +++++- pyquil/tests/test_noise.py | 4 +-- pyquil/tests/test_numpy_simulator.py | 2 +- pyquil/tests/test_operator_estimation.py | 4 +-- pyquil/tests/test_parameters.py | 1 - pyquil/tests/test_parser.py | 32 ++++++++++++++++++- pyquil/tests/test_paulis.py | 2 +- pyquil/tests/test_paulis_with_placeholders.py | 4 +-- pyquil/tests/test_qpu.py | 5 ++- pyquil/tests/test_quantum_computer.py | 3 +- pyquil/tests/test_quil.py | 4 +-- pyquil/tests/test_qvm.py | 4 +-- .../tests/test_reference_density_simulator.py | 2 +- pyquil/tests/test_unitary_tools.py | 2 +- 35 files changed, 99 insertions(+), 88 deletions(-) diff --git a/.flake8 b/.flake8 index f1038c9fc..315ae9d51 100644 --- a/.flake8 +++ b/.flake8 @@ -1,4 +1,13 @@ [flake8] +# we choose to use 100 rather than Black's default 88 max-line-length = 100 -ignore = E203,E999,E741,E126,E127,F401,F403,F405,F811,F841,E743,W503 -exclude = gen3,external +# E203 : space before ":" (not PEP8 compliant) +# E741 : ambiguous variable name (unfortunately we need to use "I") +# E743 : ambiguous function definition (unfortunately we need to use "I") +# W503 : line break before binary operator (not PEP8 compliant) +ignore = E203, E741, E743, W503 +# __init__.py : unused imports due to backwards compatibility +# external : vendored external libraries +# gen3 : autogenerated parser code +# operator_estimation.py : unused imports due to backwards compatibility +exclude = __init__.py, external, gen3, operator_estimation.py diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2c58db0b7..62e918bb9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -86,7 +86,6 @@ We've made opening one easy by providing a [Pull Request Template](.github/PULL_ that includes a checklist of things to complete before asking for code review. We look forward to reviewing your work! 🙂 - Developer How-Tos ----------------- @@ -98,19 +97,22 @@ tests yourself locally by running `make style` (to check for violations of the ` and `make formatcheck` (to see if `black` would reformat the code) in the top-level directory of the repository. If you aren't presented with any errors, then that means your code is good enough for the linter (`flake8`) and formatter (`black`). If `make formatcheck` fails, it will present -you with a diff, which you can resolve by running `make format`. Black is very opinionated, but +you with a diff, which you can resolve by running `make format`. Black is very opinionated, but saves a lot of time by removing the need for style nitpicks in PR review. We only deviate from its default behavior in one category: we choose to use a line length of 100 rather than the Black -default of 88 (this is configured in the [`pyproject.toml`](pyproject.toml) file). +default of 88 (this is configured in the [`pyproject.toml`](pyproject.toml) file). As for `flake8`, +we ignore a couple of its rules (all for good reasons), and the specific configuration can be +found in the [`.flake8`](.flake8) file. In addition to linting and formatting, we are in the process of rolling out the use of type hints for all parameters and return values, using the [PEP 484 syntax][pep-484]. This is being done on a file-by-file basis, and for ones that have been completed we now have a `make typecheck` command -that will enforce the use of types in those files as part of the CI. When a file is transitioned, -it should be added to the list in the `typecheck` target of the [`Makefile`](Makefile). Because we -use the `typing` module, types (e.g. `type` and `rtype` entries) should be omitted when writing -(useful) [Sphinx-style](https://sphinx-rtd-tutorial.readthedocs.io/en/latest/docstrings.html) -docstrings for classes, methods, and functions. +that will enforce the use of types in those files as part of the CI, using the popular static +typechecker [mypy](http://mypy-lang.org/). When a file is transitioned, it should be added to the +list in the `typecheck` target of the [`Makefile`](Makefile). For more information on the specific +configuration of `mypy` that we use for typechecking, please refer to the [`mypy.ini`](mypy.ini) +file. Also, because we use the `typing` module, types (e.g. `type` and `rtype` entries) should be +omitted when writing (useful) [Sphinx-style][sphinx] docstrings for classes, methods, and functions. As useful shorthand, all of these style-related tests can be run locally with a single command, by running the following: @@ -120,6 +122,7 @@ make checkall ``` [pep-484]: https://www.python.org/dev/peps/pep-0484/ +[sphinx]: https://sphinx-rtd-tutorial.readthedocs.io/en/latest/docstrings.html ### Running the Unit Tests diff --git a/examples/1.3_vqe_demo.py b/examples/1.3_vqe_demo.py index 44928b99f..88118a01e 100644 --- a/examples/1.3_vqe_demo.py +++ b/examples/1.3_vqe_demo.py @@ -2,7 +2,6 @@ This is a demo of VQE through the forest stack. We will do the H2 binding from the Google paper using OpenFermion to generate Hamiltonians and Forest to simulate the system """ -import sys import numpy as np import matplotlib.pyplot as plt @@ -18,7 +17,7 @@ from pyquil.quil import Program from pyquil.paulis import sX, sY, exponentiate, PauliSum -from pyquil.gates import X, I +from pyquil.gates import X from pyquil.api import QVMConnection from pyquil.unitary_tools import tensor_up diff --git a/examples/qaoa_ansatz.py b/examples/qaoa_ansatz.py index 32cc97da3..85b23365e 100755 --- a/examples/qaoa_ansatz.py +++ b/examples/qaoa_ansatz.py @@ -26,7 +26,6 @@ from pyquil.quil import Program from pyquil.gates import H from pyquil.paulis import sI, sX, sZ, exponentiate_commuting_pauli_sum -from pyquil.api import QVMConnection # Create a 4-node array graph: 0-1-2-3. graph = [(0, 1), (1, 2), (2, 3)] diff --git a/examples/website-script.py b/examples/website-script.py index e02d2fa98..4eb91f30d 100755 --- a/examples/website-script.py +++ b/examples/website-script.py @@ -10,7 +10,7 @@ """ from pyquil import Program, get_qc -from pyquil.gates import * +from pyquil.gates import H, CNOT # construct a Bell State program p = Program(H(0), CNOT(0, 1)) diff --git a/pyproject.toml b/pyproject.toml index d14cc5180..c1c665699 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,8 +17,8 @@ exclude = ''' | build | dist )/ - | gen3 # pyquil-specific - | external + | external # pyquil-specific + | gen3 ) -''' \ No newline at end of file +''' diff --git a/pyquil/api/_base_connection.py b/pyquil/api/_base_connection.py index 1c418c247..35ea04524 100644 --- a/pyquil/api/_base_connection.py +++ b/pyquil/api/_base_connection.py @@ -30,7 +30,6 @@ from pyquil.api._error_reporting import _record_call from pyquil.api._errors import error_mapping, UserMessageError, UnknownApiError, TooManyQubitsError from pyquil.api._logger import logger -from pyquil.device import Specs, ISA from pyquil.wavefunction import Wavefunction TYPE_EXPECTATION = "expectation" diff --git a/pyquil/api/_compiler.py b/pyquil/api/_compiler.py index c33a38bd0..2f13e19fd 100644 --- a/pyquil/api/_compiler.py +++ b/pyquil/api/_compiler.py @@ -34,7 +34,7 @@ from urllib.parse import urljoin from pyquil import __version__ -from pyquil.api._base_connection import ForestSession, get_session +from pyquil.api._base_connection import ForestSession from pyquil.api._qac import AbstractCompiler from pyquil.api._error_reporting import _record_call from pyquil.api._errors import UserMessageError diff --git a/pyquil/api/_qpu.py b/pyquil/api/_qpu.py index b3bc314c6..8334d92e6 100644 --- a/pyquil/api/_qpu.py +++ b/pyquil/api/_qpu.py @@ -24,7 +24,7 @@ from pyquil import Program from pyquil.parser import parse -from pyquil.api._base_connection import ForestSession, get_session +from pyquil.api._base_connection import ForestSession from pyquil.api._error_reporting import _record_call from pyquil.api._errors import UserMessageError from pyquil.api._logger import logger diff --git a/pyquil/api/_qvm.py b/pyquil/api/_qvm.py index ad739c83f..137fadd1c 100644 --- a/pyquil/api/_qvm.py +++ b/pyquil/api/_qvm.py @@ -34,7 +34,6 @@ from pyquil.api._config import PyquilConfig from pyquil.api._error_reporting import _record_call from pyquil.api._qam import QAM -from pyquil.device import Device from pyquil.gates import MOVE, MemoryReference from pyquil.noise import apply_noise_model from pyquil.paulis import PauliSum diff --git a/pyquil/api/tests/test_config.py b/pyquil/api/tests/test_config.py index 8bf14287c..f1e6454a8 100644 --- a/pyquil/api/tests/test_config.py +++ b/pyquil/api/tests/test_config.py @@ -1,5 +1,4 @@ import pytest -import os from pyquil.api._config import PyquilConfig from pyquil.api._errors import UserMessageError diff --git a/pyquil/experiment/_memory.py b/pyquil/experiment/_memory.py index 19c88d1fa..ddf693df2 100644 --- a/pyquil/experiment/_memory.py +++ b/pyquil/experiment/_memory.py @@ -20,7 +20,6 @@ import numpy as np from pyquil.paulis import PauliTerm -from pyquil.experiment._symmetrization import SymmetrizationLevel def euler_angles_RX(theta: float) -> Tuple[float, float, float]: diff --git a/pyquil/experiment/_setting.py b/pyquil/experiment/_setting.py index b60db7cbc..b2e1b8bdb 100644 --- a/pyquil/experiment/_setting.py +++ b/pyquil/experiment/_setting.py @@ -24,10 +24,6 @@ from typing import Iterable, Tuple from pyquil.paulis import PauliTerm, sI, is_identity -from pyquil.experiment._memory import ( - pauli_term_to_measurement_memory_map, - pauli_term_to_preparation_memory_map, -) if sys.version_info < (3, 7): from pyquil.external.dataclasses import dataclass diff --git a/pyquil/gates.py b/pyquil/gates.py index a4c483bae..e2f77c170 100644 --- a/pyquil/gates.py +++ b/pyquil/gates.py @@ -14,18 +14,14 @@ # limitations under the License. ############################################################################## from warnings import warn -from typing import Any, Callable, Mapping, Optional, Tuple, Union, overload +from typing import Callable, Mapping, Optional, Tuple, Union from pyquil.quilatom import ( Addr, - Expression, MemoryReference, MemoryReferenceDesignator, - Parameter, ParameterDesignator, - Qubit, QubitDesignator, - QubitPlaceholder, unpack_classical_reg, unpack_qubit, ) diff --git a/pyquil/latex/_diagram.py b/pyquil/latex/_diagram.py index 045b077a4..d8ea35ab0 100644 --- a/pyquil/latex/_diagram.py +++ b/pyquil/latex/_diagram.py @@ -23,7 +23,6 @@ from pyquil.quilbase import ( AbstractInstruction, Wait, - Reset, ResetQubit, JumpConditional, JumpWhen, diff --git a/pyquil/latex/tests/test_latex.py b/pyquil/latex/tests/test_latex.py index 101d3bb31..a52060e9f 100644 --- a/pyquil/latex/tests/test_latex.py +++ b/pyquil/latex/tests/test_latex.py @@ -3,7 +3,7 @@ from pyquil.quil import Program, Pragma from pyquil.quilbase import Declare, Measurement, JumpTarget, Jump from pyquil.quilatom import MemoryReference, Label -from pyquil.gates import H, X, Y, RX, CZ, SWAP, MEASURE, CNOT, RESET, WAIT, MOVE +from pyquil.gates import H, X, Y, RX, CZ, SWAP, MEASURE, CNOT, WAIT, MOVE from pyquil.latex import to_latex, DiagramSettings from pyquil.latex._diagram import split_on_terminal_measures diff --git a/pyquil/noise.py b/pyquil/noise.py index 9abf2e865..98c8eaec1 100644 --- a/pyquil/noise.py +++ b/pyquil/noise.py @@ -468,7 +468,6 @@ def _decoherence_noise_model( kraus_maps = [] for g in gates: targets = tuple(t.index for t in g.qubits) - key = (g.name, tuple(g.params)) if g.name in NO_NOISE: continue matrix, _ = get_noisy_gate(g.name, g.params) diff --git a/pyquil/numpy_simulator.py b/pyquil/numpy_simulator.py index ba09343f7..f00a3e84d 100644 --- a/pyquil/numpy_simulator.py +++ b/pyquil/numpy_simulator.py @@ -18,7 +18,6 @@ import numpy as np from numpy.random.mtrand import RandomState -from pyquil import Program from pyquil.gate_matrices import QUANTUM_GATES from pyquil.paulis import PauliTerm, PauliSum from pyquil.quilbase import Gate diff --git a/pyquil/pyqvm.py b/pyquil/pyqvm.py index c58902679..63aa59ac0 100644 --- a/pyquil/pyqvm.py +++ b/pyquil/pyqvm.py @@ -15,8 +15,7 @@ ############################################################################## import warnings from abc import ABC, abstractmethod -from collections import defaultdict -from typing import Type, Dict, Tuple, Union, List, Sequence +from typing import Dict, List, Sequence, Type, Union import numpy as np from numpy.random.mtrand import RandomState @@ -52,15 +51,6 @@ ClassicalDiv, ClassicalMove, ClassicalExchange, - ClassicalConvert, - ClassicalLoad, - ClassicalStore, - ClassicalComparison, - ClassicalEqual, - ClassicalLessThan, - ClassicalLessEqual, - ClassicalGreaterThan, - ClassicalGreaterEqual, Jump, Pragma, Declare, diff --git a/pyquil/quil.py b/pyquil/quil.py index cb33b141e..bc4d7a92b 100644 --- a/pyquil/quil.py +++ b/pyquil/quil.py @@ -39,7 +39,7 @@ unpack_classical_reg, unpack_qubit, ) -from pyquil.gates import MEASURE, H, RESET +from pyquil.gates import MEASURE, RESET from pyquil.quilbase import ( DefGate, Gate, @@ -65,7 +65,7 @@ class Program(object): """A list of pyQuil instructions that comprise a quantum program. >>> from pyquil import Program - >>> from pyquil.gates import * + >>> from pyquil.gates import H, CNOT >>> p = Program() >>> p += H(0) >>> p += CNOT(0, 1) diff --git a/pyquil/tests/test_gate_matrices.py b/pyquil/tests/test_gate_matrices.py index f8e68c7bd..9534e0b82 100644 --- a/pyquil/tests/test_gate_matrices.py +++ b/pyquil/tests/test_gate_matrices.py @@ -11,21 +11,16 @@ def test_singleq(): - I = QUANTUM_GATES["I"] - assert np.isclose(I, np.eye(2)).all() - X = QUANTUM_GATES["X"] - assert np.isclose(X, np.array([[0, 1], [1, 0]])).all() - Y = QUANTUM_GATES["Y"] - assert np.isclose(Y, np.array([[0, -1j], [1j, 0]])).all() - Z = QUANTUM_GATES["Z"] - assert np.isclose(Z, np.array([[1, 0], [0, -1]])).all() - - H = QUANTUM_GATES["H"] - assert np.isclose(H, (1.0 / np.sqrt(2)) * np.array([[1, 1], [1, -1]])).all() - S = QUANTUM_GATES["S"] - assert np.isclose(S, np.array([[1.0, 0], [0, 1j]])).all() - T = QUANTUM_GATES["T"] - assert np.isclose(T, np.array([[1.0, 0.0], [0.0, np.exp(1.0j * np.pi / 4.0)]])).all() + assert np.isclose(QUANTUM_GATES["I"], np.eye(2)).all() + assert np.isclose(QUANTUM_GATES["X"], np.array([[0, 1], [1, 0]])).all() + assert np.isclose(QUANTUM_GATES["Y"], np.array([[0, -1j], [1j, 0]])).all() + assert np.isclose(QUANTUM_GATES["Z"], np.array([[1, 0], [0, -1]])).all() + + assert np.isclose(QUANTUM_GATES["H"], (1.0 / np.sqrt(2)) * np.array([[1, 1], [1, -1]])).all() + assert np.isclose(QUANTUM_GATES["S"], np.array([[1.0, 0], [0, 1j]])).all() + assert np.isclose( + QUANTUM_GATES["T"], np.array([[1.0, 0.0], [0.0, np.exp(1.0j * np.pi / 4.0)]]) + ).all() def test_parametric(): diff --git a/pyquil/tests/test_magic.py b/pyquil/tests/test_magic.py index 7f33696e5..91c97077b 100644 --- a/pyquil/tests/test_magic.py +++ b/pyquil/tests/test_magic.py @@ -1,4 +1,12 @@ -from pyquil.magic import * +from pyquil.magic import ( + CNOT, + H, + I, + MEASURE, + X, + Program, + magicquil, +) @magicquil diff --git a/pyquil/tests/test_noise.py b/pyquil/tests/test_noise.py index a8ce322cd..180e30003 100644 --- a/pyquil/tests/test_noise.py +++ b/pyquil/tests/test_noise.py @@ -3,7 +3,7 @@ import numpy as np from unittest.mock import Mock -from pyquil.gates import CZ, RZ, RX, I, H +from pyquil.gates import CZ, I, RX, RZ from pyquil.noise import ( pauli_kraus_map, damping_kraus_map, @@ -134,7 +134,7 @@ def test_decoherence_noise(): # verify that gate names are translated new_prog = apply_noise_model(prog, m3) - new_gates = _get_program_gates(new_prog) + _get_program_gates(new_prog) # check that headers have been embedded headers = _noise_model_program_header(m3) diff --git a/pyquil/tests/test_numpy_simulator.py b/pyquil/tests/test_numpy_simulator.py index 3c217ec64..3ba565b70 100644 --- a/pyquil/tests/test_numpy_simulator.py +++ b/pyquil/tests/test_numpy_simulator.py @@ -5,7 +5,7 @@ from pyquil import Program from pyquil.gate_matrices import QUANTUM_GATES as GATES -from pyquil.gates import * +from pyquil.gates import CCNOT, CNOT, H, MEASURE, RX, X from pyquil.numpy_simulator import ( targeted_einsum, NumpyWavefunctionSimulator, diff --git a/pyquil/tests/test_operator_estimation.py b/pyquil/tests/test_operator_estimation.py index cfc26b588..971227b22 100644 --- a/pyquil/tests/test_operator_estimation.py +++ b/pyquil/tests/test_operator_estimation.py @@ -162,7 +162,7 @@ def test_no_complex_coeffs(forest): [ExperimentSetting(TensorProductState(), 1.0j * sY(0))], program=Program(X(0)) ) with pytest.raises(ValueError): - res = list(measure_observables(qc, suite, n_shots=2000)) + list(measure_observables(qc, suite, n_shots=2000)) def test_max_weight_operator_1(): @@ -415,7 +415,7 @@ def test_measure_observables_no_symm_calibr_raises_error(forest): exptsetting = ExperimentSetting(plusZ(0), sX(0)) suite = TomographyExperiment([exptsetting], program=Program(I(0)), symmetrization=0) with pytest.raises(ValueError): - result = list(measure_observables(qc, suite, calibrate_readout="plus-eig")) + list(measure_observables(qc, suite, calibrate_readout="plus-eig")) def test_ops_bool_to_prog(): diff --git a/pyquil/tests/test_parameters.py b/pyquil/tests/test_parameters.py index 2febda466..ebbbd7353 100644 --- a/pyquil/tests/test_parameters.py +++ b/pyquil/tests/test_parameters.py @@ -8,7 +8,6 @@ quil_cos, quil_sqrt, quil_exp, - quil_cis, _contained_parameters, format_parameter, quil_cis, diff --git a/pyquil/tests/test_parser.py b/pyquil/tests/test_parser.py index aa8b1c0d7..71c208681 100644 --- a/pyquil/tests/test_parser.py +++ b/pyquil/tests/test_parser.py @@ -16,7 +16,37 @@ import numpy as np import pytest -from pyquil.gates import * +from pyquil.gates import ( + ADD, + AND, + CNOT, + CONVERT, + CPHASE00, + DIV, + EQ, + EXCHANGE, + Gate, + GE, + GT, + H, + IOR, + LE, + LOAD, + LT, + MEASURE, + MOVE, + MUL, + NOP, + NOT, + RESET, + RX, + STORE, + SUB, + SWAP, + WAIT, + X, + XOR, +) from pyquil.parser import parse from pyquil.quilatom import MemoryReference, Parameter, quil_cos, quil_sin from pyquil.quilbase import Declare, Reset, ResetQubit diff --git a/pyquil/tests/test_paulis.py b/pyquil/tests/test_paulis.py index 5710db2ea..c4cf19835 100644 --- a/pyquil/tests/test_paulis.py +++ b/pyquil/tests/test_paulis.py @@ -776,7 +776,7 @@ def test_identity_no_qubit(): def test_qubit_validation(): with pytest.raises(ValueError): - op = sX(None) + sX(None) def test_pauli_term_from_str(): diff --git a/pyquil/tests/test_paulis_with_placeholders.py b/pyquil/tests/test_paulis_with_placeholders.py index e705713f7..3449c8e1f 100644 --- a/pyquil/tests/test_paulis_with_placeholders.py +++ b/pyquil/tests/test_paulis_with_placeholders.py @@ -172,7 +172,7 @@ def test_ids(): # Not sortable with pytest.raises(TypeError): with pytest.warns(FutureWarning): - t = term_1.id() == term_2.id() + term_1.id() == term_2.id() def test_ids_no_sort(): @@ -669,7 +669,7 @@ def test_from_list(): with pytest.raises(ValueError): # terms are not on disjoint qubits - pterm = PauliTerm.from_list([("X", q[0]), ("Y", q[0])]) + PauliTerm.from_list([("X", q[0]), ("Y", q[0])]) def test_ordered(): diff --git a/pyquil/tests/test_qpu.py b/pyquil/tests/test_qpu.py index d68bbf782..a221e1099 100644 --- a/pyquil/tests/test_qpu.py +++ b/pyquil/tests/test_qpu.py @@ -10,7 +10,6 @@ from pyquil.api._base_connection import Engagement, get_session from pyquil.api._compiler import _collect_classical_memory_write_locations from pyquil.api._config import PyquilConfig -from pyquil.api._errors import UserMessageError from pyquil.api._qpu import _extract_bitstrings from pyquil.device import NxDevice from pyquil.gates import I, X @@ -246,12 +245,12 @@ def test_run_expects_executable(qvm, qpu_compiler): p = Program(X(0)) with pytest.raises(TypeError): - result = qc.run(p) + qc.run(p) def test_qpu_not_engaged_error(): with pytest.raises(ValueError): - qpu = QPU() + QPU() def test_qpu_does_not_engage_without_session(): diff --git a/pyquil/tests/test_quantum_computer.py b/pyquil/tests/test_quantum_computer.py index 574e46fdc..687c4ec0c 100644 --- a/pyquil/tests/test_quantum_computer.py +++ b/pyquil/tests/test_quantum_computer.py @@ -7,7 +7,6 @@ from pyquil import Program, get_qc, list_quantum_computers from pyquil.api import QVM, QuantumComputer, local_forest_runtime -from pyquil.tests.utils import DummyCompiler from pyquil.api._quantum_computer import ( _symmetrization, _flip_array_to_prog, @@ -21,7 +20,7 @@ _check_min_num_trials_for_symmetrized_readout, ) from pyquil.device import NxDevice, gates_in_isa -from pyquil.gates import * +from pyquil.gates import CNOT, H, I, MEASURE, RX, X from pyquil.quilbase import Declare, MemoryReference from pyquil.noise import decoherence_noise_with_asymmetric_ro from pyquil.pyqvm import PyQVM diff --git a/pyquil/tests/test_quil.py b/pyquil/tests/test_quil.py index b7b01381a..0d7bd47bd 100755 --- a/pyquil/tests/test_quil.py +++ b/pyquil/tests/test_quil.py @@ -406,7 +406,7 @@ def test_dagger(): assert p.dagger().out() == "DAGGER H 0\nDAGGER X 0\n" p = Program(X(0), MEASURE(0, MemoryReference("ro", 0))) - with pytest.raises(ValueError) as e: + with pytest.raises(ValueError): p.dagger().out() # ensure that modifiers are preserved https://github.com/rigetti/pyquil/pull/914 @@ -1385,12 +1385,10 @@ def test_params_pi_and_precedence(): exp = str(prog[0].params[0]) assert _eval_as_np_pi(trivial_pi) == _eval_as_np_pi(exp) - less_trivial_pi = "3 * theta[0] * 2 / (pi)" prog = Program(f"RX({trivial_pi}) 0") exp = str(prog[0].params[0]) assert _eval_as_np_pi(trivial_pi) == _eval_as_np_pi(exp) - more_less_trivial_pi = "3 / (theta[0] / (pi + 1)) / pi" prog = Program(f"RX({trivial_pi}) 0") exp = str(prog[0].params[0]) assert _eval_as_np_pi(trivial_pi) == _eval_as_np_pi(exp) diff --git a/pyquil/tests/test_qvm.py b/pyquil/tests/test_qvm.py index cccc6d1eb..a7509d1bf 100644 --- a/pyquil/tests/test_qvm.py +++ b/pyquil/tests/test_qvm.py @@ -1,13 +1,11 @@ -import networkx as nx import numpy as np import pytest from rpcq.messages import PyQuilExecutableResponse from pyquil import Program -from pyquil.api import QVM, ForestConnection, QVMCompiler +from pyquil.api import ForestConnection, QVM from pyquil.api._compiler import _extract_program_from_pyquil_executable_response -from pyquil.device import NxDevice from pyquil.gates import MEASURE, X, CNOT, H from pyquil.quilbase import Declare, MemoryReference diff --git a/pyquil/tests/test_reference_density_simulator.py b/pyquil/tests/test_reference_density_simulator.py index 57dfe68c0..4e19039a1 100644 --- a/pyquil/tests/test_reference_density_simulator.py +++ b/pyquil/tests/test_reference_density_simulator.py @@ -4,7 +4,7 @@ import pyquil.gate_matrices as qmats from pyquil import Program -from pyquil.gates import * +from pyquil.gates import CNOT, H, I, MEASURE, PHASE, RX, RY, RZ, X from pyquil.pyqvm import PyQVM from pyquil.reference_simulator import ReferenceDensitySimulator, _is_valid_quantum_state from pyquil.unitary_tools import lifted_gate_matrix diff --git a/pyquil/tests/test_unitary_tools.py b/pyquil/tests/test_unitary_tools.py index 1b69e268a..e330b234b 100644 --- a/pyquil/tests/test_unitary_tools.py +++ b/pyquil/tests/test_unitary_tools.py @@ -3,7 +3,7 @@ from pyquil import Program from pyquil import gate_matrices as mat -from pyquil.gates import * +from pyquil.gates import CCNOT, CNOT, CZ, H, MEASURE, PHASE, RX, RY, RZ, X, Y, Z from pyquil.experiment import plusX, minusZ from pyquil.paulis import sX, sY, sZ from pyquil.unitary_tools import ( From d66b861712b169614c568c46d73e1d190e539e09 Mon Sep 17 00:00:00 2001 From: Peter Karalekas Date: Mon, 23 Dec 2019 14:35:46 -0800 Subject: [PATCH 2/9] Resolve strange Sphinx issue with CPHASE type annotations --- .flake8 | 4 ++-- pyquil/gates.py | 12 +++++++++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/.flake8 b/.flake8 index 315ae9d51..2a43f6bef 100644 --- a/.flake8 +++ b/.flake8 @@ -6,8 +6,8 @@ max-line-length = 100 # E743 : ambiguous function definition (unfortunately we need to use "I") # W503 : line break before binary operator (not PEP8 compliant) ignore = E203, E741, E743, W503 -# __init__.py : unused imports due to backwards compatibility +# __init__.py : unused imports for maintaining backwards compatibility / exposing an API # external : vendored external libraries # gen3 : autogenerated parser code -# operator_estimation.py : unused imports due to backwards compatibility +# operator_estimation.py : unused imports for maintaining backwards compatibility exclude = __init__.py, external, gen3, operator_estimation.py diff --git a/pyquil/gates.py b/pyquil/gates.py index e2f77c170..976b5233e 100644 --- a/pyquil/gates.py +++ b/pyquil/gates.py @@ -16,8 +16,11 @@ from warnings import warn from typing import Callable, Mapping, Optional, Tuple, Union +import numpy as np + from pyquil.quilatom import ( Addr, + Expression, MemoryReference, MemoryReferenceDesignator, ParameterDesignator, @@ -378,7 +381,14 @@ def CPHASE10(angle: ParameterDesignator, control: QubitDesignator, target: Qubit return Gate(name="CPHASE10", params=[angle], qubits=qubits) -def CPHASE(angle: ParameterDesignator, control: QubitDesignator, target: QubitDesignator) -> Gate: +# NOTE: We don't use ParameterDesignator here because of the following Sphinx error +# Cannot resolve forward reference in type annotations of "pyquil.gates.CPHASE": +# name 'Expression' is not defined +def CPHASE( + angle: Union[Expression, MemoryReference, np.int_, int, float, complex], + control: QubitDesignator, + target: QubitDesignator, +) -> Gate: """Produces a controlled-phase instruction:: CPHASE(phi) = diag([1, 1, 1, exp(1j * phi)]) From 2c142995f9b1fbc313a15c373f6709307dd67bde Mon Sep 17 00:00:00 2001 From: Peter Karalekas Date: Mon, 23 Dec 2019 14:57:00 -0800 Subject: [PATCH 3/9] Add flake8-bugbear to requirements.txt and address errors --- examples/1.3_vqe_demo.py | 4 +++- pyquil/api/_quantum_computer.py | 2 +- pyquil/latex/_diagram.py | 2 +- pyquil/quilatom.py | 2 +- pyquil/tests/test_numpy_simulator.py | 2 +- pyquil/tests/test_quantum_computer.py | 2 +- pyquil/tests/test_reference_wavefunction_simulator.py | 4 ++-- requirements.txt | 7 ++++--- 8 files changed, 14 insertions(+), 11 deletions(-) diff --git a/examples/1.3_vqe_demo.py b/examples/1.3_vqe_demo.py index 88118a01e..46e77daed 100644 --- a/examples/1.3_vqe_demo.py +++ b/examples/1.3_vqe_demo.py @@ -23,6 +23,8 @@ from grove.measurements.estimation import estimate_locally_commuting_operator +QVM_CONNECTION = QVMConnection(endpoint="http://localhost:5000") + def get_h2_dimer(bond_length): # Set molecule parameters. @@ -59,7 +61,7 @@ def ucc_circuit(theta): def objective_fun( - theta, hamiltonian=None, quantum_resource=QVMConnection(endpoint="http://localhost:5000") + theta, hamiltonian=None, quantum_resource=QVM_CONNECTION ): """ Evaluate the Hamiltonian bny operator averaging diff --git a/pyquil/api/_quantum_computer.py b/pyquil/api/_quantum_computer.py index 39b30c797..9b3eefc51 100644 --- a/pyquil/api/_quantum_computer.py +++ b/pyquil/api/_quantum_computer.py @@ -1199,7 +1199,7 @@ def hadamard(n, dtype=int): H = np.array([[1]], dtype=dtype) # Sylvester's construction - for i in range(0, lg2): + for _ in range(0, lg2): H = np.vstack((np.hstack((H, H)), np.hstack((H, -H)))) return H diff --git a/pyquil/latex/_diagram.py b/pyquil/latex/_diagram.py index d8ea35ab0..2a83ca0cd 100644 --- a/pyquil/latex/_diagram.py +++ b/pyquil/latex/_diagram.py @@ -403,7 +403,7 @@ def build(self): self.index = 0 self.working_instructions = measures - for instr in self.working_instructions: + for _ in self.working_instructions: self._build_measure() offset = max(self.settings.qubit_line_open_wire_length, 0) diff --git a/pyquil/quilatom.py b/pyquil/quilatom.py index 41be8d675..e1e5ca1cf 100644 --- a/pyquil/quilatom.py +++ b/pyquil/quilatom.py @@ -289,7 +289,7 @@ def format_parameter(element: ParameterDesignator) -> str: return str(element) elif isinstance(element, Expression): return _expression_to_string(element) - assert False, "Invalid parameter: %r" % element + raise AssertionError("Invalid parameter: %r" % element) ExpressionValueDesignator = Union[int, float, complex] diff --git a/pyquil/tests/test_numpy_simulator.py b/pyquil/tests/test_numpy_simulator.py index 3ba565b70..a593fc6ce 100644 --- a/pyquil/tests/test_numpy_simulator.py +++ b/pyquil/tests/test_numpy_simulator.py @@ -195,7 +195,7 @@ def test_expectation(): def test_expectation_vs_ref_qvm(qvm, n_qubits): - for repeat_i in range(20): + for _ in range(20): prog = _generate_random_program(n_qubits=n_qubits, length=10) operator = _generate_random_pauli(n_qubits=n_qubits, n_terms=5) print(prog) diff --git a/pyquil/tests/test_quantum_computer.py b/pyquil/tests/test_quantum_computer.py index 687c4ec0c..c8674e7d4 100644 --- a/pyquil/tests/test_quantum_computer.py +++ b/pyquil/tests/test_quantum_computer.py @@ -421,7 +421,7 @@ def test_qc_run(qvm, compiler): qc = get_qc("9q-square-noisy-qvm") bs = qc.run_and_measure(Program(X(0)), trials=3) assert len(bs) == 9 - for q, bits in bs.items(): + for _, bits in bs.items(): assert bits.shape == (3,) diff --git a/pyquil/tests/test_reference_wavefunction_simulator.py b/pyquil/tests/test_reference_wavefunction_simulator.py index 3abe03b0b..8a470da7f 100644 --- a/pyquil/tests/test_reference_wavefunction_simulator.py +++ b/pyquil/tests/test_reference_wavefunction_simulator.py @@ -922,7 +922,7 @@ def include_measures(request): def test_vs_lisp_qvm(qvm, n_qubits, prog_length): - for repeat_i in range(10): + for _ in range(10): prog = _generate_random_program(n_qubits=n_qubits, length=prog_length) lisp_wf = WavefunctionSimulator() # force lisp wfs to allocate all qubits @@ -960,7 +960,7 @@ def _generate_random_pauli(n_qubits, n_terms): def test_expectation_vs_lisp_qvm(qvm, n_qubits): - for repeat_i in range(20): + for _ in range(20): prog = _generate_random_program(n_qubits=n_qubits, length=10) operator = _generate_random_pauli(n_qubits=n_qubits, n_terms=5) lisp_wf = WavefunctionSimulator() diff --git a/requirements.txt b/requirements.txt index b5a95618f..92881cbe5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,14 +11,15 @@ rpcq >= 3.0.0 ipython # test deps +black +flake8 +flake8-bugbear +mypy==0.740 pytest pytest-cov pytest-timeout pytest-rerunfailures requests-mock -flake8 -mypy==0.740 -black # docs sphinx From 739bfca1b643fcc12c6ecabc7348279612f98b23 Mon Sep 17 00:00:00 2001 From: Peter Karalekas Date: Mon, 23 Dec 2019 15:12:34 -0800 Subject: [PATCH 4/9] Add bugbear to contributing guide, consolidate/rename style checks --- .gitlab-ci.yml | 40 ++++++++++++++++++++-------------------- CONTRIBUTING.md | 14 ++++++++------ Makefile | 31 +++++++++++++++---------------- 3 files changed, 43 insertions(+), 42 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index dedf24545..9b11eb1cb 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -15,70 +15,70 @@ services: alias: quilc command: ["-R"] -test-py36: +build-docs: image: python:3.6 tags: - github script: + - apt-get update && apt-get install -y pandoc - make install - make requirements - - make test - coverage: '/TOTAL.*\s(\d+)%/' + - make docs -test-py37: - image: python:3.7 +check-format: + image: python:3.6 tags: - github script: - make install - make requirements - - make test + - make check-format -test-py38: - image: python:3.8 +check-style: + image: python:3.6 tags: - github script: - make install - make requirements - - make test + - make check-style -style: +check-types: image: python:3.6 tags: - github script: - make install - make requirements - - make style + - make check-types -typecheck: +test-py36: image: python:3.6 tags: - github script: - make install - make requirements - - make typecheck + - make test + coverage: '/TOTAL.*\s(\d+)%/' -formatcheck: - image: python:3.6 +test-py37: + image: python:3.7 tags: - github script: - make install - make requirements - - make formatcheck + - make test -docs: - image: python:3.6 +test-py38: + image: python:3.8 tags: - github script: - - apt-get update && apt-get install -y pandoc - make install - make requirements - - make docs + - make test #################################################################################################### # MASTER-ONLY JOBS diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 62e918bb9..05e2ba803 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -93,23 +93,24 @@ Developer How-Tos We use [Black](https://black.readthedocs.io/en/stable/index.html) and `flake8` to automatically lint the code and enforce style requirements as part of the CI pipeline. You can run these style -tests yourself locally by running `make style` (to check for violations of the `flake8` rules) -and `make formatcheck` (to see if `black` would reformat the code) in the top-level directory of +tests yourself locally by running `make check-style` (to check for violations of the `flake8` rules) +and `make check-format` (to see if `black` would reformat the code) in the top-level directory of the repository. If you aren't presented with any errors, then that means your code is good enough -for the linter (`flake8`) and formatter (`black`). If `make formatcheck` fails, it will present +for the linter (`flake8`) and formatter (`black`). If `make check-format` fails, it will present you with a diff, which you can resolve by running `make format`. Black is very opinionated, but saves a lot of time by removing the need for style nitpicks in PR review. We only deviate from its default behavior in one category: we choose to use a line length of 100 rather than the Black default of 88 (this is configured in the [`pyproject.toml`](pyproject.toml) file). As for `flake8`, we ignore a couple of its rules (all for good reasons), and the specific configuration can be -found in the [`.flake8`](.flake8) file. +found in the [`.flake8`](.flake8) file. We additionally use the [`flake8-bugbear`][bugbear] +plugin to add a collection of helpful and commonly observed style rules. In addition to linting and formatting, we are in the process of rolling out the use of type hints for all parameters and return values, using the [PEP 484 syntax][pep-484]. This is being done on -a file-by-file basis, and for ones that have been completed we now have a `make typecheck` command +a file-by-file basis, and for ones that have been completed we now have a `make check-types` command that will enforce the use of types in those files as part of the CI, using the popular static typechecker [mypy](http://mypy-lang.org/). When a file is transitioned, it should be added to the -list in the `typecheck` target of the [`Makefile`](Makefile). For more information on the specific +list in the `check-types` target of the [`Makefile`](Makefile). For more information on the specific configuration of `mypy` that we use for typechecking, please refer to the [`mypy.ini`](mypy.ini) file. Also, because we use the `typing` module, types (e.g. `type` and `rtype` entries) should be omitted when writing (useful) [Sphinx-style][sphinx] docstrings for classes, methods, and functions. @@ -121,6 +122,7 @@ by running the following: make checkall ``` +[bugbear]: https://github.com/PyCQA/flake8-bugbear [pep-484]: https://www.python.org/dev/peps/pep-0484/ [sphinx]: https://sphinx-rtd-tutorial.readthedocs.io/en/latest/docstrings.html diff --git a/Makefile b/Makefile index 0f7cef262..d0640f3fa 100644 --- a/Makefile +++ b/Makefile @@ -6,8 +6,21 @@ DOCKER_TAG=rigetti/forest:$(COMMIT_HASH) .PHONY: all all: dist -.PHONY: checkall -checkall: formatcheck style typecheck +.PHONY: check-all +check-all: check-format check-types check-style + +.PHONY: check-format +check-format: + black --check --diff pyquil + +# The dream is to one day run mypy on the whole tree. For now, limit checks to known-good files. +.PHONY: check-types +check-types: + mypy pyquil/quilatom.py pyquil/quilbase.py pyquil/gates.py + +.PHONY: check-style +check-style: + flake8 .PHONY: clean clean: @@ -39,10 +52,6 @@ docker: Dockerfile format: black pyquil -.PHONY: formatcheck -formatcheck: - black --check --diff pyquil - .PHONY: info info: python -V @@ -56,20 +65,10 @@ install: requirements: requirements.txt pip install -r requirements.txt -.PHONY: style -style: - flake8 - .PHONY: test test: pytest -v --runslow --cov=pyquil -# The dream is to one day run mypy on the whole tree. For now, limit -# checks to known-good files. -.PHONY: typecheck -typecheck: - mypy pyquil/quilatom.py pyquil/quilbase.py pyquil/gates.py - .PHONY: upload upload: twine upload --repository-url https://test.pypi.org/legacy/ dist/* From 0f9efb0839a277346b2e409909f21d521eead90e Mon Sep 17 00:00:00 2001 From: Peter Karalekas Date: Mon, 23 Dec 2019 15:16:15 -0800 Subject: [PATCH 5/9] Update the changelog accordingly --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4882aec99..00e62568b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,10 @@ Changelog - Use [Black](https://black.readthedocs.io/en/stable/index.html) for code style and enforce it (along with a line length of 100) via the `style` (`flake8`) and `formatcheck` (`black --check`) CI jobs (@karalekas, gh-1132). +- Ignore fewer `flake8` style rules, add the `flake8-bugbear` plugin, and + rename the style-related `Makefile` targets and CI jobs so that they have + a uniform naming convention: `check-all`, `check-format`, `check-style`, + and `check-types` (@karalekas, gh-1133). ### Bugfixes From 423d9f096224b4de63ef59eb430c7b5e70b14f2b Mon Sep 17 00:00:00 2001 From: Peter Karalekas Date: Mon, 23 Dec 2019 15:20:38 -0800 Subject: [PATCH 6/9] Missed the check-all rename in the contributing guide --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 05e2ba803..31717ae6d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -119,7 +119,7 @@ As useful shorthand, all of these style-related tests can be run locally with a by running the following: ```bash -make checkall +make check-all ``` [bugbear]: https://github.com/PyCQA/flake8-bugbear From 50aeb109635d4d9946f5fd4811368ccf150fb110 Mon Sep 17 00:00:00 2001 From: Peter Karalekas Date: Tue, 24 Dec 2019 09:09:58 -0800 Subject: [PATCH 7/9] Appleby fixes --- pyquil/gates.py | 3 ++- pyquil/tests/test_noise.py | 1 - pyquil/tests/test_quil.py | 6 ++++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/pyquil/gates.py b/pyquil/gates.py index 976b5233e..f3bc131b1 100644 --- a/pyquil/gates.py +++ b/pyquil/gates.py @@ -381,7 +381,8 @@ def CPHASE10(angle: ParameterDesignator, control: QubitDesignator, target: Qubit return Gate(name="CPHASE10", params=[angle], qubits=qubits) -# NOTE: We don't use ParameterDesignator here because of the following Sphinx error +# NOTE: We don't use ParameterDesignator here because of the following Sphinx error. This error +# can be resolved by importing Expression, but then flake8 complains about an unused import: # Cannot resolve forward reference in type annotations of "pyquil.gates.CPHASE": # name 'Expression' is not defined def CPHASE( diff --git a/pyquil/tests/test_noise.py b/pyquil/tests/test_noise.py index 180e30003..301853370 100644 --- a/pyquil/tests/test_noise.py +++ b/pyquil/tests/test_noise.py @@ -134,7 +134,6 @@ def test_decoherence_noise(): # verify that gate names are translated new_prog = apply_noise_model(prog, m3) - _get_program_gates(new_prog) # check that headers have been embedded headers = _noise_model_program_header(m3) diff --git a/pyquil/tests/test_quil.py b/pyquil/tests/test_quil.py index 0d7bd47bd..238aa8323 100755 --- a/pyquil/tests/test_quil.py +++ b/pyquil/tests/test_quil.py @@ -1385,10 +1385,12 @@ def test_params_pi_and_precedence(): exp = str(prog[0].params[0]) assert _eval_as_np_pi(trivial_pi) == _eval_as_np_pi(exp) - prog = Program(f"RX({trivial_pi}) 0") + less_trivial_pi = "3 * theta[0] * 2 / (pi)" + prog = Program(f"RX({less_trivial_pi}) 0") exp = str(prog[0].params[0]) assert _eval_as_np_pi(trivial_pi) == _eval_as_np_pi(exp) - prog = Program(f"RX({trivial_pi}) 0") + more_less_trivial_pi = "3 / (theta[0] / (pi + 1)) / pi" + prog = Program(f"RX({more_less_trivial_pi}) 0") exp = str(prog[0].params[0]) assert _eval_as_np_pi(trivial_pi) == _eval_as_np_pi(exp) From bc205d421352f7356f3e6cd955fdf86368768a4c Mon Sep 17 00:00:00 2001 From: Peter Karalekas Date: Tue, 24 Dec 2019 09:20:37 -0800 Subject: [PATCH 8/9] trivial pies --- pyquil/tests/test_quil.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyquil/tests/test_quil.py b/pyquil/tests/test_quil.py index 238aa8323..e812368d7 100755 --- a/pyquil/tests/test_quil.py +++ b/pyquil/tests/test_quil.py @@ -1388,9 +1388,9 @@ def test_params_pi_and_precedence(): less_trivial_pi = "3 * theta[0] * 2 / (pi)" prog = Program(f"RX({less_trivial_pi}) 0") exp = str(prog[0].params[0]) - assert _eval_as_np_pi(trivial_pi) == _eval_as_np_pi(exp) + assert _eval_as_np_pi(less_trivial_pi) == _eval_as_np_pi(exp) more_less_trivial_pi = "3 / (theta[0] / (pi + 1)) / pi" prog = Program(f"RX({more_less_trivial_pi}) 0") exp = str(prog[0].params[0]) - assert _eval_as_np_pi(trivial_pi) == _eval_as_np_pi(exp) + assert _eval_as_np_pi(more_less_trivial_pi) == _eval_as_np_pi(exp) From 64feab8c629160858a5eace41d7d523457c99e03 Mon Sep 17 00:00:00 2001 From: Peter Karalekas Date: Tue, 24 Dec 2019 09:25:52 -0800 Subject: [PATCH 9/9] return of the king --- pyquil/tests/test_quil.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyquil/tests/test_quil.py b/pyquil/tests/test_quil.py index e812368d7..bdf9485f1 100755 --- a/pyquil/tests/test_quil.py +++ b/pyquil/tests/test_quil.py @@ -1376,7 +1376,7 @@ def test_placeholders_preserves_modifiers(): def _eval_as_np_pi(exp): - eval(exp.replace("pi", repr(np.pi)).replace("theta[0]", "1")) + return eval(exp.replace("pi", repr(np.pi)).replace("theta[0]", "1")) def test_params_pi_and_precedence():