Skip to content

Commit

Permalink
Fix deepcopy for new-style Bit (#10411) (#10413)
Browse files Browse the repository at this point in the history
* Make bit copy return self.

* Add release note.

* Preserve existing deepcopy behavior for old bits.

* Add tests to validate copy behaviors.

(cherry picked from commit e9c86a5)

Co-authored-by: Kevin Hartman <kevin@hart.mn>
  • Loading branch information
mergify[bot] and kevinhartman authored Jul 11, 2023
1 parent 2350fa5 commit 76d52cb
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 2 deletions.
18 changes: 18 additions & 0 deletions qiskit/circuit/bit.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"""
Quantum bit and Classical bit objects.
"""
import copy

from qiskit.circuit.exceptions import CircuitError
from qiskit.utils.deprecation import deprecate_func
Expand Down Expand Up @@ -120,3 +121,20 @@ def __eq__(self, other):
return self._repr == other._repr
except AttributeError:
return False

def __copy__(self):
# Bits are immutable.
return self

def __deepcopy__(self, memo=None):
if (self._register, self._index) == (None, None):
return self

# Old-style bits need special handling for now, since some code seems
# to rely on their registers getting deep-copied.
bit = type(self).__new__(type(self))
bit._register = copy.deepcopy(self._register, memo)
bit._index = self._index
bit._hash = self._hash
bit._repr = self._repr
return bit
7 changes: 7 additions & 0 deletions releasenotes/notes/fix-bit-copy-4b2f7349683f616a.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
fixes:
- |
Fixed an issue with copying circuits with new-style :class:`.Clbit`\ s and
:class:`.Qubit`\ s (bits without registers) where references to these bits
from the containing circuit could be broken, causing issues with
serialization and circuit visualization.
37 changes: 35 additions & 2 deletions test/python/circuit/test_bit.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
# pylint: disable=missing-function-docstring

"""Test library of quantum circuits."""

import copy
from unittest import mock

from qiskit.test import QiskitTestCase
Expand Down Expand Up @@ -57,6 +57,25 @@ def test_old_style_bit_equality(self):

self.assertNotEqual(bit.Bit(test_reg, 0), bit.Bit(reg_difftype, 0))

def test_old_style_bit_deepcopy(self):
"""Verify deep-copies of bits are equal but not the same instance."""
test_reg = mock.MagicMock(size=3, name="foo")
test_reg.__str__.return_value = "Register(3, 'foo')"

bit1 = bit.Bit(test_reg, 0)
bit2 = copy.deepcopy(bit1)

self.assertIsNot(bit1, bit2)
self.assertIsNot(bit1._register, bit2._register)
self.assertEqual(bit1, bit2)

def test_old_style_bit_copy(self):
"""Verify copies of bits are the same instance."""
bit1 = bit.Bit()
bit2 = copy.copy(bit1)

self.assertIs(bit1, bit2)


class TestNewStyleBit(QiskitTestCase):
"""Test behavior of new-style bits."""
Expand All @@ -65,7 +84,21 @@ def test_bits_do_not_require_registers(self):
"""Verify we can create a bit outside the context of a register."""
self.assertIsInstance(bit.Bit(), bit.Bit)

def test_newstyle_bit_equality(self):
def test_new_style_bit_deepcopy(self):
"""Verify deep-copies of bits are the same instance."""
bit1 = bit.Bit()
bit2 = copy.deepcopy(bit1)

self.assertIs(bit1, bit2)

def test_new_style_bit_copy(self):
"""Verify copies of bits are the same instance."""
bit1 = bit.Bit()
bit2 = copy.copy(bit1)

self.assertIs(bit1, bit2)

def test_new_style_bit_equality(self):
"""Verify bits instances are equal only to themselves."""
bit1 = bit.Bit()
bit2 = bit.Bit()
Expand Down

0 comments on commit 76d52cb

Please sign in to comment.