Skip to content

Commit

Permalink
Merge branch 'main' into fix-quantum-instance-backend-v2
Browse files Browse the repository at this point in the history
  • Loading branch information
mergify[bot] committed Feb 1, 2022
2 parents 8a0a922 + 47f2c7d commit 18fa909
Show file tree
Hide file tree
Showing 19 changed files with 373 additions and 92 deletions.
51 changes: 51 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# This code is part of Qiskit.
#
# (C) Copyright IBM 2017, 2021.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.

# EditorConfig sets project-wide editor defaults: https://EditorConfig.org

# top-most EditorConfig file, stop looking higher in the tree
root = true

# Default settings can be overidden by editorconfig file in a subdir
# or by a specific glob later in this file
[*]
end_of_line = lf
insert_final_newline = true
charset = utf-8
indent_style = space
trim_trailing_whitespace = true

# Python
[*.py]
indent_size = 4

# Javascript
[*.{js,json}]
indent_style = space
indent_size = 2

## Windows files
# [*.bat]
# end_of_line = crlf

# Makefile
[Makefile]
indent_style = tab

# Markdown
[*.md]
# trailing whitespace is used for <br/> in md (yuck)
trim_trailing_whitespace = false

# YAML
[*.{yaml,yml}]
indent_size = 2
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ Qiskit is made up of elements that work together to enable quantum computing. Th

## Installation

We encourage installing Qiskit via the pip tool (a python package manager), which installs all Qiskit elements, including Terra.
We encourage installing Qiskit via the pip tool (a python package manager). The following command installs the core Qiskit components, including Terra.

```bash
pip install qiskit
```

PIP will handle all dependencies automatically and you will always install the latest (and well-tested) version.
Pip will handle all dependencies automatically and you will always install the latest (and well-tested) version.

