qiskit 1.1.0rc1__cp38-abi3-win_amd64.whl → 1.1.2__cp38-abi3-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. qiskit/VERSION.txt +1 -1
  2. qiskit/_accelerate.pyd +0 -0
  3. qiskit/assembler/__init__.py +5 -10
  4. qiskit/circuit/__init__.py +21 -153
  5. qiskit/circuit/_classical_resource_map.py +3 -0
  6. qiskit/circuit/classical/expr/__init__.py +1 -1
  7. qiskit/circuit/classical/types/__init__.py +5 -4
  8. qiskit/circuit/classicalfunction/__init__.py +9 -0
  9. qiskit/circuit/library/__init__.py +3 -19
  10. qiskit/circuit/library/data_preparation/pauli_feature_map.py +1 -1
  11. qiskit/circuit/library/n_local/two_local.py +1 -1
  12. qiskit/circuit/library/standard_gates/x.py +2 -0
  13. qiskit/circuit/parameterexpression.py +3 -0
  14. qiskit/circuit/parametervector.py +22 -16
  15. qiskit/circuit/quantumcircuit.py +1100 -200
  16. qiskit/converters/__init__.py +17 -2
  17. qiskit/dagcircuit/dagcircuit.py +8 -1
  18. qiskit/passmanager/passmanager.py +11 -11
  19. qiskit/primitives/__init__.py +15 -9
  20. qiskit/primitives/containers/__init__.py +1 -0
  21. qiskit/primitives/containers/bit_array.py +6 -2
  22. qiskit/primitives/containers/shape.py +3 -3
  23. qiskit/providers/__init__.py +49 -17
  24. qiskit/providers/backend.py +0 -6
  25. qiskit/providers/basic_provider/__init__.py +2 -23
  26. qiskit/providers/fake_provider/__init__.py +1 -1
  27. qiskit/providers/fake_provider/generic_backend_v2.py +5 -0
  28. qiskit/providers/models/__init__.py +2 -2
  29. qiskit/pulse/builder.py +2 -2
  30. qiskit/pulse/schedule.py +3 -3
  31. qiskit/qasm2/parse.py +8 -0
  32. qiskit/qasm3/exporter.py +2 -2
  33. qiskit/qobj/converters/pulse_instruction.py +6 -6
  34. qiskit/qpy/__init__.py +60 -62
  35. qiskit/qpy/binary_io/value.py +1 -1
  36. qiskit/quantum_info/operators/symplectic/pauli.py +18 -13
  37. qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py +8 -4
  38. qiskit/result/__init__.py +6 -0
  39. qiskit/scheduler/__init__.py +10 -1
  40. qiskit/scheduler/methods/__init__.py +1 -8
  41. qiskit/synthesis/__init__.py +1 -6
  42. qiskit/synthesis/discrete_basis/generate_basis_approximations.py +1 -1
  43. qiskit/synthesis/discrete_basis/solovay_kitaev.py +22 -12
  44. qiskit/transpiler/__init__.py +5 -5
  45. qiskit/transpiler/layout.py +3 -3
  46. qiskit/transpiler/passes/__init__.py +4 -2
  47. qiskit/transpiler/passes/basis/basis_translator.py +2 -2
  48. qiskit/transpiler/passes/layout/vf2_layout.py +10 -4
  49. qiskit/transpiler/passes/layout/vf2_utils.py +2 -2
  50. qiskit/transpiler/passes/optimization/collect_cliffords.py +6 -15
  51. qiskit/transpiler/passes/routing/commuting_2q_gate_routing/commuting_2q_gate_router.py +8 -1
  52. qiskit/transpiler/passes/routing/commuting_2q_gate_routing/pauli_2q_evolution_commutation.py +5 -1
  53. qiskit/transpiler/passes/routing/star_prerouting.py +5 -5
  54. qiskit/transpiler/passes/synthesis/unitary_synthesis.py +3 -0
  55. qiskit/transpiler/preset_passmanagers/__init__.py +29 -3
  56. qiskit/transpiler/target.py +1 -1
  57. qiskit/utils/__init__.py +3 -2
  58. qiskit/utils/parallel.py +24 -15
  59. qiskit/visualization/bloch.py +44 -1
  60. qiskit/visualization/dag_visualization.py +10 -3
  61. qiskit/visualization/gate_map.py +9 -1
  62. qiskit/visualization/pass_manager_visualization.py +9 -9
  63. qiskit/visualization/pulse_v2/device_info.py +58 -31
  64. {qiskit-1.1.0rc1.dist-info → qiskit-1.1.2.dist-info}/METADATA +18 -18
  65. {qiskit-1.1.0rc1.dist-info → qiskit-1.1.2.dist-info}/RECORD +69 -69
  66. {qiskit-1.1.0rc1.dist-info → qiskit-1.1.2.dist-info}/WHEEL +1 -1
  67. {qiskit-1.1.0rc1.dist-info → qiskit-1.1.2.dist-info}/LICENSE.txt +0 -0
  68. {qiskit-1.1.0rc1.dist-info → qiskit-1.1.2.dist-info}/entry_points.txt +0 -0
  69. {qiskit-1.1.0rc1.dist-info → qiskit-1.1.2.dist-info}/top_level.txt +0 -0
