13
13
# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring
14
14
15
15
import copy
16
+ import itertools
16
17
import pickle
18
+ import random
17
19
import unittest
18
20
19
21
import ddt
20
22
import numpy as np
21
23
22
- from qiskit .circuit import Parameter
24
+ from qiskit import transpile
25
+ from qiskit .circuit import Measure , Parameter , library , QuantumCircuit
23
26
from qiskit .exceptions import QiskitError
24
27
from qiskit .quantum_info import SparseObservable , SparsePauliOp , Pauli
28
+ from qiskit .transpiler import Target
25
29
26
30
from test import QiskitTestCase , combine # pylint: disable=wrong-import-order
27
31
@@ -39,6 +43,24 @@ def single_cases():
39
43
]
40
44
41
45
46
+ def lnn_target (num_qubits ):
47
+ """Create a simple `Target` object with an arbitrary basis-gate set, and open-path
48
+ connectivity."""
49
+ out = Target ()
50
+ out .add_instruction (library .RZGate (Parameter ("a" )), {(q ,): None for q in range (num_qubits )})
51
+ out .add_instruction (library .SXGate (), {(q ,): None for q in range (num_qubits )})
52
+ out .add_instruction (Measure (), {(q ,): None for q in range (num_qubits )})
53
+ out .add_instruction (
54
+ library .CXGate (),
55
+ {
56
+ pair : None
57
+ for lower in range (num_qubits - 1 )
58
+ for pair in [(lower , lower + 1 ), (lower + 1 , lower )]
59
+ },
60
+ )
61
+ return out
62
+
63
+
42
64
class AllowRightArithmetic :
43
65
"""Some type that implements only the right-hand-sided arithmatic operations, and allows
44
66
`SparseObservable` to pass through them.
@@ -1533,3 +1555,158 @@ def test_clear(self, obs):
1533
1555
num_qubits = obs .num_qubits
1534
1556
obs .clear ()
1535
1557
self .assertEqual (obs , SparseObservable .zero (num_qubits ))
1558
+
1559
+ def test_apply_layout_list (self ):
1560
+ self .assertEqual (
1561
+ SparseObservable .zero (5 ).apply_layout ([4 , 3 , 2 , 1 , 0 ]), SparseObservable .zero (5 )
1562
+ )
1563
+ self .assertEqual (
1564
+ SparseObservable .zero (3 ).apply_layout ([0 , 2 , 1 ], 8 ), SparseObservable .zero (8 )
1565
+ )
1566
+ self .assertEqual (
1567
+ SparseObservable .identity (2 ).apply_layout ([1 , 0 ]), SparseObservable .identity (2 )
1568
+ )
1569
+ self .assertEqual (
1570
+ SparseObservable .identity (3 ).apply_layout ([100 , 10_000 , 3 ], 100_000_000 ),
1571
+ SparseObservable .identity (100_000_000 ),
1572
+ )
1573
+
1574
+ terms = [
1575
+ ("ZYX" , (4 , 2 , 1 ), 1j ),
1576
+ ("" , (), - 0.5 ),
1577
+ ("+-rl01" , (10 , 8 , 6 , 4 , 2 , 0 ), 2.0 ),
1578
+ ]
1579
+
1580
+ def map_indices (terms , layout ):
1581
+ return [
1582
+ (terms , tuple (layout [bit ] for bit in bits ), coeff ) for terms , bits , coeff in terms
1583
+ ]
1584
+
1585
+ identity = list (range (12 ))
1586
+ self .assertEqual (
1587
+ SparseObservable .from_sparse_list (terms , num_qubits = 12 ).apply_layout (identity ),
1588
+ SparseObservable .from_sparse_list (terms , num_qubits = 12 ),
1589
+ )
1590
+ # We've already tested elsewhere that `SparseObservable.from_sparse_list` produces termwise
1591
+ # sorted indices, so these tests also ensure `apply_layout` is maintaining that invariant.
1592
+ backwards = list (range (12 ))[::- 1 ]
1593
+ self .assertEqual (
1594
+ SparseObservable .from_sparse_list (terms , num_qubits = 12 ).apply_layout (backwards ),
1595
+ SparseObservable .from_sparse_list (map_indices (terms , backwards ), num_qubits = 12 ),
1596
+ )
1597
+ shuffled = [4 , 7 , 1 , 10 , 0 , 11 , 3 , 2 , 8 , 5 , 6 , 9 ]
1598
+ self .assertEqual (
1599
+ SparseObservable .from_sparse_list (terms , num_qubits = 12 ).apply_layout (shuffled ),
1600
+ SparseObservable .from_sparse_list (map_indices (terms , shuffled ), num_qubits = 12 ),
1601
+ )
1602
+ self .assertEqual (
1603
+ SparseObservable .from_sparse_list (terms , num_qubits = 12 ).apply_layout (shuffled , 100 ),
1604
+ SparseObservable .from_sparse_list (map_indices (terms , shuffled ), num_qubits = 100 ),
1605
+ )
1606
+ expanded = [78 , 69 , 82 , 68 , 32 , 97 , 108 , 101 , 114 , 116 , 33 ]
1607
+ self .assertEqual (
1608
+ SparseObservable .from_sparse_list (terms , num_qubits = 11 ).apply_layout (expanded , 120 ),
1609
+ SparseObservable .from_sparse_list (map_indices (terms , expanded ), num_qubits = 120 ),
1610
+ )
1611
+
1612
+ def test_apply_layout_transpiled (self ):
1613
+ base = SparseObservable .from_sparse_list (
1614
+ [
1615
+ ("ZYX" , (4 , 2 , 1 ), 1j ),
1616
+ ("" , (), - 0.5 ),
1617
+ ("+-r" , (3 , 2 , 0 ), 2.0 ),
1618
+ ],
1619
+ num_qubits = 5 ,
1620
+ )
1621
+
1622
+ qc = QuantumCircuit (5 )
1623
+ initial_list = [3 , 4 , 0 , 2 , 1 ]
1624
+ no_routing = transpile (
1625
+ qc , target = lnn_target (5 ), initial_layout = initial_list , seed_transpiler = 2024_10_25_0
1626
+ ).layout
1627
+ # It's easiest here to test against the `list` form, which we verify separately and
1628
+ # explicitly.
1629
+ self .assertEqual (base .apply_layout (no_routing ), base .apply_layout (initial_list ))
1630
+
1631
+ expanded = transpile (
1632
+ qc , target = lnn_target (100 ), initial_layout = initial_list , seed_transpiler = 2024_10_25_1
1633
+ ).layout
1634
+ self .assertEqual (
1635
+ base .apply_layout (expanded ), base .apply_layout (initial_list , num_qubits = 100 )
1636
+ )
1637
+
1638
+ qc = QuantumCircuit (5 )
1639
+ qargs = list (itertools .permutations (range (5 ), 2 ))
1640
+ random .Random (2024_10_25_2 ).shuffle (qargs )
1641
+ for pair in qargs :
1642
+ qc .cx (* pair )
1643
+
1644
+ routed = transpile (qc , target = lnn_target (5 ), seed_transpiler = 2024_10_25_3 ).layout
1645
+ self .assertEqual (
1646
+ base .apply_layout (routed ),
1647
+ base .apply_layout (routed .final_index_layout (filter_ancillas = True )),
1648
+ )
1649
+
1650
+ routed_expanded = transpile (qc , target = lnn_target (20 ), seed_transpiler = 2024_10_25_3 ).layout
1651
+ self .assertEqual (
1652
+ base .apply_layout (routed_expanded ),
1653
+ base .apply_layout (
1654
+ routed_expanded .final_index_layout (filter_ancillas = True ), num_qubits = 20
1655
+ ),
1656
+ )
1657
+
1658
+ def test_apply_layout_none (self ):
1659
+ self .assertEqual (SparseObservable .zero (0 ).apply_layout (None ), SparseObservable .zero (0 ))
1660
+ self .assertEqual (SparseObservable .zero (0 ).apply_layout (None , 3 ), SparseObservable .zero (3 ))
1661
+ self .assertEqual (SparseObservable .zero (5 ).apply_layout (None ), SparseObservable .zero (5 ))
1662
+ self .assertEqual (SparseObservable .zero (3 ).apply_layout (None , 8 ), SparseObservable .zero (8 ))
1663
+ self .assertEqual (
1664
+ SparseObservable .identity (0 ).apply_layout (None ), SparseObservable .identity (0 )
1665
+ )
1666
+ self .assertEqual (
1667
+ SparseObservable .identity (0 ).apply_layout (None , 8 ), SparseObservable .identity (8 )
1668
+ )
1669
+ self .assertEqual (
1670
+ SparseObservable .identity (2 ).apply_layout (None ), SparseObservable .identity (2 )
1671
+ )
1672
+ self .assertEqual (
1673
+ SparseObservable .identity (3 ).apply_layout (None , 100_000_000 ),
1674
+ SparseObservable .identity (100_000_000 ),
1675
+ )
1676
+
1677
+ terms = [
1678
+ ("ZYX" , (2 , 1 , 0 ), 1j ),
1679
+ ("" , (), - 0.5 ),
1680
+ ("+-rl01" , (10 , 8 , 6 , 4 , 2 , 0 ), 2.0 ),
1681
+ ]
1682
+ self .assertEqual (
1683
+ SparseObservable .from_sparse_list (terms , num_qubits = 12 ).apply_layout (None ),
1684
+ SparseObservable .from_sparse_list (terms , num_qubits = 12 ),
1685
+ )
1686
+ self .assertEqual (
1687
+ SparseObservable .from_sparse_list (terms , num_qubits = 12 ).apply_layout (
1688
+ None , num_qubits = 200
1689
+ ),
1690
+ SparseObservable .from_sparse_list (terms , num_qubits = 200 ),
1691
+ )
1692
+
1693
+ def test_apply_layout_failures (self ):
1694
+ obs = SparseObservable .from_list ([("IIYI" , 2.0 ), ("IIIX" , - 1j )])
1695
+ with self .assertRaisesRegex (ValueError , "duplicate" ):
1696
+ obs .apply_layout ([0 , 0 , 1 , 2 ])
1697
+ with self .assertRaisesRegex (ValueError , "does not account for all contained qubits" ):
1698
+ obs .apply_layout ([0 , 1 ])
1699
+ with self .assertRaisesRegex (ValueError , "less than the number of qubits" ):
1700
+ obs .apply_layout ([0 , 2 , 4 , 6 ])
1701
+ with self .assertRaisesRegex (ValueError , "cannot shrink" ):
1702
+ obs .apply_layout ([0 , 1 ], num_qubits = 2 )
1703
+ with self .assertRaisesRegex (ValueError , "cannot shrink" ):
1704
+ obs .apply_layout (None , num_qubits = 2 )
1705
+
1706
+ qc = QuantumCircuit (3 )
1707
+ qc .cx (0 , 1 )
1708
+ qc .cx (1 , 2 )
1709
+ qc .cx (2 , 0 )
1710
+ layout = transpile (qc , target = lnn_target (3 ), seed_transpiler = 2024_10_25 ).layout
1711
+ with self .assertRaisesRegex (ValueError , "cannot shrink" ):
1712
+ obs .apply_layout (layout , num_qubits = 2 )
0 commit comments