Skip to content

Commit e2b3221

Browse files
committed
Try multiple singletons, new one for const.
1 parent f0abf7d commit e2b3221

2 files changed

Lines changed: 28 additions & 7 deletions

File tree

qiskit/circuit/classical/types/types.py

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,11 @@
3131

3232

3333
class _Singleton(type):
34-
"""Metaclass to make the child, which should take zero initialization arguments, a singleton
34+
"""Metaclass to make the child, which should take a single "const" argument, a singleton
3535
object."""
3636

37-
def _get_singleton_instance(cls):
38-
return cls._INSTANCE
37+
def _get_singleton_instance(cls, const=False):
38+
return cls._CONST_INSTANCE if const else cls._INSTANCE
3939

4040
@classmethod
4141
def __prepare__(mcs, name, bases): # pylint: disable=unused-argument
@@ -45,6 +45,7 @@ def __prepare__(mcs, name, bases): # pylint: disable=unused-argument
4545
def __new__(cls, name, bases, namespace):
4646
out = super().__new__(cls, name, bases, namespace)
4747
out._INSTANCE = object.__new__(out) # pylint: disable=invalid-name
48+
out._CONST_INSTANCE = object.__new__(out) # pylint: disable=invalid-name
4849
return out
4950

5051

@@ -88,13 +89,18 @@ def __setstate__(self, state):
8889

8990

9091
@typing.final
91-
class Bool(Type):
92+
class Bool(Type, metaclass=_Singleton):
9293
"""The Boolean type. This has exactly two values: ``True`` and ``False``."""
9394

94-
__slots__ = ("const",)
95+
__slots__ = ()
9596

96-
def __init__(self, *, const: bool = False):
97-
super(Type, self).__setattr__("const", const)
97+
def __new__(cls, *, const=False):
98+
return cls._get_singleton_instance(const)
99+
100+
@property
101+
def const(self):
102+
# Check if this instance is the const singleton.
103+
return self is self.__class__._CONST_INSTANCE
98104

99105
def __repr__(self):
100106
return f"Bool(const={self.const})"

test/python/circuit/classical/test_expr_properties.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,21 @@ def test_bool_type_is_singleton(self):
3333
self.assertIs(types.Bool(), copy.deepcopy(types.Bool()))
3434
self.assertIs(types.Bool(), pickle.loads(pickle.dumps(types.Bool())))
3535

36+
def test_const_bool_type_is_singleton(self):
37+
"""The `Bool` type is meant (and used) as a Python singleton object for efficiency. It must
38+
always be referentially equal to all other references to it."""
39+
self.assertIs(types.Bool(const=True), types.Bool(const=True))
40+
self.assertIs(types.Bool(const=True), copy.copy(types.Bool(const=True)))
41+
self.assertIs(types.Bool(const=True), copy.deepcopy(types.Bool(const=True)))
42+
self.assertIs(types.Bool(const=True), pickle.loads(pickle.dumps(types.Bool(const=True))))
43+
44+
def test_bool_type_singleton_is_not_const_bool_type_singleton(self):
45+
"""The const `Bool` and non-const `Bool` should not share the same singleton."""
46+
self.assertIsNot(types.Bool(const=True), types.Bool())
47+
self.assertIsNot(types.Bool(const=True), copy.copy(types.Bool()))
48+
self.assertIsNot(types.Bool(const=True), copy.deepcopy(types.Bool()))
49+
self.assertIsNot(types.Bool(const=True), pickle.loads(pickle.dumps(types.Bool())))
50+
3651
@ddt.data(types.Bool(), types.Uint(8))
3752
def test_types_can_be_cloned(self, obj):
3853
"""Test that various ways of cloning a `Type` object are valid and produce equal output."""

0 commit comments

Comments
 (0)