@@ -156,7 +156,7 @@ def generate_basic_approximations(
156
156
  data = {}
157
157
  for sequence in sequences:
158
158
  gatestring = sequence.name
159
- data[gatestring] = sequence.product
159
+ data[gatestring] = (sequence.product, sequence.global_phase)
160
160
 
161
161
  np.save(filename, data)
162
162
 
@@ -16,8 +16,6 @@ from __future__ import annotations
16
16
 
17
17
  import numpy as np
18
18
 
19
- from qiskit.circuit.gate import Gate
20
-
21
19
  from .gate_sequence import GateSequence
22
20
  from .commutator_decompose import commutator_decompose
23
21
  from .generate_basis_approximations import generate_basic_approximations, _1q_gates, _1q_inverses
@@ -53,14 +51,19 @@ class SolovayKitaevDecomposition:
53
51
 
54
52
  self.basic_approximations = self.load_basic_approximations(basic_approximations)
55
53
 
56
- def load_basic_approximations(self, data: list | str | dict) -> list[GateSequence]:
54
+ @staticmethod
55
+ def load_basic_approximations(data: list | str | dict) -> list[GateSequence]:
57
56
  """Load basic approximations.
58
57
 
59
58
  Args:
60
59
  data: If a string, specifies the path to the file from where to load the data.
61
- If a dictionary, directly specifies the decompositions as ``{gates: matrix}``.
62
- There ``gates`` are the names of the gates producing the SO(3) matrix ``matrix``,
63
- e.g. ``{"h t": np.array([[0, 0.7071, -0.7071], [0, -0.7071, -0.7071], [-1, 0, 0]]}``.
60
+ If a dictionary, directly specifies the decompositions as ``{gates: matrix}``
61
+ or ``{gates: (matrix, global_phase)}``. There, ``gates`` are the names of the gates
62
+ producing the SO(3) matrix ``matrix``, e.g.
63
+ ``{"h t": np.array([[0, 0.7071, -0.7071], [0, -0.7071, -0.7071], [-1, 0, 0]]}``
64
+ and the ``global_phase`` can be given to account for a global phase difference
65
+ between the U(2) matrix of the quantum gates and the stored SO(3) matrix.
66
+ If not given, the ``global_phase`` will be assumed to be 0.
64
67
 
65
68
  Returns:
66
69
  A list of basic approximations as type ``GateSequence``.
@@ -74,13 +77,20 @@ class SolovayKitaevDecomposition:
74
77
 
75
78
  # if a file, load the dictionary
76
79
  if isinstance(data, str):
77
- data = np.load(data, allow_pickle=True)
80
+ data = np.load(data, allow_pickle=True).item()
78
81
 
79
82
  sequences = []
80
- for gatestring, matrix in data.items():
83
+ for gatestring, matrix_and_phase in data.items():
84
+ if isinstance(matrix_and_phase, tuple):
85
+ matrix, global_phase = matrix_and_phase
86
+ else:
87
+ matrix, global_phase = matrix_and_phase, 0
88
+
81
89
  sequence = GateSequence()
82
90
  sequence.gates = [_1q_gates[element] for element in gatestring.split()]
91
+ sequence.labels = [gate.name for gate in sequence.gates]
83
92
  sequence.product = np.asarray(matrix)
93
+ sequence.global_phase = global_phase
84
94
  sequences.append(sequence)
85
95
 
86
96
  return sequences
@@ -157,14 +167,14 @@ class SolovayKitaevDecomposition:
157
167
  w_n1 = self._recurse(w_n, n - 1, check_input=check_input)
158
168
  return v_n1.dot(w_n1).dot(v_n1.adjoint()).dot(w_n1.adjoint()).dot(u_n1)
159
169
 
160
- def find_basic_approximation(self, sequence: GateSequence) -> Gate:
161
- """Finds gate in ``self._basic_approximations`` that best represents ``sequence``.
170
+ def find_basic_approximation(self, sequence: GateSequence) -> GateSequence:
171
+ """Find ``GateSequence`` in ``self._basic_approximations`` that approximates ``sequence``.
162
172
 
163
173
  Args:
164
- sequence: The gate to find the approximation to.
174
+ sequence: ``GateSequence`` to find the approximation to.
165
175
 
166
176
  Returns:
167
- Gate in basic approximations that is closest to ``sequence``.
177
+ ``GateSequence`` in ``self._basic_approximations`` that approximates ``sequence``.
168
178
  """
169
179
  # TODO explore using a k-d tree here
170
180
 
@@ -650,8 +650,6 @@ manner to the "physical" qubits in an actual quantum device.
650
650
  .. image:: /source_images/mapping.png
651
651
 
652
652
 
653
-
654
-
655
653
  By default, qiskit will do this mapping for you. The choice of mapping depends on the
656
654
  properties of the circuit, the particular device you are targeting, and the optimization
657
655
  level that is chosen. The choice of initial layout is extremely important for minimizing the
@@ -684,10 +682,12 @@ Next, for the heuristic stage, 2 passes are used by default:
684
682
  :class:`~.SabreLayout` is used to select a layout if a perfect layout isn't found for
685
683
  optimization levels 1, 2, and 3.
686
684
  - :class:`~.TrivialLayout`: Always used for the layout at optimization level 0.
685
+
686
+ There are other passes than can be used for the heuristic stage, but are not included in the default
687
+ pipeline, such as:
688
+
687
689
  - :class:`~.DenseLayout`: Finds the sub-graph of the device with greatest connectivity
688
- that has the same number of qubits as the circuit. Used for
689
- optimization level 1 if there are control flow operations (such as
690
- :class:`~.IfElseOp`) present in the circuit.
690
+ that has the same number of qubits as the circuit.
691
691
 
692
692
  Let's see what layouts are automatically picked at various optimization levels. The circuits
693
693
  returned by :func:`qiskit.compiler.transpile` are annotated with this initial layout information,
@@ -454,7 +454,7 @@ class TranspileLayout:
454
454
  qubits in the circuit as it fits the circuit to the target backend. For example,
455
455
  let the input circuit be:
456
456
 
457
- .. plot:
457
+ .. plot::
458
458
  :include-source:
459
459
 
460
460
  from qiskit.circuit import QuantumCircuit, QuantumRegister
@@ -469,7 +469,7 @@ class TranspileLayout:
469
469
 
470
470
  Suppose that during the layout stage the transpiler reorders the qubits to be:
471
471
 
472
- .. plot:
472
+ .. plot::
473
473
  :include-source:
474
474
 
475
475
  from qiskit import QuantumCircuit
@@ -497,7 +497,7 @@ class TranspileLayout:
497
497
  the transpiler needs to insert swap gates, and the output circuit
498
498
  becomes:
499
499
 
500
- .. plot:
500
+ .. plot::
501
501
  :include-source:
502
502
 
503
503
  from qiskit import QuantumCircuit
@@ -154,8 +154,10 @@ The synthesis transpiler plugin documentation can be found in the
154
154
  HLSConfig
155
155
  SolovayKitaev
156
156
 
157
- Post Layout (Post transpile qubit selection)
158
- ============================================
157
+ Post Layout
158
+ ===========
159
+
160
+ These are post qubit selection.
159
161
 
160
162
  .. autosummary::
161
163
  :toctree: ../stubs/
@@ -97,8 +97,8 @@ class BasisTranslator(TransformationPass):
97
97
 
98
98
  When this error occurs it typically means that either the target basis
99
99
  is not universal or there are additional equivalence rules needed in the
100
- :clas:~.EquivalenceLibrary` instance being used by the
101
- :class:~.BasisTranslator` pass. You can refer to
100
+ :class:`~.EquivalenceLibrary` instance being used by the
101
+ :class:`~.BasisTranslator` pass. You can refer to
102
102
  :ref:`custom_basis_gates` for details on adding custom equivalence rules.
103
103
  """
104
104
 
@@ -104,15 +104,21 @@ class VF2Layout(AnalysisPass):
104
104
  limit on the number of trials will be set.
105
105
  target (Target): A target representing the backend device to run ``VF2Layout`` on.
106
106
  If specified it will supersede a set value for ``properties`` and
107
- ``coupling_map``.
107
+ ``coupling_map`` if the :class:`.Target` contains connectivity constraints. If the value
108
+ of ``target`` models an ideal backend without any constraints then the value of
109
+ ``coupling_map``
110
+ will be used.
108
111
 
109
112
  Raises:
110
113
  TypeError: At runtime, if neither ``coupling_map`` or ``target`` are provided.
111
114
  """
112
115
  super().__init__()
113
116
  self.target = target
114
- if target is not None:
115
- self.coupling_map = self.target.build_coupling_map()
117
+ if (
118
+ target is not None
119
+ and (target_coupling_map := self.target.build_coupling_map()) is not None
120
+ ):
121
+ self.coupling_map = target_coupling_map
116
122
  else:
117
123
  self.coupling_map = coupling_map
118
124
  self.properties = properties
@@ -145,7 +151,7 @@ class VF2Layout(AnalysisPass):
145
151
  )
