Skip to content

Commit

Permalink
Added qid version of Named Qubit (#3187)
Browse files Browse the repository at this point in the history
closes #3069

I have added a qid version of Named Qubit, and added the relevant tests
  • Loading branch information
oliverob committed Aug 25, 2020
1 parent e0fbadc commit 4041362
Show file tree
Hide file tree
Showing 15 changed files with 284 additions and 23 deletions.
1 change: 1 addition & 0 deletions cirq/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@
Moment,
MutableDensePauliString,
NamedQubit,
NamedQid,
OP_TREE,
Operation,
ParallelGateOperation,
Expand Down
4 changes: 3 additions & 1 deletion cirq/ops/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,9 @@
Moment,)

from cirq.ops.named_qubit import (
NamedQubit,)
NamedQubit,
NamedQid,
)

from cirq.ops.op_tree import (
flatten_op_tree,
Expand Down
118 changes: 102 additions & 16 deletions cirq/ops/named_qubit.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,22 @@
# 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 Any, Dict
import functools
from typing import Any, Dict, List, TYPE_CHECKING, TypeVar

from cirq import protocols
from cirq.ops import raw_types


class NamedQubit(raw_types.Qid):
"""A qubit identified by name.
if TYPE_CHECKING:
import cirq

By default, NamedQubit has a lexicographic order. However, numbers within
the name are handled correctly. So, for example, if you print a circuit
containing `cirq.NamedQubit('qubit22')` and `cirq.NamedQubit('qubit3')`, the
wire for 'qubit3' will correctly come before 'qubit22'.
"""
TSelf = TypeVar('TSelf', bound='_BaseNamedQid') # type: ignore


@functools.total_ordering # type: ignore
class _BaseNamedQid(raw_types.Qid):
"""The base class for `NamedQid` and `NamedQubit`."""

def __init__(self, name: str) -> None:
self._name = name
Expand All @@ -33,36 +35,120 @@ def __init__(self, name: str) -> None:
def _comparison_key(self):
return self._comp_key

@property
def name(self) -> str:
return self._name

def with_dimension(self, dimension: int) -> 'NamedQid':
return NamedQid(self._name, dimension=dimension)


class NamedQid(_BaseNamedQid):
"""A qid identified by name.
By default, `NamedQid` has a lexicographic order. However, numbers within
the name are handled correctly. So, for example, if you print a circuit
containing `cirq.NamedQid('qid22', dimension=3)` and
`cirq.NamedQid('qid3', dimension=3)`, the wire for 'qid3' will
correctly come before 'qid22'.
"""

def __init__(self, name: str, dimension: int) -> None:
"""Initializes a `NamedQid` with a given name and dimension.
Args:
name: The name.
dimension: The dimension of the qid's Hilbert space, i.e.
the number of quantum levels.
"""
super().__init__(name)
self._dimension = dimension
self.validate_dimension(dimension)

@property
def dimension(self) -> int:
return 2
return self._dimension

def __repr__(self) -> str:
return f'cirq.NamedQid({self.name!r}, dimension={self.dimension})'

def __str__(self) -> str:
return self._name
return f'{self.name} (d={self.dimension})'

@staticmethod
def range(*args, prefix: str, dimension: int) -> List['NamedQid']:
"""Returns a range of ``NamedQid``\\s.
The range returned starts with the prefix, and followed by a qid for
each number in the range, e.g.:
>>> cirq.NamedQid.range(3, prefix='a', dimension=3)
... # doctest: +NORMALIZE_WHITESPACE
[cirq.NamedQid('a0', dimension=3), cirq.NamedQid('a1', dimension=3),
cirq.NamedQid('a2', dimension=3)]
>>> cirq.NamedQid.range(2, 4, prefix='a', dimension=3)
[cirq.NamedQid('a2', dimension=3), cirq.NamedQid('a3', dimension=3)]
Args:
*args: Args to be passed to Python's standard range function.
prefix: A prefix for constructed NamedQids.
dimension: The dimension of the qid's Hilbert space, i.e.
the number of quantum levels.
Returns:
A list of ``NamedQid``\\s.
"""
return [
NamedQid(prefix + str(i), dimension=dimension) for i in range(*args)
]

def _json_dict_(self) -> Dict[str, Any]:
return protocols.obj_to_dict_helper(self, ['name', 'dimension'])


class NamedQubit(_BaseNamedQid):
"""A qubit identified by name.
By default, `NamedQubit` has a lexicographic order. However, numbers within
the name are handled correctly. So, for example, if you print a circuit
containing `cirq.NamedQubit('qubit22')` and `cirq.NamedQubit('qubit3')`, the
wire for 'qubit3' will correctly come before 'qubit22'.
"""

@property
def name(self) -> str:
def dimension(self) -> int:
return 2

def _cmp_tuple(self):
cls = NamedQid if type(self) is NamedQubit else type(self)
# Must be same as Qid._cmp_tuple but with cls in place of type(self).
return (cls.__name__, repr(cls), self._comparison_key(), self.dimension)

def __str__(self) -> str:
return self._name

def __repr__(self) -> str:
return f'cirq.NamedQubit({self._name!r})'

@staticmethod
def range(*args, prefix: str):
"""Returns a range of NamedQubits.
def range(*args, prefix: str) -> List['NamedQubit']:
"""Returns a range of ``NamedQubit``\\s.
The range returned starts with the prefix, and followed by a qubit for
each number in the range, e.g.:
NamedQubit.range(3, prefix="a") -> ["a1", "a2", "a3]
NamedQubit.range(2, 4, prefix="a") -> ["a2", "a3]
>>> cirq.NamedQubit.range(3, prefix='a')
... # doctest: +NORMALIZE_WHITESPACE
[cirq.NamedQubit('a0'), cirq.NamedQubit('a1'),
cirq.NamedQubit('a2')]
>>> cirq.NamedQubit.range(2, 4, prefix='a')
[cirq.NamedQubit('a2'), cirq.NamedQubit('a3')]
Args:
*args: Args to be passed to Python's standard range function.
prefix: A prefix for constructed NamedQubits.
Returns:
A list of NamedQubits.
A list of ``NamedQubit``\\s.
"""
return [NamedQubit(prefix + str(i)) for i in range(*args)]

Expand Down
89 changes: 87 additions & 2 deletions cirq/ops/named_qubit_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,35 +16,76 @@
from cirq.ops.named_qubit import _pad_digits


def test_init():
q = cirq.NamedQubit('a')
assert q.name == 'a'

q = cirq.NamedQid('a', dimension=3)
assert q.name == 'a'
assert q.dimension == 3


def test_named_qubit_str():
q = cirq.NamedQubit('a')
assert q.name == 'a'
assert str(q) == 'a'
qid = cirq.NamedQid('a', dimension=3)
assert qid.name == 'a'
assert str(qid) == 'a (d=3)'


def test_named_qubit_repr():
q = cirq.NamedQubit('a')
assert repr(q) == "cirq.NamedQubit('a')"
qid = cirq.NamedQid('a', dimension=3)
assert repr(qid) == "cirq.NamedQid('a', dimension=3)"


def test_named_qubit_order():
order = cirq.testing.OrderTester()
order.add_ascending(
cirq.NamedQid('', dimension=1),
cirq.NamedQubit(''),
cirq.NamedQid('', dimension=3),
cirq.NamedQid('1', dimension=1),
cirq.NamedQubit('1'),
cirq.NamedQid('1', dimension=3),
cirq.NamedQid('a', dimension=1),
cirq.NamedQubit('a'),
cirq.NamedQid('a', dimension=3),
cirq.NamedQid('a00000000', dimension=1),
cirq.NamedQubit('a00000000'),
cirq.NamedQid('a00000000', dimension=3),
cirq.NamedQid('a00000000:8', dimension=1),
cirq.NamedQubit('a00000000:8'),
cirq.NamedQid('a00000000:8', dimension=3),
cirq.NamedQid('a9', dimension=1),
cirq.NamedQubit('a9'),
cirq.NamedQid('a9', dimension=3),
cirq.NamedQid('a09', dimension=1),
cirq.NamedQubit('a09'),
cirq.NamedQid('a09', dimension=3),
cirq.NamedQid('a10', dimension=1),
cirq.NamedQubit('a10'),
cirq.NamedQid('a10', dimension=3),
cirq.NamedQid('a11', dimension=1),
cirq.NamedQubit('a11'),
cirq.NamedQid('a11', dimension=3),
cirq.NamedQid('aa', dimension=1),
cirq.NamedQubit('aa'),
cirq.NamedQid('aa', dimension=3),
cirq.NamedQid('ab', dimension=1),
cirq.NamedQubit('ab'),
cirq.NamedQid('ab', dimension=3),
cirq.NamedQid('b', dimension=1),
cirq.NamedQubit('b'),
cirq.NamedQid('b', dimension=3),
)
order.add_ascending_equivalence_group(
cirq.NamedQubit('c'),
cirq.NamedQubit('c'),
cirq.NamedQid('c', dimension=2),
cirq.NamedQid('c', dimension=2),
)


Expand All @@ -64,5 +105,49 @@ def test_named_qubit_range():
assert qubits == [cirq.NamedQubit('a0'), cirq.NamedQubit('a1')]

qubits = cirq.NamedQubit.range(-1, 4, 2, prefix='a')
assert qubits == [cirq.NamedQubit('a-1'),
cirq.NamedQubit('a1'), cirq.NamedQubit('a3')]
assert qubits == [
cirq.NamedQubit('a-1'),
cirq.NamedQubit('a1'),
cirq.NamedQubit('a3'),
]


def test_named_qid_range():
qids = cirq.NamedQid.range(2, prefix='a', dimension=3)
assert qids == [
cirq.NamedQid('a0', dimension=3),
cirq.NamedQid('a1', dimension=3)
]

qids = cirq.NamedQid.range(-1, 4, 2, prefix='a', dimension=3)
assert qids == [
cirq.NamedQid('a-1', dimension=3),
cirq.NamedQid('a1', dimension=3),
cirq.NamedQid('a3', dimension=3),
]

qids = cirq.NamedQid.range(2, prefix='a', dimension=4)
assert qids == [
cirq.NamedQid('a0', dimension=4),
cirq.NamedQid('a1', dimension=4)
]

qids = cirq.NamedQid.range(-1, 4, 2, prefix='a', dimension=4)
assert qids == [
cirq.NamedQid('a-1', dimension=4),
cirq.NamedQid('a1', dimension=4),
cirq.NamedQid('a3', dimension=4),
]


def test_to_json():
assert cirq.NamedQubit('c')._json_dict_() == {
'cirq_type': 'NamedQubit',
'name': 'c',
}

assert cirq.NamedQid('c', dimension=3)._json_dict_() == {
'cirq_type': 'NamedQid',
'name': 'c',
'dimension': 3,
}
2 changes: 2 additions & 0 deletions cirq/protocols/json_serialization.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@ def two_qubit_matrix_gate(matrix):
'_NamedConstantXmonDevice': _NamedConstantXmonDevice,
'_NoNoiseModel': _NoNoiseModel,
'NamedQubit': cirq.NamedQubit,
'NamedQid': cirq.NamedQid,
'NoIdentifierQubit': cirq.testing.NoIdentifierQubit,
'_PauliX': cirq.ops.pauli_gates._PauliX,
'_PauliY': cirq.ops.pauli_gates._PauliY,
'_PauliZ': cirq.ops.pauli_gates._PauliZ,
Expand Down
5 changes: 5 additions & 0 deletions cirq/protocols/json_test_data/NamedQid.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"cirq_type": "NamedQid",
"name": "a",
"dimension": 3
}
1 change: 1 addition & 0 deletions cirq/protocols/json_test_data/NamedQid.repr
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
cirq.NamedQid("a", dimension=3)
3 changes: 3 additions & 0 deletions cirq/protocols/json_test_data/NoIdentifierQubit.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"cirq_type": "NoIdentifierQubit"
}
1 change: 1 addition & 0 deletions cirq/protocols/json_test_data/NoIdentifierQubit.repr
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
cirq.testing.NoIdentifierQubit()
5 changes: 2 additions & 3 deletions cirq/protocols/json_test_data/_QubitAsQid.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
{
"cirq_type": "_QubitAsQid",
"qubit": {
"cirq_type": "NamedQubit",
"name": "a"
"cirq_type": "NoIdentifierQubit"
},
"dimension": 5
}
}
2 changes: 1 addition & 1 deletion cirq/protocols/json_test_data/_QubitAsQid.repr
Original file line number Diff line number Diff line change
@@ -1 +1 @@
cirq.NamedQubit('a').with_dimension(5)
cirq.testing.NoIdentifierQubit().with_dimension(5)
3 changes: 3 additions & 0 deletions cirq/testing/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@
from cirq.testing.logs import (
assert_logs,)

from cirq.testing.no_identifier_qubit import (
NoIdentifierQubit,)

from cirq.testing.order_tester import (
OrderTester,)

Expand Down

0 comments on commit 4041362

Please sign in to comment.