qiskit 1.1.1__cp38-abi3-macosx_11_0_arm64.whl → 1.2.0__cp38-abi3-macosx_11_0_arm64.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.
- qiskit/VERSION.txt +1 -1
- qiskit/__init__.py +27 -24
- qiskit/_accelerate.abi3.so +0 -0
- qiskit/_numpy_compat.py +1 -1
- qiskit/assembler/assemble_circuits.py +107 -64
- qiskit/assembler/assemble_schedules.py +5 -12
- qiskit/assembler/disassemble.py +10 -1
- qiskit/circuit/__init__.py +6 -3
- qiskit/circuit/_classical_resource_map.py +5 -5
- qiskit/circuit/_utils.py +0 -13
- qiskit/circuit/add_control.py +1 -1
- qiskit/circuit/annotated_operation.py +23 -1
- qiskit/circuit/classical/expr/expr.py +4 -4
- qiskit/circuit/classical/expr/visitors.py +1 -1
- qiskit/circuit/classical/types/__init__.py +1 -1
- qiskit/circuit/classical/types/types.py +2 -2
- qiskit/circuit/classicalfunction/__init__.py +8 -0
- qiskit/circuit/classicalfunction/boolean_expression.py +1 -1
- qiskit/circuit/classicalfunction/classical_function_visitor.py +5 -5
- qiskit/circuit/classicalfunction/utils.py +1 -1
- qiskit/circuit/classicalregister.py +1 -1
- qiskit/circuit/commutation_checker.py +83 -35
- qiskit/circuit/controlflow/_builder_utils.py +1 -1
- qiskit/circuit/controlflow/builder.py +10 -6
- qiskit/circuit/controlflow/if_else.py +2 -2
- qiskit/circuit/controlflow/switch_case.py +1 -1
- qiskit/circuit/delay.py +1 -1
- qiskit/circuit/duration.py +2 -2
- qiskit/circuit/equivalence.py +5 -7
- qiskit/circuit/gate.py +11 -8
- qiskit/circuit/instruction.py +31 -13
- qiskit/circuit/instructionset.py +2 -5
- qiskit/circuit/library/__init__.py +2 -1
- qiskit/circuit/library/arithmetic/linear_amplitude_function.py +1 -1
- qiskit/circuit/library/arithmetic/linear_pauli_rotations.py +1 -1
- qiskit/circuit/library/arithmetic/piecewise_chebyshev.py +1 -1
- qiskit/circuit/library/arithmetic/piecewise_linear_pauli_rotations.py +1 -1
- qiskit/circuit/library/arithmetic/piecewise_polynomial_pauli_rotations.py +3 -3
- qiskit/circuit/library/arithmetic/polynomial_pauli_rotations.py +1 -1
- qiskit/circuit/library/basis_change/__init__.py +1 -1
- qiskit/circuit/library/basis_change/qft.py +40 -6
- qiskit/circuit/library/blueprintcircuit.py +3 -5
- qiskit/circuit/library/data_preparation/__init__.py +9 -2
- qiskit/circuit/library/data_preparation/initializer.py +8 -0
- qiskit/circuit/library/data_preparation/state_preparation.py +98 -178
- qiskit/circuit/library/generalized_gates/isometry.py +8 -8
- qiskit/circuit/library/generalized_gates/linear_function.py +3 -2
- qiskit/circuit/library/generalized_gates/mcg_up_to_diagonal.py +4 -4
- qiskit/circuit/library/generalized_gates/permutation.py +8 -9
- qiskit/circuit/library/generalized_gates/uc.py +3 -3
- qiskit/circuit/library/generalized_gates/uc_pauli_rot.py +2 -2
- qiskit/circuit/library/generalized_gates/unitary.py +13 -11
- qiskit/circuit/library/graph_state.py +1 -1
- qiskit/circuit/library/hamiltonian_gate.py +1 -2
- qiskit/circuit/library/hidden_linear_function.py +1 -1
- qiskit/circuit/library/n_local/evolved_operator_ansatz.py +3 -2
- qiskit/circuit/library/n_local/n_local.py +4 -5
- qiskit/circuit/library/n_local/pauli_two_design.py +1 -1
- qiskit/circuit/library/n_local/qaoa_ansatz.py +6 -8
- qiskit/circuit/library/n_local/two_local.py +1 -1
- qiskit/circuit/library/overlap.py +11 -5
- qiskit/circuit/library/pauli_evolution.py +7 -3
- qiskit/circuit/library/standard_gates/dcx.py +3 -0
- qiskit/circuit/library/standard_gates/ecr.py +3 -0
- qiskit/circuit/library/standard_gates/global_phase.py +3 -0
- qiskit/circuit/library/standard_gates/h.py +13 -5
- qiskit/circuit/library/standard_gates/i.py +3 -0
- qiskit/circuit/library/standard_gates/iswap.py +3 -0
- qiskit/circuit/library/standard_gates/multi_control_rotation_gates.py +19 -10
- qiskit/circuit/library/standard_gates/p.py +14 -9
- qiskit/circuit/library/standard_gates/r.py +3 -0
- qiskit/circuit/library/standard_gates/rx.py +21 -6
- qiskit/circuit/library/standard_gates/rxx.py +40 -1
- qiskit/circuit/library/standard_gates/ry.py +21 -6
- qiskit/circuit/library/standard_gates/ryy.py +40 -1
- qiskit/circuit/library/standard_gates/rz.py +22 -6
- qiskit/circuit/library/standard_gates/rzx.py +40 -1
- qiskit/circuit/library/standard_gates/rzz.py +41 -2
- qiskit/circuit/library/standard_gates/s.py +77 -0
- qiskit/circuit/library/standard_gates/swap.py +12 -5
- qiskit/circuit/library/standard_gates/sx.py +14 -5
- qiskit/circuit/library/standard_gates/t.py +5 -0
- qiskit/circuit/library/standard_gates/u.py +22 -7
- qiskit/circuit/library/standard_gates/u1.py +8 -3
- qiskit/circuit/library/standard_gates/u2.py +3 -0
- qiskit/circuit/library/standard_gates/u3.py +22 -7
- qiskit/circuit/library/standard_gates/x.py +158 -92
- qiskit/circuit/library/standard_gates/xx_minus_yy.py +40 -1
- qiskit/circuit/library/standard_gates/xx_plus_yy.py +52 -11
- qiskit/circuit/library/standard_gates/y.py +6 -1
- qiskit/circuit/library/standard_gates/z.py +8 -1
- qiskit/circuit/operation.py +1 -1
- qiskit/circuit/parameter.py +9 -10
- qiskit/circuit/parameterexpression.py +16 -13
- qiskit/circuit/parametertable.py +1 -190
- qiskit/circuit/parametervector.py +1 -1
- qiskit/circuit/quantumcircuit.py +395 -385
- qiskit/circuit/quantumcircuitdata.py +3 -5
- qiskit/circuit/quantumregister.py +1 -1
- qiskit/circuit/random/__init__.py +1 -1
- qiskit/circuit/random/utils.py +175 -26
- qiskit/circuit/register.py +5 -7
- qiskit/circuit/singleton.py +3 -3
- qiskit/circuit/tools/pi_check.py +4 -4
- qiskit/compiler/assembler.py +95 -24
- qiskit/compiler/scheduler.py +2 -2
- qiskit/compiler/transpiler.py +42 -128
- qiskit/converters/circuit_to_dag.py +4 -6
- qiskit/converters/circuit_to_gate.py +4 -8
- qiskit/converters/circuit_to_instruction.py +5 -17
- qiskit/converters/dag_to_circuit.py +2 -6
- qiskit/dagcircuit/collect_blocks.py +2 -2
- qiskit/dagcircuit/dagcircuit.py +197 -187
- qiskit/dagcircuit/dagdependency.py +4 -4
- qiskit/dagcircuit/dagdependency_v2.py +4 -4
- qiskit/dagcircuit/dagdepnode.py +1 -1
- qiskit/dagcircuit/dagnode.py +66 -157
- qiskit/passmanager/flow_controllers.py +1 -1
- qiskit/passmanager/passmanager.py +3 -3
- qiskit/primitives/__init__.py +1 -5
- qiskit/primitives/backend_estimator.py +25 -15
- qiskit/primitives/backend_estimator_v2.py +31 -7
- qiskit/primitives/backend_sampler.py +21 -12
- qiskit/primitives/backend_sampler_v2.py +12 -3
- qiskit/primitives/base/base_estimator.py +31 -4
- qiskit/primitives/base/base_primitive.py +2 -2
- qiskit/primitives/base/base_result.py +2 -2
- qiskit/primitives/base/base_sampler.py +26 -2
- qiskit/primitives/base/estimator_result.py +2 -2
- qiskit/primitives/base/sampler_result.py +2 -2
- qiskit/primitives/containers/__init__.py +0 -1
- qiskit/primitives/containers/bindings_array.py +2 -2
- qiskit/primitives/containers/bit_array.py +113 -12
- qiskit/primitives/containers/shape.py +3 -3
- qiskit/primitives/estimator.py +9 -2
- qiskit/primitives/primitive_job.py +1 -1
- qiskit/primitives/sampler.py +10 -3
- qiskit/primitives/statevector_estimator.py +5 -3
- qiskit/primitives/statevector_sampler.py +11 -5
- qiskit/primitives/utils.py +16 -0
- qiskit/providers/backend.py +15 -6
- qiskit/providers/backend_compat.py +7 -4
- qiskit/providers/basic_provider/basic_provider_tools.py +1 -1
- qiskit/providers/basic_provider/basic_simulator.py +33 -25
- qiskit/providers/fake_provider/fake_backend.py +10 -3
- qiskit/providers/fake_provider/fake_openpulse_2q.py +157 -149
- qiskit/providers/fake_provider/fake_openpulse_3q.py +228 -220
- qiskit/providers/fake_provider/fake_pulse_backend.py +2 -1
- qiskit/providers/fake_provider/fake_qasm_backend.py +7 -2
- qiskit/providers/fake_provider/generic_backend_v2.py +519 -68
- qiskit/providers/models/__init__.py +48 -11
- qiskit/providers/models/backendconfiguration.py +50 -4
- qiskit/providers/models/backendproperties.py +13 -2
- qiskit/providers/models/pulsedefaults.py +10 -11
- qiskit/providers/options.py +13 -13
- qiskit/providers/providerutils.py +3 -1
- qiskit/pulse/configuration.py +8 -12
- qiskit/pulse/instruction_schedule_map.py +3 -5
- qiskit/pulse/instructions/acquire.py +7 -8
- qiskit/pulse/instructions/instruction.py +2 -3
- qiskit/pulse/library/samplers/decorators.py +5 -9
- qiskit/pulse/library/symbolic_pulses.py +4 -7
- qiskit/pulse/library/waveform.py +2 -5
- qiskit/pulse/macros.py +11 -6
- qiskit/pulse/parser.py +8 -10
- qiskit/pulse/schedule.py +12 -20
- qiskit/pulse/transforms/alignments.py +1 -3
- qiskit/pulse/utils.py +1 -2
- qiskit/qasm/libs/stdgates.inc +35 -28
- qiskit/qasm2/__init__.py +7 -7
- qiskit/qasm2/export.py +5 -9
- qiskit/qasm2/parse.py +1 -1
- qiskit/qasm3/ast.py +9 -25
- qiskit/qasm3/exporter.py +582 -479
- qiskit/qasm3/printer.py +7 -16
- qiskit/qobj/common.py +10 -0
- qiskit/qobj/converters/lo_config.py +9 -0
- qiskit/qobj/converters/pulse_instruction.py +13 -6
- qiskit/qobj/pulse_qobj.py +69 -15
- qiskit/qobj/qasm_qobj.py +72 -20
- qiskit/qobj/utils.py +9 -0
- qiskit/qpy/__init__.py +1 -1
- qiskit/qpy/binary_io/circuits.py +8 -5
- qiskit/qpy/binary_io/schedules.py +1 -1
- qiskit/qpy/binary_io/value.py +3 -3
- qiskit/qpy/interface.py +3 -2
- qiskit/qpy/type_keys.py +2 -2
- qiskit/quantum_info/operators/channel/quantum_channel.py +3 -6
- qiskit/quantum_info/operators/channel/superop.py +2 -2
- qiskit/quantum_info/operators/channel/transformations.py +1 -1
- qiskit/quantum_info/operators/dihedral/dihedral.py +3 -4
- qiskit/quantum_info/operators/dihedral/dihedral_circuits.py +1 -3
- qiskit/quantum_info/operators/dihedral/random.py +6 -3
- qiskit/quantum_info/operators/measures.py +2 -2
- qiskit/quantum_info/operators/op_shape.py +12 -20
- qiskit/quantum_info/operators/operator.py +14 -21
- qiskit/quantum_info/operators/predicates.py +1 -0
- qiskit/quantum_info/operators/symplectic/base_pauli.py +7 -11
- qiskit/quantum_info/operators/symplectic/clifford.py +1 -1
- qiskit/quantum_info/operators/symplectic/pauli.py +14 -12
- qiskit/quantum_info/operators/symplectic/pauli_list.py +9 -10
- qiskit/quantum_info/operators/symplectic/random.py +1 -1
- qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py +15 -17
- qiskit/quantum_info/quaternion.py +1 -1
- qiskit/quantum_info/states/densitymatrix.py +5 -8
- qiskit/quantum_info/states/stabilizerstate.py +128 -37
- qiskit/quantum_info/states/statevector.py +4 -8
- qiskit/result/counts.py +2 -2
- qiskit/result/mitigation/correlated_readout_mitigator.py +2 -2
- qiskit/result/mitigation/local_readout_mitigator.py +2 -2
- qiskit/result/mitigation/utils.py +1 -3
- qiskit/result/models.py +17 -16
- qiskit/result/result.py +15 -20
- qiskit/scheduler/lowering.py +2 -2
- qiskit/synthesis/__init__.py +2 -1
- qiskit/synthesis/clifford/__init__.py +1 -1
- qiskit/synthesis/clifford/clifford_decompose_ag.py +2 -2
- qiskit/synthesis/clifford/clifford_decompose_bm.py +10 -240
- qiskit/synthesis/clifford/clifford_decompose_greedy.py +9 -303
- qiskit/synthesis/clifford/clifford_decompose_layers.py +25 -23
- qiskit/synthesis/cnotdihedral/cnotdihedral_decompose_full.py +1 -1
- qiskit/synthesis/cnotdihedral/cnotdihedral_decompose_general.py +1 -1
- qiskit/synthesis/discrete_basis/generate_basis_approximations.py +2 -2
- qiskit/synthesis/discrete_basis/solovay_kitaev.py +24 -14
- qiskit/synthesis/evolution/evolution_synthesis.py +4 -2
- qiskit/synthesis/evolution/lie_trotter.py +46 -19
- qiskit/synthesis/evolution/product_formula.py +111 -55
- qiskit/synthesis/evolution/qdrift.py +40 -10
- qiskit/synthesis/evolution/suzuki_trotter.py +43 -33
- qiskit/synthesis/linear/__init__.py +1 -0
- qiskit/synthesis/linear/cnot_synth.py +22 -96
- qiskit/synthesis/linear/linear_depth_lnn.py +8 -8
- qiskit/synthesis/linear/linear_matrix_utils.py +13 -161
- qiskit/synthesis/linear_phase/cnot_phase_synth.py +1 -1
- qiskit/synthesis/linear_phase/cx_cz_depth_lnn.py +3 -3
- qiskit/synthesis/linear_phase/cz_depth_lnn.py +1 -1
- qiskit/synthesis/one_qubit/one_qubit_decompose.py +29 -29
- qiskit/synthesis/permutation/permutation_full.py +5 -29
- qiskit/synthesis/permutation/permutation_lnn.py +2 -24
- qiskit/synthesis/permutation/permutation_utils.py +2 -59
- qiskit/synthesis/qft/__init__.py +1 -0
- qiskit/synthesis/qft/qft_decompose_full.py +79 -0
- qiskit/synthesis/qft/qft_decompose_lnn.py +17 -9
- qiskit/synthesis/stabilizer/stabilizer_circuit.py +6 -6
- qiskit/synthesis/stabilizer/stabilizer_decompose.py +2 -2
- qiskit/synthesis/two_qubit/local_invariance.py +8 -38
- qiskit/synthesis/two_qubit/two_qubit_decompose.py +48 -129
- qiskit/synthesis/unitary/aqc/cnot_structures.py +1 -1
- qiskit/synthesis/unitary/qsd.py +5 -3
- qiskit/transpiler/__init__.py +6 -5
- qiskit/transpiler/basepasses.py +1 -1
- qiskit/transpiler/coupling.py +3 -3
- qiskit/transpiler/instruction_durations.py +1 -2
- qiskit/transpiler/layout.py +6 -6
- qiskit/transpiler/passes/__init__.py +2 -0
- qiskit/transpiler/passes/basis/basis_translator.py +84 -64
- qiskit/transpiler/passes/basis/translate_parameterized.py +3 -5
- qiskit/transpiler/passes/basis/unroll_3q_or_more.py +1 -1
- qiskit/transpiler/passes/basis/unroll_custom_definitions.py +10 -10
- qiskit/transpiler/passes/calibration/rx_builder.py +3 -3
- qiskit/transpiler/passes/calibration/rzx_builder.py +3 -3
- qiskit/transpiler/passes/layout/apply_layout.py +13 -3
- qiskit/transpiler/passes/layout/sabre_layout.py +10 -8
- qiskit/transpiler/passes/layout/sabre_pre_layout.py +4 -1
- qiskit/transpiler/passes/layout/set_layout.py +2 -2
- qiskit/transpiler/passes/layout/vf2_layout.py +1 -1
- qiskit/transpiler/passes/layout/vf2_utils.py +3 -3
- qiskit/transpiler/passes/optimization/__init__.py +1 -0
- qiskit/transpiler/passes/optimization/collect_cliffords.py +6 -15
- qiskit/transpiler/passes/optimization/collect_multiqubit_blocks.py +2 -2
- qiskit/transpiler/passes/optimization/commutation_analysis.py +7 -10
- qiskit/transpiler/passes/optimization/commutative_cancellation.py +35 -19
- qiskit/transpiler/passes/optimization/consolidate_blocks.py +17 -8
- qiskit/transpiler/passes/optimization/inverse_cancellation.py +6 -6
- qiskit/transpiler/passes/optimization/optimize_1q_decomposition.py +64 -41
- qiskit/transpiler/passes/optimization/optimize_1q_gates.py +1 -1
- qiskit/transpiler/passes/optimization/split_2q_unitaries.py +83 -0
- qiskit/transpiler/passes/optimization/template_matching/backward_match.py +1 -1
- qiskit/transpiler/passes/optimization/template_matching/forward_match.py +2 -2
- qiskit/transpiler/passes/optimization/template_matching/template_substitution.py +1 -1
- qiskit/transpiler/passes/routing/commuting_2q_gate_routing/commuting_2q_gate_router.py +3 -2
- qiskit/transpiler/passes/routing/commuting_2q_gate_routing/pauli_2q_evolution_commutation.py +5 -1
- qiskit/transpiler/passes/routing/commuting_2q_gate_routing/swap_strategy.py +1 -1
- qiskit/transpiler/passes/routing/layout_transformation.py +2 -1
- qiskit/transpiler/passes/routing/sabre_swap.py +35 -26
- qiskit/transpiler/passes/routing/star_prerouting.py +80 -105
- qiskit/transpiler/passes/routing/stochastic_swap.py +1 -3
- qiskit/transpiler/passes/scheduling/alap.py +1 -2
- qiskit/transpiler/passes/scheduling/alignments/__init__.py +2 -2
- qiskit/transpiler/passes/scheduling/alignments/check_durations.py +1 -1
- qiskit/transpiler/passes/scheduling/alignments/pulse_gate_validation.py +2 -2
- qiskit/transpiler/passes/scheduling/alignments/reschedule.py +1 -1
- qiskit/transpiler/passes/scheduling/asap.py +1 -2
- qiskit/transpiler/passes/scheduling/base_scheduler.py +5 -5
- qiskit/transpiler/passes/scheduling/dynamical_decoupling.py +3 -3
- qiskit/transpiler/passes/scheduling/padding/base_padding.py +1 -1
- qiskit/transpiler/passes/scheduling/padding/dynamical_decoupling.py +20 -14
- qiskit/transpiler/passes/scheduling/scheduling/base_scheduler.py +7 -6
- qiskit/transpiler/passes/scheduling/time_unit_conversion.py +4 -3
- qiskit/transpiler/passes/synthesis/high_level_synthesis.py +211 -36
- qiskit/transpiler/passes/synthesis/plugin.py +2 -2
- qiskit/transpiler/passes/synthesis/unitary_synthesis.py +83 -40
- qiskit/transpiler/passes/utils/__init__.py +0 -1
- qiskit/transpiler/passes/utils/check_gate_direction.py +4 -4
- qiskit/transpiler/passes/utils/check_map.py +3 -6
- qiskit/transpiler/passes/utils/convert_conditions_to_if_ops.py +3 -4
- qiskit/transpiler/passes/utils/error.py +2 -2
- qiskit/transpiler/passes/utils/fixed_point.py +3 -3
- qiskit/transpiler/passes/utils/gate_direction.py +1 -1
- qiskit/transpiler/passes/utils/gates_basis.py +1 -2
- qiskit/transpiler/passmanager.py +7 -6
- qiskit/transpiler/preset_passmanagers/__init__.py +4 -228
- qiskit/transpiler/preset_passmanagers/builtin_plugins.py +73 -18
- qiskit/transpiler/preset_passmanagers/common.py +3 -6
- qiskit/transpiler/preset_passmanagers/generate_preset_pass_manager.py +518 -0
- qiskit/transpiler/preset_passmanagers/level0.py +1 -1
- qiskit/transpiler/target.py +27 -8
- qiskit/user_config.py +29 -6
- qiskit/utils/classtools.py +3 -3
- qiskit/utils/deprecation.py +3 -2
- qiskit/utils/lazy_tester.py +2 -2
- qiskit/utils/optionals.py +8 -8
- qiskit/visualization/bloch.py +62 -24
- qiskit/visualization/circuit/_utils.py +34 -10
- qiskit/visualization/circuit/circuit_visualization.py +23 -16
- qiskit/visualization/circuit/latex.py +29 -27
- qiskit/visualization/circuit/matplotlib.py +4 -2
- qiskit/visualization/circuit/qcstyle.py +2 -2
- qiskit/visualization/circuit/text.py +9 -15
- qiskit/visualization/dag_visualization.py +12 -5
- qiskit/visualization/pass_manager_visualization.py +9 -9
- qiskit/visualization/pulse_v2/core.py +1 -1
- qiskit/visualization/pulse_v2/events.py +1 -1
- qiskit/visualization/pulse_v2/generators/frame.py +3 -4
- qiskit/visualization/pulse_v2/generators/waveform.py +5 -9
- qiskit/visualization/pulse_v2/layouts.py +1 -5
- qiskit/visualization/pulse_v2/plotters/matplotlib.py +1 -2
- qiskit/visualization/state_visualization.py +5 -6
- qiskit/visualization/timeline/plotters/matplotlib.py +1 -2
- qiskit/visualization/transition_visualization.py +7 -2
- {qiskit-1.1.1.dist-info → qiskit-1.2.0.dist-info}/METADATA +28 -28
- {qiskit-1.1.1.dist-info → qiskit-1.2.0.dist-info}/RECORD +346 -344
- {qiskit-1.1.1.dist-info → qiskit-1.2.0.dist-info}/WHEEL +1 -1
- {qiskit-1.1.1.dist-info → qiskit-1.2.0.dist-info}/entry_points.txt +3 -0
- qiskit/transpiler/passes/utils/block_to_matrix.py +0 -47
- {qiskit-1.1.1.dist-info → qiskit-1.2.0.dist-info}/LICENSE.txt +0 -0
- {qiskit-1.1.1.dist-info → qiskit-1.2.0.dist-info}/top_level.txt +0 -0
@@ -12,12 +12,17 @@
|
|
12
12
|
|
13
13
|
"""A product formula base for decomposing non-commuting operator exponentials."""
|
14
14
|
|
15
|
-
from
|
15
|
+
from __future__ import annotations
|
16
|
+
|
17
|
+
import inspect
|
18
|
+
from collections.abc import Callable
|
19
|
+
from typing import Any
|
16
20
|
from functools import partial
|
17
21
|
import numpy as np
|
18
22
|
from qiskit.circuit.parameterexpression import ParameterExpression
|
19
23
|
from qiskit.circuit.quantumcircuit import QuantumCircuit
|
20
24
|
from qiskit.quantum_info import SparsePauliOp, Pauli
|
25
|
+
from qiskit.utils.deprecation import deprecate_arg
|
21
26
|
|
22
27
|
from .evolution_synthesis import EvolutionSynthesis
|
23
28
|
|
@@ -28,15 +33,33 @@ class ProductFormula(EvolutionSynthesis):
|
|
28
33
|
:obj:`.LieTrotter` and :obj:`.SuzukiTrotter` inherit from this class.
|
29
34
|
"""
|
30
35
|
|
36
|
+
@deprecate_arg(
|
37
|
+
name="atomic_evolution",
|
38
|
+
since="1.2",
|
39
|
+
predicate=lambda callable: callable is not None
|
40
|
+
and len(inspect.signature(callable).parameters) == 2,
|
41
|
+
deprecation_description=(
|
42
|
+
"The 'Callable[[Pauli | SparsePauliOp, float], QuantumCircuit]' signature of the "
|
43
|
+
"'atomic_evolution' argument"
|
44
|
+
),
|
45
|
+
additional_msg=(
|
46
|
+
"Instead you should update your 'atomic_evolution' function to be of the following "
|
47
|
+
"type: 'Callable[[QuantumCircuit, Pauli | SparsePauliOp, float], None]'."
|
48
|
+
),
|
49
|
+
pending=True,
|
50
|
+
)
|
31
51
|
def __init__(
|
32
52
|
self,
|
33
53
|
order: int,
|
34
54
|
reps: int = 1,
|
35
55
|
insert_barriers: bool = False,
|
36
56
|
cx_structure: str = "chain",
|
37
|
-
atomic_evolution:
|
38
|
-
Callable[[
|
39
|
-
|
57
|
+
atomic_evolution: (
|
58
|
+
Callable[[Pauli | SparsePauliOp, float], QuantumCircuit]
|
59
|
+
| Callable[[QuantumCircuit, Pauli | SparsePauliOp, float], None]
|
60
|
+
| None
|
61
|
+
) = None,
|
62
|
+
wrap: bool = False,
|
40
63
|
) -> None:
|
41
64
|
"""
|
42
65
|
Args:
|
@@ -45,10 +68,18 @@ class ProductFormula(EvolutionSynthesis):
|
|
45
68
|
insert_barriers: Whether to insert barriers between the atomic evolutions.
|
46
69
|
cx_structure: How to arrange the CX gates for the Pauli evolutions, can be
|
47
70
|
``"chain"``, where next neighbor connections are used, or ``"fountain"``,
|
48
|
-
where all qubits are connected to one.
|
49
|
-
|
50
|
-
|
51
|
-
|
71
|
+
where all qubits are connected to one. This only takes effect when
|
72
|
+
``atomic_evolution is None``.
|
73
|
+
atomic_evolution: A function to apply the evolution of a single :class:`.Pauli`, or
|
74
|
+
:class:`.SparsePauliOp` of only commuting terms, to a circuit. The function takes in
|
75
|
+
three arguments: the circuit to append the evolution to, the Pauli operator to
|
76
|
+
evolve, and the evolution time. By default, a single Pauli evolution is decomposed
|
77
|
+
into a chain of ``CX`` gates and a single ``RZ`` gate.
|
78
|
+
Alternatively, the function can also take Pauli operator and evolution time as
|
79
|
+
inputs and returns the circuit that will be appended to the overall circuit being
|
80
|
+
built.
|
81
|
+
wrap: Whether to wrap the atomic evolutions into custom gate objects. This only takes
|
82
|
+
effect when ``atomic_evolution is None``.
|
52
83
|
"""
|
53
84
|
super().__init__()
|
54
85
|
self.order = order
|
@@ -58,15 +89,27 @@ class ProductFormula(EvolutionSynthesis):
|
|
58
89
|
# user-provided atomic evolution, stored for serialization
|
59
90
|
self._atomic_evolution = atomic_evolution
|
60
91
|
self._cx_structure = cx_structure
|
92
|
+
self._wrap = wrap
|
61
93
|
|
62
94
|
# if atomic evolution is not provided, set a default
|
63
95
|
if atomic_evolution is None:
|
64
|
-
atomic_evolution = partial(
|
96
|
+
self.atomic_evolution = partial(
|
97
|
+
_default_atomic_evolution, cx_structure=cx_structure, wrap=wrap
|
98
|
+
)
|
99
|
+
|
100
|
+
elif len(inspect.signature(atomic_evolution).parameters) == 2:
|
101
|
+
|
102
|
+
def wrap_atomic_evolution(output, operator, time):
|
103
|
+
definition = atomic_evolution(operator, time)
|
104
|
+
output.compose(definition, wrap=wrap, inplace=True)
|
105
|
+
|
106
|
+
self.atomic_evolution = wrap_atomic_evolution
|
65
107
|
|
66
|
-
|
108
|
+
else:
|
109
|
+
self.atomic_evolution = atomic_evolution
|
67
110
|
|
68
111
|
@property
|
69
|
-
def settings(self) ->
|
112
|
+
def settings(self) -> dict[str, Any]:
|
70
113
|
"""Return the settings in a dictionary, which can be used to reconstruct the object.
|
71
114
|
|
72
115
|
Returns:
|
@@ -85,15 +128,18 @@ class ProductFormula(EvolutionSynthesis):
|
|
85
128
|
"reps": self.reps,
|
86
129
|
"insert_barriers": self.insert_barriers,
|
87
130
|
"cx_structure": self._cx_structure,
|
131
|
+
"wrap": self._wrap,
|
88
132
|
}
|
89
133
|
|
90
134
|
|
91
135
|
def evolve_pauli(
|
136
|
+
output: QuantumCircuit,
|
92
137
|
pauli: Pauli,
|
93
|
-
time:
|
138
|
+
time: float | ParameterExpression = 1.0,
|
94
139
|
cx_structure: str = "chain",
|
95
|
-
|
96
|
-
|
140
|
+
wrap: bool = False,
|
141
|
+
label: str | None = None,
|
142
|
+
) -> None:
|
97
143
|
r"""Construct a circuit implementing the time evolution of a single Pauli string.
|
98
144
|
|
99
145
|
For a Pauli string :math:`P = \{I, X, Y, Z\}^{\otimes n}` on :math:`n` qubits and an
|
@@ -106,79 +152,91 @@ def evolve_pauli(
|
|
106
152
|
Since only a single Pauli string is evolved the circuit decomposition is exact.
|
107
153
|
|
108
154
|
Args:
|
155
|
+
output: The circuit object to which to append the evolved Pauli.
|
109
156
|
pauli: The Pauli to evolve.
|
110
157
|
time: The evolution time.
|
111
158
|
cx_structure: Determine the structure of CX gates, can be either ``"chain"`` for
|
112
159
|
next-neighbor connections or ``"fountain"`` to connect directly to the top qubit.
|
160
|
+
wrap: Whether to wrap the single Pauli evolutions into custom gate objects.
|
113
161
|
label: A label for the gate.
|
114
|
-
|
115
|
-
Returns:
|
116
|
-
A quantum circuit implementing the time evolution of the Pauli.
|
117
162
|
"""
|
118
163
|
num_non_identity = len([label for label in pauli.to_label() if label != "I"])
|
119
164
|
|
120
165
|
# first check, if the Pauli is only the identity, in which case the evolution only
|
121
166
|
# adds a global phase
|
122
167
|
if num_non_identity == 0:
|
123
|
-
|
168
|
+
output.global_phase -= time
|
124
169
|
# if we evolve on a single qubit, if yes use the corresponding qubit rotation
|
125
170
|
elif num_non_identity == 1:
|
126
|
-
|
171
|
+
_single_qubit_evolution(output, pauli, time, wrap)
|
127
172
|
# same for two qubits, use Qiskit's native rotations
|
128
173
|
elif num_non_identity == 2:
|
129
|
-
|
174
|
+
_two_qubit_evolution(output, pauli, time, cx_structure, wrap)
|
130
175
|
# otherwise do basis transformation and CX chains
|
131
176
|
else:
|
132
|
-
|
133
|
-
|
134
|
-
definition.name = f"exp(it {pauli.to_label()})"
|
177
|
+
_multi_qubit_evolution(output, pauli, time, cx_structure, wrap)
|
135
178
|
|
136
|
-
return definition
|
137
179
|
|
138
|
-
|
139
|
-
|
140
|
-
definition = QuantumCircuit(pauli.num_qubits)
|
180
|
+
def _single_qubit_evolution(output, pauli, time, wrap):
|
181
|
+
dest = QuantumCircuit(1) if wrap else output
|
141
182
|
# Note that all phases are removed from the pauli label and are only in the coefficients.
|
142
183
|
# That's because the operators we evolved have all been translated to a SparsePauliOp.
|
184
|
+
qubits = []
|
185
|
+
label = ""
|
143
186
|
for i, pauli_i in enumerate(reversed(pauli.to_label())):
|
187
|
+
idx = 0 if wrap else i
|
144
188
|
if pauli_i == "X":
|
145
|
-
|
189
|
+
dest.rx(2 * time, idx)
|
190
|
+
qubits.append(i)
|
191
|
+
label += "X"
|
146
192
|
elif pauli_i == "Y":
|
147
|
-
|
193
|
+
dest.ry(2 * time, idx)
|
194
|
+
qubits.append(i)
|
195
|
+
label += "Y"
|
148
196
|
elif pauli_i == "Z":
|
149
|
-
|
197
|
+
dest.rz(2 * time, idx)
|
198
|
+
qubits.append(i)
|
199
|
+
label += "Z"
|
150
200
|
|
151
|
-
|
201
|
+
if wrap:
|
202
|
+
gate = dest.to_gate(label=f"exp(it {label})")
|
203
|
+
qubits = [output.qubits[q] for q in qubits]
|
204
|
+
output.append(gate, qargs=qubits, copy=False)
|
152
205
|
|
153
206
|
|
154
|
-
def _two_qubit_evolution(pauli, time, cx_structure):
|
207
|
+
def _two_qubit_evolution(output, pauli, time, cx_structure, wrap):
|
155
208
|
# Get the Paulis and the qubits they act on.
|
156
209
|
# Note that all phases are removed from the pauli label and are only in the coefficients.
|
157
210
|
# That's because the operators we evolved have all been translated to a SparsePauliOp.
|
158
211
|
labels_as_array = np.array(list(reversed(pauli.to_label())))
|
159
212
|
qubits = np.where(labels_as_array != "I")[0]
|
213
|
+
indices = [0, 1] if wrap else qubits
|
160
214
|
labels = np.array([labels_as_array[idx] for idx in qubits])
|
161
215
|
|
162
|
-
|
216
|
+
dest = QuantumCircuit(2) if wrap else output
|
163
217
|
|
164
218
|
# go through all cases we have implemented in Qiskit
|
165
219
|
if all(labels == "X"): # RXX
|
166
|
-
|
220
|
+
dest.rxx(2 * time, indices[0], indices[1])
|
167
221
|
elif all(labels == "Y"): # RYY
|
168
|
-
|
222
|
+
dest.ryy(2 * time, indices[0], indices[1])
|
169
223
|
elif all(labels == "Z"): # RZZ
|
170
|
-
|
224
|
+
dest.rzz(2 * time, indices[0], indices[1])
|
171
225
|
elif labels[0] == "Z" and labels[1] == "X": # RZX
|
172
|
-
|
226
|
+
dest.rzx(2 * time, indices[0], indices[1])
|
173
227
|
elif labels[0] == "X" and labels[1] == "Z": # RXZ
|
174
|
-
|
228
|
+
dest.rzx(2 * time, indices[1], indices[0])
|
175
229
|
else: # all the others are not native in Qiskit, so use default the decomposition
|
176
|
-
|
230
|
+
_multi_qubit_evolution(output, pauli, time, cx_structure, wrap)
|
231
|
+
return
|
177
232
|
|
178
|
-
|
233
|
+
if wrap:
|
234
|
+
gate = dest.to_gate(label=f"exp(it {''.join(labels)})")
|
235
|
+
qubits = [output.qubits[q] for q in qubits]
|
236
|
+
output.append(gate, qargs=qubits, copy=False)
|
179
237
|
|
180
238
|
|
181
|
-
def _multi_qubit_evolution(pauli, time, cx_structure):
|
239
|
+
def _multi_qubit_evolution(output, pauli, time, cx_structure, wrap):
|
182
240
|
# get diagonalizing clifford
|
183
241
|
cliff = diagonalizing_clifford(pauli)
|
184
242
|
|
@@ -198,14 +256,16 @@ def _multi_qubit_evolution(pauli, time, cx_structure):
|
|
198
256
|
break
|
199
257
|
|
200
258
|
# build the evolution as: diagonalization, reduction, 1q evolution, followed by inverses
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
259
|
+
dest = QuantumCircuit(pauli.num_qubits) if wrap else output
|
260
|
+
dest.compose(cliff, inplace=True)
|
261
|
+
dest.compose(chain, inplace=True)
|
262
|
+
dest.rz(2 * time, target)
|
263
|
+
dest.compose(chain.inverse(), inplace=True)
|
264
|
+
dest.compose(cliff.inverse(), inplace=True)
|
207
265
|
|
208
|
-
|
266
|
+
if wrap:
|
267
|
+
gate = dest.to_gate(label=f"exp(it {pauli.to_label()})")
|
268
|
+
output.append(gate, qargs=output.qubits, copy=False)
|
209
269
|
|
210
270
|
|
211
271
|
def diagonalizing_clifford(pauli: Pauli) -> QuantumCircuit:
|
@@ -313,16 +373,12 @@ def cnot_fountain(pauli: Pauli) -> QuantumCircuit:
|
|
313
373
|
return chain
|
314
374
|
|
315
375
|
|
316
|
-
def _default_atomic_evolution(operator, time, cx_structure):
|
376
|
+
def _default_atomic_evolution(output, operator, time, cx_structure, wrap):
|
317
377
|
if isinstance(operator, Pauli):
|
318
378
|
# single Pauli operator: just exponentiate it
|
319
|
-
|
379
|
+
evolve_pauli(output, operator, time, cx_structure, wrap)
|
320
380
|
else:
|
321
381
|
# sum of Pauli operators: exponentiate each term (this assumes they commute)
|
322
382
|
pauli_list = [(Pauli(op), np.real(coeff)) for op, coeff in operator.to_list()]
|
323
|
-
name = f"exp(it {[pauli.to_label() for pauli, _ in pauli_list]})"
|
324
|
-
evolution_circuit = QuantumCircuit(operator.num_qubits, name=name)
|
325
383
|
for pauli, coeff in pauli_list:
|
326
|
-
|
327
|
-
|
328
|
-
return evolution_circuit
|
384
|
+
evolve_pauli(output, pauli, coeff * time, cx_structure, wrap)
|
@@ -12,11 +12,15 @@
|
|
12
12
|
|
13
13
|
"""QDrift Class"""
|
14
14
|
|
15
|
+
from __future__ import annotations
|
16
|
+
|
17
|
+
import inspect
|
15
18
|
import math
|
16
|
-
from
|
19
|
+
from collections.abc import Callable
|
17
20
|
import numpy as np
|
18
21
|
from qiskit.circuit.quantumcircuit import QuantumCircuit
|
19
22
|
from qiskit.quantum_info.operators import SparsePauliOp, Pauli
|
23
|
+
from qiskit.utils.deprecation import deprecate_arg
|
20
24
|
|
21
25
|
from .product_formula import ProductFormula
|
22
26
|
from .lie_trotter import LieTrotter
|
@@ -32,15 +36,33 @@ class QDrift(ProductFormula):
|
|
32
36
|
`arXiv:quant-ph/1811.08017 <https://arxiv.org/abs/1811.08017>`_
|
33
37
|
"""
|
34
38
|
|
39
|
+
@deprecate_arg(
|
40
|
+
name="atomic_evolution",
|
41
|
+
since="1.2",
|
42
|
+
predicate=lambda callable: callable is not None
|
43
|
+
and len(inspect.signature(callable).parameters) == 2,
|
44
|
+
deprecation_description=(
|
45
|
+
"The 'Callable[[Pauli | SparsePauliOp, float], QuantumCircuit]' signature of the "
|
46
|
+
"'atomic_evolution' argument"
|
47
|
+
),
|
48
|
+
additional_msg=(
|
49
|
+
"Instead you should update your 'atomic_evolution' function to be of the following "
|
50
|
+
"type: 'Callable[[QuantumCircuit, Pauli | SparsePauliOp, float], None]'."
|
51
|
+
),
|
52
|
+
pending=True,
|
53
|
+
)
|
35
54
|
def __init__(
|
36
55
|
self,
|
37
56
|
reps: int = 1,
|
38
57
|
insert_barriers: bool = False,
|
39
58
|
cx_structure: str = "chain",
|
40
|
-
atomic_evolution:
|
41
|
-
Callable[[
|
42
|
-
|
43
|
-
|
59
|
+
atomic_evolution: (
|
60
|
+
Callable[[Pauli | SparsePauliOp, float], QuantumCircuit]
|
61
|
+
| Callable[[QuantumCircuit, Pauli | SparsePauliOp, float], None]
|
62
|
+
| None
|
63
|
+
) = None,
|
64
|
+
seed: int | None = None,
|
65
|
+
wrap: bool = False,
|
44
66
|
) -> None:
|
45
67
|
r"""
|
46
68
|
Args:
|
@@ -48,13 +70,21 @@ class QDrift(ProductFormula):
|
|
48
70
|
insert_barriers: Whether to insert barriers between the atomic evolutions.
|
49
71
|
cx_structure: How to arrange the CX gates for the Pauli evolutions, can be
|
50
72
|
``"chain"``, where next neighbor connections are used, or ``"fountain"``, where all
|
51
|
-
qubits are connected to one.
|
52
|
-
|
53
|
-
|
54
|
-
|
73
|
+
qubits are connected to one. This only takes effect when
|
74
|
+
``atomic_evolution is None``.
|
75
|
+
atomic_evolution: A function to apply the evolution of a single :class:`.Pauli`, or
|
76
|
+
:class:`.SparsePauliOp` of only commuting terms, to a circuit. The function takes in
|
77
|
+
three arguments: the circuit to append the evolution to, the Pauli operator to
|
78
|
+
evolve, and the evolution time. By default, a single Pauli evolution is decomposed
|
79
|
+
into a chain of ``CX`` gates and a single ``RZ`` gate.
|
80
|
+
Alternatively, the function can also take Pauli operator and evolution time as
|
81
|
+
inputs and returns the circuit that will be appended to the overall circuit being
|
82
|
+
built.
|
55
83
|
seed: An optional seed for reproducibility of the random sampling process.
|
84
|
+
wrap: Whether to wrap the atomic evolutions into custom gate objects. This only takes
|
85
|
+
effect when ``atomic_evolution is None``.
|
56
86
|
"""
|
57
|
-
super().__init__(1, reps, insert_barriers, cx_structure, atomic_evolution)
|
87
|
+
super().__init__(1, reps, insert_barriers, cx_structure, atomic_evolution, wrap)
|
58
88
|
self.sampled_ops = None
|
59
89
|
self.rng = np.random.default_rng(seed)
|
60
90
|
|
@@ -12,12 +12,16 @@
|
|
12
12
|
|
13
13
|
"""The Suzuki-Trotter product formula."""
|
14
14
|
|
15
|
-
from
|
15
|
+
from __future__ import annotations
|
16
|
+
|
17
|
+
import inspect
|
18
|
+
from collections.abc import Callable
|
16
19
|
|
17
20
|
import numpy as np
|
18
21
|
|
19
22
|
from qiskit.circuit.quantumcircuit import QuantumCircuit
|
20
23
|
from qiskit.quantum_info.operators import SparsePauliOp, Pauli
|
24
|
+
from qiskit.utils.deprecation import deprecate_arg
|
21
25
|
|
22
26
|
|
23
27
|
from .product_formula import ProductFormula
|
@@ -51,15 +55,33 @@ class SuzukiTrotter(ProductFormula):
|
|
51
55
|
`arXiv:math-ph/0506007 <https://arxiv.org/pdf/math-ph/0506007.pdf>`_
|
52
56
|
"""
|
53
57
|
|
58
|
+
@deprecate_arg(
|
59
|
+
name="atomic_evolution",
|
60
|
+
since="1.2",
|
61
|
+
predicate=lambda callable: callable is not None
|
62
|
+
and len(inspect.signature(callable).parameters) == 2,
|
63
|
+
deprecation_description=(
|
64
|
+
"The 'Callable[[Pauli | SparsePauliOp, float], QuantumCircuit]' signature of the "
|
65
|
+
"'atomic_evolution' argument"
|
66
|
+
),
|
67
|
+
additional_msg=(
|
68
|
+
"Instead you should update your 'atomic_evolution' function to be of the following "
|
69
|
+
"type: 'Callable[[QuantumCircuit, Pauli | SparsePauliOp, float], None]'."
|
70
|
+
),
|
71
|
+
pending=True,
|
72
|
+
)
|
54
73
|
def __init__(
|
55
74
|
self,
|
56
75
|
order: int = 2,
|
57
76
|
reps: int = 1,
|
58
77
|
insert_barriers: bool = False,
|
59
78
|
cx_structure: str = "chain",
|
60
|
-
atomic_evolution:
|
61
|
-
Callable[[
|
62
|
-
|
79
|
+
atomic_evolution: (
|
80
|
+
Callable[[Pauli | SparsePauliOp, float], QuantumCircuit]
|
81
|
+
| Callable[[QuantumCircuit, Pauli | SparsePauliOp, float], None]
|
82
|
+
| None
|
83
|
+
) = None,
|
84
|
+
wrap: bool = False,
|
63
85
|
) -> None:
|
64
86
|
"""
|
65
87
|
Args:
|
@@ -68,10 +90,17 @@ class SuzukiTrotter(ProductFormula):
|
|
68
90
|
insert_barriers: Whether to insert barriers between the atomic evolutions.
|
69
91
|
cx_structure: How to arrange the CX gates for the Pauli evolutions, can be ``"chain"``,
|
70
92
|
where next neighbor connections are used, or ``"fountain"``, where all qubits are
|
71
|
-
connected to one.
|
72
|
-
atomic_evolution: A function to
|
73
|
-
|
74
|
-
|
93
|
+
connected to one. This only takes effect when ``atomic_evolution is None``.
|
94
|
+
atomic_evolution: A function to apply the evolution of a single :class:`.Pauli`, or
|
95
|
+
:class:`.SparsePauliOp` of only commuting terms, to a circuit. The function takes in
|
96
|
+
three arguments: the circuit to append the evolution to, the Pauli operator to
|
97
|
+
evolve, and the evolution time. By default, a single Pauli evolution is decomposed
|
98
|
+
into a chain of ``CX`` gates and a single ``RZ`` gate.
|
99
|
+
Alternatively, the function can also take Pauli operator and evolution time as
|
100
|
+
inputs and returns the circuit that will be appended to the overall circuit being
|
101
|
+
built.
|
102
|
+
wrap: Whether to wrap the atomic evolutions into custom gate objects. This only takes
|
103
|
+
effect when ``atomic_evolution is None``.
|
75
104
|
Raises:
|
76
105
|
ValueError: If order is not even
|
77
106
|
"""
|
@@ -81,7 +110,7 @@ class SuzukiTrotter(ProductFormula):
|
|
81
110
|
"Suzuki product formulae are symmetric and therefore only defined "
|
82
111
|
"for even orders."
|
83
112
|
)
|
84
|
-
super().__init__(order, reps, insert_barriers, cx_structure, atomic_evolution)
|
113
|
+
super().__init__(order, reps, insert_barriers, cx_structure, atomic_evolution, wrap)
|
85
114
|
|
86
115
|
def synthesize(self, evolution):
|
87
116
|
# get operators and time to evolve
|
@@ -97,32 +126,13 @@ class SuzukiTrotter(ProductFormula):
|
|
97
126
|
|
98
127
|
# construct the evolution circuit
|
99
128
|
single_rep = QuantumCircuit(operators[0].num_qubits)
|
100
|
-
first_barrier = False
|
101
|
-
|
102
|
-
for op, coeff in ops_to_evolve:
|
103
|
-
# add barriers
|
104
|
-
if first_barrier:
|
105
|
-
if self.insert_barriers:
|
106
|
-
single_rep.barrier()
|
107
|
-
else:
|
108
|
-
first_barrier = True
|
109
|
-
|
110
|
-
single_rep.compose(self.atomic_evolution(op, coeff), wrap=True, inplace=True)
|
111
|
-
|
112
|
-
evolution_circuit = QuantumCircuit(operators[0].num_qubits)
|
113
|
-
first_barrier = False
|
114
|
-
|
115
|
-
for _ in range(self.reps):
|
116
|
-
# add barriers
|
117
|
-
if first_barrier:
|
118
|
-
if self.insert_barriers:
|
119
|
-
single_rep.barrier()
|
120
|
-
else:
|
121
|
-
first_barrier = True
|
122
129
|
|
123
|
-
|
130
|
+
for i, (op, coeff) in enumerate(ops_to_evolve):
|
131
|
+
self.atomic_evolution(single_rep, op, coeff)
|
132
|
+
if self.insert_barriers and i != len(ops_to_evolve) - 1:
|
133
|
+
single_rep.barrier()
|
124
134
|
|
125
|
-
return
|
135
|
+
return single_rep.repeat(self.reps, insert_barriers=self.insert_barriers).decompose()
|
126
136
|
|
127
137
|
@staticmethod
|
128
138
|
def _recurse(order, time, pauli_list):
|
@@ -17,33 +17,37 @@ for optimal synthesis of linear (CNOT-only) reversible circuits.
|
|
17
17
|
"""
|
18
18
|
|
19
19
|
from __future__ import annotations
|
20
|
-
|
20
|
+
|
21
21
|
import numpy as np
|
22
22
|
from qiskit.circuit import QuantumCircuit
|
23
|
-
|
23
|
+
|
24
|
+
from qiskit._accelerate.synthesis.linear import synth_cnot_count_full_pmh as fast_pmh
|
24
25
|
|
25
26
|
|
26
27
|
def synth_cnot_count_full_pmh(
|
27
|
-
state: list[list[bool]] | np.ndarray[bool], section_size: int =
|
28
|
+
state: list[list[bool]] | np.ndarray[bool], section_size: int | None = None
|
28
29
|
) -> QuantumCircuit:
|
29
|
-
"""
|
30
|
+
r"""
|
30
31
|
Synthesize linear reversible circuits for all-to-all architecture
|
31
32
|
using Patel, Markov and Hayes method.
|
32
33
|
|
33
34
|
This function is an implementation of the Patel, Markov and Hayes algorithm from [1]
|
34
35
|
for optimal synthesis of linear reversible circuits for all-to-all architecture,
|
35
|
-
as specified by an :math:`n
|
36
|
+
as specified by an :math:`n \times n` matrix.
|
36
37
|
|
37
38
|
Args:
|
38
|
-
state: :math:`n
|
39
|
-
the state of the input circuit
|
39
|
+
state: :math:`n \times n` boolean invertible matrix, describing
|
40
|
+
the state of the input circuit.
|
40
41
|
section_size: The size of each section in the Patel–Markov–Hayes algorithm [1].
|
42
|
+
If ``None`` it is chosen to be :math:`\max(2, \alpha\log_2(n))` with
|
43
|
+
:math:`\alpha = 0.56`, which approximately minimizes the upper bound on the number
|
44
|
+
of row operations given in [1] Eq. (3).
|
41
45
|
|
42
46
|
Returns:
|
43
|
-
|
47
|
+
A CX-only circuit implementing the linear transformation.
|
44
48
|
|
45
49
|
Raises:
|
46
|
-
|
50
|
+
ValueError: When ``section_size`` is larger than the number of columns.
|
47
51
|
|
48
52
|
References:
|
49
53
|
1. Patel, Ketan N., Igor L. Markov, and John P. Hayes,
|
@@ -51,93 +55,15 @@ def synth_cnot_count_full_pmh(
|
|
51
55
|
Quantum Information & Computation 8.3 (2008): 282-294.
|
52
56
|
`arXiv:quant-ph/0302002 [quant-ph] <https://arxiv.org/abs/quant-ph/0302002>`_
|
53
57
|
"""
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
"
|
58
|
+
normalized = np.asarray(state).astype(bool)
|
59
|
+
if section_size is not None and normalized.shape[1] < section_size:
|
60
|
+
raise ValueError(
|
61
|
+
f"The section_size ({section_size}) cannot be larger than the number of columns "
|
62
|
+
f"({normalized.shape[1]})."
|
58
63
|
)
|
59
|
-
state = np.array(state)
|
60
|
-
# Synthesize lower triangular part
|
61
|
-
[state, circuit_l] = _lwr_cnot_synth(state, section_size)
|
62
|
-
state = np.transpose(state)
|
63
|
-
# Synthesize upper triangular part
|
64
|
-
[state, circuit_u] = _lwr_cnot_synth(state, section_size)
|
65
|
-
circuit_l.reverse()
|
66
|
-
for i in circuit_u:
|
67
|
-
i.reverse()
|
68
|
-
# Convert the list into a circuit of C-NOT gates
|
69
|
-
circ = QuantumCircuit(state.shape[0])
|
70
|
-
for i in circuit_u + circuit_l:
|
71
|
-
circ.cx(i[0], i[1])
|
72
|
-
return circ
|
73
|
-
|
74
|
-
|
75
|
-
def _lwr_cnot_synth(state, section_size):
|
76
|
-
"""
|
77
|
-
This function is a helper function of the algorithm for optimal synthesis
|
78
|
-
of linear reversible circuits (the Patel–Markov–Hayes algorithm). It works
|
79
|
-
like gaussian elimination, except that it works a lot faster, and requires
|
80
|
-
fewer steps (and therefore fewer CNOTs). It takes the matrix "state" and
|
81
|
-
splits it into sections of size section_size. Then it eliminates all non-zero
|
82
|
-
sub-rows within each section, which are the same as a non-zero sub-row
|
83
|
-
above. Once this has been done, it continues with normal gaussian elimination.
|
84
|
-
The benefit is that with small section sizes (m), most of the sub-rows will
|
85
|
-
be cleared in the first step, resulting in a factor m fewer row row operations
|
86
|
-
during Gaussian elimination.
|
87
64
|
|
88
|
-
|
89
|
-
|
90
|
-
Patel, Ketan N., Igor L. Markov, and John P. Hayes.
|
91
|
-
Quantum Information & Computation 8.3 (2008): 282-294.
|
92
|
-
|
93
|
-
Note:
|
94
|
-
This implementation tweaks the Patel, Markov, and Hayes algorithm by adding
|
95
|
-
a "back reduce" which adds rows below the pivot row with a high degree of
|
96
|
-
overlap back to it. The intuition is to avoid a high-weight pivot row
|
97
|
-
increasing the weight of lower rows.
|
98
|
-
|
99
|
-
Args:
|
100
|
-
state (ndarray): n x n matrix, describing a linear quantum circuit
|
101
|
-
section_size (int): the section size the matrix columns are divided into
|
102
|
-
|
103
|
-
Returns:
|
104
|
-
numpy.matrix: n by n matrix, describing the state of the output circuit
|
105
|
-
list: a k by 2 list of C-NOT operations that need to be applied
|
106
|
-
"""
|
107
|
-
circuit = []
|
108
|
-
num_qubits = state.shape[0]
|
109
|
-
cutoff = 1
|
65
|
+
# call Rust implementation with normalized input
|
66
|
+
circuit_data = fast_pmh(normalized, section_size)
|
110
67
|
|
111
|
-
#
|
112
|
-
|
113
|
-
# Remove duplicate sub-rows in section sec
|
114
|
-
patt = {}
|
115
|
-
for row in range((sec - 1) * section_size, num_qubits):
|
116
|
-
sub_row_patt = copy.deepcopy(state[row, (sec - 1) * section_size : sec * section_size])
|
117
|
-
if np.sum(sub_row_patt) == 0:
|
118
|
-
continue
|
119
|
-
if str(sub_row_patt) not in patt:
|
120
|
-
patt[str(sub_row_patt)] = row
|
121
|
-
else:
|
122
|
-
state[row, :] ^= state[patt[str(sub_row_patt)], :]
|
123
|
-
circuit.append([patt[str(sub_row_patt)], row])
|
124
|
-
# Use gaussian elimination for remaining entries in column section
|
125
|
-
for col in range((sec - 1) * section_size, sec * section_size):
|
126
|
-
# Check if 1 on diagonal
|
127
|
-
diag_one = 1
|
128
|
-
if state[col, col] == 0:
|
129
|
-
diag_one = 0
|
130
|
-
# Remove ones in rows below column col
|
131
|
-
for row in range(col + 1, num_qubits):
|
132
|
-
if state[row, col] == 1:
|
133
|
-
if diag_one == 0:
|
134
|
-
state[col, :] ^= state[row, :]
|
135
|
-
circuit.append([row, col])
|
136
|
-
diag_one = 1
|
137
|
-
state[row, :] ^= state[col, :]
|
138
|
-
circuit.append([col, row])
|
139
|
-
# Back reduce the pivot row using the current row
|
140
|
-
if sum(state[col, :] & state[row, :]) > cutoff:
|
141
|
-
state[col, :] ^= state[row, :]
|
142
|
-
circuit.append([row, col])
|
143
|
-
return [state, circuit]
|
68
|
+
# construct circuit from the data
|
69
|
+
return QuantumCircuit._from_circuit_data(circuit_data)
|