Skip to content

Add q helper function for constructing common qubit types. #5181

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Apr 9, 2022
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cirq-core/cirq/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,7 @@
ProjectorString,
ProjectorSum,
RandomGateChannel,
q,
qft,
Qid,
QuantumFourierTransformGate,
Expand Down
2 changes: 2 additions & 0 deletions cirq-core/cirq/ops/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -308,3 +308,5 @@
)

from cirq.ops.state_preparation_channel import StatePreparationChannel

from cirq.ops.util import q
68 changes: 68 additions & 0 deletions cirq-core/cirq/ops/util.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Copyright 2022 The Cirq Developers
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Can we give a better name to this module? util is too generic and it'll be hard to guess what's inside it by looking at the name. Maybe ops/qid_util.py ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# 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 typing import Optional, overload, TYPE_CHECKING, Union

if TYPE_CHECKING:
import cirq


@overload
def q(x: int) -> 'cirq.LineQubit':
...


@overload
def q(x: int, y: int) -> 'cirq.GridQubit':
...


@overload
def q(x: str) -> 'cirq.NamedQubit':
...


def q(
x: Union[int, str], y: Optional[int] = None
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we change the signature to accept *args and **kwargs, and still support mypy type checking using overloads, so that it's extensible in the future for a 4th qid type that we might introduce?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I originally tried that but mypy wasn't happy with using *args with these overloads:

cirq-core/cirq/ops/util.py:36: error: Overloaded function implementation does not accept all possible arguments of signature 1  [misc]
cirq-core/cirq/ops/util.py:36: error: Overloaded function implementation does not accept all possible arguments of signature 2  [misc]
cirq-core/cirq/ops/util.py:36: error: Overloaded function implementation does not accept all possible arguments of signature 3  [misc]

I haven't been able to figure out a way around this. However, from my perspective adding additional signatures in the future, as long as we keep these signatures working and can make mypy happy, would be a backwards-compatible change.

One thing that's a bit unfortunate is I'd like to make these "positional-only" params so you can't do, say q(x=1, y=0), but positional-only args are only supported in 3.8+. I will change the names to add an underscore prefix to indicate that these should be treated as positional-only.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Success! The right incantation was to prefix arg names with double underscore in the overload definitions and then mypy treats them as positional-only and allows using *args. Much cleaner this way, and more extensible for the future. Thanks for the suggestion!

) -> Union['cirq.LineQubit', 'cirq.GridQubit', 'cirq.NamedQubit']:
"""Constructs a qubit id of the appropriate type based on args.

This is shorthand for constructing qubit ids of common types:
>>> cirq.q(1) == cirq.LineQubit(1)
>>> cirq.q(1, 2) == cirq.GridQubit(1, 2)
>>> cirq.q("foo") == cirq.NamedQubit("foo")

Args:
x: First arg.
y: Second arg.

Returns:
cirq.LineQubit if called with one integer arg.
cirq.GridQubit if called with two integer args.
cirq.NamedQubit if called with one string arg.

Raises:
ValueError: if called with invalid arguments.
"""
import cirq # avoid circular import

if y is None:
if isinstance(x, int):
return cirq.LineQubit(x)
elif isinstance(x, str):
return cirq.NamedQubit(x)
else:
if isinstance(x, int) and isinstance(y, int):
return cirq.GridQubit(x, y)
raise ValueError(f"Could not construct qubit: args={(x, y)}")
31 changes: 31 additions & 0 deletions cirq-core/cirq/ops/util_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Copyright 2022 The Cirq Developers
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# 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


def test_q() -> None:
assert cirq.q(0) == cirq.LineQubit(0)
assert cirq.q(1, 2) == cirq.GridQubit(1, 2)
assert cirq.q("foo") == cirq.NamedQubit("foo")


def test_q_invalid() -> None:
# Ignore static type errors so we can test runtime typechecks.
with pytest.raises(ValueError):
cirq.q([1, 2, 3]) # type: ignore[call-overload]
with pytest.raises(ValueError):
cirq.q(1, "foo") # type: ignore[call-overload]