Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into custom-backend-stages
Browse files Browse the repository at this point in the history
  • Loading branch information
mtreinish committed Oct 3, 2022
2 parents f8e8fde + 53e215c commit bb45d9d
Show file tree
Hide file tree
Showing 35 changed files with 1,154 additions and 179 deletions.
1 change: 1 addition & 0 deletions qiskit/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
sys.modules["qiskit._accelerate.sparse_pauli_op"] = qiskit._accelerate.sparse_pauli_op
sys.modules["qiskit._accelerate.results"] = qiskit._accelerate.results
sys.modules["qiskit._accelerate.optimize_1q_gates"] = qiskit._accelerate.optimize_1q_gates
sys.modules["qiskit._accelerate.sampled_exp_val"] = qiskit._accelerate.sampled_exp_val


# Extend namespace for backwards compat
Expand Down
15 changes: 14 additions & 1 deletion qiskit/algorithms/eigensolvers/vqd.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ def compute_eigenvalues(

eval_time = time() - start_time

self._update_vqd_result(result, opt_result, eval_time, self.ansatz)
self._update_vqd_result(result, opt_result, eval_time, self.ansatz.copy())

if aux_operators is not None:
aux_value = estimate_observables(
Expand Down Expand Up @@ -388,6 +388,7 @@ def _build_vqd_result() -> VQDResult:
result.optimizer_times = []
result.eigenvalues = []
result.optimizer_results = []
result.optimal_circuits = []
return result

@staticmethod
Expand All @@ -400,6 +401,7 @@ def _update_vqd_result(result, opt_result, eval_time, ansatz) -> VQDResult:
result.optimizer_times.append(eval_time)
result.eigenvalues.append(opt_result.fun + 0j)
result.optimizer_results.append(opt_result)
result.optimal_circuits.append(ansatz)
return result


Expand All @@ -414,6 +416,7 @@ def __init__(self) -> None:
self._optimal_points = None
self._optimal_parameters = None
self._optimizer_results = None
self._optimal_circuits = None

@property
def cost_function_evals(self) -> Sequence[int] | None:
Expand Down Expand Up @@ -474,3 +477,13 @@ def optimizer_results(self) -> Sequence[OptimizerResult] | None:
def optimizer_results(self, value: Sequence[OptimizerResult]) -> None:
"""Sets optimizer results"""
self._optimizer_results = value

@property
def optimal_circuits(self) -> list[QuantumCircuit]:
"""The optimal circuits. Along with the optimal parameters,
these can be used to retrieve the different eigenstates."""
return self._optimal_circuits

@optimal_circuits.setter
def optimal_circuits(self, optimal_circuits: list[QuantumCircuit]) -> None:
self._optimal_circuits = optimal_circuits
9 changes: 8 additions & 1 deletion qiskit/algorithms/minimum_eigensolvers/sampling_vqe.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,12 @@ def compute_minimum_eigenvalue(
aux_operators_evaluated = None

return self._build_sampling_vqe_result(
optimizer_result, aux_operators_evaluated, best_measurement, final_state, optimizer_time
self.ansatz.copy(),
optimizer_result,
aux_operators_evaluated,
best_measurement,
final_state,
optimizer_time,
)

def _get_evaluate_energy(
Expand Down Expand Up @@ -306,6 +311,7 @@ def evaluate_energy(parameters):

def _build_sampling_vqe_result(
self,
ansatz: QuantumCircuit,
optimizer_result: OptimizerResult,
aux_operators_evaluated: ListOrDict[tuple[complex, tuple[complex, int]]],
best_measurement: dict[str, Any],
Expand All @@ -323,6 +329,7 @@ def _build_sampling_vqe_result(
result.optimizer_result = optimizer_result
result.best_measurement = best_measurement["best"]
result.eigenstate = final_state
result.optimal_circuit = ansatz
return result


Expand Down
6 changes: 5 additions & 1 deletion qiskit/algorithms/minimum_eigensolvers/vqe.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,9 @@ def compute_minimum_eigenvalue(
else:
aux_operators_evaluated = None

return self._build_vqe_result(optimizer_result, aux_operators_evaluated, optimizer_time)
return self._build_vqe_result(
self.ansatz, optimizer_result, aux_operators_evaluated, optimizer_time
)

@classmethod
def supports_aux_operators(cls) -> bool:
Expand Down Expand Up @@ -308,11 +310,13 @@ def _check_operator_ansatz(self, operator: BaseOperator | PauliSumOp):

def _build_vqe_result(
self,
ansatz: QuantumCircuit,
optimizer_result: OptimizerResult,
aux_operators_evaluated: ListOrDict[tuple[complex, tuple[complex, int]]],
optimizer_time: float,
) -> VQEResult:
result = VQEResult()
result.optimal_circuit = ansatz.copy()
result.eigenvalue = optimizer_result.fun
result.cost_function_evals = optimizer_result.nfev
result.optimal_point = optimizer_result.x
Expand Down
14 changes: 14 additions & 0 deletions qiskit/algorithms/variational_algorithm.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
from abc import ABC, abstractmethod
import numpy as np

from qiskit.circuit import QuantumCircuit

from .algorithm_result import AlgorithmResult
from .optimizers import OptimizerResult

Expand Down Expand Up @@ -61,6 +63,7 @@ def __init__(self) -> None:
self._optimal_point = None
self._optimal_parameters = None
self._optimizer_result = None
self._optimal_circuit = None

@property
def optimizer_evals(self) -> Optional[int]:
Expand Down Expand Up @@ -121,3 +124,14 @@ def optimizer_result(self) -> Optional[OptimizerResult]:
def optimizer_result(self, value: OptimizerResult) -> None:
"""Sets optimizer result"""
self._optimizer_result = value

@property
def optimal_circuit(self) -> QuantumCircuit:
"""The optimal circuits. Along with the optimal parameters,
these can be used to retrieve the minimum eigenstate.
"""
return self._optimal_circuit

@optimal_circuit.setter
def optimal_circuit(self, optimal_circuit: QuantumCircuit) -> None:
self._optimal_circuit = optimal_circuit
4 changes: 2 additions & 2 deletions qiskit/circuit/quantumcircuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -1834,7 +1834,7 @@ def draw(
# safely forward-referenced.
ax: Optional[typing.Any] = None,
initial_state: bool = False,
cregbundle: bool = True,
cregbundle: bool = None,
wire_order: list = None,
):
"""Draw the quantum circuit. Use the output parameter to choose the drawing format:
Expand Down Expand Up @@ -1916,7 +1916,7 @@ def draw(
initial_state (bool): Optional. Adds ``|0>`` in the beginning of the wire.
Default is False.
cregbundle (bool): Optional. If set True, bundle classical registers.
Default is True.
Default is True, except for when ``output`` is set to ``"text"``.
wire_order (list): Optional. A list of integers used to reorder the display
of the bits. The list must have an entry for every bit with the bits
in the range 0 to (num_qubits + num_clbits).
Expand Down
5 changes: 4 additions & 1 deletion qiskit/quantum_info/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,4 +163,7 @@
XXDecomposer,
)

from .analysis import hellinger_distance, hellinger_fidelity
from .analysis import (
hellinger_distance,
hellinger_fidelity,
)
2 changes: 1 addition & 1 deletion qiskit/quantum_info/analysis/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# This code is part of Qiskit.
#
# (C) Copyright IBM 2017, 2018.
# (C) Copyright IBM 2017, 2022.
#
# 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
Expand Down
12 changes: 10 additions & 2 deletions qiskit/result/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,14 @@
ProbDistribution
QuasiDistribution
Expectation values
==================
.. autosummary::
:toctree: ../stubs/
sampled_expectation_value
Mitigation
==========
.. autosummary::
Expand All @@ -54,8 +62,8 @@
from .utils import marginal_memory
from .counts import Counts

from .distributions.probability import ProbDistribution
from .distributions.quasi import QuasiDistribution
from .distributions import QuasiDistribution, ProbDistribution
from .sampled_expval import sampled_expectation_value
from .mitigation.base_readout_mitigator import BaseReadoutMitigator
from .mitigation.correlated_readout_mitigator import CorrelatedReadoutMitigator
from .mitigation.local_readout_mitigator import LocalReadoutMitigator
2 changes: 2 additions & 0 deletions qiskit/result/distributions/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,5 @@
"""
Distributions
"""
from .probability import ProbDistribution
from .quasi import QuasiDistribution
85 changes: 85 additions & 0 deletions qiskit/result/sampled_expval.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# This code is part of Qiskit.
#
# (C) Copyright IBM 2022.
#
# 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.
# pylint: disable=cyclic-import

"""Routines for computing expectation values from sampled distributions"""
import numpy as np


# pylint: disable=import-error
from qiskit._accelerate.sampled_exp_val import sampled_expval_float, sampled_expval_complex
from qiskit.exceptions import QiskitError
from .distributions import QuasiDistribution, ProbDistribution


# A list of valid diagonal operators
OPERS = {"Z", "I", "0", "1"}


def sampled_expectation_value(dist, oper):
"""Computes expectation value from a sampled distribution
Note that passing a raw dict requires bit-string keys.
Parameters:
dist (Counts or QuasiDistribution or ProbDistribution or dict): Input sampled distribution
oper (str or Pauli or PauliOp or PauliSumOp or SparsePauliOp): The operator for
the observable
Returns:
float: The expectation value
Raises:
QiskitError: if the input distribution or operator is an invalid type
"""
from .counts import Counts
from qiskit.quantum_info import Pauli, SparsePauliOp
from qiskit.opflow import PauliOp, PauliSumOp

# This should be removed when these return bit-string keys
if isinstance(dist, (QuasiDistribution, ProbDistribution)):
dist = dist.binary_probabilities()

if not isinstance(dist, (Counts, dict)):
raise QiskitError("Invalid input distribution type")
if isinstance(oper, str):
oper_strs = [oper.upper()]
coeffs = np.asarray([1.0])
elif isinstance(oper, Pauli):
oper_strs = [oper.to_label()]
coeffs = np.asarray([1.0])
elif isinstance(oper, PauliOp):
oper_strs = [oper.primitive.to_label()]
coeffs = np.asarray([1.0])
elif isinstance(oper, PauliSumOp):
spo = oper.primitive
oper_strs = spo.paulis.to_labels()
coeffs = np.asarray(spo.coeffs) * oper.coeff
elif isinstance(oper, SparsePauliOp):
oper_strs = oper.paulis.to_labels()
coeffs = np.asarray(oper.coeffs)
else:
raise QiskitError("Invalid operator type")

# Do some validation here
bitstring_len = len(next(iter(dist)))
if any(len(op) != bitstring_len for op in oper_strs):
raise QiskitError(
f"One or more operators not same length ({bitstring_len}) as input bitstrings"
)
for op in oper_strs:
if set(op).difference(OPERS):
raise QiskitError(f"Input operator {op} is not diagonal")
# Dispatch to Rust routines
if coeffs.dtype == np.dtype(complex).type:
return sampled_expval_complex(oper_strs, coeffs, dist)
else:
return sampled_expval_float(oper_strs, coeffs, dist)
63 changes: 43 additions & 20 deletions qiskit/transpiler/passes/utils/check_gate_direction.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@

"""Check if the gates follow the right direction with respect to the coupling map."""

from qiskit.transpiler.layout import Layout
from qiskit.circuit import ControlFlowOp
from qiskit.converters import circuit_to_dag
from qiskit.transpiler.basepasses import AnalysisPass


Expand All @@ -33,6 +34,41 @@ def __init__(self, coupling_map, target=None):
self.coupling_map = coupling_map
self.target = target

def _coupling_map_visit(self, dag, wire_map, edges=None):
if edges is None:
edges = self.coupling_map.get_edges()
# Don't include directives to avoid things like barrier, which are assumed always supported.
for node in dag.op_nodes(include_directives=False):
if isinstance(node.op, ControlFlowOp):
for block in node.op.blocks:
inner_wire_map = {
inner: wire_map[outer] for outer, inner in zip(node.qargs, block.qubits)
}
if not self._coupling_map_visit(circuit_to_dag(block), inner_wire_map, edges):
return False
elif (
len(node.qargs) == 2
and (wire_map[node.qargs[0]], wire_map[node.qargs[1]]) not in edges
):
return False
return True

def _target_visit(self, dag, wire_map):
# Don't include directives to avoid things like barrier, which are assumed always supported.
for node in dag.op_nodes(include_directives=False):
if isinstance(node.op, ControlFlowOp):
for block in node.op.blocks:
inner_wire_map = {
inner: wire_map[outer] for outer, inner in zip(node.qargs, block.qubits)
}
if not self._target_visit(circuit_to_dag(block), inner_wire_map):
return False
elif len(node.qargs) == 2 and not self.target.instruction_supported(
node.op.name, (wire_map[node.qargs[0]], wire_map[node.qargs[1]])
):
return False
return True

def run(self, dag):
"""Run the CheckGateDirection pass on `dag`.
Expand All @@ -42,22 +78,9 @@ def run(self, dag):
Args:
dag (DAGCircuit): DAG to check.
"""
self.property_set["is_direction_mapped"] = True
edges = self.coupling_map.get_edges()
trivial_layout = Layout.generate_trivial_layout(*dag.qregs.values())
if self.target is None:
for gate in dag.two_qubit_ops():
physical_q0 = trivial_layout[gate.qargs[0]]
physical_q1 = trivial_layout[gate.qargs[1]]

if (physical_q0, physical_q1) not in edges:
self.property_set["is_direction_mapped"] = False
return
else:
for gate in dag.two_qubit_ops():
physical_q0 = trivial_layout[gate.qargs[0]]
physical_q1 = trivial_layout[gate.qargs[1]]

if (physical_q0, physical_q1) not in self.target[gate.op.name]:
self.property_set["is_direction_mapped"] = False
return
wire_map = {bit: i for i, bit in enumerate(dag.qubits)}
self.property_set["is_direction_mapped"] = (
self._coupling_map_visit(dag, wire_map)
if self.target is None
else self._target_visit(dag, wire_map)
)
Loading

0 comments on commit bb45d9d

Please sign in to comment.