Skip to content

Commit

Permalink
Merge branch 'main' into dd_issue
Browse files Browse the repository at this point in the history
  • Loading branch information
NoureldinYosri authored Jan 14, 2025
2 parents f3041c5 + cd9a3ff commit 7deb8a8
Show file tree
Hide file tree
Showing 26 changed files with 155 additions and 2,154 deletions.
1 change: 1 addition & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ updates:
directory: "/cirq-web/cirq_ts/"
schedule:
interval: "weekly"
versioning-strategy: "widen"
labels:
- "area/dependencies"
- "area/javascript"
Expand Down
21 changes: 21 additions & 0 deletions .markdownlintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Summary: markdownlint-cli config file for Cirq
#
# Note: there are multiple programs programs named "markdownlint". For Cirq,
# we use https://github.com/igorshubovych/markdownlint-cli/, which is the one
# you get with "brew install markdownlint" on MacOS.

# For a list of configuration options, see the following page:
# https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md
# (Beware that the above looks similar but is NOT the same as the page
# https://github.com/markdownlint/markdownlint/blob/main/docs/RULES.md.)

# Make exception to the line-length limit of 80 chars in code blocks & tables.
[line-length]
code_blocks = false
tables = false

# Raw HTML is allowed in Markdown, and supported by GitHub.
no-inline-html = false

# Bare URLs are allowed in GitHub-flavored Markdown.
no-bare-urls = false
24 changes: 11 additions & 13 deletions cirq-core/cirq/circuits/moment.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"""A simplified time-slice of operations within a sequenced circuit."""