146
152
  # Filter qubits without any supported operations. If they don't support any operations
147
153
  # They're not valid for layout selection
148
- if self.target is not None:
154
+ if self.target is not None and self.target.qargs is not None:
149
155
  has_operations = set(itertools.chain.from_iterable(self.target.qargs))
150
156
  to_remove = set(range(len(cm_nodes))).difference(has_operations)
151
157
  if to_remove:
@@ -145,7 +145,7 @@ def score_layout(
145
145
  def build_average_error_map(target, properties, coupling_map):
146
146
  """Build an average error map used for scoring layouts pre-basis translation."""
147
147
  num_qubits = 0
148
- if target is not None:
148
+ if target is not None and target.qargs is not None:
149
149
  num_qubits = target.num_qubits
150
150
  avg_map = ErrorMap(len(target.qargs))
151
151
  elif coupling_map is not None:
@@ -157,7 +157,7 @@ def build_average_error_map(target, properties, coupling_map):
157
157
  # object
158
158
  avg_map = ErrorMap(0)
159
159
  built = False
160
- if target is not None:
160
+ if target is not None and target.qargs is not None:
161
161
  for qargs in target.qargs:
162
162
  if qargs is None:
163
163
  continue
@@ -22,6 +22,7 @@ from qiskit.transpiler.passes.optimization.collect_and_collapse import (
22
22
  )
23
23
 
24
24
  from qiskit.quantum_info.operators import Clifford
25
+ from qiskit.quantum_info.operators.symplectic.clifford_circuits import _BASIS_1Q, _BASIS_2Q
25
26
 
26
27
 
27
28
  class CollectCliffords(CollectAndCollapse):
@@ -69,21 +70,11 @@ class CollectCliffords(CollectAndCollapse):
69
70
  )