To install from source, follow the instructions in the [documentation](https://qiskit.org/documentation/contributing_to_qiskit.html#install-install-from-source-label).

Expand Down
3 changes: 3 additions & 0 deletions qiskit/circuit/library/data_preparation/z_feature_map.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ def __init__(
feature_dimension: int,
reps: int = 2,
data_map_func: Optional[Callable[[np.ndarray], float]] = None,
parameter_prefix: str = "x",
insert_barriers: bool = False,
name: str = "ZFeatureMap",
) -> None:
Expand All @@ -87,6 +88,7 @@ def __init__(
reps: The number of repeated circuits. Defaults to 2, has a minimum value of 1.
data_map_func: A mapping function for data x which can be supplied to override the
default mapping from :meth:`self_product`.
parameter_prefix: The prefix used if default parameters are generated.
insert_barriers: If True, barriers are inserted in between the evolution instructions
and hadamard layers.
Expand All @@ -96,6 +98,7 @@ def __init__(
paulis=["Z"],
reps=reps,
data_map_func=data_map_func,
parameter_prefix=parameter_prefix,
insert_barriers=insert_barriers,
name=name,
)
3 changes: 3 additions & 0 deletions qiskit/circuit/library/data_preparation/zz_feature_map.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ def __init__(
reps: int = 2,
entanglement: Union[str, List[List[int]], Callable[[int], List[int]]] = "full",
data_map_func: Optional[Callable[[np.ndarray], float]] = None,
parameter_prefix: str = "x",
insert_barriers: bool = False,
name: str = "ZZFeatureMap",
) -> None:
Expand All @@ -74,6 +75,7 @@ def __init__(
entanglement: Specifies the entanglement structure. Refer to
:class:`~qiskit.circuit.library.NLocal` for detail.
data_map_func: A mapping function for data x.
parameter_prefix: The prefix used if default parameters are generated.
insert_barriers: If True, barriers are inserted in between the evolution instructions
and hadamard layers.
Expand All @@ -92,6 +94,7 @@ def __init__(
entanglement=entanglement,
paulis=["Z", "ZZ"],
data_map_func=data_map_func,
parameter_prefix=parameter_prefix,
insert_barriers=insert_barriers,
name=name,
)
20 changes: 12 additions & 8 deletions qiskit/circuit/library/pauli_evolution.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"""A gate to implement time-evolution of operators."""

from typing import Union, Optional
import numpy as np

from qiskit.circuit.gate import Gate
from qiskit.circuit.parameterexpression import ParameterExpression
Expand Down Expand Up @@ -127,14 +128,17 @@ def _to_sparse_pauli_op(operator):
if isinstance(operator, PauliSumOp):
sparse_pauli = operator.primitive
sparse_pauli._coeffs *= operator.coeff
return sparse_pauli
if isinstance(operator, PauliOp):
elif isinstance(operator, PauliOp):
sparse_pauli = SparsePauliOp(operator.primitive)
sparse_pauli._coeffs *= operator.coeff
return sparse_pauli
if isinstance(operator, Pauli):
return SparsePauliOp(operator)
if isinstance(operator, SparsePauliOp):
return operator
elif isinstance(operator, Pauli):
sparse_pauli = SparsePauliOp(operator)
elif isinstance(operator, SparsePauliOp):
sparse_pauli = operator
else:
raise ValueError(f"Unsupported operator type for evolution: {type(operator)}.")

raise ValueError(f"Unsupported operator type for evolution: {type(operator)}.")
if any(np.iscomplex(sparse_pauli.coeffs)):
raise ValueError("Operator contains complex coefficients, which are not supported.")

return sparse_pauli
2 changes: 1 addition & 1 deletion qiskit/opflow/primitive_ops/pauli_sum_op.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ def permute(self, permutation: List[int]) -> "PauliSumOp":
if length > self.num_qubits:
spop = self.primitive.tensor(SparsePauliOp(Pauli("I" * (length - self.num_qubits))))
else:
spop = self.primitive
spop = self.primitive.copy()

permutation = [i for i in range(length) if i not in permutation] + permutation
permu_arr = np.arange(length)[np.argsort(permutation)]
Expand Down
24 changes: 11 additions & 13 deletions qiskit/opflow/state_fns/cvar_measurement.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
from qiskit.opflow.exceptions import OpflowError
from qiskit.opflow.list_ops import ListOp, SummedOp, TensoredOp
from qiskit.opflow.operator_base import OperatorBase
from qiskit.opflow.primitive_ops import PauliOp
from qiskit.opflow.primitive_ops import PauliOp, PauliSumOp
from qiskit.opflow.state_fns.circuit_state_fn import CircuitStateFn
from qiskit.opflow.state_fns.dict_state_fn import DictStateFn
from qiskit.opflow.state_fns.operator_state_fn import OperatorStateFn
Expand Down Expand Up @@ -360,24 +360,22 @@ def _check_is_diagonal(operator: OperatorBase) -> bool:
"""
if isinstance(operator, PauliOp):
# every X component must be False
if not np.any(operator.primitive.x):
return True
return False
return not np.any(operator.primitive.x)

if isinstance(operator, SummedOp):
# cover the case of sums of diagonal paulis, but don't raise since there might be summands
# canceling the non-diagonal parts
# For sums (PauliSumOp and SummedOp), we cover the case of sums of diagonal paulis, but don't
# raise since there might be summand canceling the non-diagonal parts. That case is checked
# in the inefficient matrix check at the bottom.
if isinstance(operator, PauliSumOp):
if not np.any(operator.primitive.paulis.x):
return True

# ignoring mypy since we know that all operators are PauliOps
elif isinstance(operator, SummedOp):
if all(isinstance(op, PauliOp) and not np.any(op.primitive.x) for op in operator.oplist):
return True

if isinstance(operator, ListOp):
elif isinstance(operator, ListOp):
return all(operator.traverse(_check_is_diagonal))

# cannot efficiently check if a operator is diagonal, converting to matrix
matrix = operator.to_matrix()

if np.all(matrix == np.diag(np.diagonal(matrix))):
return True
return False
return np.all(matrix == np.diag(np.diagonal(matrix)))
14 changes: 10 additions & 4 deletions qiskit/synthesis/evolution/product_formula.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,8 @@ def evolve_pauli(

def _single_qubit_evolution(pauli, time):
definition = QuantumCircuit(pauli.num_qubits)
# Note that all phases are removed from the pauli label and are only in the coefficients.
# That's because the operators we evolved have all been translated to a SparsePauliOp.
for i, pauli_i in enumerate(reversed(pauli.to_label())):
if pauli_i == "X":
definition.rx(2 * time, i)
Expand All @@ -150,8 +152,10 @@ def _single_qubit_evolution(pauli, time):


def _two_qubit_evolution(pauli, time, cx_structure):
# get the Paulis and the qubits they act on
labels_as_array = np.array(list(pauli.to_label()))
# Get the Paulis and the qubits they act on.
# Note that all phases are removed from the pauli label and are only in the coefficients.
# That's because the operators we evolved have all been translated to a SparsePauliOp.
labels_as_array = np.array(list(reversed(pauli.to_label())))
qubits = np.where(labels_as_array != "I")[0]
labels = np.array([labels_as_array[idx] for idx in qubits])

Expand All @@ -165,9 +169,9 @@ def _two_qubit_evolution(pauli, time, cx_structure):
elif all(labels == "Z"): # RZZ
definition.rzz(2 * time, qubits[0], qubits[1])
elif labels[0] == "Z" and labels[1] == "X": # RZX
definition.rzx(2 * time, qubits[1], qubits[0])
elif labels[0] == "X" and labels[1] == "Z": # RXZ
definition.rzx(2 * time, qubits[0], qubits[1])
elif labels[0] == "X" and labels[1] == "Z": # RXZ
definition.rzx(2 * time, qubits[1], qubits[0])
else: # all the others are not native in Qiskit, so use default the decomposition
definition = _multi_qubit_evolution(pauli, time, cx_structure)

Expand All @@ -186,6 +190,8 @@ def _multi_qubit_evolution(pauli, time, cx_structure):

# determine qubit to do the rotation on
target = None
# Note that all phases are removed from the pauli label and are only in the coefficients.
# That's because the operators we evolved have all been translated to a SparsePauliOp.
for i, pauli_i in enumerate(reversed(pauli.to_label())):
if pauli_i != "I":
target = i
Expand Down
67 changes: 41 additions & 26 deletions qiskit/transpiler/passes/optimization/optimize_1q_gates.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from qiskit.circuit.library.standard_gates.u1 import U1Gate
from qiskit.circuit.library.standard_gates.u2 import U2Gate
from qiskit.circuit.library.standard_gates.u3 import U3Gate
from qiskit.circuit import ParameterExpression
from qiskit.circuit.gate import Gate
from qiskit.transpiler.basepasses import TransformationPass
from qiskit.quantum_info.synthesis import Quaternion
Expand Down Expand Up @@ -97,9 +98,15 @@ def run(self, dag):
and current_node.op.definition.global_phase
):
right_global_phase += current_node.op.definition.global_phase

# If there are any sympy objects coming from the gate convert
# to numpy.
left_parameters = tuple(float(x) for x in left_parameters)
try:
left_parameters = tuple(float(x) for x in left_parameters)
except TypeError:
# If left_parameters contained any unbound Parameters
pass

# Compose gates
name_tuple = (left_name, right_name)
if name_tuple in (("u1", "u1"), ("p", "p")):
Expand Down Expand Up @@ -200,7 +207,8 @@ def run(self, dag):

# Y rotation is 0 mod 2*pi, so the gate is a u1
if (
abs(np.mod(right_parameters[0], (2 * np.pi))) < self.eps
not isinstance(right_parameters[0], ParameterExpression)
and abs(np.mod(right_parameters[0], (2 * np.pi))) < self.eps
and right_name != "u1"
and right_name != "p"
):
Expand All @@ -215,31 +223,34 @@ def run(self, dag):
)
# Y rotation is pi/2 or -pi/2 mod 2*pi, so the gate is a u2
if right_name in ("u3", "u"):
# theta = pi/2 + 2*k*pi
right_angle = right_parameters[0] - np.pi / 2
if abs(right_angle) < self.eps:
right_angle = 0
if abs(np.mod((right_angle), 2 * np.pi)) < self.eps:
right_name = "u2"
right_parameters = (
np.pi / 2,
right_parameters[1],
right_parameters[2] + (right_parameters[0] - np.pi / 2),
)
# theta = -pi/2 + 2*k*pi
right_angle = right_parameters[0] + np.pi / 2
if abs(right_angle) < self.eps:
right_angle = 0
if abs(np.mod(right_angle, 2 * np.pi)) < self.eps:
right_name = "u2"
right_parameters = (
np.pi / 2,
right_parameters[1] + np.pi,
right_parameters[2] - np.pi + (right_parameters[0] + np.pi / 2),
)
if not isinstance(right_parameters[0], ParameterExpression):
# theta = pi/2 + 2*k*pi
right_angle = right_parameters[0] - np.pi / 2
if abs(right_angle) < self.eps:
right_angle = 0
if abs(np.mod((right_angle), 2 * np.pi)) < self.eps:
right_name = "u2"
right_parameters = (
np.pi / 2,
right_parameters[1],
right_parameters[2] + (right_parameters[0] - np.pi / 2),
)
# theta = -pi/2 + 2*k*pi
right_angle = right_parameters[0] + np.pi / 2
if abs(right_angle) < self.eps:
right_angle = 0
if abs(np.mod(right_angle, 2 * np.pi)) < self.eps:
right_name = "u2"
right_parameters = (
np.pi / 2,
right_parameters[1] + np.pi,
right_parameters[2] - np.pi + (right_parameters[0] + np.pi / 2),
)

# u1 and lambda is 0 mod 2*pi so gate is nop (up to a global phase)
if (
right_name in ("u1", "p")
not isinstance(right_parameters[2], ParameterExpression)
and right_name in ("u1", "p")
and abs(np.mod(right_parameters[2], 2 * np.pi)) < self.eps
):
right_name = "nop"
Expand Down Expand Up @@ -335,7 +346,11 @@ def _split_runs_on_parameters(runs):

out = []
for run in runs:
groups = groupby(run, lambda x: x.op.is_parameterized())
# We exclude only u3 and u gate because for u1 and u2 we can really straightforward
# merge two gate with parameters.
# It would be great to combine all gate with parameters but this requires
# support parameters in qiskit.quantum_info.synthesis.Quaternion.
groups = groupby(run, lambda x: x.op.is_parameterized() and x.op.name in ("u3", "u"))

for group_is_parameterized, gates in groups:
if not group_is_parameterized:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
features:
- |
Adds an argument ``parameter_prefix`` to the initializer of
:class:`~qiskit.circuit.library.ZFeatureMap` and
:class:`~qiskit.circuit.library.ZZFeatureMap`, to set the prefix
of parameters of the data encoding circuit::
from qiskit.circuit.library import ZFeatureMap
feature_map = ZFeatureMap(feature_dimension=4,
parameter_prefix="my_prefix")
7 changes: 7 additions & 0 deletions releasenotes/notes/cvar-paulisumop-fe48698236b77f9b.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
fixes:
- |
Fixed a bug, where the :class:`~qiskit.opflow.CVaRMeasurement` attempted to convert a
:class:`~qiskit.opflow.PauliSumOp` to a dense matrix to check whether it is diagonal.
For large operators (> 16 qubits) this computation is extremely expensive and raised
an error if not explicitly enabled using ``qiskit.utils.algorithm_globals.massive = True``.
Loading

0 comments on commit 18fa909

Please sign in to comment.