import itertools
from functools import cached_property
from types import NotImplementedType
from typing import (
AbstractSet,
Expand Down Expand Up @@ -113,7 +114,6 @@ def __init__(self, *contents: 'cirq.OP_TREE', _flatten_contents: bool = True) ->
raise ValueError(f'Overlapping operations: {self.operations}')
self._qubit_to_op[q] = op

self._qubits = frozenset(self._qubit_to_op.keys())
self._measurement_key_objs: Optional[FrozenSet['cirq.MeasurementKey']] = None
self._control_keys: Optional[FrozenSet['cirq.MeasurementKey']] = None

Expand All @@ -135,9 +135,9 @@ def from_ops(cls, *ops: 'cirq.Operation') -> 'cirq.Moment':
def operations(self) -> Tuple['cirq.Operation', ...]:
return self._operations

@property
@cached_property
def qubits(self) -> FrozenSet['cirq.Qid']:
return self._qubits
return frozenset(self._qubit_to_op)

def operates_on_single_qubit(self, qubit: 'cirq.Qid') -> bool:
"""Determines if the moment has operations touching the given qubit.
Expand All @@ -157,7 +157,7 @@ def operates_on(self, qubits: Iterable['cirq.Qid']) -> bool:
Returns:
Whether this moment has operations involving the qubits.
"""
return not self._qubits.isdisjoint(qubits)
return not self._qubit_to_op.keys().isdisjoint(qubits)

def operation_at(self, qubit: raw_types.Qid) -> Optional['cirq.Operation']:
"""Returns the operation on a certain qubit for the moment.
Expand Down Expand Up @@ -185,14 +185,13 @@ def with_operation(self, operation: 'cirq.Operation') -> 'cirq.Moment':
Raises:
ValueError: If the operation given overlaps a current operation in the moment.
"""
if any(q in self._qubits for q in operation.qubits):
if any(q in self._qubit_to_op for q in operation.qubits):
raise ValueError(f'Overlapping operations: {operation}')

# Use private variables to facilitate a quick copy.
m = Moment(_flatten_contents=False)
m._operations = self._operations + (operation,)
m._sorted_operations = None
m._qubits = self._qubits.union(operation.qubits)
m._qubit_to_op = {**self._qubit_to_op, **{q: operation for q in operation.qubits}}

m._measurement_key_objs = self._measurement_key_objs_().union(
Expand Down Expand Up @@ -222,14 +221,11 @@ def with_operations(self, *contents: 'cirq.OP_TREE') -> 'cirq.Moment':
m = Moment(_flatten_contents=False)
# Use private variables to facilitate a quick copy.
m._qubit_to_op = self._qubit_to_op.copy()
qubits = set(self._qubits)
for op in flattened_contents:
if any(q in qubits for q in op.qubits):
if any(q in m._qubit_to_op for q in op.qubits):
raise ValueError(f'Overlapping operations: {op}')
qubits.update(op.qubits)
for q in op.qubits:
m._qubit_to_op[q] = op
m._qubits = frozenset(qubits)

m._operations = self._operations + flattened_contents
m._sorted_operations = None
Expand Down Expand Up @@ -450,7 +446,9 @@ def expand_to(self, qubits: Iterable['cirq.Qid']) -> 'cirq.Moment':
@_compat.cached_method()
def _has_kraus_(self) -> bool:
"""Returns True if self has a Kraus representation and self uses <= 10 qubits."""
return all(protocols.has_kraus(op) for op in self.operations) and len(self.qubits) <= 10
return (
all(protocols.has_kraus(op) for op in self.operations) and len(self._qubit_to_op) <= 10
)

def _kraus_(self) -> Sequence[np.ndarray]:
r"""Returns Kraus representation of self.
Expand All @@ -475,7 +473,7 @@ def _kraus_(self) -> Sequence[np.ndarray]:
if not self._has_kraus_():
return NotImplemented

qubits = sorted(self.qubits)
qubits = sorted(self._qubit_to_op)
n = len(qubits)
if n < 1:
return (np.array([[1 + 0j]]),)
Expand Down Expand Up @@ -602,7 +600,7 @@ def to_text_diagram(
"""

# Figure out where to place everything.
qs = set(self.qubits) | set(extra_qubits)
qs = self._qubit_to_op.keys() | set(extra_qubits)
points = {xy_breakdown_func(q) for q in qs}
x_keys = sorted({pt[0] for pt in points}, key=_SortByValFallbackToType)
y_keys = sorted({pt[1] for pt in points}, key=_SortByValFallbackToType)
Expand Down
2 changes: 1 addition & 1 deletion cirq-core/cirq/experiments/qubit_characterizations.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ def pauli_error(self) -> float:
$$r_p = (1 - 1/d^2) * (1 - p),$$
where $d = 2^N_Q$ is the Hilbert space dimension and $N_Q$ is the number of qubits.
where $d = 2^{N_Q}$ is the Hilbert space dimension and $N_Q$ is the number of qubits.
"""
opt_params, _ = self._fit_exponential()
p = opt_params[2]
Expand Down
1 change: 1 addition & 0 deletions cirq-core/cirq/json_resolver_cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ def _symmetricalqidpair(qids):
'IdentityGate': cirq.IdentityGate,
'InitObsSetting': cirq.work.InitObsSetting,
'InsertionNoiseModel': InsertionNoiseModel,
'_InverseCompositeGate': raw_types._InverseCompositeGate,
'KeyCondition': cirq.KeyCondition,
'KrausChannel': cirq.KrausChannel,
'LinearDict': cirq.LinearDict,
Expand Down
4 changes: 2 additions & 2 deletions cirq-core/cirq/ops/common_channels.py
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,7 @@ def p(self) -> float:
"""The probability that one of the Pauli gates is applied.
Each of the Pauli gates is applied independently with probability
$p / (4^n_qubits - 1)$.
$p / (4^n - 1)$, where $n$ is `n_qubits`.
"""
return self._p

Expand Down Expand Up @@ -372,7 +372,7 @@ def depolarize(p: float, n_qubits: int = 1) -> DepolarizingChannel:
Args:
p: The probability that one of the Pauli gates is applied. Each of
the Pauli gates is applied independently with probability
$p / (4^n - 1)$.
$p / (4^n - 1)$, where $n$ is n_qubits.
n_qubits: The number of qubits.
Raises:
Expand Down
1 change: 0 additions & 1 deletion cirq-core/cirq/ops/gate_operation_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -506,7 +506,6 @@ def all_subclasses(cls):
cirq.transformers.measurement_transformers._ConfusionChannel,
cirq.transformers.measurement_transformers._ModAdd,
cirq.transformers.routing.visualize_routed_circuit._SwapPrintGate,
cirq.ops.raw_types._InverseCompositeGate,
cirq.circuits.qasm_output.QasmTwoQubitGate,
cirq.ops.MSGate,
# Interop gates
Expand Down
2 changes: 1 addition & 1 deletion cirq-core/cirq/ops/phased_x_z_gate.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ def from_zyz_angles(cls, z0_rad: float, y_rad: float, z1_rad: float) -> 'cirq.Ph
def from_zyz_exponents(cls, z0: float, y: float, z1: float) -> 'cirq.PhasedXZGate':
"""Create a PhasedXZGate from ZYZ exponents.
The returned gate is equivalent to $Z^z0 Y^y Z^z1$ (in time order).
The returned gate is equivalent to $Z^{z0} Y^y Z^{z1}$ (in time order).
"""
return PhasedXZGate(axis_phase_exponent=-z0 + 0.5, x_exponent=y, z_exponent=z0 + z1)

Expand Down
14 changes: 13 additions & 1 deletion cirq-core/cirq/ops/raw_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,9 @@ def with_tags(self, *new_tags: Hashable) -> 'cirq.Operation':
resulting operation to be eventually serialized into JSON, you should
also restrict the operation to be JSON serializable.
Please note that tags should be instantiated if classes are
used. Raw types are not allowed.
Args:
*new_tags: The tags to wrap this operation in.
"""
Expand Down Expand Up @@ -779,14 +782,17 @@ class TaggedOperation(Operation):
Tags added can be of any type, but they should be Hashable in order
to allow equality checking. If you wish to serialize operations into
JSON, you should restrict yourself to only use objects that have a JSON
serialization.
serialization. Tags cannot be raw types and should be instantiated
if classes are used.
See `Operation.with_tags()` for more information on intended usage.
"""

def __init__(self, sub_operation: 'cirq.Operation', *tags: Hashable):
self._sub_operation = sub_operation
self._tags = tuple(tags)
if any(isinstance(tag, type) for tag in tags):
raise ValueError('Tags cannot be types. Did you forget to instantiate the tag type?')

@property
def sub_operation(self) -> 'cirq.Operation':
Expand Down Expand Up @@ -836,6 +842,9 @@ def with_tags(self, *new_tags: Hashable) -> 'cirq.TaggedOperation':
Overloads Operation.with_tags to create a new TaggedOperation
that has the tags of this operation combined with the new_tags
specified as the parameter.
Please note that tags should be instantiated if classes are
used. Raw types are not allowed.
"""
if not new_tags:
return self
Expand Down Expand Up @@ -1066,6 +1075,9 @@ def __repr__(self) -> str:
def __str__(self) -> str:
return f'{self._original!s}†'

def _json_dict_(self) -> Dict[str, Any]:
return {'original': self._original}


def _validate_qid_shape(val: Any, qubits: Sequence['cirq.Qid']) -> None:
"""Helper function to validate qubits for gates and operations.
Expand Down
6 changes: 6 additions & 0 deletions cirq-core/cirq/ops/raw_types_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -458,6 +458,12 @@ def test_tagged_operation():
assert op.with_qubits(q2).qubits == (q2,)
assert not cirq.is_measurement(op)

# Tags can't be types
# This is to prevent typos of cirq.X(q1).with_tags(TagType)
# when you meant cirq.X(q1).with_tags(TagType())
with pytest.raises(ValueError, match="cannot be types"):
_ = cirq.X(q1).with_tags(cirq.Circuit)


def test_with_tags_returns_same_instance_if_possible():
untagged = cirq.X(cirq.GridQubit(1, 1))
Expand Down
10 changes: 10 additions & 0 deletions cirq-core/cirq/protocols/json_test_data/_InverseCompositeGate.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"cirq_type": "_InverseCompositeGate",
"original": {
"cirq_type": "QubitPermutationGate",
"permutation": [
0,
1,
2
]}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
(cirq.QubitPermutationGate(permutation=(0, 1, 2))**-1)
4 changes: 2 additions & 2 deletions cirq-core/cirq/testing/sample_gates.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def _matrix_for_phasing_state(num_qubits, phase_state, phase):

@dataclasses.dataclass(frozen=True)
class PhaseUsingCleanAncilla(ops.Gate):
r"""Phases the state $|phase_state>$ by $\exp(1j * \pi * \theta)$ using one clean ancilla."""
r"""Phases the state $|phase\_state>$ by $\exp(1j * \pi * \theta)$ using one clean ancilla."""

theta: float
phase_state: int = 1
Expand All @@ -56,7 +56,7 @@ def narrow_unitary(self) -> np.ndarray:

@dataclasses.dataclass(frozen=True)
class PhaseUsingDirtyAncilla(ops.Gate):
r"""Phases the state $|phase_state>$ by -1 using one dirty ancilla."""
r"""Phases the state $|phase\_state>$ by -1 using one dirty ancilla."""

phase_state: int = 1
target_bitsize: int = 1
Expand Down
2 changes: 1 addition & 1 deletion cirq-google/cirq_google/devices/google_noise_properties.py
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,6 @@ class NoiseModelFromGoogleNoiseProperties(devices.NoiseModelFromNoiseProperties)
"""A noise model defined from noise properties of a Google device."""

def is_virtual(self, op: cirq.Operation) -> bool:
return isinstance(op.gate, cirq.ZPowGate) and cirq_google.PhysicalZTag not in op.tags
return isinstance(op.gate, cirq.ZPowGate) and cirq_google.PhysicalZTag() not in op.tags

# noisy_moments is implemented by the superclass.
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ def test_with_params_opid_with_gate():
@pytest.mark.parametrize(
'op',
[
(cirq.Z(cirq.LineQubit(0)) ** 0.3).with_tags(cirq_google.PhysicalZTag),
(cirq.Z(cirq.LineQubit(0)) ** 0.3).with_tags(cirq_google.PhysicalZTag()),
cirq.PhasedXZGate(x_exponent=0.8, z_exponent=0.2, axis_phase_exponent=0.1).on(
cirq.LineQubit(0)
),
Expand Down
2 changes: 0 additions & 2 deletions dev_tools/notebooks/isolated_notebook_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,6 @@
# Requires OpenQASM 3.0 support from cirq 1.6
'docs/build/interop.ipynb',
# get_qcs_objects_for_notebook
'docs/noise/calibration_api.ipynb',
'docs/noise/floquet_calibration_example.ipynb',
'docs/noise/qcvv/xeb_calibration_example.ipynb',
]

Expand Down
2 changes: 0 additions & 2 deletions dev_tools/notebooks/notebook_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,6 @@
'docs/tutorials/google/identifying_hardware_changes.ipynb',
'docs/tutorials/google/echoes.ipynb',
'docs/noise/qcvv/xeb_calibration_example.ipynb',
'docs/noise/calibration_api.ipynb',
'docs/noise/floquet_calibration_example.ipynb',
# temporary: need to fix QVM metrics and device spec
'docs/tutorials/google/spin_echoes.ipynb',
'docs/tutorials/google/visualizing_calibration_metrics.ipynb',
Expand Down
3 changes: 1 addition & 2 deletions docs/build/ecosystem.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,7 @@ The following document provides an ecosystem overview of how the various tools c

|Name|Main sponsor|Description|
|--- |--- |--- |
|[MITIQ](https://github.com/unitaryfund/mitiq)|Unitary Fund|An open-source library for error mitigation|
|[Orquestra](https://www.zapatacomputing.com/orquestra/)|Zapata|A proprietary workflow management tool for quantum computing|
|[Mitiq](https://github.com/unitaryfund/mitiq)|Unitary Foundation|An open-source library for error mitigation|
|[pyGSTi](https://www.pygsti.info/)|Sandia National Labs|An open-source library for modeling and characterizing noisy quantum information processors|
|[QUEKO](https://github.com/UCLA-VAST/QUEKO-benchmark)|UCLA|An open-source tool to generate benchmarks with the known optimal solutions|
|[tket](https://cqcl.github.io/tket/pytket/api/index.html)|Cambridge Quantum|An open-source and platform agnostic SDK for circuit optimisation, compilation and noise mitigation|
Expand Down
2 changes: 1 addition & 1 deletion docs/google/best_practices.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,7 @@
"source": [
"### Use batches if sweeps are not possible\n",
"\n",
"The engine has a method called `run_batch()` that can be used to send multiple\n",
"The sampler has a method called `run_batch()` that can be used to send multiple\n",
"circuits in a single request. This can be used to increase the efficiency\n",
"of your program so that more repetitions are completed per second.\n",
"\n",
Expand Down
Loading

0 comments on commit 7deb8a8

Please sign in to comment.