70
71
 
71
72
 
72
- clifford_gate_names = [
73
- "x",
74
- "y",
75
- "z",
76
- "h",
77
- "s",
78
- "sdg",
79
- "cx",
80
- "cy",
81
- "cz",
82
- "swap",
83
- "clifford",
84
- "linear_function",
85
- "pauli",
86
- ]
73
+ clifford_gate_names = (
74
+ list(_BASIS_1Q.keys())
75
+ + list(_BASIS_2Q.keys())
76
+ + ["clifford", "linear_function", "pauli", "permutation"]
77
+ )
87
78
 
88
79
 
89
80
  def _is_clifford_gate(node):
@@ -160,8 +160,13 @@ class Commuting2qGateRouter(TransformationPass):
160
160
  if len(dag.qubits) != next(iter(dag.qregs.values())).size:
161
161
  raise TranspilerError("Circuit has qubits not contained in the qubit register.")
162
162
 
163
- new_dag = dag.copy_empty_like()
163
+ # Fix output permutation -- copied from ElidePermutations
164
+ input_qubit_mapping = {qubit: index for index, qubit in enumerate(dag.qubits)}
165
+ self.property_set["original_layout"] = Layout(input_qubit_mapping)
166
+ if self.property_set["original_qubit_indices"] is None:
167
+ self.property_set["original_qubit_indices"] = input_qubit_mapping
164
168
 
