diff --git a/.github/dependabot.yml b/.github/dependabot.yml index cd6f523c049..a26cfc2607a 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -22,6 +22,8 @@ version: 2 updates: - package-ecosystem: "docker" + # The "docker" ecosystem directive makes Dependabot look for a Dockerfile + # in the specified directory. directory: "/" schedule: interval: "weekly" @@ -31,6 +33,8 @@ updates: - "kind/health" - package-ecosystem: "github-actions" + # The "github-actions" code explicitly looks in /.github/workflows if the + # value "/" is given for the directory attribute. Yes, that's confusing. directory: "/" schedule: interval: "weekly" @@ -40,7 +44,9 @@ updates: - "kind/health" - package-ecosystem: "npm" - directory: "/" + # The "npm" ecosystem directive makes Dependabot look for package.json in + # the specified directory. + directory: "/cirq-web/cirq_ts/" schedule: interval: "weekly" labels: @@ -49,9 +55,21 @@ updates: - "kind/health" - package-ecosystem: "pip" - directory: "/" + # Cirq has requirements.txt files in multiple places. N.b. the use of + # attribute "directories" instead of "directory" here. + directories: + - "/" + - "/cirq-aqt" + - "/cirq-core" + - "/cirq-google" + - "/cirq-ionq" + - "/cirq-pasqal" + - "/cirq-rigetti" + - "/cirq-web" + - "/dev_tools/requirements" schedule: interval: "weekly" + versioning-strategy: "increase-if-necessary" labels: - "area/dependencies" - "area/python" diff --git a/cirq-core/cirq/experiments/random_quantum_circuit_generation.py b/cirq-core/cirq/experiments/random_quantum_circuit_generation.py index 8a5d92058eb..350211a3ad9 100644 --- a/cirq-core/cirq/experiments/random_quantum_circuit_generation.py +++ b/cirq-core/cirq/experiments/random_quantum_circuit_generation.py @@ -250,6 +250,7 @@ def generate_library_of_2q_circuits( q0: 'cirq.Qid' = devices.LineQubit(0), q1: 'cirq.Qid' = devices.LineQubit(1), random_state: 'cirq.RANDOM_STATE_OR_SEED_LIKE' = None, + tags: Sequence[Any] = (), ) -> List['cirq.Circuit']: """Generate a library of two-qubit Circuits. @@ -266,6 +267,7 @@ def generate_library_of_2q_circuits( q0: The first qubit to use when constructing the circuits. q1: The second qubit to use when constructing the circuits random_state: A random state or seed used to deterministically sample the random circuits. + tags: Tags to add to the two qubit operations. """ rs = value.parse_random_state(random_state) exponents = np.linspace(0, 7 / 4, 8) @@ -278,7 +280,7 @@ def generate_library_of_2q_circuits( q0, q1, depth=max_cycle_depth, - two_qubit_op_factory=lambda a, b, _: two_qubit_gate(a, b), + two_qubit_op_factory=lambda a, b, _: two_qubit_gate(a, b).with_tags(*tags), single_qubit_gates=single_qubit_gates, seed=rs, ) diff --git a/cirq-core/cirq/experiments/random_quantum_circuit_generation_test.py b/cirq-core/cirq/experiments/random_quantum_circuit_generation_test.py index 115bf737dc2..c8397415d04 100644 --- a/cirq-core/cirq/experiments/random_quantum_circuit_generation_test.py +++ b/cirq-core/cirq/experiments/random_quantum_circuit_generation_test.py @@ -457,3 +457,20 @@ def add_pair(neighbor: 'cirq.GridQubit'): add_pair(cirq.GridQubit(qubit.row + 1, qubit.col)) return pairs + + +def test_generate_library_of_2q_circuits_with_tags(): + circuits = generate_library_of_2q_circuits( + n_library_circuits=5, + two_qubit_gate=cirq.FSimGate(3, 4), + max_cycle_depth=13, + random_state=9, + tags=('test_tag',), + ) + assert len(circuits) == 5 + for circuit in circuits: + for op in circuit.all_operations(): + if cirq.num_qubits(op) == 1: + continue + assert op.tags == ('test_tag',) + assert op.gate == cirq.FSimGate(3, 4) diff --git a/cirq-core/cirq/experiments/two_qubit_xeb.py b/cirq-core/cirq/experiments/two_qubit_xeb.py index 12162d0e883..6fe20529dd6 100644 --- a/cirq-core/cirq/experiments/two_qubit_xeb.py +++ b/cirq-core/cirq/experiments/two_qubit_xeb.py @@ -13,7 +13,7 @@ # limitations under the License. """Provides functions for running and analyzing two-qubit XEB experiments.""" -from typing import Sequence, TYPE_CHECKING, Optional, Tuple, Dict, cast, Mapping +from typing import Sequence, TYPE_CHECKING, Optional, Tuple, Dict, cast, Mapping, Any from dataclasses import dataclass from types import MappingProxyType @@ -402,6 +402,7 @@ def parallel_xeb_workflow( pairs: Optional[Sequence[tuple['cirq.GridQubit', 'cirq.GridQubit']]] = None, pool: Optional['multiprocessing.pool.Pool'] = None, batch_size: int = 9, + tags: Sequence[Any] = (), **plot_kwargs, ) -> Tuple[pd.DataFrame, Sequence['cirq.Circuit'], pd.DataFrame]: """A utility method that runs the full XEB workflow. @@ -422,6 +423,7 @@ def parallel_xeb_workflow( batch_size: We call `run_batch` on the sampler, which can speed up execution in certain environments. The number of (circuit, cycle_depth) tasks to be run in each batch is given by this number. + tags: Tags to add to two qubit operations. **plot_kwargs: Arguments to be passed to 'plt.Axes.plot'. Returns: @@ -450,6 +452,7 @@ def parallel_xeb_workflow( two_qubit_gate=entangling_gate, random_state=rs, max_cycle_depth=max(cycle_depths), + tags=tags, ) combs_by_layer = rqcg.get_random_combinations_for_device( @@ -488,6 +491,7 @@ def parallel_two_qubit_xeb( ax: Optional[plt.Axes] = None, pairs: Optional[Sequence[tuple['cirq.GridQubit', 'cirq.GridQubit']]] = None, batch_size: int = 9, + tags: Sequence[Any] = (), **plot_kwargs, ) -> TwoQubitXEBResult: """A convenience method that runs the full XEB workflow. @@ -507,6 +511,7 @@ def parallel_two_qubit_xeb( batch_size: We call `run_batch` on the sampler, which can speed up execution in certain environments. The number of (circuit, cycle_depth) tasks to be run in each batch is given by this number. + tags: Tags to add to two qubit operations. **plot_kwargs: Arguments to be passed to 'plt.Axes.plot'. Returns: A TwoQubitXEBResult object representing the results of the experiment. @@ -525,6 +530,7 @@ def parallel_two_qubit_xeb( random_state=random_state, ax=ax, batch_size=batch_size, + tags=tags, **plot_kwargs, ) return TwoQubitXEBResult(fit_exponential_decays(fids)) @@ -544,6 +550,7 @@ def run_rb_and_xeb( random_state: 'cirq.RANDOM_STATE_OR_SEED_LIKE' = None, pairs: Optional[Sequence[tuple['cirq.GridQubit', 'cirq.GridQubit']]] = None, batch_size: int = 9, + tags: Sequence[Any] = (), ) -> InferredXEBResult: """A convenience method that runs both RB and XEB workflows. @@ -561,6 +568,7 @@ def run_rb_and_xeb( batch_size: We call `run_batch` on the sampler, which can speed up execution in certain environments. The number of (circuit, cycle_depth) tasks to be run in each batch is given by this number. + tags: Tags to add to two qubit operations. Returns: An InferredXEBResult object representing the results of the experiment. @@ -590,6 +598,7 @@ def run_rb_and_xeb( n_combinations=xeb_combinations, random_state=random_state, batch_size=batch_size, + tags=tags, ) return InferredXEBResult(rb, xeb) diff --git a/cirq-core/cirq/experiments/z_phase_calibration.py b/cirq-core/cirq/experiments/z_phase_calibration.py index 363810fbd13..8e58cab7569 100644 --- a/cirq-core/cirq/experiments/z_phase_calibration.py +++ b/cirq-core/cirq/experiments/z_phase_calibration.py @@ -13,7 +13,7 @@ # limitations under the License. """Provides a method to do z-phase calibration for excitation-preserving gates.""" -from typing import Union, Optional, Sequence, Tuple, Dict, TYPE_CHECKING +from typing import Union, Optional, Sequence, Tuple, Dict, TYPE_CHECKING, Any import multiprocessing import multiprocessing.pool @@ -41,6 +41,8 @@ def z_phase_calibration_workflow( random_state: 'cirq.RANDOM_STATE_OR_SEED_LIKE' = None, atol: float = 1e-3, num_workers_or_pool: Union[int, 'multiprocessing.pool.Pool'] = -1, + pairs: Optional[Sequence[Tuple['cirq.GridQubit', 'cirq.GridQubit']]] = None, + tags: Sequence[Any] = (), ) -> Tuple[xeb_fitting.XEBCharacterizationResult, 'pd.DataFrame']: """Perform z-phase calibration for excitation-preserving gates. @@ -77,6 +79,8 @@ def z_phase_calibration_workflow( A zero value means no multiprocessing. A positive integer value will create a pool with the given number of workers. A negative value will create pool with maximum number of workers. + pairs: Pairs to use. If not specified, use all pairs between adjacent qubits. + tags: Tags to add to two qubit operations. Returns: - An `XEBCharacterizationResult` object that contains the calibration result. - A `pd.DataFrame` comparing the before and after fidelities. @@ -100,6 +104,8 @@ def z_phase_calibration_workflow( n_combinations=n_combinations, random_state=random_state, pool=pool, + tags=tags, + pairs=pairs, ) if options is None: @@ -148,6 +154,8 @@ def calibrate_z_phases( random_state: 'cirq.RANDOM_STATE_OR_SEED_LIKE' = None, atol: float = 1e-3, num_workers_or_pool: Union[int, 'multiprocessing.pool.Pool'] = -1, + pairs: Optional[Sequence[Tuple['cirq.GridQubit', 'cirq.GridQubit']]] = None, + tags: Sequence[Any] = (), ) -> Dict[Tuple['cirq.Qid', 'cirq.Qid'], 'cirq.PhasedFSimGate']: """Perform z-phase calibration for excitation-preserving gates. @@ -184,6 +192,8 @@ def calibrate_z_phases( A zero value means no multiprocessing. A positive integer value will create a pool with the given number of workers. A negative value will create pool with maximum number of workers. + pairs: Pairs to use. If not specified, use all pairs between adjacent qubits. + tags: Tags to add to two qubit operations. Returns: - A dictionary mapping qubit pairs to the calibrated PhasedFSimGates. @@ -210,6 +220,8 @@ def calibrate_z_phases( random_state=random_state, atol=atol, num_workers_or_pool=num_workers_or_pool, + tags=tags, + pairs=pairs, ) gates = {} diff --git a/dev_tools/conftest.py b/dev_tools/conftest.py index 2e7bb5fc556..708c77a3347 100644 --- a/dev_tools/conftest.py +++ b/dev_tools/conftest.py @@ -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. + import os import shutil import sys @@ -18,6 +19,7 @@ import uuid from pathlib import Path from typing import Tuple +from unittest import mock import pytest from filelock import FileLock @@ -26,6 +28,14 @@ from dev_tools.env_tools import create_virtual_env +@pytest.fixture(scope="session", autouse=True) +def disable_local_gcloud_credentials(tmp_path_factory): + # Ensure tests cannot authenticate to production servers with user credentials + empty_dir = tmp_path_factory.mktemp("empty_gcloud_config", numbered=False) + with mock.patch.dict(os.environ, {"CLOUDSDK_CONFIG": str(empty_dir)}): + yield + + @pytest.fixture(scope="session") def cloned_env(testrun_uid, worker_id): """Fixture to allow tests to run in a clean virtual env.