Skip to content

Commit

Permalink
Pytest without cirq.google (#3894)
Browse files Browse the repository at this point in the history
Enables testing without the existence of cirq.google. 

Namely: 
- check/pytest gets a `--cirq-only` arg, that deletes the cirq/google directory and runs pytest  - also adds a new CI job just for this reason
- this forced the removal of some remaining cirq.google references from cirq core and some was made optional (see next point)
- cirq.google references can still be made from tests but they will be skipped using `@cirq.testing.skip_if_module_not_exists(module="cirq.google")` - the examples folder, performance benchmarking are places where this is okay. In other places this is considered technical debt and we'll need to figure out how to remove those instances cleanly (probably by creating the test infra for those features that the modules can call into)

Note: this should be merged after #3888 (it is merged into this branch).

Closes #3737.
  • Loading branch information
balopat committed Mar 19, 2021
1 parent 2128257 commit 06e0f1c
Show file tree
Hide file tree
Showing 19 changed files with 100 additions and 58 deletions.
25 changes: 21 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: Continuous Integration

on: [pull_request]
on: [ pull_request ]

jobs:
quick_test:
Expand Down Expand Up @@ -100,12 +100,29 @@ jobs:
architecture: 'x64'
- name: Doc check
run: check/nbformat
cirq-only:
name: Pytest (cirq-only) Ubuntu
runs-on: ubuntu-16.04
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v1
with:
python-version: '3.8'
architecture: 'x64'
- name: Install requirements
run: |
pip install -r requirements.txt
pip install -r dev_tools/conf/pip-list-dev-tools.txt
# TODO: move to requirements.txt after #3704
pip install codeowners==0.1.2 # linux only package
- name: Pytest check
run: check/pytest --cirq-only --ignore=cirq/contrib --actually-quiet
pytest:
name: Pytest Ubuntu
strategy:
matrix:
# TODO(#3800): remove 3.6 when Colab switches to 3.7
python-version: ['3.6', '3.7', '3.8']
python-version: [ '3.6', '3.7', '3.8' ]
runs-on: ubuntu-16.04
steps:
- uses: actions/checkout@v2
Expand Down Expand Up @@ -178,7 +195,7 @@ jobs:
name: Pytest Windows
strategy:
matrix:
python-version: ['3.7', '3.8']
python-version: [ '3.7', '3.8' ]
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
Expand All @@ -197,7 +214,7 @@ jobs:
name: Pytest MacOS
strategy:
matrix:
python-version: ['3.7', '3.8']
python-version: [ '3.7', '3.8' ]
runs-on: macos-10.15
steps:
- uses: actions/checkout@v2
Expand Down
10 changes: 10 additions & 0 deletions check/pytest
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,21 @@ ACTUALLY_QUIET=""
for arg in $@; do
if [[ "${arg}" == "--actually-quiet" ]]; then
ACTUALLY_QUIET=1
elif [[ "${arg}" == "--cirq-only" ]]; then
tmp_dir=$(mktemp -d "/tmp/cirq-pytest.XXXXXXXXXXXXXXXX")
cp -R ./* ${tmp_dir}/
cp -R ./.github ${tmp_dir}/
cp -R ./.git ${tmp_dir}/
cd ${tmp_dir}
rm -rf cirq/google
trap "{ rm -rf ${tmp_dir}; }" EXIT
else
PYTEST_ARGS+=("${arg}")
fi
done



if [ -z "${ACTUALLY_QUIET}" ]; then
pytest "${PYTEST_ARGS[@]}"
else
Expand Down
9 changes: 7 additions & 2 deletions cirq/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from logging import warning

from cirq import _import

Expand Down Expand Up @@ -51,7 +52,6 @@
interop,
# Applications
experiments,
google,
# Extra (nothing should depend on these)
testing,
)
Expand Down Expand Up @@ -564,12 +564,17 @@
# Unflattened sub-modules.

from cirq import (
google,
ionq,
pasqal,
testing,
)

try:
from cirq import google
except ImportError as ex:
# coverage: ignore
warning("Can't import cirq.google: ", ex)


def _register_resolver() -> None:
"""Registers the cirq module's public classes for JSON serialization."""
Expand Down
2 changes: 1 addition & 1 deletion cirq/circuits/frozen_circuit_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,4 @@ def test_immutable():
c.moments = (cirq.Moment(cirq.H(q)), cirq.Moment(cirq.X(q)))

with pytest.raises(AttributeError, match="can't set attribute"):
c.device = cirq.google.devices.Foxtail
c.device = cirq.UNCONSTRAINED_DEVICE
10 changes: 5 additions & 5 deletions cirq/contrib/paulistring/recombine_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,9 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import pytest

import cirq

from cirq.contrib.paulistring import (
convert_and_separate_circuit,
pauli_string_dag_from_circuit,
Expand All @@ -29,6 +28,7 @@ def _assert_no_multi_qubit_pauli_strings(circuit: cirq.Circuit) -> None:


def test_move_non_clifford_into_clifford():
cg = pytest.importorskip("cirq.google")
q0, q1, q2 = cirq.LineQubit.range(3)
c_orig = cirq.testing.nonoptimal_toffoli_circuit(q0, q1, q2)

Expand All @@ -43,8 +43,8 @@ def test_move_non_clifford_into_clifford():
_assert_no_multi_qubit_pauli_strings(c_recombined1)
_assert_no_multi_qubit_pauli_strings(c_recombined2)

baseline_len = len(cirq.google.optimized_for_xmon(c_orig))
opt_len1 = len(cirq.google.optimized_for_xmon(c_recombined1))
opt_len2 = len(cirq.google.optimized_for_xmon(c_recombined2))
baseline_len = len(cg.optimized_for_xmon(c_orig))
opt_len1 = len(cg.optimized_for_xmon(c_recombined1))
opt_len2 = len(cg.optimized_for_xmon(c_recombined2))
assert opt_len1 <= baseline_len
assert opt_len2 <= baseline_len
2 changes: 2 additions & 0 deletions cirq/experiments/google_v2_supremacy_circuit_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ def test_google_v2_supremacy_circuit():


def test_google_v2_supremacy_bristlecone():
pytest.importorskip("cirq.google")
# Check instance consistency
c = supremacy_v2.generate_boixo_2018_supremacy_circuits_v2_bristlecone(
n_rows=11, cz_depth=8, seed=0
Expand All @@ -71,6 +72,7 @@ def test_google_v2_supremacy_bristlecone():


def test_n_rows_less_than_2():
pytest.importorskip("cirq.google")
with pytest.raises(AssertionError):
supremacy_v2.generate_boixo_2018_supremacy_circuits_v2_bristlecone(
n_rows=1, cz_depth=0, seed=0
Expand Down
7 changes: 6 additions & 1 deletion cirq/experiments/random_quantum_circuit_generation_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,11 @@ def _cz_with_adjacent_z_rotations(
yield cirq.Z(b) ** z_exponents[3]


class FakeSycamoreGate(cirq.FSimGate):
def __init__(self):
super().__init__(theta=np.pi / 2, phi=np.pi / 6)


@pytest.mark.parametrize(
'qubits, depth, two_qubit_op_factory, pattern, '
'single_qubit_gates, add_final_single_qubit_layer, '
Expand All @@ -278,7 +283,7 @@ def _cz_with_adjacent_z_rotations(
(
cirq.GridQubit.rect(4, 3),
20,
lambda a, b, _: cirq.google.SYC(a, b),
lambda a, b, _: FakeSycamoreGate()(a, b),
cirq.experiments.HALF_GRID_STAGGERED_PATTERN,
(cirq.X ** 0.5, cirq.Y ** 0.5, cirq.Z ** 0.5),
True,
Expand Down
34 changes: 19 additions & 15 deletions cirq/google/devices/known_devices_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import pytest

import cirq
import cirq.google as cg
import cirq.google
import cirq.google.common_serializers as cgc
import cirq.google.devices.known_devices as known_devices

Expand Down Expand Up @@ -301,7 +301,7 @@ def test_create_device_proto_for_irregular_grid():


def test_multiple_gate_sets():
halfPiGateSet = cg.serializable_gate_set.SerializableGateSet(
halfPiGateSet = cirq.google.serializable_gate_set.SerializableGateSet(
gate_set_name='half_pi_gateset',
serializers=[*cgc.SINGLE_QUBIT_HALF_PI_SERIALIZERS, cgc.MEASUREMENT_SERIALIZER],
deserializers=[*cgc.SINGLE_QUBIT_HALF_PI_DESERIALIZERS, cgc.MEASUREMENT_DESERIALIZER],
Expand All @@ -314,7 +314,7 @@ def test_multiple_gate_sets():
'meas': 14_141,
}
test_proto = known_devices.create_device_proto_from_diagram(
"aa\naa", [cg.gate_sets.XMON, halfPiGateSet], durations_dict
"aa\naa", [cirq.google.gate_sets.XMON, halfPiGateSet], durations_dict
)
assert (
str(test_proto)
Expand Down Expand Up @@ -457,7 +457,7 @@ def test_multiple_gate_sets():


def test_json_dict():
assert cg.Foxtail._json_dict_() == {
assert cirq.google.Foxtail._json_dict_() == {
'cirq_type': '_NamedConstantXmonDevice',
'constant': 'cirq.google.Foxtail',
}
Expand All @@ -471,7 +471,7 @@ def test_json_dict():
known_devices._NamedConstantXmonDevice._from_json_dict_('the_unknown_fiddler')


@pytest.mark.parametrize('device', [cg.Sycamore, cg.Sycamore23])
@pytest.mark.parametrize('device', [cirq.google.Sycamore, cirq.google.Sycamore23])
def test_sycamore_devices(device):
q0 = cirq.GridQubit(5, 3)
q1 = cirq.GridQubit(5, 4)
Expand All @@ -489,26 +489,28 @@ def test_sycamore_grid_layout():
q1 = cirq.GridQubit(5, 6)
syc = cirq.FSimGate(theta=np.pi / 2, phi=np.pi / 6)(q0, q1)
sqrt_iswap = cirq.FSimGate(theta=np.pi / 4, phi=0)(q0, q1)
cg.Sycamore.validate_operation(syc)
cg.Sycamore.validate_operation(sqrt_iswap)
cirq.google.Sycamore.validate_operation(syc)
cirq.google.Sycamore.validate_operation(sqrt_iswap)

with pytest.raises(ValueError):
cg.Sycamore23.validate_operation(syc)
cirq.google.Sycamore23.validate_operation(syc)
with pytest.raises(ValueError):
cg.Sycamore23.validate_operation(sqrt_iswap)
cirq.google.Sycamore23.validate_operation(sqrt_iswap)


def test_proto_with_waitgate():
wait_gateset = cg.serializable_gate_set.SerializableGateSet(
wait_gateset = cirq.google.serializable_gate_set.SerializableGateSet(
gate_set_name='wait_gateset',
serializers=[cgc.WAIT_GATE_SERIALIZER],
deserializers=[cgc.WAIT_GATE_DESERIALIZER],
)
wait_proto = cg.devices.known_devices.create_device_proto_from_diagram(
wait_proto = cirq.google.devices.known_devices.create_device_proto_from_diagram(
"aa\naa",
[wait_gateset],
)
wait_device = cg.SerializableDevice.from_proto(proto=wait_proto, gate_sets=[wait_gateset])
wait_device = cirq.google.SerializableDevice.from_proto(
proto=wait_proto, gate_sets=[wait_gateset]
)
q0 = cirq.GridQubit(1, 1)
wait_op = cirq.wait(q0, nanos=25)
wait_device.validate_operation(wait_op)
Expand Down Expand Up @@ -560,7 +562,7 @@ def test_proto_with_waitgate():


def test_adding_gates_multiple_times():
waiting_for_godot = cg.serializable_gate_set.SerializableGateSet(
waiting_for_godot = cirq.google.serializable_gate_set.SerializableGateSet(
gate_set_name='wait_gateset',
serializers=[cgc.WAIT_GATE_SERIALIZER, cgc.WAIT_GATE_SERIALIZER, cgc.WAIT_GATE_SERIALIZER],
deserializers=[
Expand All @@ -569,11 +571,13 @@ def test_adding_gates_multiple_times():
cgc.WAIT_GATE_DESERIALIZER,
],
)
wait_proto = cg.devices.known_devices.create_device_proto_from_diagram(
wait_proto = cirq.google.devices.known_devices.create_device_proto_from_diagram(
"aa",
[waiting_for_godot],
)
wait_device = cg.SerializableDevice.from_proto(proto=wait_proto, gate_sets=[waiting_for_godot])
wait_device = cirq.google.SerializableDevice.from_proto(
proto=wait_proto, gate_sets=[waiting_for_godot]
)
q0 = cirq.GridQubit(0, 0)
wait_op = cirq.wait(q0, nanos=25)
wait_device.validate_operation(wait_op)
Expand Down
2 changes: 1 addition & 1 deletion cirq/ops/measurement_gate.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from typing import Any, Dict, Iterable, Optional, Tuple, Sequence, TYPE_CHECKING, List
from typing import Any, Dict, Iterable, Optional, Tuple, Sequence, TYPE_CHECKING

import numpy as np

Expand Down
16 changes: 11 additions & 5 deletions cirq/optimizers/cphase_to_fsim_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,16 @@
import cirq


class FakeSycamoreGate(cirq.FSimGate):
def __init__(self):
super().__init__(theta=np.pi / 2, phi=np.pi / 6)


def test_parameterized_gates():
t = sympy.Symbol('t')
with pytest.raises(ValueError):
cphase_gate = cirq.CZPowGate(exponent=t)
fsim_gate = cirq.google.SYC
fsim_gate = FakeSycamoreGate()
cirq.decompose_cphase_into_two_fsim(cphase_gate, fsim_gate=fsim_gate)

with pytest.raises(ValueError):
Expand All @@ -29,18 +34,19 @@ def test_parameterized_gates():
def test_invalid_qubits():
with pytest.raises(ValueError):
cirq.decompose_cphase_into_two_fsim(
cphase_gate=cirq.CZ, fsim_gate=cirq.google.SYC, qubits=cirq.LineQubit.range(3)
cphase_gate=cirq.CZ, fsim_gate=FakeSycamoreGate(), qubits=cirq.LineQubit.range(3)
)


def test_circuit_structure():
ops = cirq.decompose_cphase_into_two_fsim(cirq.CZ, fsim_gate=cirq.google.SYC)
syc = FakeSycamoreGate()
ops = cirq.decompose_cphase_into_two_fsim(cirq.CZ, fsim_gate=syc)
num_interaction_moments = 0
for op in ops:
assert len(op.qubits) in (0, 1, 2)
if len(op.qubits) == 2:
num_interaction_moments += 1
assert isinstance(op.gate, cirq.google.SycamoreGate)
assert isinstance(op.gate, FakeSycamoreGate)
assert num_interaction_moments == 2


Expand All @@ -56,7 +62,7 @@ def assert_decomposition_valid(cphase_gate, fsim_gate):
)
def test_decomposition_to_sycamore_gate(exponent):
cphase_gate = cirq.CZPowGate(exponent=exponent)
assert_decomposition_valid(cphase_gate, cirq.google.SYC)
assert_decomposition_valid(cphase_gate, FakeSycamoreGate())


@pytest.mark.parametrize(
Expand Down
1 change: 0 additions & 1 deletion cirq/optimizers/two_qubit_to_fsim_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@

FEASIBLE_FSIM_GATES = [
cirq.ISWAP,
cirq.google.SYC,
cirq.FSimGate(np.pi / 2, 0),
cirq.FSimGate(-np.pi / 2, 0),
cirq.FSimGate(np.pi / 2, np.pi / 6),
Expand Down
2 changes: 1 addition & 1 deletion cirq/protocols/json_test_data/TaggedOperation.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
[
"tag1",
{
"cirq_type": "PhysicalZTag"
"cirq_type": "VirtualTag"
}
]
}
2 changes: 1 addition & 1 deletion cirq/protocols/json_test_data/TaggedOperation.repr
Original file line number Diff line number Diff line change
@@ -1 +1 @@
cirq.TaggedOperation(cirq.X.on(cirq.NamedQubit('q1')), 'tag1', cirq.google.PhysicalZTag())
cirq.TaggedOperation(cirq.X.on(cirq.NamedQubit('q1')), 'tag1', cirq.ops.VirtualTag())
9 changes: 7 additions & 2 deletions dev_tools/notebooks/notebook_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import glob
import os
import subprocess
from logging import warning
from typing import Set

import pytest
Expand All @@ -45,8 +46,12 @@


def _list_all_notebooks() -> Set[str]:
output = subprocess.check_output(['git', 'ls-files', '*.ipynb'])
return set(output.decode('utf-8').splitlines())
try:
output = subprocess.check_output(['git', 'ls-files', '*.ipynb'])
return set(output.decode('utf-8').splitlines())
except subprocess.CalledProcessError as ex:
warning("It seems that tests are run from not a git repo, notebook tests are skipped", ex)
return set()


def _tested_notebooks():
Expand Down

0 comments on commit 06e0f1c

Please sign in to comment.