169
+ new_dag = dag.copy_empty_like()
165
170
  current_layout = Layout.generate_trivial_layout(*dag.qregs.values())
166
171
 
167
172
  # Used to keep track of nodes that do not decompose using swap strategies.
@@ -183,6 +188,8 @@ class Commuting2qGateRouter(TransformationPass):
183
188
 
184
189
  self._compose_non_swap_nodes(accumulator, current_layout, new_dag)
185
190
 
191
+ self.property_set["virtual_permutation_layout"] = current_layout
192
+
186
193
  return new_dag
187
194
 
188
195
  def _compose_non_swap_nodes(
@@ -51,7 +51,11 @@ class FindCommutingPauliEvolutions(TransformationPass):
51
51
  sub_dag = self._decompose_to_2q(dag, node.op)
52
52
 
53
53
  block_op = Commuting2qBlock(set(sub_dag.op_nodes()))
54
- wire_order = {wire: idx for idx, wire in enumerate(dag.qubits)}
54
+ wire_order = {
55
+ wire: idx
56
+ for idx, wire in enumerate(sub_dag.qubits)
57
+ if wire not in sub_dag.idle_wires()
58
+ }
55
59
  dag.replace_block_with_op([node], block_op, wire_order)
56
60
 
57
61
  return dag
@@ -329,13 +329,13 @@ class StarPreRouting(TransformationPass):
329
329
  last_2q_gate = None
330
330
 
331
331
  int_digits = floor(log10(len(processing_order))) + 1
332
- processing_order_s = set(processing_order)
332
+ processing_order_index_map = {
333
+ node: f"a{str(index).zfill(int(int_digits))}"
334
+ for index, node in enumerate(processing_order)
335
+ }
333
336
 
334
337
  def tie_breaker_key(node):
335
- if node in processing_order_s:
336
- return "a" + str(processing_order.index(node)).zfill(int(int_digits))
337
- else:
338
- return node.sort_key
338
+ return processing_order_index_map.get(node, node.sort_key)
339
339
 
340
340
  for node in dag.topological_op_nodes(key=tie_breaker_key):
341
341
  block_id = node_to_block_id.get(node, None)
@@ -49,6 +49,7 @@ from qiskit.circuit.library.standard_gates import (
49
49
  CZGate,
50
50
  RXXGate,
51
51
  RZXGate,
52
+ RZZGate,
52
53
  ECRGate,
53
54
  )
54
55
  from qiskit.transpiler.passes.synthesis import plugin
@@ -744,6 +745,8 @@ class DefaultUnitarySynthesis(plugin.UnitarySynthesisPlugin):
744
745
  op = RXXGate(pi / 2)
745
746
  elif isinstance(op, RZXGate) and isinstance(op.params[0], Parameter):
746
747
  op = RZXGate(pi / 4)
748
+ elif isinstance(op, RZZGate) and isinstance(op.params[0], Parameter):
749
+ op = RZZGate(pi / 2)
747
750
  return op
748
751
 
749
752
  try:
@@ -96,11 +96,37 @@ def generate_preset_pass_manager(
96
96
  ):
97
97
  """Generate a preset :class:`~.PassManager`
98
98
 
99
- This function is used to quickly generate a preset pass manager. A preset pass
100
- manager are the default pass managers used by the :func:`~.transpile`
99
+ This function is used to quickly generate a preset pass manager. Preset pass
100
+ managers are the default pass managers used by the :func:`~.transpile`
101
101
  function. This function provides a convenient and simple method to construct
102
- a standalone :class:`~.PassManager` object that mirrors what the transpile
102
+ a standalone :class:`~.PassManager` object that mirrors what the :func:`~.transpile`
103
+ function internally builds and uses.
103
104
 
105
+ The target constraints for the pass manager construction can be specified through a :class:`.Target`
106
+ instance, a :class:`.BackendV1` or :class:`.BackendV2` instance, or via loose constraints
107
+ (``basis_gates``, ``inst_map``, ``coupling_map``, ``backend_properties``, ``instruction_durations``,
108
+ ``dt`` or ``timing_constraints``).
109
+ The order of priorities for target constraints works as follows: if a ``target``
110
+ input is provided, it will take priority over any ``backend`` input or loose constraints.
111
+ If a ``backend`` is provided together with any loose constraint
112
+ from the list above, the loose constraint will take priority over the corresponding backend
113
+ constraint. This behavior is independent of whether the ``backend`` instance is of type
114
+ :class:`.BackendV1` or :class:`.BackendV2`, as summarized in the table below. The first column
115
+ in the table summarizes the potential user-provided constraints, and each cell shows whether
116
+ the priority is assigned to that specific constraint input or another input
117
+ (`target`/`backend(V1)`/`backend(V2)`).
118
+
119
+ ============================ ========= ======================== =======================
120
+ User Provided target backend(V1) backend(V2)
121
+ ============================ ========= ======================== =======================
122
+ **basis_gates** target basis_gates basis_gates
123
+ **coupling_map** target coupling_map coupling_map
124
+ **instruction_durations** target instruction_durations instruction_durations
125
+ **inst_map** target inst_map inst_map
126
+ **dt** target dt dt
127
+ **timing_constraints** target timing_constraints timing_constraints
128
+ **backend_properties** target backend_properties backend_properties
129
+ ============================ ========= ======================== =======================
104
130
 
105
131
  Args:
106
132
  optimization_level (int): The optimization level to generate a
@@ -890,7 +890,7 @@ class Target(Mapping):
890
890
  return False
891
891
  if qargs not in self._gate_map[operation_name]:
892
892
  return False
893
- return getattr(self._gate_map[operation_name][qargs], "_calibration") is not None
893
+ return getattr(self._gate_map[operation_name][qargs], "_calibration", None) is not None
894
894
 
895
895
  def get_calibration(
896
896
  self,
qiskit/utils/__init__.py CHANGED
@@ -44,7 +44,7 @@ Multiprocessing
44
44
  .. autofunction:: local_hardware_info
45
45
  .. autofunction:: is_main_process
46
46
 
47
- A helper function for calling a custom function with python
47
+ A helper function for calling a custom function with Python
48
48
  :class:`~concurrent.futures.ProcessPoolExecutor`. Tasks can be executed in parallel using this function.
49
49
 
50
50
  .. autofunction:: parallel_map
@@ -70,7 +70,7 @@ from .lazy_tester import LazyDependencyManager, LazyImportTester, LazySubprocess
70
70
 
71
71
  from . import optionals
72
72
 
73
- from .parallel import parallel_map
73
+ from .parallel import parallel_map, should_run_in_parallel
74
74
 
75
75
  __all__ = [
76
76
  "LazyDependencyManager",
@@ -85,4 +85,5 @@ __all__ = [
85
85
  "is_main_process",
86
86
  "apply_prefix",
87
87
  "parallel_map",
88
+ "should_run_in_parallel",
88
89
  ]
qiskit/utils/parallel.py CHANGED
@@ -48,6 +48,8 @@ Routines for running Python functions in parallel using process pools
48
48
  from the multiprocessing library.
49
49
  """
50
50
 
51
+ from __future__ import annotations
52
+
51
53
  import os
52
54
  from concurrent.futures import ProcessPoolExecutor
53
55
  import sys
@@ -101,6 +103,21 @@ def _task_wrapper(param):
101
103
  return task(value, *task_args, **task_kwargs)
102
104
 
103
105
 
106
+ def should_run_in_parallel(num_processes: int | None = None) -> bool:
107
+ """Return whether the current parallelisation configuration suggests that we should run things
108
+ like :func:`parallel_map` in parallel (``True``) or degrade to serial (``False``).
109
+
110
+ Args:
111
+ num_processes: the number of processes requested for use (if given).
112
+ """
113
+ num_processes = CPU_COUNT if num_processes is None else num_processes
114
+ return (
115
+ num_processes > 1
116
+ and os.getenv("QISKIT_IN_PARALLEL", "FALSE") == "FALSE"
117
+ and CONFIG.get("parallel_enabled", PARALLEL_DEFAULT)
118
+ )
119
+
120
+
104
121
  def parallel_map( # pylint: disable=dangerous-default-value
105
122
  task, values, task_args=(), task_kwargs={}, num_processes=CPU_COUNT
106
123
  ):
@@ -110,21 +127,20 @@ def parallel_map( # pylint: disable=dangerous-default-value
110
127
 
111
128
  result = [task(value, *task_args, **task_kwargs) for value in values]
112
129
 
113
- On Windows this function defaults to a serial implementation to avoid the
114
- overhead from spawning processes in Windows.
130
+ This will parallelise the results if the number of ``values`` is greater than one, and the
131
+ current system configuration permits parallelization.
115
132
 
116
133
  Args:
117
134
  task (func): Function that is to be called for each value in ``values``.
118
- values (array_like): List or array of values for which the ``task``
119
- function is to be evaluated.
135
+ values (array_like): List or array of values for which the ``task`` function is to be
136
+ evaluated.
120
137
  task_args (list): Optional additional arguments to the ``task`` function.
121
138
  task_kwargs (dict): Optional additional keyword argument to the ``task`` function.
122
139
  num_processes (int): Number of processes to spawn.
123
140
 
124
141
  Returns:
125
- result: The result list contains the value of
126
- ``task(value, *task_args, **task_kwargs)`` for
127
- each value in ``values``.
142
+ result: The result list contains the value of ``task(value, *task_args, **task_kwargs)`` for
143
+ each value in ``values``.
128
144
 
129
145
  Raises:
130
146
  QiskitError: If user interrupts via keyboard.
@@ -147,12 +163,7 @@ def parallel_map( # pylint: disable=dangerous-default-value
147
163
  if len(values) == 1:
148
164
  return [task(values[0], *task_args, **task_kwargs)]
149
165
 
150
- # Run in parallel if not Win and not in parallel already
151
- if (
152
- num_processes > 1
153
- and os.getenv("QISKIT_IN_PARALLEL") == "FALSE"
154
- and CONFIG.get("parallel_enabled", PARALLEL_DEFAULT)
155
- ):
166
+ if should_run_in_parallel(num_processes):
156
167
  os.environ["QISKIT_IN_PARALLEL"] = "TRUE"
157
168
  try:
158
169
  results = []
@@ -173,8 +184,6 @@ def parallel_map( # pylint: disable=dangerous-default-value
173
184
  os.environ["QISKIT_IN_PARALLEL"] = "FALSE"
174
185
  return results
175
186
 
176
- # Cannot do parallel on Windows , if another parallel_map is running in parallel,
177
- # or len(values) == 1.
178
187
  results = []
179
188
  for _, value in enumerate(values):
180
189
  result = task(value, *task_args, **task_kwargs)
@@ -50,6 +50,7 @@ __all__ = ["Bloch"]
50
50
 
51
51
  import math
52
52
  import os
53
+ import re
53
54
  import numpy as np
54
55
  import matplotlib
55
56
  import matplotlib.pyplot as plt
@@ -60,6 +61,47 @@ from mpl_toolkits.mplot3d.art3d import Patch3D
60
61
  from .utils import matplotlib_close_if_inline
61
62
 
62
63
 
64
+ # This version pattern is taken from the pypa packaging project:
65
+ # https://github.com/pypa/packaging/blob/21.3/packaging/version.py#L223-L254
66
+ # which is dual licensed Apache 2.0 and BSD see the source for the original
67
+ # authors and other details
68
+ VERSION_PATTERN = (
69
+ "^"
70
+ + r"""
71
+ v?
72
+ (?:
73
+ (?:(?P<epoch>[0-9]+)!)? # epoch
74
+ (?P<release>[0-9]+(?:\.[0-9]+)*) # release segment
75
+ (?P<pre> # pre-release
76
+ [-_\.]?
77
+ (?P<pre_l>(a|b|c|rc|alpha|beta|pre|preview))
78
+ [-_\.]?
79
+ (?P<pre_n>[0-9]+)?
80
+ )?
81
+ (?P<post> # post release
82
+ (?:-(?P<post_n1>[0-9]+))
83
+ |
84
+ (?:
85
+ [-_\.]?
86
+ (?P<post_l>post|rev|r)
87
+ [-_\.]?
88
+ (?P<post_n2>[0-9]+)?
89
+ )
90
+ )?
91
+ (?P<dev> # dev release
92
+ [-_\.]?
93
+ (?P<dev_l>dev)
94
+ [-_\.]?
95
+ (?P<dev_n>[0-9]+)?
96
+ )?
97
+ )
98
+ (?:\+(?P<local>[a-z0-9]+(?:[-_\.][a-z0-9]+)*))? # local version
99
+ """
100
+ + "$"
101
+ )
102
+ VERSION_PATTERN_REGEX = re.compile(VERSION_PATTERN, re.VERBOSE | re.IGNORECASE)
103
+
104
+
63
105
  class Arrow3D(Patch3D, FancyArrowPatch):
64
106
  """Makes a fancy arrow"""
65
107
 
@@ -419,7 +461,8 @@ class Bloch:
419
461
  self.fig = plt.figure(figsize=self.figsize)
420
462
 
421
463
  if not self._ext_axes:
422
- if tuple(int(x) for x in matplotlib.__version__.split(".")) >= (3, 4, 0):
464
+ version_match = VERSION_PATTERN_REGEX.search(matplotlib.__version__)
465
+ if tuple(int(x) for x in version_match.group("release").split(".")) >= (3, 4, 0):
423
466
  self.axes = Axes3D(
424
467
  self.fig, azim=self.view[0], elev=self.view[1], auto_add_to_figure=False
425
468
  )
@@ -174,10 +174,13 @@ def dag_drawer(dag, scale=0.7, filename=None, style="color"):
174
174
  label = register_bit_labels.get(
175
175
  node.wire, f"q_{dag.find_bit(node.wire).index}"
176
176
  )
177
- else:
177
+ elif isinstance(node.wire, Clbit):
178
178
  label = register_bit_labels.get(
179
179
  node.wire, f"c_{dag.find_bit(node.wire).index}"
180
180
  )
181
+ else:
182
+ label = str(node.wire.name)
183
+
181
184
  n["label"] = label
182
185
  n["color"] = "black"
183
186
  n["style"] = "filled"
@@ -187,10 +190,12 @@ def dag_drawer(dag, scale=0.7, filename=None, style="color"):
187
190
  label = register_bit_labels.get(
188
191
  node.wire, f"q[{dag.find_bit(node.wire).index}]"
189
192
  )
190
- else:
193
+ elif isinstance(node.wire, Clbit):
191
194
  label = register_bit_labels.get(
192
195
  node.wire, f"c[{dag.find_bit(node.wire).index}]"
193
196
  )
197
+ else:
198
+ label = str(node.wire.name)
194
199
  n["label"] = label
195
200
  n["color"] = "black"
196
201
  n["style"] = "filled"
@@ -203,8 +208,10 @@ def dag_drawer(dag, scale=0.7, filename=None, style="color"):
203
208
  e = {}
204
209
  if isinstance(edge, Qubit):
205
210
  label = register_bit_labels.get(edge, f"q_{dag.find_bit(edge).index}")
206
- else:
211
+ elif isinstance(edge, Clbit):
207
212
  label = register_bit_labels.get(edge, f"c_{dag.find_bit(edge).index}")
213
+ else:
214
+ label = str(edge.name)
208
215
  e["label"] = label
209
216
  return e
210
217
 
@@ -1039,7 +1039,9 @@ def plot_coupling_map(
1039
1039
  graph = CouplingMap(coupling_map).graph
1040
1040
 
1041
1041
  if not plot_directed:
1042
+ line_color_map = dict(zip(graph.edge_list(), line_color))
1042
1043
  graph = graph.to_undirected(multigraph=False)
1044
+ line_color = [line_color_map[edge] for edge in graph.edge_list()]
1043
1045
 
1044
1046
  for node in graph.node_indices():
1045
1047
  graph[node] = node
@@ -1122,7 +1124,13 @@ def plot_circuit_layout(circuit, backend, view="virtual", qubit_coordinates=None
1122
1124
  Args:
1123
1125
  circuit (QuantumCircuit): Input quantum circuit.
1124
1126
  backend (Backend): Target backend.
1125
- view (str): Layout view: either 'virtual' or 'physical'.
1127
+ view (str): How to label qubits in the layout. Options:
1128
+
1129
+ - ``"virtual"``: Label each qubit with the index of the virtual qubit that
1130
+ mapped to it.
1131
+ - ``"physical"``: Label each qubit with the index of the physical qubit that it
1132
+ corresponds to on the device.
1133
+
1126
1134
  qubit_coordinates (Sequence): An optional sequence input (list or array being the
1127
1135
  most common) of 2d coordinates for each qubit. The length of the
1128
1136
  sequence must match the number of qubits on the backend. The sequence