Skip to content
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

Set operations and tests added #5983

Merged
merged 41 commits into from
Jul 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
fce3d8d
Set operations and tests added
austingmhuang Jul 10, 2024
9b8c9b6
changelog and docstrigns
austingmhuang Jul 10, 2024
e20e0cd
codefactor unhappy
austingmhuang Jul 10, 2024
1e8c249
pylint
austingmhuang Jul 10, 2024
9c3fe5d
fix errors for py3.9
austingmhuang Jul 11, 2024
e966a81
wrong place for future import
austingmhuang Jul 11, 2024
39b6084
indentation error...
austingmhuang Jul 11, 2024
8b21c06
black
austingmhuang Jul 11, 2024
63124ec
maybe dont need from future?
austingmhuang Jul 11, 2024
b1de740
Update pennylane/wires.py
austingmhuang Jul 15, 2024
0e68949
Update pennylane/wires.py
austingmhuang Jul 15, 2024
be5844e
Update pennylane/wires.py
austingmhuang Jul 15, 2024
effec8e
Update pennylane/wires.py
austingmhuang Jul 15, 2024
af54d3a
address comments and update tests accordingly
austingmhuang Jul 15, 2024
a714ccd
docstring
austingmhuang Jul 15, 2024
2a6980d
Merge branch 'master' into set-based-operations
austingmhuang Jul 15, 2024
f0cf271
fixes to tests
austingmhuang Jul 15, 2024
b9414e9
Merge branch 'master' into set-based-operations
austingmhuang Jul 15, 2024
3ee6dd0
duck typing for wires, removed tests related to typeErrors that are n…
austingmhuang Jul 16, 2024
2a43240
Merge branch 'master' into set-based-operations
austingmhuang Jul 18, 2024
e1c3746
Update pennylane/wires.py
austingmhuang Jul 19, 2024
3fd94b3
Update pennylane/wires.py
austingmhuang Jul 19, 2024
138eb31
Update pennylane/wires.py
austingmhuang Jul 19, 2024
c5847a3
Update pennylane/wires.py
austingmhuang Jul 19, 2024
e0fe314
Update pennylane/wires.py
austingmhuang Jul 19, 2024
2ca88d7
Update pennylane/wires.py
austingmhuang Jul 19, 2024
c4e7f95
Update pennylane/wires.py
austingmhuang Jul 19, 2024
8acb343
small impelementation change
austingmhuang Jul 19, 2024
7023a6e
Update pennylane/wires.py
austingmhuang Jul 19, 2024
0e6caa7
revert
austingmhuang Jul 19, 2024
5dd10ff
Update pennylane/wires.py
austingmhuang Jul 24, 2024
9f349de
Update pennylane/wires.py
austingmhuang Jul 24, 2024
475333b
Update pennylane/wires.py
austingmhuang Jul 24, 2024
c234fad
Update pennylane/wires.py
austingmhuang Jul 24, 2024
796219c
Update pennylane/wires.py
austingmhuang Jul 24, 2024
b7a9c8d
fixes
austingmhuang Jul 25, 2024
675dbcb
Merge branch 'master' into set-based-operations
austingmhuang Jul 25, 2024
018c7f5
Merge branch 'master' into set-based-operations
austingmhuang Jul 26, 2024
5a859ae
Merge branch 'master' into set-based-operations
austingmhuang Jul 26, 2024
4d5ef07
Update doc/releases/changelog-dev.md
austingmhuang Jul 26, 2024
d21786c
Merge branch 'master' into set-based-operations
austingmhuang Jul 26, 2024
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
3 changes: 3 additions & 0 deletions doc/releases/changelog-dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@
* `QuantumScript.hash` is now cached, leading to performance improvements.
[(#5919)](https://github.com/PennyLaneAI/pennylane/pull/5919)

* Set operations are now supported by Wires.
[(#5983)](https://github.com/PennyLaneAI/pennylane/pull/5983)

* `qml.dynamic_one_shot` now supports circuits using the `"tensorflow"` interface.
[(#5973)](https://github.com/PennyLaneAI/pennylane/pull/5973)

Expand Down
201 changes: 201 additions & 0 deletions pennylane/wires.py
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,207 @@ def unique_wires(list_of_wires):

return Wires(tuple(unique), _override=True)

def union(self, other):
"""Return the union of the current Wires object and either another Wires object or an
iterable that can be interpreted like a Wires object e.g., List.

Args:
other (Any): Wires or any iterable that can be interpreted like a Wires object
to perform the union with. See _process for details on the interpretation.

Returns:
Wires: A new Wires object representing the union of the two Wires objects.

**Example**
austingmhuang marked this conversation as resolved.
Show resolved Hide resolved

>>> from pennylane.wires import Wires
>>> wires1 = Wires([1, 2, 3])
>>> wires2 = Wires([3, 4, 5])
austingmhuang marked this conversation as resolved.
Show resolved Hide resolved
>>> wires1.union(wires2)
Wires([1, 2, 3, 4, 5])

Alternatively, use the | operator:
>>> wires1 | wires2
Wires([1, 2, 3, 4, 5])
"""
return Wires((set(self.labels) | set(_process(other))))

def __or__(self, other):
"""Return the union of the current Wires object and either another Wires object or an
iterable that can be interpreted like a Wires object e.g., List.

Args:
other (Any): Wires or any iterable that can be interpreted like a Wires object
to perform the union with. See _process for details on the interpretation.

Returns:
Wires: A new Wires object representing the union of the two Wires objects.

**Example**

>>> from pennylane.wires import Wires
>>> wires1 = Wires([1, 2, 3])
>>> wires2 = Wires([3, 4, 5])
austingmhuang marked this conversation as resolved.
Show resolved Hide resolved
>>> wires1 | wires2
austingmhuang marked this conversation as resolved.
Show resolved Hide resolved
Wires([1, 2, 3, 4, 5])
"""
return self.union(other)

def __ror__(self, other):
"""Right-hand version of __or__."""
return self.union(other)
austingmhuang marked this conversation as resolved.
Show resolved Hide resolved

def intersection(self, other):
"""Return the intersection of the current Wires object and either another Wires object or
an iterable that can be interpreted like a Wires object e.g., List.

Args:
other (Any): Wires or any iterable that can be interpreted like a Wires object
to perform the union with. See _process for details on the interpretation.

Returns:
Wires: A new Wires object representing the intersection of the two Wires objects.

**Example**

>>> from pennylane.wires import Wires
>>> wires1 = Wires([1, 2, 3])
>>> wires2 = Wires([2, 3, 4])
austingmhuang marked this conversation as resolved.
Show resolved Hide resolved
>>> wires1.intersection(wires2)
Wires([2, 3])

austingmhuang marked this conversation as resolved.
Show resolved Hide resolved
Alternatively, use the & operator:
>>> wires1 & wires2
Wires([2, 3])
"""
return Wires((set(self.labels) & set(_process(other))))

def __and__(self, other):
"""Return the intersection of the current Wires object and either another Wires object or
an iterable that can be interpreted like a Wires object e.g., List.

Args:
other (Any): Wires or any iterable that can be interpreted like a Wires object
to perform the union with. See _process for details on the interpretation.

Returns:
Wires: A new Wires object representing the intersection of the two Wires objects.

**Example**

>>> from pennylane.wires import Wires
>>> wires1 = Wires([1, 2, 3])
>>> wires2 = Wires([2, 3, 4])
austingmhuang marked this conversation as resolved.
Show resolved Hide resolved
>>> wires1 & wires2
Wires([2, 3])
"""
return self.intersection(other)

def __rand__(self, other):
"""Right-hand version of __and__."""
return self.intersection(other)
austingmhuang marked this conversation as resolved.
Show resolved Hide resolved

def difference(self, other):
austingmhuang marked this conversation as resolved.
Show resolved Hide resolved
"""Return the difference of the current Wires object and either another Wires object or
an iterable that can be interpreted like a Wires object e.g., List.

Args:
other (Any): Wires object or any iterable that can be interpreted like a Wires object
to perform the union with. See _process for details on the interpretation.

Returns:
Wires: A new Wires object representing the difference of the two Wires objects.

**Example**

>>> from pennylane.wires import Wires
>>> wires1 = Wires([1, 2, 3])
>>> wires2 = Wires([2, 3, 4])
austingmhuang marked this conversation as resolved.
Show resolved Hide resolved
>>> wires1.difference(wires2)
Wires([1])

Alternatively, use the - operator:
>>> wires1 - wires2
Wires([1])
"""
return Wires((set(self.labels) - set(_process(other))))

def __sub__(self, other):
"""Return the difference of the current Wires object and either another Wires object or
an iterable that can be interpreted like a Wires object e.g., List.

Args:
other (Any): Wires or any iterable that can be interpreted like a Wires object
to perform the union with. See _process for details on the interpretation.

Returns:
Wires: A new Wires object representing the difference of the two Wires objects.

**Example**

>>> from pennylane.wires import Wires
>>> wires1 = Wires([1, 2, 3])
austingmhuang marked this conversation as resolved.
Show resolved Hide resolved
>>> wires2 = Wires([2, 3, 4])
>>> wires1 - wires2
Wires([1])
"""
return self.difference(other)

def __rsub__(self, other):
"""Right-hand version of __sub__."""
return Wires((set(_process(other)) - set(self.labels)))

def symmetric_difference(self, other):
"""Return the symmetric difference of the current Wires object and either another Wires
object or an iterable that can be interpreted like a Wires object e.g., List.

Args:
other (Any): Wires or any iterable that can be interpreted like a Wires object
to perform the union with. See _process for details on the interpretation.

Returns:
Wires: A new Wires object representing the symmetric difference of the two Wires objects.

**Example**

>>> from pennylane.wires import Wires
>>> wires1 = Wires([1, 2, 3])
>>> wires2 = Wires([3, 4, 5])
austingmhuang marked this conversation as resolved.
Show resolved Hide resolved
>>> wires1.symmetric_difference(wires2)
Wires([1, 2, 4, 5])

Alternatively, use the ^ operator:
>>> wires1 ^ wires2
Wires([1, 2, 4, 5])
"""

return Wires((set(self.labels) ^ set(_process(other))))

def __xor__(self, other):
"""Return the symmetric difference of the current Wires object and either another Wires
object or an iterable that can be interpreted like a Wires object e.g., List.

Args:
other (Any): Wires or any iterable that can be interpreted like a Wires object
to perform the union with. See _process for details on the interpretation.

Returns:
Wires: A new Wires object representing the symmetric difference of the two Wires objects.

**Example**

>>> from pennylane.wires import Wires
>>> wires1 = Wires([1, 2, 3])
>>> wires2 = Wires([3, 4, 5])
austingmhuang marked this conversation as resolved.
Show resolved Hide resolved
>>> wires1 ^ wires2
Wires([1, 2, 4, 5])
"""
return self.symmetric_difference(other)

def __rxor__(self, other):
"""Right-hand version of __xor__."""
return Wires((set(_process(other)) ^ set(self.labels)))


# Register Wires as a PyTree-serializable class
register_pytree(Wires, Wires._flatten, Wires._unflatten) # pylint: disable=protected-access
110 changes: 110 additions & 0 deletions tests/test_wires.py
Original file line number Diff line number Diff line change
Expand Up @@ -377,3 +377,113 @@ def test_wires_pytree(self, source):
wires2 = tree_unflatten(tree, wires_flat)
assert isinstance(wires2, Wires), f"{wires2} is not Wires"
assert wires == wires2, f"{wires} != {wires2}"

@pytest.mark.parametrize(
"wire_a, wire_b, expected",
[
(Wires([0, 1]), Wires([2, 3]), Wires([0, 1, 2, 3])),
austingmhuang marked this conversation as resolved.
Show resolved Hide resolved
(Wires([0, 1]), [2, 3], Wires([0, 1, 2, 3])),
([], Wires([1, 2, 3]), Wires([1, 2, 3])),
({4, 5}, Wires([1, 2, 3]), Wires([1, 2, 3, 4, 5])),
(Wires([1, 2]), Wires([1, 2, 3]), Wires([1, 2, 3])),
],
)
def test_union(self, wire_a, wire_b, expected):
"""
Test the union operation (|) between two Wires objects.
"""
assert wire_a | wire_b == expected
assert wire_b | wire_a == expected

@pytest.mark.parametrize(
"wire_a, wire_b, expected",
[
(Wires([0, 1, 2]), Wires([2, 3, 4]), Wires([2])),
(Wires([1, 2, 3]), Wires([]), Wires([])),
(Wires([1, 2, 3]), [], Wires([])),
(Wires([1, 2, 3]), "2", Wires([])),
(Wires([1, 2, 3]), {3}, Wires([3])),
(Wires([1, 2, 3]), Wires([1, 2, 3, 4]), Wires([1, 2, 3])),
],
)
def test_intersection(self, wire_a, wire_b, expected):
"""
Test the intersection operation (&) between two Wires objects.
"""
assert wire_a & wire_b == expected
assert wire_b & wire_a == expected

@pytest.mark.parametrize(
"wire_a, wire_b, expected",
[
(Wires([0, 1, 2, 3]), Wires([2, 3, 4]), Wires([0, 1])),
(Wires([1, 2, 3]), Wires([]), Wires([1, 2, 3])),
(Wires([1, 2, 3]), Wires([1, 2, 3, 4]), Wires([])),
(Wires([1, 2, 3]), [], Wires([1, 2, 3])),
([1, 2, 3], Wires([]), Wires([1, 2, 3])),
([], Wires([]), Wires([])),
(Wires([]), [], Wires([])),
],
)
def test_difference(self, wire_a, wire_b, expected):
"""
Test the difference operation (-) between two Wires objects.
"""
assert wire_a - wire_b == expected

@pytest.mark.parametrize(
"wire_a, wire_b, expected",
[
(Wires([0, 1, 2]), Wires([2, 3, 4]), Wires([0, 1, 3, 4])),
([0, 1, 2], Wires([2, 3, 4]), Wires([0, 1, 3, 4])),
(Wires([0, 1, 2]), [2, 3, 4], Wires([0, 1, 3, 4])),
(Wires([]), Wires([1, 2, 3]), Wires([1, 2, 3])),
(Wires([1, 2, 3]), Wires([1, 2, 3]), Wires([])),
],
)
def test_symmetric_difference(self, wire_a, wire_b, expected):
"""
Test the symmetric difference operation (^) between two Wires objects.
"""
assert wire_a ^ wire_b == expected

@pytest.mark.parametrize(
"wire_a, wire_b, wire_c, wire_d, expected",
[
(
Wires([0, 1]),
Wires([2, 3]),
Wires([4, 5]),
Wires([6, 7]),
Wires([0, 1, 2, 3, 4, 5, 6, 7]),
),
(Wires([0, 1]), Wires([1, 2]), Wires([2, 3]), Wires([3, 4]), Wires([0, 1, 2, 3, 4])),
(Wires([]), Wires([1, 2]), Wires([2, 3]), Wires([3, 4, 5]), Wires([1, 2, 3, 4, 5])),
],
)
# pylint: disable=too-many-arguments
def test_multiple_union(self, wire_a, wire_b, wire_c, wire_d, expected):
"""
Test the union operation (|) with multiple Wires objects.
"""
result = wire_a | wire_b | wire_c | wire_d
assert result == expected
assert wire_a.union(wire_b.union(wire_c.union(wire_d))) == expected

def test_complex_operation(self):
"""
Test a complex operation involving multiple set operations.
This test combines union, intersection, difference, and symmetric difference operations.
"""
wire_a = Wires([0, 1, 2, 3])
wire_b = Wires([2, 3, 4, 5])
wire_c = Wires([4, 5, 6, 7])
wire_d = Wires([6, 7, 8, 9])

# ((A ∪ B) ∩ (C ∪ D)) ^ ((A - D) ∪ (C - B))
result = ((wire_a | wire_b) & (wire_c | wire_d)) ^ ((wire_a - wire_d) | (wire_c - wire_b))
soranjh marked this conversation as resolved.
Show resolved Hide resolved
assert (wire_a | wire_b) & (wire_c | wire_d) == Wires([4, 5])
assert (wire_a - wire_d) | (wire_c - wire_b) == Wires([0, 1, 2, 3, 6, 7])

expected = Wires([0, 1, 2, 3, 4, 5, 6, 7])
assert result == expected
Loading