diff --git a/qiskit_runtime/__init__.py b/qiskit_runtime/__init__.py new file mode 100644 index 000000000..2ba089a0d --- /dev/null +++ b/qiskit_runtime/__init__.py @@ -0,0 +1,19 @@ +# This code is part of qiskit-runtime. +# +# (C) Copyright IBM 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. + +"""Main entry point for the qiskit_runtime""" + + +try: + from .version import version as __version__ +except ImportError: + __version__ = "0.0.0" diff --git a/qiskit_runtime/circuit_runner/__init__.py b/qiskit_runtime/circuit_runner/__init__.py new file mode 100644 index 000000000..24326fd26 --- /dev/null +++ b/qiskit_runtime/circuit_runner/__init__.py @@ -0,0 +1,15 @@ +# This code is part of qiskit-runtime. +# +# (C) Copyright IBM 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. + +""" +Qiskit circuit runner module +""" diff --git a/qiskit_runtime/circuit_runner/circuit_runner.json b/qiskit_runtime/circuit_runner/circuit_runner.json new file mode 100644 index 000000000..b32e38692 --- /dev/null +++ b/qiskit_runtime/circuit_runner/circuit_runner.json @@ -0,0 +1,23 @@ +{ + "name": "circuit-runner", + "description": "A runtime program that takes one or more circuits, compiles them, executes them, and optionally applies measurement error mitigation.", + "max_execution_time": 14400, + "version": "1.0", + "parameters": [ + {"name": "circuits", "description": "A circuit or a list of circuits.", "type": "A QuantumCircuit or a list of QuantumCircuits.", "required": true}, + {"name": "shots", "description": "Number of repetitions of each circuit, for sampling. Default: 1024.", "type": "int", "required": false}, + {"name": "initial_layout", "description": "Initial position of virtual qubits on physical qubits.", "type": "dict or list", "required": false}, + {"name": "layout_method", "description": "Name of layout selection pass ('trivial', 'dense', 'noise_adaptive', 'sabre')", "type": "string", "required": false}, + {"name": "routing_method", "description": "Name of routing pass ('basic', 'lookahead', 'stochastic', 'sabre').", "type": "string", "required": false}, + {"name": "translation_method", "description": "Name of translation pass ('unroller', 'translator', 'synthesis').", "type": "string", "required": false}, + {"name": "seed_transpiler", "description": "Sets random seed for the stochastic parts of the transpiler.", "type": "int", "required": false}, + {"name": "optimization_level", "description": "How much optimization to perform on the circuits (0-3). Higher levels generate more optimized circuits. Default is 1.", "type": "int", "required": false}, + {"name": "init_qubits", "description": "Whether to reset the qubits to the ground state for each shot.", "type": "bool", "required": false}, + {"name": "rep_delay", "description": "Delay between programs in seconds.", "type": "float", "required": false}, + {"name": "transpiler_options", "description": "Additional compilation options.", "type": "dict", "required": false}, + {"name": "measurement_error_mitigation", "description": "Whether to apply measurement error mitigation. Default is False.", "type": "bool", "required": false} + ], + "return_values": [ + {"name": "-", "description": "Circuit execution results.", "type": "RunnerResult object"} + ] +} diff --git a/qiskit_runtime/circuit_runner/circuit_runner.py b/qiskit_runtime/circuit_runner/circuit_runner.py new file mode 100644 index 000000000..82a9aaf7d --- /dev/null +++ b/qiskit_runtime/circuit_runner/circuit_runner.py @@ -0,0 +1,68 @@ +# This code is part of qiskit-runtime. +# +# (C) Copyright IBM 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. + +"""Circuit-runner runtime program. + +This is a simplified version of the circuit-runner program. +""" + +from qiskit.compiler import transpile, schedule + + +def main( + backend, + user_messenger, # pylint: disable=unused-argument + circuits, + initial_layout=None, + seed_transpiler=None, + optimization_level=None, + transpiler_options=None, + scheduling_method=None, + schedule_circuit=False, + inst_map=None, + meas_map=None, + measurement_error_mitigation=False, + **kwargs, +): + """Run the circuits on the backend.""" + + # transpiling the circuits using given transpile options + transpiler_options = transpiler_options or {} + circuits = transpile( + circuits, + initial_layout=initial_layout, + seed_transpiler=seed_transpiler, + optimization_level=optimization_level, + backend=backend, + **transpiler_options, + ) + + if schedule_circuit: + circuits = schedule( + circuits=circuits, + backend=backend, + inst_map=inst_map, + meas_map=meas_map, + method=scheduling_method, + ) + + if not isinstance(circuits, list): + circuits = [circuits] + + # Compute raw results + result = backend.run(circuits, **kwargs).result() + + if measurement_error_mitigation: + # Performs measurement error mitigation. + pass + + return result.to_dict() diff --git a/qiskit_runtime/hello_world/__init__.py b/qiskit_runtime/hello_world/__init__.py new file mode 100644 index 000000000..5ecd53827 --- /dev/null +++ b/qiskit_runtime/hello_world/__init__.py @@ -0,0 +1,13 @@ +# This code is part of qiskit-runtime. +# +# (C) Copyright IBM 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. + +"""Package containing ``hello-world`` Qiskit quantum program.""" diff --git a/qiskit_runtime/hello_world/hello_world.json b/qiskit_runtime/hello_world/hello_world.json new file mode 100644 index 000000000..513904206 --- /dev/null +++ b/qiskit_runtime/hello_world/hello_world.json @@ -0,0 +1,41 @@ +{ + "name": "hello-world", + "description": "A sample runtime program.", + "max_execution_time": 300, + "spec": { + "backend_requirements": { + "min_num_qubits": 5 + }, + "parameters": { + "$schema": "https://json-schema.org/draft/2019-09/schema", + "properties": { + "iterations": { + "type": "integer", + "minimum": 0, + "description": "Number of iterations to run. Each iteration generates a runs a random circuit." + } + }, + "required": [ + "iterations" + ] + }, + "return_values": { + "$schema": "https://json-schema.org/draft/2019-09/schema", + "description": "A string that says 'All done!'.", + "type": "string" + }, + "interim_results": { + "$schema": "https://json-schema.org/draft/2019-09/schema", + "properties": { + "iteration": { + "type": "integer", + "description": "Iteration number." + }, + "counts": { + "description": "Histogram data of the circuit result.", + "type": "object" + } + } + } + } +} diff --git a/qiskit_runtime/hello_world/hello_world.py b/qiskit_runtime/hello_world/hello_world.py new file mode 100644 index 000000000..ad265013d --- /dev/null +++ b/qiskit_runtime/hello_world/hello_world.py @@ -0,0 +1,55 @@ +# This code is part of qiskit-runtime. +# +# (C) Copyright IBM 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. + +"""A sample runtime program called hello-world that submits random circuits +for user-specified iterations. +""" + +import random +from typing import Any + +from qiskit import transpile +from qiskit.circuit.random import random_circuit + + +def prepare_circuits(backend): + """Generate a random circuit. + + Args: + backend (qiskit.providers.Backend): Backend used for transpilation. + + Returns: + qiskit.QuantumCircuit: Generated circuit. + """ + circuit = random_circuit(num_qubits=5, depth=4, measure=True, seed=random.randint(0, 1000)) + return transpile(circuit, backend) + + +def main(backend, user_messenger, **kwargs) -> Any: + """Main entry point of the program. + + Args: + backend (qiskit.providers.Backend): Backend to submit the circuits to. + user_messenger (qiskit.providers.ibmq.runtime.UserMessenger): Used to communicate with the + program consumer. + kwargs: User inputs. + + Returns: + Final result of the program. + """ + iterations = kwargs.pop("iterations", 1) + for it in range(iterations): + qc = prepare_circuits(backend) + result = backend.run(qc).result() + user_messenger.publish({"iteration": it, "counts": result.get_counts()}) + + return "All done!" diff --git a/qiskit_runtime/qaoa/qaoa.py b/qiskit_runtime/qaoa/qaoa.py new file mode 100644 index 000000000..341c3f8a4 --- /dev/null +++ b/qiskit_runtime/qaoa/qaoa.py @@ -0,0 +1,2473 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2018, 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. + +"""A self-contained QAOA runtime with the SWAP strategies.""" + +from typing import Dict, List, Optional, Set, Tuple +from warnings import warn + +import copy +import retworkx as rx +import numpy as np + +from qiskit import QuantumCircuit +from qiskit.algorithms import VQE +from qiskit.algorithms.optimizers import SPSA +from qiskit.circuit import ParameterVector, Parameter, Gate +from qiskit.circuit.library import NLocal, EvolvedOperatorAnsatz +from qiskit.circuit.exceptions import CircuitError +from qiskit.circuit.quantumregister import QuantumRegister +from qiskit.converters import circuit_to_dag +from qiskit.opflow import PauliExpectation, CVaRExpectation, OperatorBase +from qiskit.opflow.primitive_ops.pauli_sum_op import PauliSumOp +from qiskit.utils import QuantumInstance +from qiskit.providers.backend import Backend +from qiskit.dagcircuit import DAGCircuit +from qiskit.ignis.mitigation import TensoredMeasFitter +from qiskit.exceptions import QiskitError +from qiskit.circuit.library.standard_gates.equivalence_library import ( + StandardEquivalenceLibrary as std_eqlib, +) +from qiskit.transpiler import ( + PassManager, + CouplingMap, + AnalysisPass, + TransformationPass, + Layout, +) +from qiskit.transpiler.passes import ( + Collect2qBlocks, + ConsolidateBlocks, + Optimize1qGatesDecomposition, + FullAncillaAllocation, + EnlargeWithAncilla, + ApplyLayout, + BasisTranslator, + UnrollCustomDefinitions, +) +from qiskit.transpiler.passes.calibration.builders import RZXCalibrationBuilderNoEcho +from qiskit.transpiler.passes.optimization.echo_rzx_weyl_decomposition import ( + EchoRZXWeylDecomposition, +) + +from qiskit_optimization import QuadraticProgram + + +class Publisher: + """Class used to publish interim results.""" + + def __init__(self, messenger): + self._messenger = messenger + + def callback(self, *args, **kwargs): + text = list(args) + for k, v in kwargs.items(): + text.append({k: v}) + self._messenger.publish(text) + + +# begin QAOA Gate + + +class QAOAAnsatz(EvolvedOperatorAnsatz): + """A generalized QAOA quantum circuit with a support of custom initial states and mixers. + + References: + + [1]: Farhi et al., A Quantum Approximate Optimization Algorithm. + `arXiv:1411.4028 `_ + """ + + def __init__( + self, + cost_operator=None, + reps: int = 1, + initial_state: Optional[QuantumCircuit] = None, + mixer_operator=None, + name: str = "QAOA", + ): + r""" + Args: + cost_operator (OperatorBase, optional): The operator representing the cost of + the optimization problem, denoted as :math:`U(C, \gamma)` in the original paper. + Must be set either in the constructor or via property setter. + reps (int): The integer parameter p, which determines the depth of the circuit, + as specified in the original paper, default is 1. + initial_state (QuantumCircuit, optional): An optional initial state to use. + If `None` is passed then a set of Hadamard gates is applied as an initial state + to all qubits. + mixer_operator (OperatorBase or QuantumCircuit, optional): An optional custom mixer + to use instead of the global X-rotations, denoted as :math:`U(B, \beta)` + in the original paper. Can be an operator or an optionally parameterized quantum + circuit. + name (str): A name of the circuit, default 'qaoa' + """ + super().__init__(reps=reps, name=name) + + self._cost_operator = None + self._reps = reps + self._initial_state = initial_state + self._mixer = mixer_operator + + # set this circuit as a not-built circuit + self._bounds = None + + # store cost operator and set the registers if the operator is not None + self.cost_operator = cost_operator + + def _check_configuration(self, raise_on_failure: bool = True) -> bool: + valid = True + + if not super()._check_configuration(raise_on_failure): + return False + + if self.cost_operator is None: + valid = False + if raise_on_failure: + raise ValueError( + "The operator representing the cost of the optimization problem is not set" + ) + + if self.initial_state is not None and self.initial_state.num_qubits != self.num_qubits: + valid = False + if raise_on_failure: + raise ValueError( + "The number of qubits of the initial state {} does not match " + "the number of qubits of the cost operator {}".format( + self.initial_state.num_qubits, self.num_qubits + ) + ) + + if self.mixer_operator is not None and self.mixer_operator.num_qubits != self.num_qubits: + valid = False + if raise_on_failure: + raise ValueError( + "The number of qubits of the mixer {} does not match " + "the number of qubits of the cost operator {}".format( + self.mixer_operator.num_qubits, self.num_qubits + ) + ) + + return valid + + @property + def parameter_bounds( + self, + ) -> Optional[List[Tuple[Optional[float], Optional[float]]]]: + """The parameter bounds for the unbound parameters in the circuit. + + Returns: + A list of pairs indicating the bounds, as (lower, upper). None indicates an unbounded + parameter in the corresponding direction. If None is returned, problem is fully + unbounded. + """ + if self._bounds is not None: + return self._bounds + + # if the mixer is a circuit, we set no bounds + if isinstance(self.mixer_operator, QuantumCircuit): + return None + + # default bounds: None for gamma (cost operator), [0, 2pi] for gamma (mixer operator) + beta_bounds = (0, 2 * np.pi) + gamma_bounds = (None, None) + bounds = [] + + if not _is_pauli_identity(self.mixer_operator): + bounds += self.reps * [beta_bounds] + + if not _is_pauli_identity(self.cost_operator): + bounds += self.reps * [gamma_bounds] + + return bounds + + @parameter_bounds.setter + def parameter_bounds( + self, bounds: Optional[List[Tuple[Optional[float], Optional[float]]]] + ) -> None: + """Set the parameter bounds. + + Args: + bounds: The new parameter bounds. + """ + self._bounds = bounds + + @property + def operators(self): + """The operators that are evolved in this circuit. + + Returns: + List[Union[OperatorBase, QuantumCircuit]]: The operators to be evolved (and circuits) + in this ansatz. + """ + return [self.cost_operator, self.mixer_operator] + + @property + def cost_operator(self): + """Returns an operator representing the cost of the optimization problem. + + Returns: + OperatorBase: cost operator. + """ + return self._cost_operator + + @cost_operator.setter + def cost_operator(self, cost_operator) -> None: + """Sets cost operator. + + Args: + cost_operator (OperatorBase, optional): cost operator to set. + """ + self._cost_operator = cost_operator + self.qregs = [QuantumRegister(self.num_qubits, name="q")] + self._invalidate() + + @property + def reps(self) -> int: + """Returns the `reps` parameter, which determines the depth of the circuit.""" + return self._reps + + @reps.setter + def reps(self, reps: int) -> None: + """Sets the `reps` parameter.""" + self._reps = reps + self._invalidate() + + @property + def initial_state(self) -> Optional[QuantumCircuit]: + """Returns an optional initial state as a circuit""" + if self._initial_state is not None: + return self._initial_state + + # if no initial state is passed and we know the number of qubits, then initialize it. + if self.cost_operator is not None: + return _get_default_initial_state(self.cost_operator) + + # otherwise we cannot provide a default + return None + + @initial_state.setter + def initial_state(self, initial_state: Optional[QuantumCircuit]) -> None: + """Sets initial state.""" + self._initial_state = initial_state + self._invalidate() + + # we can't directly specify OperatorBase as a return type, it causes a circular import + # and pylint objects if return type is not documented + @property + def mixer_operator(self): + """Returns an optional mixer operator expressed as an operator or a quantum circuit. + + Returns: + OperatorBase or QuantumCircuit, optional: mixer operator or circuit. + """ + if self._mixer is not None: + return self._mixer + + # if no mixer is passed and we know the number of qubits, then initialize it. + if self.cost_operator is not None: + return _get_default_mixer(self.cost_operator) + + # otherwise we cannot provide a default + return None + + @mixer_operator.setter + def mixer_operator(self, mixer_operator) -> None: + """Sets mixer operator. + + Args: + mixer_operator (OperatorBase or QuantumCircuit, optional): mixer operator or circuit + to set. + """ + self._mixer = mixer_operator + self._invalidate() + + def _build_gate(self): + """Return a QAOAGate based on the current settings.""" + return QAOAGate( + cost_operator=self.cost_operator, + reps=self.reps, + initial_state=self.initial_state, + mixer_operator=self.mixer_operator, + label=self.name, + ) + + def _build(self): + if self._data is not None: + return + + # need to check configuration here to ensure the operators are not None + self._check_configuration() + self._data = [] + num_qubits = self.num_qubits + + qr = QuantumRegister(num_qubits, "q") + if qr.name not in [qreg.name for qreg in self.qregs]: + # if the register already exists, probably because of a previous composition. + # Otherwise, add it. + self.add_register(qr) + + self._append( + self._build_gate(), + qargs=self.qubits, + cargs=[], + ) + + num_cost = 0 if _is_pauli_identity(self.cost_operator) else 1 + if isinstance(self.mixer_operator, QuantumCircuit): + num_mixer = self.mixer_operator.num_parameters + else: + num_mixer = 0 if _is_pauli_identity(self.mixer_operator) else 1 + + betas = ParameterVector("β", self.reps * num_mixer) + gammas = ParameterVector("γ", self.reps * num_cost) + + # Create a permutation to take us from (cost_1, mixer_1, cost_2, mixer_2, ...) + # to (cost_1, cost_2, ..., mixer_1, mixer_2, ...), or if the mixer is a circuit + # with more than 1 parameters, from (cost_1, mixer_1a, mixer_1b, cost_2, ...) + # to (cost_1, cost_2, ..., mixer_1a, mixer_1b, mixer_2a, mixer_2b, ...) + reordered = [] + for rep in range(self.reps): + reordered.extend(gammas[rep * num_cost : (rep + 1) * num_cost]) + reordered.extend(betas[rep * num_mixer : (rep + 1) * num_mixer]) + + self.assign_parameters(reordered, inplace=True) + + +class QAOAGate(Gate): + """A generalized QAOA gate with a support of custom initial states and mixers. + + References: + + [1]: Farhi et al., A Quantum Approximate Optimization Algorithm. + `arXiv:1411.4028 `_ + """ + + def __init__( + self, + cost_operator, + reps: int = 1, + initial_state: Optional[QuantumCircuit] = None, + mixer_operator=None, + label: str = None, + ): + r""" + Args: + cost_operator (OperatorBase, optional): The operator representing the cost of + the optimization problem, denoted as :math:`U(C, \gamma)` in the original paper. + Must be set either in the constructor or via property setter. + reps (int): The integer parameter p, which determines the depth of the circuit, + as specified in the original paper, default is 1. + initial_state (QuantumCircuit, optional): An optional initial state to use. + If `None` is passed then a set of Hadamard gates is applied as an initial state + to all qubits. + mixer_operator (OperatorBase or QuantumCircuit, optional): An optional custom mixer + to use instead of the global X-rotations, denoted as :math:`U(B, \beta)` + in the original paper. Can be an operator or an optionally parameterized quantum + circuit. + label (str): A label. + Raises: + AttributeError: when cost_operator and initial_state are not compatible + """ + # pylint: disable=cyclic-import + from qiskit.opflow import PauliOp, PauliTrotterEvolution + + self.cost_operator = cost_operator + + if initial_state is None: + initial_state = _get_default_initial_state(cost_operator) + else: + if initial_state.num_qubits != cost_operator.num_qubits: + raise AttributeError( + "initial_state and cost_operator has incompatible number of qubits" + ) + + if mixer_operator is None: + mixer_operator = _get_default_mixer(cost_operator) + + self.mixer_operator = mixer_operator + self.operators = [cost_operator, mixer_operator] + self.reps = reps + self.evolution = PauliTrotterEvolution() + self.initial_state = initial_state + + # determine how many parameters the circuit will contain + num_parameters = 0 + for op in self.operators: + if isinstance(op, QuantumCircuit): + num_parameters += op.num_parameters + else: + # check if the operator is just the identity, if yes, skip it + if isinstance(op, PauliOp): + sig_qubits = np.logical_or(op.primitive.x, op.primitive.z) + if sum(sig_qubits) == 0: + continue + num_parameters += 1 + + super().__init__( + "QAOA", + self.operators[0].num_qubits, + params=list(ParameterVector("t", reps * num_parameters)), + label=label, + ) + + def _define(self): + """Build the circuit by evolving the operators and using NLocal for the repetitions.""" + coeff = Parameter("c") + circuits = [] + bind_parameter = [] + for op in self.operators: + # if the operator is already the evolved circuit just append it + if isinstance(op, QuantumCircuit): + circuits.append(op) + bind_parameter.append(False) + else: + evolved_op = self.evolution.convert((coeff * op).exp_i()).reduce() + circuit = evolved_op.to_circuit() + # if the operator was the identity it is amounts only to a global phase and no + # parameter is added + bind_parameter.append(circuit.num_parameters > 0) + circuits.append(circuit) + + self.definition = ( + NLocal( + circuits[0].num_qubits, + rotation_blocks=[], + entanglement_blocks=circuits, + reps=self.reps, + initial_state=self.initial_state, + ) + .decompose() + .assign_parameters(self.params) + ) + + +def _validate_operators(operators): + if not isinstance(operators, list): + operators = [operators] + + if len(operators) > 1: + num_qubits = operators[0].num_qubits + if any(operators[i].num_qubits != num_qubits for i in range(1, len(operators))): + raise ValueError("All operators must act on the same number of qubits.") + + return operators + + +def _validate_prefix(parameter_prefix, operators): + if isinstance(parameter_prefix, str): + return len(operators) * [parameter_prefix] + if len(parameter_prefix) != len(operators): + raise ValueError("The number of parameter prefixes must match the operators.") + + return parameter_prefix + + +def _is_pauli_identity(operator): + from qiskit.opflow import PauliOp + + if isinstance(operator, PauliOp): + return not np.any(np.logical_or(operator.primitive.x, operator.primitive.z)) + return False + + +def _get_default_mixer(cost_operator): + # local imports to avoid circular imports + from qiskit.opflow import I, X + + num_qubits = cost_operator.num_qubits + + # Mixer is just a sum of single qubit X's on each qubit. Evolving by this operator + # will simply produce rx's on each qubit. + active_indices = _active_qubits(cost_operator) + + if len(active_indices) == 0: + return 0 * (I ^ num_qubits) + + mixer_terms = [(I ^ left) ^ X ^ (I ^ (num_qubits - left - 1)) for left in active_indices] + return sum(mixer_terms) + + +def _get_default_initial_state(cost_operator): + initial_state = QuantumCircuit(cost_operator.num_qubits) + active_indices = _active_qubits(cost_operator) + + if len(active_indices) > 0: + # Opflow indices are reversed with respect to circuit indices + active_indices = [cost_operator.num_qubits - 1 - index for index in active_indices] + initial_state.h(active_indices) + + return initial_state + + +def _active_qubits(operator): + from qiskit.opflow import PauliSumOp, PauliOp + + # active qubit selection only supported for PauliSumOps + if isinstance(operator, PauliSumOp): + sparse_pauli = operator.primitive + paulis = sparse_pauli.paulis.to_labels() + elif isinstance(operator, PauliOp): + paulis = [operator.primitive.to_label()] + else: + return list(range(operator.num_qubits)) + + # for each Pauli string get a list which Pauli is the identity (i.e. not active) + is_identity = [list(map(lambda pauli: pauli == "I", pauli_string)) for pauli_string in paulis] + + # use numpy act a logical and on each index across the Pauli strings + idle_qubits = np.all(np.array(is_identity), axis=0) + + # return the indices of the qubits that are not idle + active_indices = [index for index, idle in enumerate(idle_qubits) if not idle] + + return active_indices + + +# end QAOA gate + + +def line_coloring(num_vertices) -> Dict: + """ + Creates an edge coloring of the line graph, corresponding to the optimal + line swap strategy, given as a dictionary where the keys + correspond to the different colors and the values are lists of edges (where edges + are specified as tuples). The graph coloring consists of one color for all even-numbered + edges and one color for all odd-numbered edges. + Args: + num_vertices: The number of vertices in the line graph + Returns: + Graph coloring as a dictionary of edge lists + """ + line_coloring = {} + for i in range(num_vertices - 1): + line_coloring[(i, i + 1)] = i % 2 + line_coloring[(i + 1, i)] = i % 2 + return line_coloring + + +class SwapStrategy: + """A class representing SWAP strategies for coupling maps. + + A swap strategy is a list of swap layers to apply to the physical coupling map. Each swap layer + is specified by a set of tuples which correspond to the edges of the physical coupling map that + are swapped. At each swap layer SWAP gates are applied to the corresponding edges. This class + stores the permutations of the qubits resulting from the swap strategy. + + """ + + def __init__( + self, + coupling_map: CouplingMap, + swap_layers: List[List[Tuple[int, int]]], + edge_coloring: Optional[Dict[Tuple[int, int], int]] = None, + ): + """ + Args: + coupling_map: The coupling map the strategy is implemented for. + swap_layers: The swap layers of the strategy, specified as a list of sets of + edges (edges can be represented as lists, sets or tuples containing two integers). + edge_coloring: (Optional) edge coloring of the coupling map, specified as a set of + sets of edges (edges can be represented as lists, sets or tuples containing two + integers). The edge coloring is used for efficient gate parallelization when + using the swap strategy in a transpiler pass. + """ + self.coupling_map = copy.deepcopy(coupling_map) + self.num_vertices = coupling_map.size() + self.swap_layers = swap_layers + self.edge_coloring = edge_coloring + self._distance_matrix = None + self._inverse_composed_permutation = {0: list(range(self.num_vertices))} + + @property + def coupling_map(self) -> CouplingMap: + """Returns the coupling map of the SWAP strategy.""" + return self._coupling_map + + @coupling_map.setter + def coupling_map(self, coupling_map: CouplingMap) -> None: + """Sets the coupling map of the SWAP strategy.""" + self._coupling_map = coupling_map + self._invalidate() + + @property + def swap_layers(self) -> List: + """Returns the SWAP layers of the SWAP strategy.""" + return self._swap_layers + + @swap_layers.setter + def swap_layers(self, swap_layers: List) -> None: + """Sets the SWAP layers of the SWAP strategy.""" + self._swap_layers = swap_layers + self._invalidate() + + @property + def max_distance(self, qubits: Optional[List] = None) -> Optional[int]: + """ + Return the maximum distance in the SWAP strategy between a list of specified qubits or + all qubits if no list is given. + + Args: + qubits: List of qubits to consider. + + Returns: + The maximum distance between qubits. None if two qubits in the list have no defined + distance. + """ + if qubits is None: + qubits = list(range(self.num_vertices)) + + max_distance = 0 + + for i in range(len(qubits)): + for j in range(i): + qubit1 = qubits[i] + qubit2 = qubits[j] + if self.distance_matrix[qubit1][qubit2] is None: + return None + + max_distance = max(max_distance, self.distance_matrix[qubit1][qubit2]) + + return max_distance + + def __len__(self) -> int: + """Return the length of the strategy as the number of layers.""" + return len(self.swap_layers) + + def __repr__(self) -> str: + """Print the swap strategy.""" + description = f"{self.__class__.__name__} with swap layers:\n" + + for layer in self.swap_layers: + description += f"{layer},\n" + + description += f"on {self.coupling_map} coupling map." + + return description + + @property + def distance_matrix(self) -> List[List]: + """ + Returns the distance matrix of the SWAP strategy as a nested list, where the entry (i,j) + corresponds to the number of SWAP layers that need to be applied to obtain a connection + between physical qubits i and j. + + Returns: + Distance matrix for the SWAP strategy as a nested list. + """ + self._check_configuration(raise_on_failure=True) + + # Only compute the distance matrix if it has not been computed before + if self._distance_matrix is None: + distance_matrix = [[None] * self.num_vertices for _ in range(self.num_vertices)] + + for i in range(self.num_vertices): + distance_matrix[i][i] = 0 + + for i in range(0, len(self.swap_layers) + 1): + for [j, k] in self.swapped_coupling_map(i).get_edges(): + if distance_matrix[j][k] is None: + distance_matrix[j][k] = i + distance_matrix[k][j] = i + + self._distance_matrix = distance_matrix + + return self._distance_matrix + + def permute_labels(self, permutation: List[int], inplace: bool = True): + """ + Permute the labels of the underlying coupling map of the SWAP strategy + and adapt the corresponding SWAP layers accordingly. + + Args: + permutation: The permutation to swap labels by, specified as a list of integers + inplace: Specifies whether the swap strategy object should be modified or a new + swap strategy object created. + + Returns: + A newly created swap strategy if inplace = False, else None + """ + permuted_coupling_map = CouplingMap( + couplinglist=[ + [permutation[edge[0]], permutation[edge[1]]] + for edge in self.coupling_map.get_edges() + ] + ) + permuted_swap_layers = [] + for swap_layer in self.swap_layers: + permuted_swap_layer = [(permutation[i], permutation[j]) for (i, j) in swap_layer] + permuted_swap_layers.append(permuted_swap_layer) + + if inplace: + self.coupling_map = permuted_coupling_map + self.swap_layers = permuted_swap_layers + else: + return SwapStrategy( + coupling_map=permuted_coupling_map, + swap_layers=permuted_swap_layers, + edge_coloring=self.edge_coloring, + ) + + def new_connections(self, idx: int) -> List[Set]: + """ + Returns the new connections obtained after applying the SWAP layer specified by idx, i.e. + a list of qubit pairs that are adjacent to one another after idx steps of the SWAP strategy. + + Args: + idx: The index of the SWAP layer. 1 refers to the first SWAP layer + whereas idx = 0 will return the connections present in the original coupling map + + Returns: + A set of edges representing the new qubit connections + """ + connections = [] + for i in range(self.num_vertices): + for j in range(i): + if self.distance_matrix[i][j] == idx: + connections.append({i, j}) + + return connections + + def missing_couplings(self) -> Set[Tuple[int, int]]: + """Returns the set of couplings that cannot be reached.""" + physical_qubits = list(set(sum(self._coupling_map.get_edges(), ()))) + missed_edges = set() + for i in range(len(physical_qubits)): + for j in range(i + 1, len(physical_qubits)): + missed_edges.add((physical_qubits[i], physical_qubits[j])) + missed_edges.add((physical_qubits[j], physical_qubits[i])) + + for layer_idx in range(len(self) + 1): + for edge in self.new_connections(layer_idx): + for edge_tuple in [tuple(edge), tuple(edge)[::-1]]: + try: + missed_edges.remove(edge_tuple) + except KeyError: + pass + + return missed_edges + + def reaches_full_connectivity(self) -> bool: + """Return True if the swap strategy reaches full connectivity.""" + return len(self.missing_couplings()) == 0 + + def swapped_coupling_map(self, idx: int) -> CouplingMap: + """ + Returns the coupling map after applying a specified number of SWAP layers + from the SWAP strategy. + + Args: + idx: The number of SWAP layers to apply. For idx = 0, the original coupling + map is returned. + + Returns: + The swapped coupling map. + """ + permutation = self.inverse_composed_permutation(idx) + + edges = [[permutation[i], permutation[j]] for [i, j] in self.coupling_map.get_edges()] + + return CouplingMap(couplinglist=edges) + + def apply_swap_layer(self, list_to_swap: List, idx: int) -> List: + """ + Apply SWAPS from a layer specified by idx to a list of elements by + interchanging their positions. + + Args: + list_to_swap: The list of elements to swap + idx: The index of the SWAP layer to apply + + Returns: + The list with swapped elements + """ + x = copy.copy(list_to_swap) + + for edge in self.swap_layers[idx]: + (i, j) = tuple(edge) + x[i], x[j] = x[j], x[i] + + return x + + def composed_permutation(self, idx) -> List[int]: + """ + Returns the composed permutation of all SWAP layers applied up to a specified index. + Permutations are represented by list of integers where the ith element corresponds + to the mapping of i under the permutation. + + Args: + idx: The number of SWAP layers to apply + + Returns: + The permutation as a list of integer values + """ + return self.invert_permutation(self.inverse_composed_permutation(idx)) + + def inverse_composed_permutation(self, idx) -> List[int]: + """ + Returns the inversed composed permutation of all SWAP layers applied up to a specified + index. Permutations are represented by list of integers where the ith element corresponds + to the mapping of i under the permutation. + + Args: + idx: The number of SWAP layers to apply + + Returns: + The inversed permutation as a list of integer values + """ + # Only compute the inverse permutation if it has not been computed before + self._check_configuration(raise_on_failure=True) + try: + return self._inverse_composed_permutation[idx] + + except KeyError: + if idx == 0: + return list(range(self.num_vertices)) + + self._inverse_composed_permutation[idx] = self.apply_swap_layer( + self.inverse_composed_permutation(idx - 1), idx - 1 + ) + + return self._inverse_composed_permutation[idx] + + @staticmethod + def invert_permutation(permutation: List) -> List: + """ + Inverts a permutation specified by a list of integers where the ith element corresponds + to the mapping of i under the permutation. + + Args: + The original permutation + + Returns: + The inverse of the permutation + """ + inverse_permutation = [0] * len(permutation) + for i, j in enumerate(permutation): + if not isinstance(j, int): + raise ValueError(f"Permutation contains non-integer element {j}.") + if j > len(permutation): + raise ValueError( + f"Permutation of length {len(permutation)} contains " + f"element {j} and is therefore invalid." + ) + inverse_permutation[j] = i + return inverse_permutation + + def _invalidate(self) -> None: + """Reset all precomputed properties of the SWAP strategy""" + self._distance_matrix = None + self._inverse_composed_permutation = {} + + def _check_configuration(self, raise_on_failure: bool = True) -> bool: + """ + Check that the configuration of the SWAP strategy is valid + + Args: + raise_on_failure: Specifies whether the function should raise an exception + if the SWAP strategy is invalid. + + Returns: + True if the SWAP strategy is valid, False otherwise (unless an exception is raised) + """ + if self.coupling_map is None: + if raise_on_failure: + raise RuntimeError("The coupling map is None.") + return False + + if self.swap_layers is None: + if raise_on_failure: + raise RuntimeError("SWAP layers are None.") + return False + + edge_set = set(self.coupling_map.get_edges()) + for i, layer in enumerate(self.swap_layers): + for edge in layer: + if edge not in edge_set: + if raise_on_failure: + raise RuntimeError( + f"The {i}th SWAP layer contains the edge {edge} which is not " + f"part of the underlying coupling map with {edge_set} edges." + ) + return False + + return True + + def embed_in(self, coupling_map, vertex_mapping=None, retain_edge_coloring=True): + """ + Given a coupling map and optionally some mapping between vertices of self.coupling_map + and the given coupling map, this function creates a new SWAP strategy by embedding the + existing SWAP strategy in the new coupling map. For instance, this allows us to use the + line strategy in any coupling map that includes a line graph. + + Args: + coupling_map: The new coupling map + vertex_mapping: An optional mapping between vertices of the old and the new coupling + map. If None, a trivial mapping (0 -> 0, 1 -> 1, etc.) is used + retain_edge_coloring: Specifies whether edge coloring of old SWAP strategy should be + used in the embedded strategy. Note that this can lead to an incomplete coloring + in the new strategy. + + Returns: + The new SWAP strategy obtained from embedding the existing SWAP strategy in the new + coupling map + """ + if coupling_map.size() < self.num_vertices: + raise RuntimeError( + f"Cannot embed SWAP strategy for coupling map with {self.num_vertices} " + f"in coupling map with {coupling_map.size()} vertices." + ) + + if vertex_mapping is None: + vertex_mapping = {i: i for i in range(self.num_vertices)} + + swap_layers = [] + for swap_layer in self.swap_layers: + swap_layers.append([(vertex_mapping[i], vertex_mapping[j]) for (i, j) in swap_layer]) + + if retain_edge_coloring and self.edge_coloring is not None: + edge_coloring = { + (vertex_mapping[i], vertex_mapping[j]): self.edge_coloring[(i, j)] + for (i, j) in self.edge_coloring.keys() + } + else: + edge_coloring = None + + return SwapStrategy( + coupling_map=coupling_map, + swap_layers=swap_layers, + edge_coloring=edge_coloring, + ) + + +class LineSwapStrategy(SwapStrategy): + """An optimal SWAP strategy for a line.""" + + def __init__(self, line: List[int], num_swap_layers: int = None): + """ + Creates a swap strategy for a line graph with the specified number of SWAP layers. + This SWAP strategy will use the full line if instructed to do so (i.e. num_variables + is None or equal to num_vertices). If instructed otherwise then the first num_variables + nodes of the line will be used in the swap strategy. + + Args: + line: A line given as a list of nodes, e.g. [0, 2, 3, 4]. + num_swap_layers: Number of swap layers the swap manager should be initialized with + + Returns: + Swap strategy for the line graph + """ + + if num_swap_layers is None: + num_swap_layers = len(line) - 2 + + elif num_swap_layers < 0: + raise ValueError(f"Negative number {num_swap_layers} passed for number of swap layers.") + + swap_layer0 = [(line[i], line[i + 1]) for i in range(0, len(line) - 1, 2)] + swap_layer1 = [(line[i], line[i + 1]) for i in range(1, len(line) - 1, 2)] + + base_layers = [swap_layer0, swap_layer1] + + swap_layers = [] + for i in range(num_swap_layers): + swap_layers.append(base_layers[i % 2]) + + couplings = [] + for idx in range(len(line) - 1): + couplings.append((line[idx], line[idx + 1])) + couplings.append((line[idx + 1], line[idx])) + + super().__init__( + coupling_map=CouplingMap(couplings), + swap_layers=swap_layers, + edge_coloring=line_coloring(num_vertices=len(line)), + ) + + +class FiveQubitTeeSwapStrategy(SwapStrategy): + """A swap strategy for a coupling map of the form + + .. parsed-literal:: + + 0 -- 1 -- 2 + | + 3 + | + 4 + + """ + + def __init__(self): + """Initialize the swap strategy.""" + + swaps = [ + [(1, 3)], + [(0, 1), (3, 4)], + [(1, 3)], + ] + + edges = [[0, 1], [1, 2], [1, 3], [3, 4]] + + coupling_map = CouplingMap(edges) + coupling_map.make_symmetric() + + super().__init__(coupling_map, swaps) + + +class SevenQubitHeavySwapStrategy(SwapStrategy): + """A swap strategy for a coupling map of the form + + .. parsed-literal:: + + 0 -- 1 -- 2 + | + 3 + | + 4 -- 5 -- 6 + + """ + + def __init__(self, n_qubits: int): + """Initialize the swap strategy.""" + + if n_qubits == 7: + swaps = [ + [(0, 1), (3, 5)], + [(1, 3), (4, 5)], + [(0, 1), (3, 5)], + [(1, 2), (5, 6)], + [(3, 5)], + [(1, 3), (5, 6)], + [(3, 5)], + ] + + edges = [[0, 1], [1, 2], [1, 3], [3, 5], [4, 5], [5, 6]] + else: + swaps = [ + [(0, 1), (3, 5)], + [(1, 3), (4, 5)], + [(0, 1), (3, 5)], + [(1, 2)], + [(3, 5)], + [(1, 3)], + [(3, 5)], + ] + + edges = [[0, 1], [1, 2], [1, 3], [3, 5], [4, 5]] + + coupling_map = CouplingMap(edges) + coupling_map.make_symmetric() + + super().__init__(coupling_map, swaps) + + +class DoubleRingSwapStrategy(SwapStrategy): + """Full connectivity swap strategies for a coupling map of the form + + .. parsed-literal:: + + 6 17 + | | + 0 -- 1 -- 4 -- 7 -- 10 -- 12 -- 15 -- 18 -- 21 -- 23 + | | | + 2 13 24 + | | | + 3 -- 5 -- 8 -- 11 -- 14 -- 16 -- 19 -- 22 -- 25 -- 26 + | | + 9 20 + + The swap layers and qubits used depend on the number of qubits. If the number of + qubits is smaller than the length of the longest line then a longest line swap + strategy should be used. For larger number of qubits this class will create the swap + layers to apply. It also uses predefined swap layers and qubits to include which + depend on the number of needed qubits. + """ + + def __init__(self, n_qubits: int) -> None: + """Initialize the swap strategy. + + Args: + n_qubits: The number of qubits for which to generate the swap strategy. + + Raises: + QiskitError: If the number of qubits is smaller than 22 or larger then 27. + In the first case a line swap strategy should be used and in the second + case the backend does not have enough qubits. + """ + + self._lline = [ + 0, + 1, + 2, + 3, + 5, + 8, + 11, + 14, + 16, + 19, + 22, + 25, + 24, + 23, + 21, + 18, + 15, + 12, + 10, + 7, + 6, + ] + + # Create the longest line edges for the coupling map. + # Forward direction + self._longest_line_map = [ + [self._lline[idx], self._lline[idx + 1]] for idx in range(len(self._lline) - 1) + ] + + # Backward direction + self._longest_line_map += [ + [self._lline[idx + 1], self._lline[idx]] for idx in range(len(self._lline) - 1) + ] + + # Defines the extra swaps to apply for a number of qubits. + full_connectivity_strats = { + 22: {"n_line_layers": len(self._lline) + 3, "extra_swaps": {7: [(8, 9)]}}, + 23: { + "n_line_layers": len(self._lline) + 6, + "extra_swaps": {7: [(8, 9), (19, 20)], 17: [(19, 20)]}, + }, + 24: { + "n_line_layers": len(self._lline) + 10, + "extra_swaps": {7: [(8, 9), (19, 20)], 17: [(19, 20)]}, + }, + 25: { + "n_line_layers": len(self._lline) + 10, + "extra_swaps": { + 7: [(8, 9), (19, 20), (14, 13)], + 17: [(8, 9), (19, 20)], + }, + }, + 26: { + "n_line_layers": len(self._lline) + 10, + "extra_swaps": { + 7: [(8, 9), (19, 20), (14, 13)], + 17: [(8, 9), (19, 20), (17, 18)], + }, + }, + 27: { + "n_line_layers": len(self._lline) + 10, + "extra_swaps": { + 7: [(8, 9), (19, 20), (14, 13), (4, 1)], + 17: [(8, 9), (19, 20), (17, 18), (4, 1)], + }, + }, + } + + # The physical qubits to attach to the longest line. + # The key is the number of required qubits and the value is the qubits + # that will be added. + self._qubits_to_add = { + 22: [9], + 23: [9, 20], + 24: [9, 20, 13], + 25: [9, 20, 13, 26], + 26: [9, 20, 13, 26, 17], + 27: [9, 20, 13, 26, 17, 4], + } + + # The physical qubits used. + self._qubits_used = self._lline + self._qubits_to_add[n_qubits] + + self._dangling_qubits_edges = { + 9: [(9, 8), (8, 9)], + 20: [(20, 19), (19, 20)], + 13: [(13, 14), (14, 13), (12, 13), (13, 12)], + 26: [(26, 25), (25, 26)], + 17: [(17, 18), (18, 17)], + 4: [(1, 4), (4, 1), (4, 7), (7, 4)], + } + + # Create the swap strategy. + super().__init__( + self._make_coupling_map(n_qubits), + self._create_swap_layers(**full_connectivity_strats[n_qubits]), + ) + + def _create_swap_layers( + self, n_line_layers: int, extra_swaps: Dict[int, List[Tuple]] + ) -> List[List[Tuple[int, int]]]: + """Creates the swap layers for a given number of line layers and extra swaps. + + Args: + n_line_layers: The number of layers of the line strategy to apply. + extra_swaps: A dict where the swap layer index is the key and the value is a lis + of swaps (specified as tuples to apply.) + + Returns: + The swap layers to apply in the swap strategy. + """ + + # Swap gates on even edges, e.g. (0, 1), (2, 3) + layer1 = [ + (self._lline[idx], self._lline[idx + 1]) for idx in range(0, len(self._lline) - 1, 2) + ] + + # Swap gates on odd edges, e.g. (1, 2) + layer2 = [(self._lline[idx], self._lline[idx + 1]) for idx in range(1, len(self._lline), 2)] + + swap_strat = [] + for idx in range(n_line_layers): + if idx % 2 == 0: + swap_strat.append(layer1) + else: + swap_strat.append(layer2) + + if idx in extra_swaps: + swap_strat.append(extra_swaps[idx]) + + # The swap strategy above has been defined on the physical qubits for clarity but + # for the transpiler pass to work we must define them on the virtual qubits. + virtual_swaps = [] + for layer in swap_strat: + virtual_swaps.append([]) + for swap in layer: + pq0, pq1 = swap[0], swap[1] # physical qubits + virtual_swaps[-1].extend( + [(self.qubits_used.index(pq0), self.qubits_used.index(pq1))] + ) + + return virtual_swaps + + @property + def qubits_used(self) -> List[int]: + """Return the physical qubits that we use.""" + return self._qubits_used + + def _make_coupling_map(self, n_qubits: int) -> CouplingMap: + """Make the coupling map that we will use.""" + + # Add the extra edges to a copy of the longest line coupling map. + coupling_map = [edge for edge in self._longest_line_map] + for qubit in self._qubits_to_add[n_qubits]: + for edge in self._dangling_qubits_edges[qubit]: + coupling_map.append(edge) + + # The coupling map has been defined on the physical qubits for clarity + # however the swap strategy works with virtual qubits so we remap to virtual + virtual_map = [] + for edge in coupling_map: + pq0, pq1 = edge[0], edge[1] # physical qubits + virtual_map.extend([(self.qubits_used.index(pq0), self.qubits_used.index(pq1))]) + + return CouplingMap(virtual_map) + + +def get_swap_strategy( + backend_name: str, n_qubits: Optional[int] = None +) -> Tuple[SwapStrategy, List[int]]: + """Get a swap strategy and the mapping based on the backend. + + Args: + backend_name: the name of the backend as a string. + n_qubits: the number of qubits to use. If not specified then all qubits will be used. + + Returns: + A tuple where the first element is a swap strategy for the given backend and the second + element is the list of physical qubits that the swap strategy applies to. + """ + + if backend_name in [ + "ibmq_belem", + "ibmq_quito", + "ibmq_lime", + "fake_belem", + "fake_quito", + "fake_lime", + ]: + return FiveQubitTeeSwapStrategy(), list(range(5)) + + if backend_name in [ + "fake_lagos", + "ibm_lagos", + "ibmq_casablanca", + "ibm_nairobi", + "ibmq_jakarta", + ]: + return SevenQubitHeavySwapStrategy(n_qubits), list(range(n_qubits)) + + if backend_name in ["ibmq_santiagio", "ibmq_bogota", " ibmq_manila"]: + return LineSwapStrategy(list(range(5))), list(range(5)) + + devices_27q = [ + "ibmq_montreal", + "ibmq_mumbai", + "ibmq_kolkata", + "ibmq_dublin", + "ibm_cairo", + "ibm_hanoi", + "ibmq_toronto", + "ibmq_sydney", + "fake_montreal", + "fake_mumbai", + "fake_kolkata", + "fake_dublin", + "fake_cairo", + "fake_hanoi" "ibmq_toronto", + "fake_sydney", + ] + + if backend_name in devices_27q: + strat = DoubleRingSwapStrategy(n_qubits) + return strat, strat.qubits_used + + raise ValueError(f"No swap strategy found for {backend_name}") + + +class HWQAOAAnsatz(QAOAAnsatz): + """Class for a hardware efficient QAOAAnsatz.""" + + def __init__( + self, + cost_operator=None, + reps: int = 1, + initial_state=None, + mixer_operator=None, + name: str = "QAOA", + swap_strategy: SwapStrategy = None, + initial_layout: Layout = None, + ): + r""" + Args: + cost_operator (OperatorBase, optional): The operator representing the cost of + the optimization problem, denoted as :math:`U(C, \gamma)` in the original paper. + Must be set either in the constructor or via property setter. + reps (int): The integer parameter p, which determines the depth of the circuit, + as specified in the original paper, default is 1. + initial_state (QuantumCircuit, optional): An optional initial state to use. + If `None` is passed then a set of Hadamard gates is applied as an initial state + to all qubits. + mixer_operator (OperatorBase or QuantumCircuit, optional): An optional custom mixer + to use instead of the global X-rotations, denoted as :math:`U(B, \beta)` + in the original paper. Can be an operator or an optionally parameterized quantum + circuit. + name (str): A name of the circuit, default 'qaoa' + swap_strategy (SwapStrategy): The SWAP strategy used for insertion of SWAP layers + initial_layout (Layout): The initial layout mapping logical to physical qubits. If not + specified, a trivial initial layout is used + """ + self._num_logical_qubits = None + self._num_physical_qubits = None + self._swapped_layout = None + self._cost_matrix = None + + super().__init__( + cost_operator=cost_operator, + reps=reps, + initial_state=initial_state, + mixer_operator=mixer_operator, + name=name, + ) + + self.swap_strategy = swap_strategy + self.initial_layout = initial_layout + + @property + def final_layout(self) -> Layout: + """ + Returns: + Returns a Layout object representing the layout after application of the + mapped QAOA circuit. + """ + if self.reps % 2 == 0: + return self.initial_layout + else: + return self._swapped_layout + + @property + def num_qubits(self) -> int: + """ + The number of qubits in the circuit. This is given as the number of physical qubits, i.e. + the size of the coupling map the QAOA circuit is mapped to. If no SWAP strategy and + therefore no coupling map is specified, this defaults to the number of logical qubits. + Returns 0 if no cost operator has been set yet. + + Returns: + The number of qubits + """ + if self._num_physical_qubits is not None: + return self._num_physical_qubits + elif self._num_logical_qubits is not None: + return self._num_logical_qubits + else: + return 0 + + @property + def cost_operator(self): + """Returns an operator representing the cost of the optimization problem. + + We need to override the cost_operator property to set the cost_matrix accordingly. + + Returns: + OperatorBase: cost operator. + """ + return self._cost_operator + + @cost_operator.setter + def cost_operator(self, cost_operator: Optional[OperatorBase]) -> None: + """ + Sets cost operator. + Args: + cost_operator: cost operator to set. + """ + self._cost_operator = cost_operator + self._invalidate() + self._num_logical_qubits = cost_operator.num_qubits if cost_operator else None + self._cost_matrix = self._get_cost_matrix(cost_operator) if cost_operator else None + + @property + def swap_strategy(self) -> Optional[SwapStrategy]: + """Returns the swap strategy used for mapping the circuit to a coupling map.""" + return self._swap_strategy + + @swap_strategy.setter + def swap_strategy(self, swap_strategy: Optional[SwapStrategy]) -> None: + """Sets the swap strategy used for mapping the circuit to a coupling map.""" + self._swap_strategy = swap_strategy + self._num_physical_qubits = swap_strategy.num_vertices if swap_strategy else None + self._invalidate() + + @property + def initial_layout(self) -> Optional[Layout]: + """Returns the initial layout (mapping of physical to logical qubits) of the circuit.""" + return self._initial_layout + + @initial_layout.setter + def initial_layout(self, initial_layout: Optional[Layout]) -> None: + """Sets the initial layout (mapping of physical to logical qubits).""" + self._initial_layout = initial_layout + self._invalidate() + + @staticmethod + def _get_cost_matrix(cost_operator: OperatorBase) -> np.array: + """Returns the cost matrix of a cost operator + + Args: + cost_operator: An operator representing the cost of an optimization problem. + + Returns: + The corresponding cost matrix as a nested list. + """ + # TODO: Add check for cost operator? + quadratic_program = QuadraticProgram() + quadratic_program.from_ising(qubit_op=cost_operator) + size = len(quadratic_program.variables) + objective = quadratic_program.objective + matrix = objective.quadratic.to_array(symmetric=True) + linear_coeff = objective.linear.to_array() + + for i in range(size): + matrix[i][i] += linear_coeff[i] + + return matrix + + def _check_configuration(self, raise_on_failure: bool = True) -> bool: + """Check if the current configuration allows the circuit to be built. + + Args: + raise_on_failure: If True, raise if the configuration is invalid. If False, return + False if the configuration is invalid. + + Returns: + True, if the configuration is valid. Otherwise, depending on the value of + ``raise_on_failure`` an error is raised or False is returned. + """ + # Check standard QAOA configuration requirements + if not super()._check_configuration(raise_on_failure=raise_on_failure): + return False + + if self.swap_strategy is not None and self.cost_operator is not None: + # Check that circuit can be mapped to coupling map of specified SWAP strategy + if self._num_logical_qubits > self._num_physical_qubits: + if raise_on_failure: + raise AttributeError( + f"Cannot map a circuit with {self._num_logical_qubits} qubits to " + f"a physical device with {self._num_physical_qubits} qubits." + ) + return False + + # Check that initial layout is compatible with coupling map and that process terminates! + + # 1) build all possible connections that the swap strategy builds. + possible_edges = set() + for swap_layer_idx in range(len(self._swap_strategy) + 1): + for edge in self._swap_strategy.swapped_coupling_map(swap_layer_idx).get_edges(): + possible_edges.add(edge) + + # 2) Get a list of Pauli strings in the operator, e.g. ["IIZZ", "ZIIZ"] + pauli_strings = [] + for ops in self.operators: + pauli_strings.extend([str(pauli) for pauli in ops.primitive.paulis]) + + # 3) Convert the strings to edges. + required_edges = set() + for pauli_str in pauli_strings: + edge = tuple([i for i, p in enumerate(pauli_str[::-1]) if p != "I"]) + + if len(edge) == 2: + required_edges.add(edge) + + if len(edge) > 2: + if raise_on_failure: + raise ValueError( + f"The Pauli {pauli_str} is non-local (i.e. has more than 2 Z-terms)." + ) + + return False + + # 4) Check that the swap strategy supports all required edges + for edge in required_edges: + if edge not in possible_edges: + if raise_on_failure: + raise ValueError( + f"The edge {edge} is not supported by the SWAP strategy " + f"{self.swap_strategy} which creates edges {possible_edges}." + ) + + return False + + return True + + def _build(self) -> None: + """Build the circuit.""" + + # If the _data property is set the circuit has already been built + if self._data is not None: + return + + # Default to standard QAOAAnsatz if no SWAP strategy has been set + if self.swap_strategy is None: + super()._build() + return + + self._check_configuration() + self._data = [] + + # Set the registers + try: + qr = QuantumRegister(self.num_qubits, "q") + self.add_register(qr) + except CircuitError: + # The register already exists, probably because of a previous composition + pass + + # Set initial layout to trivial layout if not set + if self._initial_layout is None: + self._initial_layout = Layout.generate_trivial_layout(*self.qregs) + + # Order qubit pairs by the minimal number of steps after which they are adjacent during + # the SWAP process, i.e. by the SWAP layer depth after which the corresponding + # interaction can be applied + distance_matrix = self.swap_strategy.distance_matrix + gate_layers = {} + for i in range(self._num_logical_qubits): + for j in range(i): + + rotation_angle = self._cost_matrix[i][j] + + if np.isclose(rotation_angle, 0): + continue + + distance = distance_matrix[self.initial_layout.get_virtual_bits()[self.qubits[i]]][ + self.initial_layout.get_virtual_bits()[self.qubits[j]] + ] + + if distance not in gate_layers.keys(): + gate_layers[distance] = {(i, j): rotation_angle} + else: + gate_layers[distance][(i, j)] = rotation_angle + + max_distance = max(gate_layers.keys()) + final_permutation = self.swap_strategy.composed_permutation(idx=max_distance) + self._swapped_layout = Layout() + self._swapped_layout.from_dict( + { + self.initial_layout.get_physical_bits()[i]: final_permutation[i] + for i in range(self._num_physical_qubits) + } + ) + + # Apply QAOA layers + qaoa_circuit = QuantumCircuit(*self.qregs, name=self.name) + parameters = ParameterVector("t", 2 * self.reps) + for i in range(self.reps): + qaoa_circuit.compose( + self._mapped_qaoa_layer( + mixer_parameter=parameters[2 * i + 1], + cost_parameter=parameters[2 * i], + rzz_layers=gate_layers, + reverse_ops=(i % 2 == 1), + ), + inplace=True, + ) + + # Prepend an initial state (defaults to the equal superposition state if not specified) + if self.initial_state: + qaoa_circuit.compose( + self.initial_state, + front=True, + inplace=True, + qubits=list(range(self._num_logical_qubits)), + ) + + try: + instr = qaoa_circuit.to_gate() + except QiskitError: + instr = qaoa_circuit.to_instruction() + + self.append(instr, self.qubits) + + def _mapped_qaoa_layer( + self, mixer_parameter, cost_parameter, rzz_layers, reverse_ops=False + ) -> QuantumCircuit: + """ + Creates a single mapped QAOA layer with specified parameters and rzz_layers. The mapped + circuit consists of alternating RZZ and SWAP layers. + + Args: + mixer_parameter: Parameter to use for mixer layer + cost_parameter: Parameter to use for cost layer + rzz_layers: RZZ layers specified as a dictionary with integer keys corresponding to the + layer index and values corresponding to the RZZ gates in the layer. RZZ gates are + specified as dictionaries with integer tuples specifying the corresponding qubit + pairs as keys and rotational angles as values. + reverse_ops: Reverses the QAOA layer by reversing the order of the instructions. This + allows us to alternate between even and odd layers. + + Returns: + The mapped QAOA layer as a quantum circuit + """ + + # Build the layer for the cost Hamiltonian + qaoa_cost_layer = QuantumCircuit(self._num_physical_qubits) + + # Applying Ising gates for single qubit rotations + for j in range(self._num_logical_qubits): + rotation_angle = 0 + for k in range(self._num_logical_qubits): + rotation_angle -= self._cost_matrix[j][k] + rotation_angle -= self._cost_matrix[k][j] + + if abs(rotation_angle) > 1.0e-14: + qaoa_cost_layer.rz(rotation_angle * cost_parameter, j) + + # Iterate over and apply gate layers + max_distance = max(rzz_layers.keys()) + current_layout = Layout() + for i in range(max_distance + 1): + # Set the current layout depending on the number of SWAP layers already applied + current_permutation = self.swap_strategy.composed_permutation(idx=i) + current_layout.from_dict( + { + self.initial_layout.get_physical_bits()[j]: current_permutation[j] + for j in range(self._num_physical_qubits) + } + ) + # Determine SWAP gates in upcoming SWAP layer + try: + upcoming_swaps = copy.copy(self.swap_strategy.swap_layers[i]) + except IndexError: + upcoming_swaps = [] + + # Get current layer and replace the problem indices j,k by the corresponding + # positions in the coupling map + rzz_layer = rzz_layers.get(i, {}) + rzz_layer = { + ( + current_layout.get_virtual_bits()[self.qubits[j]], + current_layout.get_virtual_bits()[self.qubits[k]], + ): rotation_angle + for (j, k), rotation_angle in rzz_layer.items() + } + + # Build a list of RZZ gates that overlap with next SWAP layer and should be applied at + # the end of the current RZZ layer to cancel as many CNOT gates as possible. + final_sublayer = {} + layer_new = {} + for (j, k), rotation_angle in rzz_layer.items(): + if (j, k) in upcoming_swaps: + final_sublayer[(j, k)] = rotation_angle + upcoming_swaps.remove((j, k)) + if (k, j) in upcoming_swaps: + final_sublayer[(j, k)] = rotation_angle + upcoming_swaps.remove((k, j)) + else: + layer_new[(j, k)] = rotation_angle + rzz_layer = layer_new + + # Build sub layers according to graph coloring or greedily if no graph + # coloring has been specified TODO: outsource this to a single function? + edge_coloring = self.swap_strategy.edge_coloring + if edge_coloring is not None: + sublayers = [{} for _ in range(max(edge_coloring.values()) + 1)] + for edge, rotation_angle in rzz_layer.items(): + sublayers[edge_coloring[edge]][edge] = rotation_angle + else: + sublayers = [] + while rzz_layer: + current_layer = {} + remaining_gates = {} + blocked_vertices = set() + for edge, rotation_angle in rzz_layer.items(): + if all([j not in blocked_vertices for j in edge]): + current_layer[edge] = rotation_angle + blocked_vertices = blocked_vertices.union(edge) + else: + remaining_gates[edge] = rotation_angle + rzz_layer = remaining_gates + sublayers.append(current_layer) + + # Apply sub-layers + for sublayer in sublayers: + for edge, rotation_angle in sublayer.items(): + (j, k) = map( + lambda vertex: self.initial_layout.get_physical_bits()[vertex], + edge, + ) + qaoa_cost_layer.cx(j, k) + qaoa_cost_layer.rz(rotation_angle * cost_parameter, k) + qaoa_cost_layer.cx(j, k) + + # Apply final sublayer + for edge, rotation_angle in final_sublayer.items(): + (j, k) = map(lambda vertex: self.initial_layout.get_physical_bits()[vertex], edge) + qaoa_cost_layer.cx(j, k) + qaoa_cost_layer.rz(rotation_angle * cost_parameter, k) + + # Add a swap if we are not at the end + if i < max_distance: + qaoa_cost_layer.cx(k, j) + qaoa_cost_layer.cx(j, k) + else: + qaoa_cost_layer.cx(j, k) + + # Apply SWAP gates + if i < max_distance: + for swap in upcoming_swaps: + (j, k) = map( + lambda vertex: self.initial_layout.get_physical_bits()[vertex], + swap, + ) + qaoa_cost_layer.cx(j, k) + qaoa_cost_layer.cx(k, j) + qaoa_cost_layer.cx(j, k) + + # Possibly reverse cost layer and apply mixer Hamiltonian to correct qubits + if reverse_ops: + qaoa_layer = qaoa_cost_layer.reverse_ops() + for i in range(self._num_logical_qubits): + qaoa_layer.rx(2.0 * mixer_parameter, i) + else: + qaoa_layer = qaoa_cost_layer + for i in range(self._num_logical_qubits): + idx = self.initial_layout.get_physical_bits()[ + self._swapped_layout.get_virtual_bits()[self.qubits[i]] + ] + qaoa_layer.rx(2.0 * mixer_parameter, idx) + + return qaoa_layer + + +class QAOASwapPass(TransformationPass): + """A transpiler pass for QAOA circuits.""" + + def run(self, dag: DAGCircuit) -> DAGCircuit: + """Replace all QAOAGates with a hardware efficient implementation of QAOA. + + Args: + dag: The DAG to run the transpiler on. + + Returns: + The DAG with :class:`~qiskit.circuit.library.n_local.qaoa_ansatz.QAOAGate`s + replaced by a hardware efficient implementation. + """ + + current_layout = self.property_set.get("layout", None) + if current_layout is None: + current_layout = Layout().generate_trivial_layout(*dag.qregs.values()) + + swap_strategy = self.property_set.get("qaoa_swap_strategy", None) + if swap_strategy is None: + warn(f"{self.__class__.__name__} did not do anything as no swap strategy was found.") + + return dag + + for node in dag.op_nodes(): + op = node.op + if isinstance(op, QAOAGate): + # Generate initial layout for the qubits the QAOA is run on + qaoa_layout = Layout() + qaoa_layout.from_dict({qubit: current_layout[qubit] for qubit in node.qargs}) + + # Create and insert the hardware efficient QAOA in the DAG + qaoa = HWQAOAAnsatz( + cost_operator=op.cost_operator, + reps=op.reps, + initial_state=op.initial_state, + mixer_operator=op.mixer_operator, + swap_strategy=swap_strategy, + initial_layout=qaoa_layout, + ) + + # Reassign the parameters to those of the original circuit. + rebound = qaoa.assign_parameters(op.params) + + dag.substitute_node_with_dag(node, circuit_to_dag(rebound.decompose())) + + mapping = self.property_set.get("qaoa_swap_layout", None) + if mapping is not None: + self.property_set["layout"] = Layout.from_intlist(mapping, *dag.qregs.values()) + + return dag + + +class SwapStrategyCreator(AnalysisPass): + """Create the swap strategy and mapping to physical qubits for QAOA. + + This class analyses the coupling map to first see if a line swap strategy can be used. + The best line is determined based on the fidelity of the two-qubit gates. If the backend + has enough qubits but no simple path can be found (i.e. no line) then a swap strategy + from the swap strategy library is used. + """ + + def __init__( + self, + backend: Backend, + two_qubit_gate: str = "cx", + use_fidelity: bool = True, + swap_strategy: Optional[SwapStrategy] = None, + swap_strategy_qubits: Optional[List[int]] = None, + ): + """ + Args: + backend: The backend that we will analyze. + two_qubit_gate: The name of the two-qubit gate of the backend. + use_fidelity: Whether or not to use the fidelity of the two-qubit gate + to determine the best path. By default this variable is set to True. + swap_strategy: The swap strategy to use. If this variable is not given (i.e. + the default) then the swap strategy will be created based on the problem + size and optionally the gate fidelity. If this variable is given then the + swap_strategy_qubits variable must also be given. + swap_strategy_qubits: A list of integers representing the physical qubits + to run on. If this list is None (the default value) then the qubits + will be determined based on the problem size and optionally the fidelity + of the two-qubit gates. + """ + super().__init__() + + coupling_map = backend.configuration().coupling_map + + if coupling_map is None: + raise QiskitError( + "Cannot create a swap strategy if the backend does not have a coupling map." + ) + + self._coupling_map = CouplingMap(coupling_map) + self._two_qubit_fidelity = {} + self._max_problem_size = backend.configuration().num_qubits + self._name = backend.name() + self._use_fidelity = use_fidelity + + props = backend.properties() + for edge in coupling_map: + self._two_qubit_fidelity[tuple(edge)] = 1 - props.gate_error(two_qubit_gate, edge) + + if swap_strategy is not None and swap_strategy_qubits is not None: + self._swap_strategy = swap_strategy + self._path = swap_strategy_qubits + elif swap_strategy is None and swap_strategy_qubits is None: + self._swap_strategy = None + self._path = None + else: + raise QiskitError( + "Either both swap_strategy and swap_strategy_qubits are None or neither are None." + ) + + def find_path(self, length: int) -> Optional[List[int]]: + """Find the paths of the coupling map with the appropriate length. + + Args: + length: The length of the simple path to find. + + Returns: + The best path that could be found. If no path was found then None is returned. + """ + + paths, size = [], self._coupling_map.size() + + for node1 in range(size): + for node2 in range(node1 + 1, size): + paths.extend( + rx.all_simple_paths( + self._coupling_map.graph, + node1, + node2, + min_depth=length, + cutoff=length, + ) + ) + if len(paths) == 0: + return None + + if not self._use_fidelity: + return paths[0] + + fidelities = [self.evaluate_path(path) for path in paths] + + return self.get_best_path(paths, fidelities) + + @staticmethod + def get_best_path(paths: List[List[int]], fidelities: List[float]) -> List[int]: + """Sort the paths according to their fidelity. + + Args: + paths: The paths on the qubits. Each sublist is a path. + fidelities: The fidelity for each path. + + Returns: + The paths sorted by fidelity. The highest fidelity path is at index 0. + """ + + return min(zip(paths, fidelities), key=lambda x: -x[1])[0] + + def evaluate_path(self, path: List[int]) -> float: + """Compute the fidelity of the path. + + This function uses a heuristic to compute the fidelity of the two-qubit gates on the given + path by multiplying the fidelity of all two-qubit gates on the path. + + Args: + path: The path as a list of qubits. + + Returns: + The fidelity of the path as the product of gate fidelities. + """ + if not path or len(path) == 1: + return 0.0 + + fidelity = 1.0 + for idx in range(len(path) - 1): + fidelity *= self._two_qubit_fidelity[(path[idx], path[idx + 1])] + + return fidelity + + def run(self, dag: DAGCircuit) -> DAGCircuit: + """create a swap strategy on the coupling map for the given problem size. + + First, we try and find a line that can accommodate problems of the given size. + If no such line is found use the full swap strategy for the coupling map taken from + the library of swap strategies. The swap strategy and path are saved in the property + set of the pass manager. + + Args: + dag: the number of qubits in the dag determines the problem size. + """ + + if self._swap_strategy is None or self._path is None: + problem_size = dag.num_qubits() + + if problem_size > self._max_problem_size: + raise ValueError( + f"{self._name} can only handle problems up to size " + f"{self._max_problem_size}. Received {problem_size}" + ) + + self._path = self.find_path(problem_size) + + if self._path is None: + self._swap_strategy, self._path = get_swap_strategy(self._name, problem_size) + else: + self._swap_strategy = LineSwapStrategy(list(range(len(self._path)))) + + self.property_set["qaoa_swap_strategy"] = self._swap_strategy + self.property_set["qaoa_swap_layout"] = self._path + + return dag + + +class InitialQubitMapper(TransformationPass): + """Reorder the decision variables based on the swap strategy. + + This class iteratively reorders the decision variables in a PauliSumOp based on + a swap strategy to reduce the number of two-qubit operations. The swap strategy + defines a matrix of distances :math:`d_{i,j}` that corresponds to the distance + measured in number of swap layers between qubit :math:`i` and :math:`j`. For + example, adjacent qubits in the coupling map have a distance of 0 and next-nearest + neighbours have a distance of 1 if the next swap layer connects them. At each + iteration, the virtual qubit with the largest sum product of distances and pauli + pre-factors to the virtual qubits that have already been mapped to physical qubits + is the next qubit to be mapped. For this qubit, we then identify the unmapped + physical qubit in the coupling map that minimizes the sum-product of distances + and pauli pre-factors. + """ + + def permute_operator(self, cost_op: PauliSumOp, swap_strategy: SwapStrategy) -> PauliSumOp: + """Permute the Paulis in cost_op to minimize the number of CNOT gates when swapping. + + This is the main method of this class. It permutes the operators in the given + cost_op according to the distance matrix of the swap strategy. This helps reduce + the number of CNOT gates that will be needed when applying the swap strategy. This + is only needed for sparse cost operators. + + Args: + cost_op: The cost operator for which to create the mapping based on the + swap_strategy stored in the mapper. + swap_strategy: The swap strategy that provides the distance matrix. + + Returns: + The permuted cost operator. + + Raises: + QiskitError: if the cost operator is too large for the swap strategy. + """ + if len(swap_strategy.distance_matrix) < cost_op.num_qubits: + raise QiskitError( + f"The cost operator with {cost_op.num_qubits} qubits is too large " + f"for the swap strategy {swap_strategy}." + ) + + rotation_angles = self._map_rotation_angles(cost_op) + + # A mapping with the logical qubit as key and the physical qubit as value. + physical_mapping = dict() + + # The distance matrix between the qubits in the swap strategy. + distance_mat = swap_strategy.distance_matrix + + unmapped_virtual_qubits = set(range(cost_op.num_qubits)) + unmapped_physical_qubits = set(range(cost_op.num_qubits)) + + while unmapped_virtual_qubits: + + # Get the next virtual qubit to map using the sum of rotations to the mapped qubits. + v_qubit = self._get_next_qubit_to_map( + list(unmapped_virtual_qubits), physical_mapping, rotation_angles + ) + + # Find the physical qubit to which to map the virtual qubit. + p_qubit = self._min_distance( + v_qubit, + physical_mapping, + unmapped_physical_qubits, + rotation_angles, + distance_mat, + ) + + # Update state variables. + physical_mapping[v_qubit] = p_qubit + unmapped_virtual_qubits.remove(v_qubit) + unmapped_physical_qubits.remove(p_qubit) + + # reorder data to get a permutations list + permutation = [0] * cost_op.num_qubits + for v_qubit, p_qubit in physical_mapping.items(): + permutation[v_qubit] = p_qubit + + return copy.deepcopy(cost_op).permute(permutation) + + def _min_distance( + self, + qubit: int, + physical_mapping: Dict[int, int], + free_positions: Set[int], + rotation_angles: Dict[Tuple, float], + distance_mat: List[List[int]], + ) -> int: + r"""Find the physical qubit to which to map the virtual qubit. + + The cost function that is computed is + + .. math:: + + \sum_{j\in M} d_{v, v(j)}\theta_{ij}. + + Here, :math:`d_{v, v(j)}` is the swap distance between the physical node v + and the position of the physical qubit :math:`v(j)` to which decision variable + :math:`j` has been mapped. :math:`\theta_{ij}` is the angle between the + decision variable to map, i.e. :math:`i`, and the mapped decision variable :math:`j`. + The set :math:`M` is the set of decision variables that have already been mapped + to a physical qubit. + + Args: + qubit: The decision variable to map, i.e. :math:`i`. + physical_mapping: The dictionary of decisions variables (i.e. virtual qubits) as keys + that have already been mapped to physical variables as values. + rotation_angles: A dict connecting the decision variables to their corresponding + rotations. E.g. 4.0*ZIIZ has rotation_angles {(0, 3): 4.0, (3, 0): 4.0}. + distance_mat: The distance matrix from the swap strategy to use. + + Returns: + The index of the physical qubit to which the decision variable (i.e. the qubit) + argument will be mapped. + """ + smallest_cost, smallest_idx = np.Inf, None + + def calculate_cost(pos): + # Compute the sum in the cost function to minimize + cost = 0.0 + for v_qubit, p_qubit in physical_mapping.items(): + theta = rotation_angles.get((qubit, v_qubit), 0) + cost += distance_mat[pos][p_qubit] * theta + return cost + + return min(free_positions, key=calculate_cost) + + @staticmethod + def _map_rotation_angles(cost_op: PauliSumOp) -> Dict[Tuple, float]: + """Return the rotation angle between two qubits. + + Args: + cost_op: A sum of Paulis whose coefficients determine the rotation angles + that will be extracted and put into a dict. + + Returns: + A dictionary where the keys are the decision variable indices, e.g. (2, 3) + and the values are the coefficients of the corresponding pauli Z terms. + The returned dictionary contains keys (A, B) and (B, A) which both point + to the same rotation angle since the RZZGate is symmetric. + """ + + rotation_angles = dict() + + for pauli, coeff in zip(cost_op.primitive.paulis, cost_op.primitive.coeffs): + indices = tuple([idx for idx, char in enumerate(pauli) if str(char) == "Z"]) + + rotation_angles[indices] = coeff + rotation_angles[indices[::-1]] = coeff + + return rotation_angles + + def _get_next_qubit_to_map( + self, + unmapped: List[int], + mapped: Dict[int, int], + rotation_angles: Dict[Tuple, float], + ) -> int: + """Return the next virtual qubit to map to a physical qubit. + + Args: + unmapped: Qubits that have not yet been mapped to a physical qubit + in the initial layout. + mapped: The dictionary of decisions variables (i.e. virtual qubits) as keys + that have already been mapped to physical variables as values. + rotation_angles: A dict connecting the decision variables to their corresponding + rotations. E.g. 4.0*ZIIZ has rotation_angles {(0, 3): 4.0, (3, 0): 4.0}. + + Returns: + A list of sum of rotations between apped and unmapped qubits. + """ + + # If no qubit has been mapped we first consider the qubit with the highest + # number of connections. + if not mapped: + max_rotation_count = 0 + max_qubit = 0 + for idx1, qb1 in enumerate(unmapped): + + rotation_count = 0 + for idx2, qb2 in enumerate(unmapped[idx1 + 1 :]): + if (idx1, idx2) in rotation_angles: + rotation_count += 1 + + if rotation_count > max_rotation_count: + max_qubit = qb1 + + return max_qubit + + # Consider the following qubits using the largest som of rotations. + sum_of_rotations = [ + self._rotation_sum(qubit, list(mapped.keys()), rotation_angles) for qubit in unmapped + ] + + return max(zip(sum_of_rotations, unmapped), key=lambda x: x[0])[1] + + @staticmethod + def _rotation_sum( + qubit: int, other_qubits: List[int], rotation_angles: Dict[Tuple, float] + ) -> float: + """Compute the rotation sum between the given qubit and the other qubits. + + Args: + qubit: The qubit for which we compute the sum of all other rotation angles. + other_qubits: The list of qubits that have already been mapped to physical + qubits. + + Returns: + The sum of the rotation angles between the given qubit and the unmapped qubit. + """ + return sum(rotation_angles.get((qubit, other), 0) for other in other_qubits) + + def run(self, dag: DAGCircuit) -> DAGCircuit: + """Reorder the terms in the QAOA cost operator according to a swap strategy. + + This pass does not modify the circuit but simply reorders the terms in the cost + operator of any QAOA gates. + + Args: + dag: The DAG to run the transpiler on. + + Returns: + The DAG in which the :class:`~qiskit.circuit.library.n_local.qaoa_ansatz.QAOAGate`s + cost operators have been remapped. + """ + + swap_strategy = self.property_set.get("qaoa_swap_strategy", None) + if swap_strategy is None: + warn(f"{self.__class__.__name__} did not do anything as no swap strategy was found.") + + return dag + + for node in dag.op_nodes(QAOAGate): + op = node.op + op.cost_operator = self.permute_operator(op.cost_operator, swap_strategy) + + return dag + + +def swap_pass_manager_creator( + backend, + swap_strategy: Optional[SwapStrategy] = None, + swap_strategy_qubits: Optional[List[int]] = None, + use_initial_mapping: bool = False, +) -> PassManager: + """Create a swap strategy pass manager. + + Args: + backend: The backend for which to create the swap strategy pass manager. + swap_strategy: An optional swap strategy that lets users pass in swap strategies + instead of using the default methodology in the :class:`SwapStrategyCreator`. + swap_strategy_qubits: The list of physical qubits that are involved in the swap strategy. + This variable must also be given if the swap_strategy variable is not None. + use_initial_mapping: If True (the default is false), add an initial + mapping to the transpilation passes that will reorganize the Pauli operations in + the cost operator to reduce the number of two-qubit gates that the SWAP strategy will + implement. + """ + + if swap_strategy is not None and swap_strategy_qubits is None: + warn("swap_strategy will be ignored since swap_strategy_qubits is None.") + + basis_gates = backend.configuration().basis_gates + + swap_pm = PassManager() + swap_pm.append( + SwapStrategyCreator( + backend, + swap_strategy=swap_strategy, + swap_strategy_qubits=swap_strategy_qubits, + ) + ) + + if use_initial_mapping: + swap_pm.append(InitialQubitMapper()) + + coupling_map = CouplingMap(backend.configuration().coupling_map) + + swap_pm.append( + [ + QAOASwapPass(), + FullAncillaAllocation(coupling_map), + EnlargeWithAncilla(), + ApplyLayout(), + UnrollCustomDefinitions(std_eqlib, basis_gates), + BasisTranslator(std_eqlib, basis_gates), + Optimize1qGatesDecomposition(basis_gates), + ] + ) + + return swap_pm + + +def pulse_pass_creator(backend) -> PassManager: + """Create a pass manager for pulse-efficient transpilation. + + Args: + backend: The backend for which to create the passes. + + Returns: + The pulse efficient pass manager. + """ + rzx_basis = ["rzx", "rz", "x", "sx"] + # a collection of passes to build the pulse-efficient pass manager. + pulse_efficient_passes = [ + # Consolidate consecutive two-qubit operations. + Collect2qBlocks(), + ConsolidateBlocks(basis_gates=["rz", "sx", "x", "rxx"]), + # Rewrite circuit in terms of Weyl-decomposed echoed RZX gates. + EchoRZXWeylDecomposition(backend.defaults().instruction_schedule_map), + # Attach scaled CR pulse schedules to the RZX gates. + RZXCalibrationBuilderNoEcho(backend), + # Simplify single-qubit gates. + UnrollCustomDefinitions(std_eqlib, rzx_basis), + BasisTranslator(std_eqlib, rzx_basis), + Optimize1qGatesDecomposition(rzx_basis), + ] + return PassManager(pulse_efficient_passes) + + +def main(backend, user_messenger, **kwargs): + """Entry function.""" + # parse inputs + mandatory = {"operator"} + missing = mandatory - set(kwargs.keys()) + if len(missing) > 0: + raise ValueError(f"The following mandatory arguments are missing: {missing}.") + + # Extract the input form the kwargs and build serializable kwargs for book keeping. + serialized_inputs = {} + operator = kwargs["operator"] + + if not isinstance(operator, PauliSumOp): + try: + operator = PauliSumOp.from_list([(str(operator), 1)]) + except QiskitError as err: + raise QiskitError( + f"Cannot convert {operator} of type {type(operator)} to a PauliSumOp." + ) from err + + serialized_inputs["operator"] = operator.primitive.to_list() + + aux_operators = kwargs.get("aux_operators", None) + if aux_operators is not None: + serialized_inputs["aux_operators"] = [] + for op in aux_operators: + if not isinstance(op, PauliSumOp): + try: + op = PauliSumOp.from_list([(str(op), 1)]) + except QiskitError as err: + raise QiskitError( + f"Cannot convert {op} of type {type(op)} to a PauliSumOp" + ) from err + + serialized_inputs["aux_operators"].append(op.primitive.to_list()) + + initial_point = kwargs.get("initial_point", None) + serialized_inputs["initial_point"] = list(initial_point) + + use_initial_mapping = kwargs.get("use_initial_mapping", False) + serialized_inputs["use_initial_mapping"] = use_initial_mapping + + optimizer = kwargs.get("optimizer", SPSA(maxiter=300)) + serialized_inputs["optimizer"] = { + "__class__.__name__": optimizer.__class__.__name__, + "__class__": str(optimizer.__class__), + "settings": getattr(optimizer, "settings", {}), + } + + reps = kwargs.get("reps", 1) + serialized_inputs["reps"] = reps + + shots = kwargs.get("shots", 1024) + serialized_inputs["shots"] = shots + + alpha = kwargs.get("alpha", 1.0) # CVaR expectation + serialized_inputs["alpha"] = alpha + + measurement_error_mitigation = kwargs.get("measurement_error_mitigation", False) + serialized_inputs["measurement_error_mitigation"] = measurement_error_mitigation + + use_swap_strategies = kwargs.get("use_swap_strategies", True) + serialized_inputs["use_swap_strategies"] = use_swap_strategies + + use_pulse_efficient = kwargs.get("use_pulse_efficient", False) + serialized_inputs["use_pulse_efficient"] = use_pulse_efficient + + optimization_level = kwargs.get("optimization_level", 1) + serialized_inputs["optimization_level"] = optimization_level + + # select expectation algorithm + if alpha == 1: + expectation = PauliExpectation() + else: + expectation = CVaRExpectation(alpha, PauliExpectation()) + + # add measurement error mitigation if specified + if measurement_error_mitigation: + # allow for TensoredMeasFitter as soon as runtime runs on latest Terra + measurement_error_mitigation_cls = TensoredMeasFitter + measurement_error_mitigation_shots = shots + else: + measurement_error_mitigation_cls = None + measurement_error_mitigation_shots = None + + # Define the transpiler passes to use. + pass_manager = None + if use_swap_strategies: + pass_manager = swap_pass_manager_creator(backend, use_initial_mapping=use_initial_mapping) + + pulse_passes = pulse_pass_creator(backend) if use_pulse_efficient else None + + # set up quantum instance + quantum_instance = QuantumInstance( + backend, + shots=shots, + measurement_error_mitigation_shots=measurement_error_mitigation_shots, + measurement_error_mitigation_cls=measurement_error_mitigation_cls, + pass_manager=pass_manager, + bound_pass_manager=pulse_passes, + optimization_level=optimization_level, + ) + + quantum_instance.circuit_summary = True + + # publisher for user-server communication + publisher = Publisher(user_messenger) + + # dictionary to store the history of the optimization + history = {"nfevs": [], "params": [], "energy": [], "std": []} + + def store_history_and_forward(nfevs, params, energy, std): + # store information + history["nfevs"].append(nfevs) + history["params"].append(params) + history["energy"].append(energy) + history["std"].append(std) + + # and forward information to users callback + publisher.callback(nfevs, params, energy, std) + + # construct the QAOA instance + qaoa = VQE( + ansatz=QAOAAnsatz(operator, reps), + optimizer=optimizer, + initial_point=initial_point, + expectation=expectation, + callback=store_history_and_forward, + quantum_instance=quantum_instance, + ) + result = qaoa.compute_minimum_eigenvalue(operator, aux_operators) + + serialized_result = { + "optimizer_time": result.optimizer_time, + "optimal_value": result.optimal_value, + "optimal_point": result.optimal_point, + "optimal_parameters": None, # ParameterVectorElement is not serializable + "cost_function_evals": result.cost_function_evals, + "eigenstate": result.eigenstate, + "eigenvalue": result.eigenvalue, + "aux_operator_eigenvalues": result.aux_operator_eigenvalues, + "optimizer_history": history, + "inputs": serialized_inputs, + } + + return serialized_result diff --git a/qiskit_runtime/qaoa/qaoa_metadata.json b/qiskit_runtime/qaoa/qaoa_metadata.json new file mode 100644 index 000000000..593589a6c --- /dev/null +++ b/qiskit_runtime/qaoa/qaoa_metadata.json @@ -0,0 +1,118 @@ +{ + "name": "qaoa", + "description": "Qiskit Runtime QAOA program", + "max_execution_time": 18000, + "spec": { + "parameters": { + "$schema": "https://json-schema.org/draft/2019-09/schema", + "properties": { + "operator": { + "description": "The cost Hamiltonian, consisting of Pauli I and Z operators, whose smallest eigenvalue we're trying to find. The type must be a PauliSumOp.", + "type": "object" + }, + "optimizer": { + "description": "The classical optimizer used to update the parameters in each iteration. Per default, SPSA with automatic calibration of the learning rate is used. The type must be a qiskit.algorithms.optimizers.Optimizer.", + "type": "object" + }, + "initial_point": { + "description": "Initial parameters of the ansatz. Can be an array or the string ``'random'`` to choose random initial parameters. The type must be numpy.ndarray or str.", + "type": [ + "array", + "string" + ] + }, + "aux_operators": { + "description": "A list of operators to be evaluated at the final, optimized state. This must be a List[PauliSumOp].", + "type": "array" + }, + "reps": { + "description": "The number of QAOA repetitions, i.e. the QAOA depth typically labeled p. This value defaults to 1. This is an integer.", + "type": "integer", + "default": 1 + }, + "shots": { + "description": "The integer number of shots used for each circuit evaluation. Defaults to 1024.", + "type": "integer", + "default": 1024 + }, + "alpha": { + "description": "The fraction of top measurement samples to be used for the expectation value (CVaR expectation). Defaults to 1, i.e. using all samples to construct the expectation value.", + "type": "number" + }, + "measurement_error_mitigation": { + "description": "Whether to apply measurement error mitigation in form of a tensored measurement fitter to the measurements. Defaults to False.", + "type": "boolean", + "default": false + }, + "use_swap_strategies": { + "description": "A boolean on whether or not to use swap strategies when transpiling. This flag is set to True by default. If this is False then the standard transpiler with the given optimization level will run.", + "type": "boolean", + "default": true + }, + "use_pulse_efficient": { + "description": "A boolean on whether or not to use a pulse-efficient transpilation. This flag is set to False by default.", + "type": "boolean", + "default": false + }, + "optimization_level": { + "description": "The optimization level to run if the swap strategies are not used. This value is 1 by default. This is an integer.", + "type": "integer", + "default": 1 + }, + "use_initial_mapping": { + "description": "A boolean flag that, if set to True (the default is False), runs a heuristic algorithm to permute the Paulis in the cost operator to better fit the coupling map and the swap strategy. This is only needed when the optimization problem is sparse and when using swap strategies to transpile.", + "type": "boolean", + "default": false + } + }, + "required": [ + "operator" + ] + }, + "return_values": { + "$schema": "https://json-schema.org/draft/2019-09/schema", + "properties": { + "optimizer_time": { + "description": "The total time taken by the optimizer.", + "type": "number" + }, + "optimal_value": { + "description": "The smallest value found during the optimization. Equal to the ``eigenvalue`` attribute.", + "type": "number" + }, + "optimal_point": { + "description": "The optimal parameter values found during the optimization.", + "type": "array" + }, + "optimal_parameters": { + "description": "Not supported at the moment, therefore ``None``.", + "type": "null" + }, + "cost_function_evals": { + "description": "The number of cost function (energy) evaluations. This is an integer.", + "type": "number" + }, + "eigenstate": { + "description": "The square root of sampling probabilities for each computational basis state of the circuit with optimal parameters.", + "type": "object" + }, + "eigenvalue": { + "description": "The estimated smallest eigenvalue.", + "type": "number" + }, + "aux_operator_eigenvalues": { + "description": "The expectation values of the auxiliary operators at the optimal state.", + "type": "array" + }, + "optimizer_history": { + "description": "A dictionary containing information about the optimization process: the value objective function, parameters, and a timestamp. The type is Dict[str, Any].", + "type": "object" + }, + "inputs": { + "description": "A dictionary of the serialized input keyword arguments. The type is Dict[str, Any].", + "type": "object" + } + } + } + } +} diff --git a/qiskit_runtime/qka/__init__.py b/qiskit_runtime/qka/__init__.py new file mode 100644 index 000000000..8531147df --- /dev/null +++ b/qiskit_runtime/qka/__init__.py @@ -0,0 +1,43 @@ +# This code is part of qiskit-runtime. +# +# (C) Copyright IBM 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. + +""" +Quantum Kernel Alignment modules +================================ + +.. currentmodule:: qiskit_runtime.qka + +Aux files +--------- + +The ``aux_file`` directory contains datasets for binary classification. + +KernelMatrix class +------------------ + +.. autosummary:: + :toctree: ../stubs/ + + KernelMatrix + +FeatureMap class +------------------ + +.. autosummary:: + :toctree: ../stubs/ + + FeatureMap + +""" + +from .kernel_matrix import KernelMatrix +from .featuremaps import FeatureMap diff --git a/qiskit_runtime/qka/aux_file/dataset_graph10.csv b/qiskit_runtime/qka/aux_file/dataset_graph10.csv new file mode 100644 index 000000000..44ebf4efe --- /dev/null +++ b/qiskit_runtime/qka/aux_file/dataset_graph10.csv @@ -0,0 +1,200 @@ +-2.007916541292168455e-01,1.256050442151832869e-01,1.314111543464645260e-02,1.646477350505198833e-01,-3.560597642700872312e-01,-1.695353554892262138e-01,5.994882770776622377e-02,1.592180384397914317e-01,9.348020040604623770e-02,2.469030420366628009e-01,1.727334581588330087e-01,-2.454461286442135293e-01,-3.109430434520887854e-02,2.276514532517784073e-01,-1.263686379290062944e-01,-4.877100848583847126e-02,4.738988751082016115e-01,4.235600882163070846e-02,-2.479262827528456170e-01,1.403230798247892541e-01,1.000000000000000000e+00 +-3.619770471633777520e-01,3.102410201186359684e-01,1.955016471216194651e-01,2.519809753315865875e-01,1.292735649705571421e-01,6.078849113761808365e-02,4.964318280869732536e-02,-1.190043419084264448e-01,-8.907888847960093726e-02,1.806225748809974496e-01,-3.891721410299649880e-02,3.119682589033747622e-01,1.072508355964233162e-01,-1.099388113086208346e-01,-3.845260712817669124e-02,4.212519615885600144e-02,1.856383482499662785e-01,-3.290732894176815471e-01,1.326495303771342282e-01,3.258614622629292523e-02,-1.000000000000000000e+00 +-1.699567981135229555e+00,-2.499023652979625032e-01,-5.942634989765910314e-02,-1.490057110759272163e+00,-2.034813523011680669e-01,-1.341924090873996001e-01,-4.491966344218056156e-02,-2.196993563951041978e-02,2.403923643158739909e-01,1.826132765017626780e-01,1.266957737287774388e-01,-2.635429686288550122e-01,1.070721972466185806e-01,2.189779380592951075e-01,-4.680750353227767224e-01,-2.713874573953329872e-01,2.929498505299598099e-01,-1.069986603149688797e-01,-2.833521453233959764e-01,1.322852304793778744e-01,1.000000000000000000e+00 +-3.380223718184083292e-01,1.370041451716388914e-01,5.901130877775062622e-02,1.253411046206272805e-01,4.827749586157329409e-01,2.863104589419421808e-01,6.332412929986380723e-02,2.267424279624475625e-01,-5.708031373436329992e-02,-3.523595850569669063e-02,4.112363501220001050e-02,4.658254689076685717e-01,1.899918493528713614e-01,-1.686181006797268456e+00,-1.470504435177556246e+00,2.292900865442913383e-01,1.291532701981612796e-01,-1.561609060412599614e+00,-7.667967188191603112e-02,4.761345531711647094e-03,-1.000000000000000000e+00 +-2.806172170557401091e-01,2.345528404427950075e-01,2.786230084666336815e-01,8.324341648692490381e-02,-1.087067331237027423e-01,-2.470171436270433563e-01,-1.105264854755428350e-01,2.233875130486916993e-01,3.031562242994962908e-01,2.550889741263078214e-01,2.005462828553836174e-01,-1.498809094605016157e-01,7.245672564705890606e-02,-1.429262924191095685e+00,-1.782486420238048819e+00,1.298841118026229091e-01,3.343505737727016092e-01,-1.794784374883469757e+00,-2.153012737029798285e-01,2.977441142038910193e-01,1.000000000000000000e+00 +-2.346721068161321533e-01,1.194903595863296142e-01,5.917843371820045995e-02,2.835171835455025380e-01,1.699647496214566345e-01,-1.272769149921026299e+00,-1.789720273338611722e+00,2.264168273447128912e-02,-1.251693019916260696e-01,-1.355586874585725354e+00,-4.668978510920989561e-02,2.510663893922865375e-01,3.253702075238998903e-01,-2.664341538171773882e-01,-2.546299148618048624e-01,-2.764602557068271405e-01,1.857795514152297822e-01,-7.640354454908623860e-02,9.936511318800467230e-05,9.440223825573818006e-02,-1.000000000000000000e+00 +-1.814810242586567313e+00,-1.997525669651355473e-01,1.969548223903713202e-01,-1.583230326698438439e+00,-1.150166153676587671e-01,-9.435141913297197258e-02,-5.347941540267860117e-02,1.300284229724378848e-01,3.364188457449710512e-01,2.781038237105381006e-01,1.529703121157700851e-01,-2.680246024070674360e-01,2.148996979174693234e-01,-1.394628059165991196e+00,-1.484846598147639307e+00,2.773773581400869326e-02,2.998946494828771403e-01,-1.703850060811981759e+00,-2.422215832158954862e-01,1.761989570765227220e-01,1.000000000000000000e+00 +-3.068878993243701614e-01,1.458429151629054010e-01,7.900337902815834923e-02,2.413892675648690034e-01,2.008437205135009396e-01,-1.255590334176830858e+00,-1.321669830791672551e+00,3.592599513955692381e-02,-1.231808282789535053e-01,-1.429931887152628311e+00,-1.301734423857004441e-01,9.290559734769254785e-02,1.535010499455963795e-01,-1.627845519807247010e+00,-1.798953197751451683e+00,1.450263369791208456e-01,3.040604037053139530e-01,-1.647861822886954153e+00,7.039330985744604585e-02,8.906782256419291943e-02,-1.000000000000000000e+00 +-2.475185490887347184e-01,9.420921282229396954e-02,5.737282722007587543e-02,3.067577078253846609e-02,-2.072072091156420548e-01,-1.568386399799159558e-01,3.625790937428378241e-02,3.160429162302552664e-02,-5.688512450148203170e-02,7.932343149769234547e-02,2.075570007682833729e-02,-2.379648165439725571e-01,-1.027001489524781430e-03,1.311064025402823296e-01,-1.412349284243120673e-01,-1.631336636555825059e+00,-1.378349038814703276e+00,2.156114329793494822e-01,-1.942461747151418283e-01,-1.372603263961538644e+00,1.000000000000000000e+00 +-3.712681993563175520e-01,2.447203123167054906e-01,1.674824609253608432e-01,-1.446717305613078430e+00,-1.420563357980569430e+00,-3.609458750875077504e-01,1.540679229703752900e-02,-1.625051620109700057e+00,-5.736007283963888426e-02,9.804019182766415819e-02,-8.996342455644670832e-02,2.026924048741309825e-01,3.653067457918324212e-01,-2.225456451124414037e-01,-1.408978414783976507e-01,-2.002353048192771490e-01,1.324373334310313677e-01,-1.255958370812784719e-01,9.761506581143294947e-02,1.122444049740187882e-02,-1.000000000000000000e+00 +-1.814997372005986653e+00,-1.941450819739447553e-01,1.653741602290772494e-01,-1.588775077627082366e+00,-9.586166821127925508e-02,3.122259066613891632e-02,2.145107584268255707e-01,4.641439422694794426e-02,2.989727635230137182e-01,1.449741841347681748e-01,1.679038267000977636e-01,-5.138476561718791080e-02,1.807859838991984214e-01,1.237539920076329292e-01,7.541819841429758808e-02,-1.632146375789857107e+00,-1.312653953351071934e+00,1.741179444086803085e-01,-2.423301838396551833e-01,-1.451627771151446478e+00,1.000000000000000000e+00 +-1.913286986041110582e-01,2.450407650849422692e-01,3.545361380153044251e-01,-1.436025934901113299e+00,-1.358754408733316765e+00,-3.345361517269413998e-01,9.228629189717228731e-02,-1.627704820726677992e+00,-9.910154579885172010e-03,1.213180773019647829e-01,9.271637029225321402e-02,1.930591674110951339e-01,2.607463373998891720e-01,-1.584432208669410036e+00,-1.914545203619924862e+00,7.672603635086822083e-02,2.618090199653315131e-01,-1.797289647501532972e+00,6.244643382207429316e-02,1.646627836234521303e-01,-1.000000000000000000e+00 +-2.827169272843018111e-01,-8.938278041821995368e-02,8.689099131522518005e-02,8.801569791382071739e-02,-2.091786619535809799e-01,-1.932951335710459684e-01,-3.421476103753014197e-02,1.458947150913895707e-01,1.416774496008315898e-01,2.267086822419316361e-01,1.930543033835727951e-01,-2.612848042443172147e-01,1.645347232003737947e-01,-1.377589491351909334e+00,-1.684355193891810876e+00,-1.222844897880694059e+00,-1.274121514402086763e+00,-1.373542996575246589e+00,-3.515611588040133961e-01,-1.292597459201656251e+00,1.000000000000000000e+00 +-2.898068069005064751e-01,3.362530106259646878e-01,4.039937736397130053e-01,-1.292134742263436831e+00,-1.327694121309146658e+00,-1.781255923277610176e+00,-1.474615078949816471e+00,-1.707491897761346689e+00,-6.347522520753053066e-02,-1.556758761129486857e+00,3.139049175431805200e-02,1.615493989436921352e-01,1.323517093486887286e-01,-9.163479094230082833e-02,-8.476455173744942839e-02,-8.388291365994691995e-02,-6.105412411432364905e-03,-1.115762012557646732e-01,-2.509796522546856035e-02,4.234376927324820516e-02,-1.000000000000000000e+00 +-1.814790777736557059e+00,-9.654571037546932910e-02,-1.547101539374872714e-01,-1.492103527123674001e+00,-2.653854804365027853e-01,-1.556251496039434690e-01,-1.524839474212562607e-01,-6.663369304520916359e-02,2.793381062230014256e-01,7.889239734507461077e-02,2.769616381192322452e-01,-8.894663379787948188e-02,6.234700937376988494e-02,-1.340385343202203039e+00,-1.556828923430347178e+00,-1.453807041951613588e+00,-1.236379895728524581e+00,-1.475549128646258534e+00,-2.682858355304210130e-01,-1.285611067987255218e+00,1.000000000000000000e+00 +-3.908878157228444783e-01,2.113366908644658537e-01,2.228160196256315229e-01,-1.481040542017491690e+00,-1.383617867835357451e+00,-1.721604745865471298e+00,-1.414072410664353185e+00,-1.615445833161077926e+00,-4.576111109960588619e-02,-1.632679036459264932e+00,-1.234401773580307005e-03,2.811519385075775257e-01,2.014740758147050559e-01,-1.620445921842751158e+00,-1.724348080122484417e+00,1.510452929498979913e-01,1.141521430830667733e-01,-1.655542359199389191e+00,-3.369205777448615324e-02,7.371810551873218820e-02,-1.000000000000000000e+00 +-2.030544519688619043e-01,-1.406268088042871733e+00,-1.646983584535837597e+00,3.689334738254190782e-02,-3.421474950398201709e-01,-1.694136682326066179e+00,1.021401297592262059e-01,3.099079545410267714e-02,2.477327019364135852e-01,1.890797137836061625e-01,2.360554195655827359e-01,-3.439769556005214390e-01,1.309297254528933308e-01,3.071109644024896390e-01,-9.727248652127777617e-02,2.445132909854294545e-02,2.597117486340805859e-01,-2.995306108773132525e-01,-1.144873034107466298e-01,8.332311310990911379e-02,1.000000000000000000e+00 +-2.620082274319464610e-01,1.309241126975579095e-01,1.550173404465169380e-01,2.290351928486012434e-01,1.355830014974737097e-01,1.841756991634939666e-01,3.740495859304824450e-02,1.432554810241620236e-01,-1.318531366877148947e-01,-5.250125578672842908e-02,-1.872262906681710498e-02,2.208668508096660876e-01,1.719656748930894485e-01,-2.664136995765063742e-02,-3.048994777487798458e-02,-1.554169790273430829e-01,3.189090560269032260e-01,-1.618584889809290051e+00,-1.417869631432152167e+00,2.926453270514067784e-02,-1.000000000000000000e+00 +-1.906075541058226630e+00,-1.684024007286910596e+00,-1.340778937578472885e+00,-1.469986073671453619e+00,-2.579063985079173560e-01,-1.619864788609417738e+00,-1.533062362767425024e-01,1.024990390979742955e-01,2.722139247877681245e-01,3.416922464760236977e-01,1.998637733527559024e-01,-1.085948848370048153e-01,2.513993049009481395e-01,3.083956725607817084e-01,4.168154406519475330e-02,-1.614654912656046104e-01,2.233822286873882179e-01,-1.082611399000658892e-01,-3.102175660124854017e-01,2.767709743371943865e-01,1.000000000000000000e+00 +-1.403865864129003604e-01,1.399259893403922705e-01,1.822987714289338868e-01,2.233337833692906893e-01,2.711380196075012794e-01,4.136960628999039047e-01,-1.278568634968769296e-02,-7.653098140936862026e-02,-2.572331698025254881e-02,1.707020466287472726e-01,2.065920513285800508e-01,1.183980052005483008e-01,1.075900892651548701e-01,-1.555623109767140466e+00,-1.909353313594174706e+00,7.389214672735844314e-02,6.165284928008352150e-02,2.672516810167926971e-02,-1.437578316133034884e+00,1.044033025676337212e-02,-1.000000000000000000e+00 +-3.408276321069046988e-01,-1.146037249935432101e+00,-1.501258508232188316e+00,2.618804000103412688e-03,-3.833992773836243928e-01,-1.786375783453783894e+00,2.824428717436953257e-02,5.969626703842215165e-02,3.398492171004255380e-01,3.196012159857406498e-01,1.573379930471782706e-01,-2.098223058925809681e-01,1.593055053841970214e-01,-1.347342479348194288e+00,-1.674166194778130334e+00,1.528908359522206106e-01,8.505383672976472853e-02,-1.729846339704632197e+00,-2.337434662739074076e-01,8.752659306713833631e-02,1.000000000000000000e+00 +-1.412783597316017437e-01,4.731801517553746450e-02,2.343150309962709898e-01,2.573249258932783778e-01,1.910417264810912175e-01,-1.448915033225833371e+00,-1.668482896109561464e+00,-2.185696090003557968e-01,-2.576158986434173154e-02,-1.564639023958924513e+00,-3.033109236920639856e-02,1.986130184165910417e-01,2.692243894750914746e-01,-2.989017527663618418e-02,-2.447858565867271352e-01,-1.546116379421747788e-01,1.911562244413266831e-01,-1.761274715944235458e+00,-1.558771609949650250e+00,1.185904653993593988e-01,-1.000000000000000000e+00 +-1.869606538605044443e+00,-1.742161305177921893e+00,-1.705962237545518567e+00,-1.680726774343433316e+00,-2.857918908775195499e-01,-1.933229486682525478e+00,-7.713614059712470272e-02,-7.657536195635973597e-02,1.924996180568824267e-01,3.080269644621186353e-03,2.458123927851449952e-01,-2.384431090062408276e-01,4.062257989091383470e-02,-1.269543731853680413e+00,-1.805576190862282493e+00,1.471755939521104672e-01,5.569043668847070694e-02,-1.617454615027889675e+00,-2.240814913791160934e-01,8.825325484191345882e-02,1.000000000000000000e+00 +-9.196904305524630785e-02,5.969966196298605310e-02,1.086359809752615313e-01,2.512606763871440552e-01,2.094744769886715818e-01,-1.303860010467098540e+00,-1.617071325959459349e+00,4.671505936492224842e-02,1.082264663551268935e-02,-1.347863119666699250e+00,-3.034928686809768017e-04,2.294002941270299745e-01,1.181027073768831509e-01,-1.664669319623928612e+00,-1.804692453724187073e+00,8.403241356683452401e-02,1.735037831530633623e-01,5.715596369227590734e-02,-1.544565301931799528e+00,-3.264092251927003946e-02,-1.000000000000000000e+00 +-1.555022682862563510e-01,-1.125508257373301069e+00,-1.501457806107648896e+00,-7.326900178082657034e-02,-1.295022601664582163e-01,-1.531828174481691507e+00,-6.302335743966225312e-02,-1.634728460917521109e-01,-1.367145896989113707e-02,1.641630582463626531e-01,2.708179693392485099e-01,-3.283178875772138072e-01,3.473293642587648167e-01,3.770264645556749006e-01,-1.048516243520047075e-01,-1.735934660954378206e+00,-1.405843359387919600e+00,2.119896565479701955e-01,-2.917055976828555330e-01,-1.556421047832049043e+00,1.000000000000000000e+00 +-1.535012995537096414e-01,1.646959501643460166e-01,2.722442285579929622e-01,-1.675350761225579799e+00,-1.429210405398552153e+00,-1.925582100503623051e-01,1.795411376065435272e-01,-1.719388905193311334e+00,-1.287123594519922753e-01,-1.698818447393583841e-02,-4.319585086121631445e-03,2.068592938283637661e-01,6.763226153019955000e-02,-1.143541532515680720e-01,-2.263818919984550704e-01,-2.028328628885033014e-02,1.711518704530328727e-01,-1.576459216968581600e+00,-1.535806894782790755e+00,3.407970358393891397e-02,-1.000000000000000000e+00 +-1.786388176514004167e+00,-1.685889696824095507e+00,-1.608441581101702322e+00,-1.665274141382779849e+00,-3.107298980188865078e-01,-1.703074441527982597e+00,-8.020214110244330841e-02,1.159612282370702380e-01,2.790597354042892442e-01,1.195713541401621227e-01,1.827992363200610093e-01,-2.172429686024237583e-01,-6.056191698649612820e-02,2.799762459115179847e-01,-1.049038409106583247e-01,-1.702234460492283885e+00,-1.309815879622063362e+00,6.573030944695713862e-02,-1.000791266829980791e-01,-1.361385183472438865e+00,1.000000000000000000e+00 +-1.407177086790832587e-01,2.852591968528649380e-01,2.951756655992362477e-01,-1.449733370168583058e+00,-1.449509169095344108e+00,-1.880505444262600412e-01,1.166729570154840523e-01,-1.522909106459823469e+00,-2.239815175130627223e-01,8.318864492281358158e-02,2.137627597733297857e-01,1.992375630005557563e-01,4.048339239098861508e-01,-1.739775740934404125e+00,-1.837135735844621198e+00,2.133512564098599407e-01,3.844720367327983945e-01,9.980635896652981232e-02,-1.400939648441999807e+00,-3.883190660159061003e-02,-1.000000000000000000e+00 +-2.126020741230422451e-01,-1.311872790849536230e+00,-1.391669019449700340e+00,-7.875753174027555481e-04,-2.396346173070124375e-01,-1.754607623443531939e+00,-1.075734915011122839e-01,1.146509379456045127e-02,2.952091447636051025e-01,1.383247610116168369e-01,2.415999236601309441e-01,-9.295107545531325344e-02,3.205064148694648785e-02,-1.377446146155375084e+00,-1.696033905545283549e+00,-1.524333008493155894e+00,-1.431227615493637861e+00,-1.586872455940936399e+00,-2.421020614860027176e-01,-1.465509609927389656e+00,1.000000000000000000e+00 +-9.756487538296562678e-02,1.905828894672605966e-01,1.843750990526454858e-01,-1.360775204799180793e+00,-1.388809615833678679e+00,-1.758455596000101151e+00,-1.598231564606595656e+00,-1.603536979723443778e+00,-9.221819699563088557e-02,-1.475907296959872328e+00,4.609333441812505511e-02,2.021473772991954077e-01,1.269652458713305399e-01,-2.684483074984664319e-01,-1.962621436883517090e-01,-7.669821748926668181e-02,2.410952035134545368e-01,-1.701232758668220768e+00,-1.621400018873538684e+00,1.450006659205423376e-01,-1.000000000000000000e+00 +-1.919357411192960550e+00,-1.746523422285083393e+00,-1.589260229764755339e+00,-1.442571556730520399e+00,-2.789995926641056778e-01,-1.764857351249993656e+00,1.630000458435644228e-01,6.571362908834758182e-03,3.549137380973362843e-01,1.399597894133332576e-01,3.685141050889269021e-01,-3.130230034828292984e-01,3.053191735768333581e-01,-1.308207621013695476e+00,-1.577611713332105481e+00,-1.422370698021114377e+00,-1.478454732679892025e+00,-1.350695461184106438e+00,-2.198106740649748980e-01,-1.431188114865378891e+00,1.000000000000000000e+00 +-1.959723628626568459e-01,1.167913563581796932e-01,1.824795050519394946e-01,-1.511100532387372741e+00,-1.362183845710490449e+00,-1.920632789045093380e+00,-1.579189158682562599e+00,-1.484211511568479969e+00,-5.702889149716390549e-02,-1.454884880955336079e+00,6.729963140093969676e-02,3.690251353496150077e-01,2.324761940787350434e-01,-1.767268937057435263e+00,-1.766247949277564899e+00,1.532858233068019604e-01,1.552477914085325650e-01,-4.242737087494632964e-03,-1.555376670312583531e+00,-1.315702222326894377e-01,-1.000000000000000000e+00 +1.186726607285704116e-02,4.155599789751587281e-01,-1.251641913720423493e-02,-6.365542085251635340e-02,-1.321759374937407638e-01,-1.401692109535647712e-01,-4.792549716968761558e-02,-1.717425529678146212e+00,-1.346413395291601534e+00,-1.262823367737433700e-01,2.916611676991012203e-01,-1.729744498154153209e+00,1.027791152745445857e-01,1.846903559336730005e-01,-1.720817935394969300e-02,-1.813911094811911096e-01,1.743076865679060172e-01,-9.950741364948334500e-03,-4.235305658076960467e-01,1.543634301802584152e-01,1.000000000000000000e+00 +-1.839914152790248725e+00,-2.990889535071097338e-01,1.680357158788539640e-01,-1.447544727917205121e+00,2.176590145518261243e-01,1.325996131777733567e-01,1.390231493227850612e-01,-3.977803235383932379e-02,-1.532318405957663887e-02,1.224971194701602178e-01,3.141260429582210523e-01,2.573758769608737174e-01,2.382882287407745159e-01,-8.074346680799386611e-02,-2.279345605647102313e-01,-1.086430140186502163e-01,4.225392268433366127e-01,-1.186557328974662678e-01,-9.009329746143089623e-02,9.710452121918558555e-02,-1.000000000000000000e+00 +-1.612823155584582668e+00,-1.492408620970711031e-01,-2.927947714038789395e-02,-1.581898585174331684e+00,-2.639617439540696275e-01,4.074645484559386022e-02,-2.427247458169240826e-02,-1.681546120251358634e+00,-1.354123195036015304e+00,-8.456689840799687385e-02,1.220819181697277850e-01,-1.756485518145850966e+00,1.962228551288944522e-01,1.859644055105026117e-01,-1.552383657208578904e-01,-6.000494554658519775e-02,1.782292462513375697e-01,-4.014661907783578787e-02,3.379121773648191551e-02,1.016106829555737945e-01,1.000000000000000000e+00 +-1.780081046958263746e+00,-4.192030499949445099e-02,1.515788495426312610e-01,-1.427950548919541873e+00,2.592200930100108658e-01,1.312811868218668465e-01,1.718496148904553184e-02,-5.089705078785578679e-03,-1.266770518198281625e-01,1.002672490239872038e-01,4.249914927660260883e-02,2.590657667105403239e-01,2.273463138642181913e-01,-1.734370842420827730e+00,-1.651225611069535582e+00,-2.144473951662861921e-02,5.717543232942556841e-02,-1.556967000434823500e+00,-3.302190069655809579e-02,1.313883514775121553e-01,-1.000000000000000000e+00 +-1.336939134088614978e-01,2.876079839638858715e-01,2.395292122218867226e-02,7.718235769613168706e-04,-1.210079550689721201e-01,-6.948296810014226166e-02,1.107304529536503046e-01,-1.449098811951648802e+00,-1.416850769923243991e+00,-1.894821220905087145e-01,2.056007501673507987e-01,-1.629376976711315095e+00,2.652108869293851034e-01,-1.349109149383652451e+00,-1.626720009842021808e+00,9.262742226531375800e-02,1.952059358058028382e-01,-1.777446761957121035e+00,-1.366881397742658599e-01,1.802715457876161997e-01,1.000000000000000000e+00 +-1.796272690921347559e+00,-1.774123902044983658e-01,2.670581478373795781e-01,-1.278963068583510942e+00,1.469453996846848132e-01,-1.414317002840989490e+00,-1.376532910749186600e+00,1.581883384851158481e-01,-2.392557560679413331e-02,-1.485883854601798282e+00,1.397769915317580991e-01,3.184880880991732832e-01,2.909126178028541565e-01,5.254223392782449154e-02,-1.501833053874064572e-01,-1.967275627066273347e-01,3.699415999172078195e-01,-1.406049116031088952e-01,6.622133603538822577e-02,1.961873639760809973e-01,-1.000000000000000000e+00 +-1.785220638062096077e+00,-2.295243944030855021e-01,3.824649826658470253e-02,-1.648181044644347892e+00,-2.742777977271766976e-01,-2.575974150183087952e-01,-1.566125524681920636e-02,-1.429465606904201769e+00,-1.281389396059050778e+00,-2.009397256986373004e-01,2.219921338796986554e-01,-1.587302010170268396e+00,1.701491631286058959e-01,-1.451422498647342074e+00,-1.629706576250242733e+00,4.765660089118081699e-02,1.513431476593253278e-01,-1.744954124120697614e+00,-3.000077540398696230e-01,1.473663347261450762e-01,1.000000000000000000e+00 +-1.915984716237936869e+00,-2.334096645682603444e-01,2.969182944477763986e-01,-1.462364225027299458e+00,-9.038357273379871160e-04,-1.501601560571436567e+00,-1.569491762709352756e+00,-1.413126429675328095e-03,-2.823024453240063208e-02,-1.627235235307024297e+00,1.158073992136935126e-01,2.766491873201362139e-01,1.606535465287846354e-01,-1.612968800083773635e+00,-1.704458739669437284e+00,1.641455785950739488e-01,3.710519682415764908e-01,-1.704170907609168450e+00,-4.815258597647618932e-02,-4.618627193132339437e-02,-1.000000000000000000e+00 +-1.432041439121052584e-01,1.176013730967366833e-01,1.209900020741062621e-01,-4.888365609557257518e-04,-2.535302243252841592e-01,-2.654853813955358743e-02,7.763789683125185692e-02,-1.745798948827284480e+00,-1.583027137084846103e+00,-1.364820093782884713e-01,2.849165470997524485e-01,-1.641591214804483334e+00,9.071169690031330446e-02,2.004624515894348757e-01,-8.350256926870230911e-02,-1.665780049054627288e+00,-1.480118862690187775e+00,1.138372271329863294e-01,-4.213227756064114038e-03,-1.480969809958756933e+00,1.000000000000000000e+00 +-1.947212966606190365e+00,-1.109567050567978608e-01,9.189978066414331015e-02,1.377949703620761357e-01,-1.358465210982832128e+00,-1.191888379927300290e-01,1.348297913431806966e-02,-1.628476840873991360e+00,8.208056027268331722e-03,5.754932724803086402e-02,1.502609168270740425e-01,9.015234033881530662e-02,2.712580623103985622e-01,-7.811883730421693983e-02,-1.209705944878042005e-01,-1.897155308102837223e-01,3.321993044877320500e-01,-1.423230162477970018e-01,-4.922241589906117820e-04,1.276779005151182922e-01,-1.000000000000000000e+00 +-1.643142401212486892e+00,-4.452179782252499440e-02,2.761625880228188445e-02,-1.444505141847691521e+00,-1.864488011141737789e-01,4.235134262320812415e-02,-1.060818489425193190e-01,-1.530886704442798951e+00,-1.290949591393974583e+00,-1.578542625770643082e-01,2.071111419625968342e-01,-1.736294468528262191e+00,1.881918380973065519e-01,2.112289351330886022e-01,-5.637226055998517671e-02,-1.734874621224534508e+00,-1.383487644273780015e+00,1.199881250258862941e-01,-1.480144127027813172e-01,-1.548442094397331381e+00,1.000000000000000000e+00 +-1.849688848902060645e+00,-4.571354376676887987e-02,8.791423855935898146e-02,9.320985568107194308e-02,-1.209440457784467071e+00,-2.541410595060626942e-01,1.167608598191240132e-01,-1.472532643300827893e+00,-1.303253406076957110e-01,-4.200988326309663956e-02,-2.265506228267986644e-01,1.786626037264705258e-01,2.812036326502925787e-01,-1.662884083493961329e+00,-1.847554130340076650e+00,3.404476020084349908e-01,2.714841486400764170e-01,-1.642850792893414846e+00,-1.127204049092180166e-01,5.515944350723923917e-02,-1.000000000000000000e+00 +-1.059080862694527225e-01,2.413223144436135448e-01,1.188793573122913783e-01,2.759458307752131190e-01,-3.155105305463620224e-01,-4.618161741368533718e-02,-1.787389365572205069e-01,-1.675692373916033651e+00,-1.367666161849348638e+00,-2.735719631365116644e-01,2.681588822287698237e-01,-1.810320595153056988e+00,2.234518121411552816e-01,-1.309036186758603559e+00,-1.629338507535421199e+00,-1.586640938538630419e+00,-1.252224138790772212e+00,-1.510137892836192597e+00,-7.003442782371488429e-02,-1.457702130539146435e+00,1.000000000000000000e+00 +-1.825734321933270454e+00,-1.988173085426351294e-01,2.184221411006121993e-01,1.520609502758123732e-01,-1.243887567058084453e+00,-1.710232011557398790e+00,-1.424856931155371820e+00,-1.590944974346635732e+00,7.050679230094457350e-02,-1.371683718348912562e+00,-6.861154885317624630e-02,1.581601008181294232e-01,2.681787861269692708e-01,-1.504471581425083637e-01,-3.471875534253440754e-02,-9.232204486171889657e-02,1.687579589557818283e-01,-1.528039563690852809e-03,2.036078932836379307e-01,1.589408223412197008e-01,-1.000000000000000000e+00 +-1.821739796368849973e+00,-1.186406610554362334e-01,1.657895857813671547e-01,-1.623415940529338908e+00,-2.527373807759313307e-01,-4.807783132028257272e-02,-1.885058399457829187e-02,-1.612100802806384348e+00,-1.318830896830129662e+00,-2.655886226639870840e-01,2.087351429592352869e-01,-1.836812016330601871e+00,5.679997717382348488e-02,-1.326177945238083744e+00,-1.741631677579875470e+00,-1.552712102848185394e+00,-1.450288170305659596e+00,-1.572891868987263297e+00,-1.505080380159262154e-01,-1.401630510513969696e+00,1.000000000000000000e+00 +-1.925496555102242491e+00,-1.906305748726313254e-01,1.280547991103812289e-01,2.654419405709938506e-01,-1.239396153611779461e+00,-1.881188922805395958e+00,-1.387309755378072440e+00,-1.618537046170873017e+00,-4.292296268015079880e-02,-1.684758651153038134e+00,1.233955963793083588e-01,3.570822832340531106e-01,1.706324194092417867e-02,-1.774329777974420486e+00,-1.659496535286899777e+00,8.980207268961742928e-02,1.570836997909211830e-01,-1.883374127297818390e+00,-8.317299220505475210e-03,1.339917436991019395e-01,-1.000000000000000000e+00 +-4.604935331407342125e-02,-1.234022535552339317e+00,-1.489861091426315731e+00,-4.913043052586447967e-03,-2.590156368024213451e-01,-1.662104163159621617e+00,-5.207369989469273069e-02,-1.665437349206309259e+00,-1.229647888137037626e+00,-2.130412650900186111e-01,2.536501330803943577e-01,-1.787062084880876034e+00,6.970503853135476269e-02,3.262479990769734384e-01,-1.865786699508110991e-01,9.903207563649846223e-03,1.576147272749244821e-01,2.427976713302304468e-03,-1.703172656802548202e-01,8.404738727319956137e-02,1.000000000000000000e+00 +-1.871247213242097862e+00,-1.585637251903615763e-01,6.992527411208426802e-02,-1.475375407303459063e+00,-6.213881796473447183e-02,-7.554214817526197501e-02,2.442784591842216690e-01,2.511980873992987126e-02,9.329472155898771435e-03,1.454098576025833478e-02,-4.576900179080329450e-02,1.619136782862597157e-01,6.664832475728971195e-02,-1.353540837371562944e-01,-1.792257851943249436e-01,3.541029725012719009e-02,1.706196530629354724e-01,-1.548850570319505904e+00,-1.566653235345892847e+00,-9.476332446157373357e-02,-1.000000000000000000e+00 +-1.664312032916881012e+00,-1.792424242934206102e+00,-1.550040679881295125e+00,-1.722628866135070158e+00,-3.358656399764347045e-01,-1.609050084232439115e+00,-3.260669242273393081e-02,-1.399839822461237615e+00,-1.517878066986744434e+00,-1.829662077058151481e-01,3.590013195660626821e-02,-1.872548082063439701e+00,6.671205940344417107e-02,-3.332763700905849724e-02,-1.357451803972037074e-01,-8.369038624500416135e-02,2.200551866090619257e-02,-1.573374340703240226e-01,-1.710462813366873946e-01,1.267990236431605355e-01,1.000000000000000000e+00 +-1.810067284149239564e+00,-3.578680540845750047e-01,1.076401651684563421e-01,-1.298810633698214367e+00,1.221107915355371665e-01,2.726840583892822800e-01,1.445993161010468431e-03,-1.968125426327854144e-01,-2.781251465871359407e-01,1.331973805238755859e-01,-1.338553699277940934e-01,1.552493371473668837e-01,1.242063675351776919e-01,-1.747457590161428653e+00,-1.886239978448203347e+00,1.475060927519487863e-01,1.903783085105796857e-01,-2.908784561962698412e-01,-1.347455131304474696e+00,-2.255556498385771214e-01,-1.000000000000000000e+00 +-2.351463841078031136e-01,-1.356221143936683804e+00,-1.548648917862450070e+00,-1.615190690304739787e-02,-2.329305829899785962e-01,-1.563544415731793702e+00,9.712065439353889662e-02,-1.691415670496308588e+00,-1.388081797844022081e+00,-1.632355704138472863e-01,2.233080087874433906e-01,-1.875345470231006839e+00,-1.420174321688624786e-02,-1.478479012048540042e+00,-1.703585643539405226e+00,9.877765948046887678e-02,1.713826636563931527e-01,-1.684931314778971290e+00,-1.740550633523509361e-01,9.656289639812394576e-02,1.000000000000000000e+00 +-1.807051206113242658e+00,-1.702030573975785166e-01,1.457697719140259862e-01,-1.401432021185573307e+00,1.345289163726789416e-01,-1.356331314153615741e+00,-1.633447265801356130e+00,1.312671853011658854e-02,-2.688291691020219809e-02,-1.553230978892023328e+00,1.363204035600718012e-02,2.054077132999896360e-01,1.865197473453200638e-01,-8.720626685161123048e-02,-1.437604573095554172e-01,-1.211220257470444622e-01,1.462310180151815686e-01,-1.580157720731842019e+00,-1.349661545793155115e+00,-7.460394207961704161e-02,-1.000000000000000000e+00 +-1.931195335870454999e+00,-1.835012547290038487e+00,-1.563035808850954167e+00,-1.365951090765915588e+00,-2.419776476979298985e-01,-1.507160458977945705e+00,-8.698048833069951802e-02,-1.647913694272572949e+00,-1.427110558973703647e+00,-6.158962399442696745e-02,2.602267054057313400e-01,-1.645387388779374405e+00,1.823394304296815649e-01,-1.178329356963998098e+00,-1.590525171370741120e+00,8.104795490439978267e-02,8.563665889620825089e-02,-1.774907163470250460e+00,-2.473909510286317892e-01,1.307471694166303378e-01,1.000000000000000000e+00 +-1.861598019139751425e+00,-6.684922331319176347e-03,1.222846918474984612e-01,-1.346678003138850332e+00,4.520385029215109873e-02,-1.267055205377054738e+00,-1.427484046466195089e+00,-1.127116818049106323e-01,-3.035474687264514554e-02,-1.582916251647630279e+00,3.008096301293413516e-02,1.793840427488923583e-01,3.840534720938503233e-01,-1.621601357089720974e+00,-1.602307822388481906e+00,1.143111850867933044e-01,1.964837344560850796e-01,-6.220973587830265494e-02,-1.465073997817349927e+00,-7.554554908694857684e-02,-1.000000000000000000e+00 +-1.678736409827504550e-01,-1.397225422498413305e+00,-1.365152865990226561e+00,-1.024009527884461868e-01,-1.690446713422619207e-01,-1.516140030873107447e+00,9.681822913903759265e-02,-1.476257944029218949e+00,-1.411731881032226577e+00,-1.703173430826560031e-01,7.916337182364427405e-02,-1.768682251963919372e+00,7.807644879584546560e-03,2.850324494224795169e-01,-2.784570630307360573e-02,-1.552327257047553477e+00,-1.278326320403236460e+00,1.918599970261308241e-01,-1.350110322304288180e-01,-1.295089347496607868e+00,1.000000000000000000e+00 +-1.698609511154017593e+00,-1.719161528378636561e-01,1.470416765064267994e-01,2.653323661514429554e-01,-1.364127946851214856e+00,-2.333175690266216240e-01,-7.861458406690191580e-02,-1.426970427975007283e+00,1.117479189963837688e-01,9.526638619662716745e-02,2.269126506552598399e-01,2.995679068958879143e-01,1.814027885997889222e-01,1.185629160547931049e-02,-1.884169765307397992e-01,-1.145857514130308175e-01,3.129948381144793679e-01,-1.723617348532140880e+00,-1.384166647916821979e+00,-1.045716089535642102e-02,-1.000000000000000000e+00 +-1.658897420735486294e+00,-1.620726660547902265e+00,-1.660113144619751901e+00,-1.460868895984654303e+00,-2.255437947025988321e-01,-1.744884626955741647e+00,-1.362731967233296315e-01,-1.457680464175254986e+00,-1.303540666045219121e+00,-1.718893334632473346e-01,2.164523847625174358e-01,-1.884134174199312284e+00,1.430050120792410451e-01,1.974210530718532863e-01,-1.147592315053487799e-01,-1.699573877831422886e+00,-1.438276927437653452e+00,2.286490534459723056e-01,-3.407135222878493463e-01,-1.631851369298568777e+00,1.000000000000000000e+00 +-1.750902203433937432e+00,-1.890080437519559564e-01,1.785743931325385614e-01,2.414132558003883045e-01,-1.517025238295976575e+00,-3.302434795072037299e-02,7.619542695607210703e-03,-1.543139691965131455e+00,-2.701194395846782115e-01,1.521473262932916681e-01,-3.177843420423827336e-03,2.443282642050864373e-01,7.253439665310934537e-02,-1.493056011791469295e+00,-1.759651362925155338e+00,1.136859516285110977e-01,2.436539507012564498e-01,-1.175244628411053344e-01,-1.509998795837532715e+00,-9.186531512934625887e-02,-1.000000000000000000e+00 +-1.417466391479324517e-01,-1.346903524100661720e+00,-1.530929088287875217e+00,-8.903360448265199012e-02,-2.719380145026851214e-01,-1.783814970375273479e+00,7.056489037692566940e-03,-1.350262034702866876e+00,-1.405801340336546135e+00,-1.825652336646692853e-01,2.381831403450938855e-01,-1.639463560709983003e+00,1.586699744579586624e-02,-1.426067408359480426e+00,-1.774135337473782048e+00,-1.263571548561680746e+00,-1.423664858305446090e+00,-1.341604123953090477e+00,-1.745310855853467458e-01,-1.260971454265339631e+00,1.000000000000000000e+00 +-1.919642635345555792e+00,-1.214332625549610356e-01,2.029412211540288224e-01,1.199843114974788738e-01,-1.335700473640089481e+00,-1.815504579463949719e+00,-1.513510260756883996e+00,-1.745957621058216835e+00,-1.207211194117849551e-01,-1.306597022005618225e+00,-1.397984640592999372e-01,2.918508859246253251e-01,1.222937644932370754e-01,-4.161281462589887237e-02,-2.428571363426271223e-01,-2.448264522598121717e-02,7.152417885188167013e-03,-1.717509711129774574e+00,-1.641718810659424932e+00,-6.696670886160294156e-02,-1.000000000000000000e+00 +-1.961262659443256950e+00,-1.867099466739700286e+00,-1.544511259144617954e+00,-1.555113136470289925e+00,-3.633517516101433253e-01,-1.575628606715051339e+00,-1.169198757416956003e-01,-1.541564375167977952e+00,-1.384660207425590706e+00,-2.087540513422468103e-01,2.039514156118009125e-01,-1.587861423462168409e+00,2.521155683909973577e-01,-1.372270524029966632e+00,-1.550817814164088260e+00,-1.426026796856582290e+00,-1.365768584159129384e+00,-1.311865436581222788e+00,-1.850893218702704124e-01,-1.637070564823309038e+00,1.000000000000000000e+00 +-1.747474111846821110e+00,-2.290166316437901584e-01,2.180988537522543513e-01,2.694592974029106358e-01,-1.211201451859689193e+00,-1.767984885951759289e+00,-1.502625876119413562e+00,-1.438947562452083551e+00,8.065893288718263554e-02,-1.528688378021011207e+00,1.087299778884808776e-01,3.198929781779950021e-01,1.039774594364287486e-01,-1.798111513407557371e+00,-1.545989842407716974e+00,9.064067535532020770e-02,2.432173424281315610e-01,-2.081961545829282001e-01,-1.659416484977199513e+00,-9.793454098837411914e-02,-1.000000000000000000e+00 +-1.541891116190167443e-01,1.198641927962546261e-01,-9.696410479856587550e-03,-1.477477922413563460e+00,-1.597051621274383271e+00,2.406839794158164292e-01,1.407643727635641273e-01,-1.553446610635859315e+00,2.989015888209632932e-01,3.165877637380919429e-01,1.169824338328576685e-01,-2.008026315114123383e-01,-6.220300738583128819e-02,2.915046298468972896e-01,-1.616502083500595544e-01,-1.022217754874521073e-01,9.035042558882541708e-02,-1.332018434095425530e-01,-3.146826509327730337e-01,6.291195189475287664e-02,1.000000000000000000e+00 +-1.901571954471936166e-01,2.036693401068984388e-01,-8.558809588610771790e-04,1.799349081329099098e-01,9.517185955184281898e-02,3.590268720909888245e-01,1.130070230940566217e-01,-1.649686655919097245e+00,-1.567966253893172901e+00,5.390840436566476934e-02,1.478514498943867550e-01,-1.164278779706381162e+00,2.523227821439278373e-01,-1.771175375557790255e-01,-1.835251468954054443e-01,-7.142510660604928230e-02,1.156365524626673247e-01,1.752520883288299913e-02,1.580674384399360921e-01,1.595989854848350875e-01,-1.000000000000000000e+00 +-1.977287914640140531e+00,-1.086192057027827729e-01,1.128753655542010237e-01,7.440106077279728947e-02,-1.829164392143819207e+00,1.001733311909408508e-01,1.656542203717096262e-01,-1.530841270169906565e+00,1.540936542463245962e-01,6.463618023479822394e-02,4.251644257692598350e-01,-1.355725998615462391e-01,1.214504897439154657e-01,4.189501652900345929e-01,-1.659671719591041539e-01,-2.173559202300396342e-01,2.715598643706897408e-01,4.647816024550471825e-02,-2.818481592934465874e-01,-3.720732940340260031e-02,1.000000000000000000e+00 +-1.533720518381189724e-01,3.892743879497452819e-01,4.180672620449811527e-02,1.013359898957980343e-01,7.252934689412099400e-02,2.300045152220870637e-01,-2.934514337618384511e-02,-1.349080214839665226e+00,-1.695051566604298010e+00,-2.374758368466359515e-01,3.977900521952173335e-02,-1.247430214337799992e+00,6.920324582981349226e-02,-1.651284782420658681e+00,-1.725574998237646929e+00,1.484493211461310747e-01,1.961649923164716092e-01,-1.743295333073816789e+00,-7.901003630137488909e-02,-6.681325434829828158e-02,-1.000000000000000000e+00 +-2.096774839232835308e-01,3.133813251872709094e-01,4.719834877226380954e-03,-1.486312632979153836e+00,-1.556459141653098399e+00,4.912658260630371754e-02,7.744509731644938499e-02,-1.568503548805284309e+00,2.819467580128540152e-01,2.525988221138925072e-01,1.034772777057548221e-01,-7.926864226769231303e-02,2.438139918066015988e-01,-1.101530864062478798e+00,-1.641623168577236003e+00,-1.198354932584080879e-01,1.378072651587314845e-01,-1.785790733459343693e+00,-3.457637697154141154e-01,1.918905251071924556e-01,1.000000000000000000e+00 +-2.750489793675787720e-01,6.148307212179922321e-02,1.736431353070554684e-01,1.579328863818725837e-01,2.462079915353442006e-01,-1.411825664811311221e+00,-1.534168967146540163e+00,-1.551232148260871746e+00,-1.535742722413438566e+00,-1.770318007937173865e+00,1.751673676029186222e-02,-1.190055568191122015e+00,2.059950403699849497e-01,-1.012485079418455336e-01,-2.060936796710344243e-01,3.384030592975885432e-02,2.505534977666529217e-01,2.009368311663959072e-01,2.824259611451782614e-01,8.170939704749366483e-02,-1.000000000000000000e+00 +-1.917642069761332779e+00,-2.926585656134242375e-01,1.528819448980941464e-01,1.815071978415521148e-01,-1.882235658081898855e+00,1.575446717586391765e-01,-1.408146613731111989e-02,-1.571190358338920401e+00,1.657830368464734416e-01,1.461993811029305701e-01,2.901607518443575229e-01,-2.063763375229137609e-01,9.105827938715693737e-02,-1.402960325339824843e+00,-1.809441313322212919e+00,5.135189814511180451e-02,2.236863619589410657e-01,-1.729600227087127795e+00,-1.870865326701982301e-01,2.039074972169292455e-01,1.000000000000000000e+00 +-4.366449421615858917e-01,-1.003944793806665903e-02,1.918228198993621181e-03,2.955270038624893125e-01,1.648973129255825454e-01,-1.169406948177582839e+00,-1.631843085827449524e+00,-1.802517558474467707e+00,-1.706914345186495652e+00,-1.690513861068017265e+00,7.390782350365482367e-02,-1.409653456133216576e+00,3.350455036101138151e-01,-1.564931056937633347e+00,-1.688642083380686554e+00,7.406743163755136194e-02,1.813613607643461134e-01,-1.772481085071356821e+00,3.485842607448004504e-02,1.003860651007464078e-01,-1.000000000000000000e+00 +-1.798900272653050914e-01,2.389637741664693005e-01,1.663524608467675803e-02,-1.353765703336621495e+00,-1.959743191110952631e+00,2.264950660320298459e-01,1.806454108733758779e-01,-1.559512852094719459e+00,3.648320019934763181e-01,2.358935068061861207e-01,3.078967316911840890e-01,-3.361268876809028749e-01,7.867525194677538192e-03,3.361952823430025550e-01,1.096649834255997713e-01,-1.651150588517939832e+00,-1.265011938517930368e+00,1.393637110926818079e-02,-2.419700909072255435e-01,-1.331374404824197111e+00,1.000000000000000000e+00 +-3.309211948171192352e-01,2.421839490070105683e-01,6.136022709953796450e-02,-1.201853652373070069e+00,-1.436604493724723097e+00,-1.198067495246635250e-01,1.474701755038044515e-01,1.895277933144060289e-01,-1.638327916991755062e+00,-1.563818191245952005e-01,-1.077378310694679930e-01,-1.403821945296813034e+00,1.368941449902785490e-01,-3.477763887439616619e-03,7.226365175294849341e-02,-6.143754737558303897e-02,2.795623814128017637e-01,-1.289968880168595733e-01,-3.133117511980514081e-02,-1.017084218553041403e-01,-1.000000000000000000e+00 +-1.686938541095525990e+00,-5.872095876893711208e-02,2.443208985248077059e-01,9.212828068375636392e-02,-1.739992588081891389e+00,-8.489541302770341191e-02,-4.399330434624062341e-02,-1.530747659062598354e+00,2.738022830851473688e-01,9.055475238662805126e-02,6.543081035374409549e-02,2.659068042593432102e-02,2.048421980933542441e-01,3.147989394625860804e-01,-1.865517304471441262e-01,-1.775850268471311999e+00,-1.600212304956398990e+00,4.336575018149410599e-02,-9.172339110144461793e-02,-1.218500129682053457e+00,1.000000000000000000e+00 +-2.147425078722564606e-01,1.363834862484123445e-01,6.828257638775982186e-02,-1.481775071019213907e+00,-1.351991532810939312e+00,-3.283521414091315482e-01,-3.722540922456851059e-02,3.703523150812216419e-02,-1.661559154803918359e+00,-7.850714527956696720e-02,7.419368308494322939e-03,-1.244710516344766704e+00,2.207972406114958874e-01,-1.606888032720614534e+00,-1.770546800386025632e+00,1.274118906779919680e-03,1.911757525444973127e-01,-1.672845872229138164e+00,1.400165475014619743e-01,-1.208990314961668616e-01,-1.000000000000000000e+00 +-2.772045361255325302e-01,1.711467383161677236e-01,2.451393212895625756e-02,-1.587166372966360095e+00,-1.577276868896203776e+00,1.972544845751455778e-01,-1.032205822818711427e-01,-1.672327308636493370e+00,2.545592984995520558e-01,1.816840435404763476e-01,5.871646934329571854e-02,-2.502838370781583222e-01,2.337932822664689214e-01,-1.254707426736235476e+00,-1.578635681096223875e+00,-1.264382673090489906e+00,-1.367165619666614651e+00,-1.566260933111969456e+00,-1.542597882273265830e-01,-1.271568528877850435e+00,1.000000000000000000e+00 +-1.192345410485343982e-01,2.876314065052951729e-01,1.673852107512977250e-01,-1.390331194293130679e+00,-1.455016824134779840e+00,-1.768413949953279607e+00,-1.450095613466445554e+00,1.650535627965662722e-01,-1.561735774153465428e+00,-1.470408862817280893e+00,2.583023118478519967e-02,-1.241302664921004606e+00,1.513177057589079055e-01,-3.912346900213074796e-02,1.172815152082321011e-02,-1.029777163955075681e-02,2.244996134321211312e-01,-1.300648754226581272e-02,7.335149050516499847e-02,5.772273621679507860e-02,-1.000000000000000000e+00 +-1.813573345588654018e+00,-2.220478181890921665e-01,5.467008543926200892e-02,3.689254172690206723e-02,-1.665402596540724867e+00,4.922176964755629186e-02,-1.284888778791378472e-01,-1.414102000818379556e+00,1.580772852890507674e-01,1.775797928577861062e-01,1.632271718891248613e-01,-1.012449705210102247e-01,1.195179981520401447e-01,-1.362363523651744623e+00,-1.796199810051825718e+00,-1.432072896554568597e+00,-1.283539847349572938e+00,-1.532459423013815769e+00,-3.101370512901745480e-01,-1.425056629662605934e+00,1.000000000000000000e+00 +-2.321712416461661488e-01,1.197055710457237271e-01,2.331169061402619258e-01,-1.359777347085656896e+00,-1.205652424485054386e+00,-1.656023067673558780e+00,-1.663543935883512015e+00,-4.702894733069958033e-02,-1.640070771258239635e+00,-1.355343200009919924e+00,-8.415678213258656615e-03,-1.486702329052909999e+00,2.426856412005025387e-01,-1.659145659484962065e+00,-1.741035399782207449e+00,2.753416158423056537e-01,4.119729903099007551e-01,-1.851604568082790614e+00,9.514474688383869894e-02,3.355685959805613239e-01,-1.000000000000000000e+00 +-1.500485907047857115e-01,-1.221950377757007544e+00,-1.442912336766244419e+00,-1.681670222929709801e+00,-1.788854353140276299e+00,-1.454613944941973269e+00,2.198577103874525124e-02,-1.560819643228220199e+00,3.205792112120700033e-01,1.354250606290548220e-01,2.327374602498822609e-01,-4.973251672414213953e-02,1.334910473910854434e-01,2.103981666313979315e-01,-1.271344326814805581e-01,-4.364511088781411474e-02,2.178793215798139060e-01,-2.499218596906974676e-01,-2.186440282503921972e-01,1.744750978160619914e-01,1.000000000000000000e+00 +-2.192300878144892440e-01,2.129718927342666435e-01,3.987026058767699499e-02,2.000062748130256585e-01,2.628984287197382863e-01,2.270013246051293954e-01,-8.614670694217999025e-02,-1.543148031218893435e+00,-1.776428408697541705e+00,-3.957910129421529644e-02,-1.445144355756989851e-01,-1.229806842034160796e+00,1.711086066476480427e-01,-5.751169330957496550e-02,-1.759722175187910798e-01,-8.966529641510720405e-03,2.386008593071037298e-02,-1.414976449392172819e+00,-1.633366652713292622e+00,-1.020480016423235825e-01,-1.000000000000000000e+00 +-1.797731426999819693e+00,-1.752605155053432062e+00,-1.548023547333538863e+00,-1.775979434727979062e-02,-1.752352156931570981e+00,-1.478995192351079568e+00,-3.220116500219568495e-02,-1.565956927518345143e+00,1.036726623198800656e-01,2.016167245776253059e-01,2.007474174373383158e-01,-2.181649250102777327e-01,1.803902425467817372e-01,2.805682531273903102e-01,-3.578751536483423118e-02,-3.153716615350411479e-01,3.526011840434244426e-01,-1.555955680862944890e-01,-1.788745791555167719e-01,2.078745575009158519e-01,1.000000000000000000e+00 +-1.490027622720656031e-01,2.289246899079383502e-01,4.028490913735319201e-01,3.967488799718776882e-02,3.347084845734267233e-01,2.145937412022381197e-01,2.485608235905084618e-01,-1.579188017895288620e+00,-1.762261402419999401e+00,-8.870698298634535628e-02,-4.629015332510245245e-02,-1.174944430751200697e+00,2.415701140845060158e-01,-1.916344927146877319e+00,-1.677645663723282832e+00,1.576777221398396600e-01,1.798218039140481450e-01,-1.175696772797820699e-01,-1.526183409926597845e+00,-1.406353750619999954e-02,-1.000000000000000000e+00 +-2.804450381031173278e-01,-1.388453345596431854e+00,-1.416961652978935504e+00,-1.605161445656936747e+00,-1.755528150368922180e+00,-1.524795721013195937e+00,-1.240352383476334697e-01,-1.636548475370386946e+00,2.790995590094988987e-01,4.497646242908202863e-02,1.997987723294708806e-01,-3.019569124547694106e-01,8.327915621047257688e-02,-1.287148835552201165e+00,-1.625334029447224937e+00,1.748064638310643515e-01,2.640000800107265011e-01,-1.725001420021360810e+00,-2.696708474908042219e-01,1.930095222890825801e-01,1.000000000000000000e+00 +-1.926953285475565270e-01,2.189375352722924828e-01,4.539331996887961684e-02,3.008432471831398169e-01,2.565262844758790517e-01,-1.293581987033126879e+00,-1.548045675190745873e+00,-1.451162655981990124e+00,-1.525072498757130024e+00,-1.758901982256172669e+00,-3.355741212560588854e-02,-1.307502230564176804e+00,2.578444830799039855e-01,-8.432854820882182412e-02,-1.144569608876211675e-01,9.693908609789249342e-02,3.263164181899512184e-01,-1.789185589176669255e+00,-1.470419019157990004e+00,-4.516418069697201998e-02,-1.000000000000000000e+00 +-1.681524041973221362e+00,-1.757453944303776217e+00,-1.708761911120806110e+00,-1.549405097104009088e-01,-1.902663562777817674e+00,-1.548997546776542000e+00,7.351945697999465179e-02,-1.605305977413101326e+00,1.995461886745794333e-01,-4.181048681232629738e-02,3.660708897173430287e-01,-6.655161461117785338e-02,2.064950518813776226e-01,-1.398963379454520872e+00,-1.715046108526779189e+00,2.116278766484822604e-01,8.234758695042038168e-02,-1.915033149590992467e+00,-1.661268372122445269e-01,1.262595222157170127e-01,1.000000000000000000e+00 +-1.962283649628915050e-01,1.122058887764890850e-01,1.381278041680962521e-01,8.728184706616783084e-02,1.127894522815892503e-01,-1.609889772273879860e+00,-1.525538454944675548e+00,-1.598660103027582879e+00,-1.769575663994145120e+00,-1.600323143094793954e+00,2.014436967072089357e-01,-1.416089356524305787e+00,2.288057989463968267e-01,-1.556363659198443550e+00,-1.692201985594345981e+00,1.091619765004213927e-01,1.858051152871127432e-01,-3.681708277874619351e-02,-1.506373588134407582e+00,8.912630388338534104e-02,-1.000000000000000000e+00 +-5.188785821684105404e-02,-1.374675205996179317e+00,-1.369049881942256075e+00,-1.535576187239738433e+00,-1.788853910025122618e+00,-1.607163819785809356e+00,-1.372988750116458656e-01,-1.451845451849335111e+00,1.683149804906874658e-01,5.818964701482091939e-02,2.432364208586114407e-01,-1.366346592229962797e-01,2.882246812514612233e-01,2.323272716510418334e-01,-1.156633142470376435e-01,-1.722224223048894443e+00,-1.356919905977978402e+00,2.339505846669169831e-01,-2.983554805679451971e-01,-1.444471577260626693e+00,1.000000000000000000e+00 +-1.723282596120939703e-02,9.922894674221888200e-03,2.114255707134858886e-01,-1.256611435471116200e+00,-1.338172796419482058e+00,-1.393884748518721406e-01,-1.422037572426880780e-01,7.951209810074390261e-02,-1.701304200230044028e+00,-1.278531280046300567e-01,1.665352764802276175e-02,-1.531460413957198563e+00,1.201004855627265944e-01,9.143562050819743769e-02,-8.495073340818597163e-02,-8.640815974854335058e-02,3.051292050533321865e-01,-1.782691346137809996e+00,-1.541673503964391179e+00,4.359225158923388244e-02,-1.000000000000000000e+00 +-1.759731435909149821e+00,-1.722540069232149307e+00,-1.350575599538822669e+00,1.530087231441150664e-01,-1.919227638739745778e+00,-1.522311312217412116e+00,2.663977705038157034e-02,-1.462895398911590927e+00,2.346870079004382825e-01,5.298448739890479486e-02,3.208235596105920018e-01,-3.225522417942412456e-01,2.151378677463345324e-01,2.244448666307395646e-01,-1.351102521335871276e-01,-1.674098601469833580e+00,-1.321534060738071670e+00,2.232323694424712124e-01,-2.410302168203579953e-01,-1.419962320981015935e+00,1.000000000000000000e+00 +-2.633866754547229960e-01,1.358270758478960505e-02,2.574503224364339538e-01,-1.443596796605898014e+00,-1.397195964349720265e+00,-2.066669973860401355e-01,-1.758563845619880994e-01,5.331874892815188782e-02,-1.852381209734429879e+00,-1.805032153887715096e-01,1.195129378885405880e-01,-1.364271966209453790e+00,2.664623692271294031e-01,-1.600743433266965665e+00,-1.788286601561143163e+00,3.068265358594896020e-01,2.559739465456911667e-01,-1.971470390451849075e-01,-1.650667372783046982e+00,8.264199620782243516e-02,-1.000000000000000000e+00 +-2.481697005315772475e-01,-1.381967198312306300e+00,-1.512006400315489163e+00,-1.617024483795716172e+00,-1.836827134394403815e+00,-1.545026569114187343e+00,-1.582826646629847955e-02,-1.507168721185889737e+00,1.310109632937027091e-01,1.921808605290821204e-01,1.145040726005610437e-01,-1.219314979512694519e-01,2.975686261079176775e-01,-1.410124262462820521e+00,-1.564041871941007189e+00,-1.616683010822192346e+00,-1.563536729174665130e+00,-1.367834179879528822e+00,-1.919167707969222370e-01,-1.321645793082277809e+00,1.000000000000000000e+00 +-2.250694852470974394e-01,2.586746931010264205e-01,1.270736451133486211e-01,-1.453749431420937599e+00,-1.331400637263621345e+00,-1.916989105138284133e+00,-1.470519389589342474e+00,-2.575670503203331130e-02,-1.546322707545896735e+00,-1.678294797276697059e+00,1.804925820963580729e-01,-1.338470925613644669e+00,9.358071893679681397e-02,-2.050571700633527783e-01,-2.592183686402053100e-01,-2.760682876197858016e-01,2.474594164336252622e-01,-1.622977875468254227e+00,-1.600052899883098068e+00,-2.545593658316831764e-02,-1.000000000000000000e+00 +-2.008138185897252459e+00,-1.769840576784453168e+00,-1.594166575905737071e+00,1.744045374285137229e-02,-1.788144034741654620e+00,-1.604207237451122481e+00,7.450619461755142448e-02,-1.570630280276348323e+00,2.986456564471063890e-01,1.557489853546121794e-01,1.807124548561135913e-01,-3.018706573418425410e-01,1.530498899233971588e-02,-1.296057054467206537e+00,-1.703412346621872908e+00,-1.300735058435303459e+00,-1.236030663661329188e+00,-1.545097966404944456e+00,-2.497731242540399133e-01,-1.288660072959546321e+00,1.000000000000000000e+00 +-2.017963239069477388e-01,2.254262838857801210e-01,2.351494609794932966e-01,-1.293976900602812563e+00,-1.611052001077982299e+00,-1.690434313162011648e+00,-1.662958075946455505e+00,4.345128772439012244e-02,-1.672573879733247004e+00,-1.547695599399579569e+00,3.607367517426384590e-02,-1.305287577992165327e+00,2.629757329469552540e-01,-1.620581609283257007e+00,-1.849230605960164198e+00,4.143028843267702876e-02,1.501687731134105919e-01,-2.245417608825056588e-01,-1.591117214434435212e+00,-2.607642312533986484e-02,-1.000000000000000000e+00 +-4.239786628194559537e-01,1.954984361132021675e-01,-1.001112260934661768e-01,-1.521021702018586108e+00,-1.847664734870663183e+00,3.476951476860069834e-02,3.908534827535986578e-02,-3.441650628764165304e-02,-1.461542283580969714e+00,-4.302677154180577224e-02,1.998236051372857813e-01,-1.747447584324808734e+00,2.825811816454654180e-01,2.453530971542590700e-01,-1.326706774301590896e-01,-6.007190044516801108e-02,2.910221889601137812e-01,-2.185449015932421912e-01,4.480722461949049329e-02,8.659013180298130719e-02,1.000000000000000000e+00 +-1.801324665127946645e+00,-1.719544891428557709e-01,1.088482489085384614e-01,-1.299025138906894750e+00,2.763992571685174870e-01,2.530370326242789569e-01,1.448570234705879867e-01,-1.611682407748750157e+00,-1.743856501784443847e+00,-1.956733507539220041e-01,-1.178153849953956361e-02,-1.489959017649406636e+00,1.875208794936734369e-01,5.437841947870207526e-02,-1.402454379129729478e-01,-1.203440513115870830e-01,2.307435421811548926e-01,-2.925237475086465144e-01,1.891221967490180206e-02,1.194696285086674498e-01,-1.000000000000000000e+00 +-1.846673338107843509e+00,-3.023959190237470729e-01,2.286684893430069587e-01,5.411855215442593070e-02,-1.960626821316767021e+00,3.792700526303909303e-01,-8.648872687750591703e-02,2.606519216137613559e-02,-1.380530914991796365e+00,-1.484316578276552445e-01,7.369935626330553768e-02,-1.661572947509190268e+00,9.848980253666997364e-02,2.517583000120153236e-01,-9.123950666640436025e-02,-1.318329915585552681e-01,2.108636616192697266e-01,-2.370676094466734884e-01,-4.348157446128733117e-01,2.992036293923540646e-01,1.000000000000000000e+00 +-1.883699443687262276e+00,-1.882364363868459511e-01,2.543184252246958876e-01,-1.382661717471148188e+00,2.087119397685269773e-01,1.230869317918208139e-01,-1.017833311620702230e-01,-1.621497538584965925e+00,-1.533846599591235815e+00,-9.491133323523429088e-02,3.604890941637624302e-02,-1.593771289307023498e+00,2.864784080541166178e-01,-1.413364263952484379e+00,-1.781620287627969201e+00,1.188810739693684504e-01,3.699356811418870183e-01,-1.680023429240815380e+00,1.637000803747315214e-01,1.490366681935645088e-01,-1.000000000000000000e+00 +-2.710907429413343950e-01,2.994346243336623026e-01,-2.260029579715211456e-02,-1.492730556851320056e+00,-1.746468660413026175e+00,-1.010087151397107141e-02,-5.373222621421234330e-02,1.648961498104867252e-01,-1.496528298955463798e+00,-2.121406732334990553e-03,2.401872943645969871e-01,-1.619673633875661656e+00,2.162543120494445081e-01,-1.387091666676599333e+00,-1.638646216342895423e+00,3.141293775056863691e-02,2.319621855529500343e-01,-1.730590469786037477e+00,-1.413321507921310727e-01,4.549543055515198331e-01,1.000000000000000000e+00 +-1.778511147832783301e+00,-1.013988032028912412e-01,2.823156451699946334e-01,-1.485894784482977737e+00,1.917891809315158680e-01,-1.207316588191012396e+00,-1.580868066414439221e+00,-1.395163889088425702e+00,-1.649384640267517854e+00,-1.687806656840818098e+00,-1.896454383487397866e-01,-1.222417558878858390e+00,2.100555357210017182e-01,9.232630392069701708e-03,-2.889446551393878293e-01,-1.061335260786943485e-01,3.086988443948957550e-01,-5.761031263168545774e-02,-8.365443100916615526e-02,-2.975471396689306769e-02,-1.000000000000000000e+00 +-1.902045354412928546e+00,-2.251341199966401529e-01,-2.937591749939713615e-02,5.633827003579487680e-02,-1.667111122422912040e+00,6.912288299899774224e-03,4.347368841819030522e-02,-2.944643422612748807e-01,-1.394011698904371599e+00,-1.911564700887848911e-01,2.281464495289923788e-01,-1.561766753820800879e+00,1.966269197021561221e-01,-1.180009473192372926e+00,-1.712197917581733231e+00,2.464873639021550322e-01,1.919760488022198286e-01,-1.663469084195813963e+00,-1.645175843233691593e-01,1.209532845560358227e-01,1.000000000000000000e+00 +-1.833114143413420383e+00,-6.171028862943107873e-02,5.754822885933238985e-02,-1.560724420388548861e+00,3.632564576682422164e-01,-1.481945268653765746e+00,-1.279587882018693001e+00,-1.656645719030719555e+00,-1.736443148111497914e+00,-1.729863960812176193e+00,-6.536101742447317231e-02,-1.330954960290977374e+00,2.008390928839592915e-01,-1.718026687430719956e+00,-1.745088642128966860e+00,8.660695288671575731e-02,8.378037007097807742e-02,-1.719867788414559451e+00,7.806008967015282130e-02,2.804763975134820508e-02,-1.000000000000000000e+00 +-2.768067646498343914e-01,2.125317705033215876e-01,1.271250686981756362e-01,-1.582895884622139926e+00,-1.906587196338887846e+00,2.675521541774043138e-02,2.390045238483756718e-02,5.588645404529107852e-03,-1.503449110022147872e+00,-2.881893140624296468e-01,3.390313987583383759e-01,-1.628713365152743453e+00,-1.375870843740442662e-01,2.395937847036929413e-01,-2.358512458605327711e-01,-1.754802431243140504e+00,-1.361949088710851852e+00,1.429327083128897058e-01,-4.043767937558144787e-01,-1.490996219627224839e+00,1.000000000000000000e+00 +-1.761864421231023936e+00,-2.778956981999408660e-01,2.267530183011868539e-01,1.389414089794701324e-01,-1.488586038940487688e+00,-1.930455733800497398e-01,7.982816361017525120e-02,9.220722791774814409e-02,-1.646106816827008990e+00,-2.116877029822330503e-01,-6.571921097744903850e-02,-1.295791224367208283e+00,3.388574340213011959e-01,-4.880479281179687934e-02,-5.330984855600520711e-02,-1.394783381758671892e-01,2.125230646922146027e-01,-8.815444589911280993e-02,5.209478083777161622e-02,2.513044830152091902e-01,-1.000000000000000000e+00 +-1.873191616492320577e+00,-2.056873989135975522e-01,1.788792433776593138e-01,6.440261287751487818e-02,-1.639983345842225937e+00,7.431984483779473238e-02,-6.737981502640266740e-03,-9.228866042587285934e-02,-1.423709368258853081e+00,-3.519541367219973793e-01,1.929667786579778721e-01,-1.741194061661679227e+00,-8.842165688775438515e-03,3.301823485436581884e-01,-1.892267665999154413e-01,-1.723186355638004041e+00,-1.297342676980458576e+00,2.243686521156938174e-01,-1.225553682741842010e-01,-1.322983351201722391e+00,1.000000000000000000e+00 +-1.845933991953369935e+00,-1.018255445810124504e-01,1.283198692785773587e-01,2.944784400217750897e-01,-1.276526946838344667e+00,-2.567000966561716058e-01,1.870128664907933641e-02,9.175820873300864766e-02,-1.624991653992264418e+00,-7.030674276811187118e-02,2.318219308332492359e-02,-1.103949973968188836e+00,1.916707891925400475e-01,-1.833345479214313567e+00,-1.548042311660148052e+00,1.195313668833518361e-01,1.679486183195668114e-01,-1.705850794960609873e+00,2.550563100626263013e-02,-1.997707596345787984e-02,-1.000000000000000000e+00 +-2.446585241405007105e-01,2.309835849814308173e-01,1.443057578971227439e-01,-1.515667559477737569e+00,-1.619770971134667059e+00,1.187375512054550664e-01,6.649109859940552369e-02,7.432639227162841922e-02,-1.380992404316611077e+00,-6.001711401765588705e-02,2.792694154722341615e-01,-1.763976851410502311e+00,6.032378343654248853e-03,-1.387988888669036358e+00,-1.723065599013226423e+00,-1.243252983977291226e+00,-1.257042860606533363e+00,-1.321430117125417691e+00,-3.429405920406701780e-01,-1.226405993804544181e+00,1.000000000000000000e+00 +-1.865169228647927779e+00,-1.237879373561084628e-01,7.405335204670369931e-02,6.890642354928330771e-02,-1.498597461518269114e+00,-1.590548669947394078e+00,-1.461212144711625749e+00,3.490170736471687185e-02,-1.573357413030800789e+00,-1.537814114421869771e+00,2.186615813812642084e-01,-1.292038367648431718e+00,8.948357088198743980e-02,-8.928658184420408539e-02,-8.429750889904033540e-02,-2.334090783460711638e-01,3.705548010630302924e-01,-2.252870064453939836e-01,-2.091505472947417787e-02,-1.204439228438229648e-02,-1.000000000000000000e+00 +-1.707915085551401813e+00,-2.014512879346425089e-01,-4.914441854039292013e-02,-9.839895374084701996e-02,-1.726056384131820831e+00,9.440942940517532689e-03,1.001794835526170491e-01,5.070658569607315591e-02,-1.242348225775620874e+00,-1.159321605234600316e-01,3.127579004505120497e-01,-1.840778784331079887e+00,-1.658154698399136318e-02,-1.316138131065869743e+00,-1.894058600534813674e+00,-1.433057168579106255e+00,-1.278327361537103357e+00,-1.507053632706953916e+00,4.855031501400158822e-02,-1.410377662069259408e+00,1.000000000000000000e+00 +-1.927330015855815537e+00,-3.023244728403503556e-01,-1.004440616059082414e-02,2.475147107586016804e-01,-1.521246841214893841e+00,-1.832048881318087741e+00,-1.634658137038252690e+00,-1.202191394984148259e-01,-1.720221659787346091e+00,-1.575718727875419445e+00,4.137234899508839686e-02,-1.114256397670778220e+00,3.386845536114191368e-01,-1.552188726862925972e+00,-1.737857642087943022e+00,1.996806574321181027e-01,6.313713665147491216e-02,-1.632778985039999409e+00,-9.376998928433753766e-02,3.399858858802459749e-02,-1.000000000000000000e+00 +-1.599149871765311592e-01,-1.394954387635932491e+00,-1.431275230204140270e+00,-1.658557878945149389e+00,-1.665066556585239388e+00,-1.382801562304887710e+00,-1.903937519451994742e-01,-3.984804340760764868e-03,-1.498487313441081659e+00,-1.414217101692847312e-01,1.861058267637868135e-01,-1.770039703316339974e+00,2.855961150446482688e-01,2.631672011561230207e-01,-2.365965900204080041e-01,-1.170920995135496523e-02,2.603230639002764857e-01,-1.464618248912564757e-01,-3.436496738455320221e-01,1.613615754372600131e-01,1.000000000000000000e+00 +-1.901763756315645981e+00,-1.140130436048093776e-01,-1.589885895296563878e-02,-1.347714557200887731e+00,1.559893111253480291e-01,2.943525095945195846e-01,-8.762013730972015302e-02,-1.698111418051260424e+00,-1.582255231623669767e+00,-8.344383709300481899e-02,-3.070355736728235668e-02,-1.428494191647693201e+00,1.429522525186431858e-01,-1.034098271437848549e-01,-4.141065512465620657e-02,-2.107235320287753622e-03,2.025146775974647206e-01,-1.682346242604868536e+00,-1.622323666331404901e+00,-1.107978489145008821e-01,-1.000000000000000000e+00 +-1.662023350391828025e+00,-1.654770176908459289e+00,-1.557597998770770875e+00,1.203487712134641702e-01,-1.838685072399005804e+00,-1.463835343836004110e+00,-6.355507345375289996e-02,7.890762379367925045e-03,-1.342721534829666385e+00,-2.374024144962470495e-01,1.993536497136917662e-01,-1.710146411338983707e+00,7.197835586250633799e-02,2.953825947571455668e-01,-1.829940855642144815e-01,-3.553528407017891855e-04,1.407134797205625565e-01,-1.308717950203963365e-01,-3.207728021164623078e-01,3.767952619169859241e-02,1.000000000000000000e+00 +-1.887184419707484961e+00,-1.853757098381225432e-01,2.872420446140629968e-01,-1.233903244711152869e+00,4.264280874515810060e-01,1.539815503152855702e-01,-2.511728916094144864e-02,-1.430601943163283485e+00,-1.794794334882835241e+00,2.279592536379622392e-02,6.505452523012145161e-02,-1.304092811701644594e+00,3.616317939265475956e-02,-1.684500566958359480e+00,-1.748425444466124912e+00,1.320542591078140704e-01,1.988450335786769163e-01,-1.457390216866339960e-01,-1.681082243555220623e+00,-1.444260182732841624e-01,-1.000000000000000000e+00 +-2.147591247930463187e-01,-1.295880574644723060e+00,-1.414181058229858623e+00,-1.761577153389257244e+00,-1.816617960661231157e+00,-1.499391787906862561e+00,-9.587358913735103891e-03,6.155900339266405508e-02,-1.315616638581871056e+00,-2.276183033416775969e-01,3.757495554385740233e-01,-1.970598391636864566e+00,1.298988905924748360e-01,-1.210652947605888263e+00,-1.654496270186754714e+00,1.786756798855035910e-01,2.257526784963599653e-01,-1.563851404462621053e+00,-9.310080301164969097e-02,-1.898015221483945192e-03,1.000000000000000000e+00 +-1.757629487888254349e+00,2.750813333173868225e-03,1.909472985348114882e-01,-1.413755901540125759e+00,3.334691618878731223e-01,-1.487572786021322813e+00,-1.704150413544582410e+00,-1.438446898524446071e+00,-1.587959853361427420e+00,-1.756280186303360802e+00,-5.018627473955431656e-02,-1.418801834989586785e+00,7.241102316773673220e-02,-1.254257779836668307e-01,-2.277330462722834681e-01,-1.127309666191127768e-01,3.136523642408932666e-01,-1.717027655520484197e+00,-1.574448929584145285e+00,-7.734777858761476832e-03,-1.000000000000000000e+00 +-1.673765524340555588e+00,-1.873599457951749203e+00,-1.669534734460675462e+00,7.532090305687616505e-02,-1.779359154952696365e+00,-1.588579491977466729e+00,-4.716969564667664733e-02,1.002003555818729991e-01,-1.351159720441112988e+00,-2.036155985690527026e-01,1.352720472680977726e-01,-1.795540273054498925e+00,2.419025125333562620e-01,-1.355743421377280544e+00,-1.701051084926724188e+00,7.121744646704183945e-02,4.833416624330702516e-02,-1.771424242246609326e+00,-1.325963547716367819e-01,1.863729830672161969e-01,1.000000000000000000e+00 +-1.824610374325886353e+00,-2.596331126462229077e-01,1.971523564757886038e-01,-1.243699053737473248e+00,2.473200182543848491e-01,-1.414641461934736055e+00,-1.463653921120485091e+00,-1.723371048229100477e+00,-1.524784161373843983e+00,-1.485507312847800021e+00,1.495881799387448474e-01,-1.369066816825430521e+00,4.668849072457398330e-02,-1.608590022098949079e+00,-1.786438967324652571e+00,1.124377585141832503e-01,2.755700291238519783e-01,-1.233772338920482020e-01,-1.591254715929807162e+00,9.133259280459660179e-03,-1.000000000000000000e+00 +-2.180542142296966468e-01,-1.270062896352505577e+00,-1.495328868863751604e+00,-1.769646165049767905e+00,-1.840127173811692662e+00,-1.615188554748361094e+00,-1.359222778364617967e-01,-3.831082836164435845e-02,-1.445589010003945463e+00,-2.321568870857112499e-01,9.598219204448811548e-02,-1.625105004571937073e+00,1.327266192783912169e-01,2.573210482151609413e-01,-2.440372059621254697e-01,-1.733700222071140606e+00,-1.371266470740743815e+00,1.259516749085466680e-01,-2.967927586243156446e-01,-1.583235986500352688e+00,1.000000000000000000e+00 +-1.780336768017378724e+00,-1.154440834181787029e-01,7.668296338194463546e-03,2.831619677306277394e-01,-1.452473720257523970e+00,-4.285690418951215719e-03,1.654404361349363084e-01,1.514950521350017128e-03,-1.549922508433306589e+00,-1.957878405107431052e-01,4.364706251310906948e-02,-1.346951740306844059e+00,1.799600142998423191e-01,-1.325391087201296392e-01,-1.359185952055554369e-01,-5.762326559486587868e-02,2.491579101794483553e-01,-1.439385722027020531e+00,-1.545230148793086622e+00,-1.970738583918421050e-01,-1.000000000000000000e+00 +-1.629813547646396632e+00,-1.722040278342896746e+00,-1.439454653795339878e+00,8.464614837008242743e-02,-2.052730565713883681e+00,-1.407304653711857645e+00,-3.951692706296879021e-02,1.626117039329012526e-01,-1.242088494892461981e+00,-2.128027926490991262e-02,2.370403982931431064e-01,-1.860509553164634955e+00,1.115267716483113181e-01,1.591178760324968022e-01,-1.357813641817019745e-02,-1.514209434610088501e+00,-1.410136060264903124e+00,1.080091317138559270e-01,-4.215093146274401237e-02,-1.466606840271246659e+00,1.000000000000000000e+00 +-1.912817834617272172e+00,-6.515174009513001463e-02,9.790766084813876113e-02,1.054421055807980984e-01,-1.279485933635208683e+00,-4.091736000026738873e-01,-7.667769932443077585e-02,-1.375716133940347585e-01,-1.576176057217403059e+00,-8.098090482344666441e-05,1.224971632641589092e-01,-1.331235313536060172e+00,2.196543467199380817e-01,-1.620085556824799600e+00,-1.821855582416168406e+00,6.413801000505675587e-02,1.377073901576458814e-01,-9.338564267780208317e-03,-1.650633297537569710e+00,7.399495933207961285e-03,-1.000000000000000000e+00 +-9.984736554360375271e-02,-1.320494716944914604e+00,-1.330790725290356358e+00,-1.697057370467569104e+00,-1.840069058158641191e+00,-1.647605116110829604e+00,6.373820669563598496e-02,2.957061551848822606e-02,-1.389983119018888491e+00,-3.466061775014659951e-01,2.756236428698094487e-01,-1.954052051555470237e+00,1.912412080119063329e-01,-1.217490196055438290e+00,-1.623270425196640776e+00,-1.414692981415264450e+00,-1.321947685066984457e+00,-1.388634985873986816e+00,-2.403447451229275555e-01,-1.448571437282810015e+00,1.000000000000000000e+00 +-1.883952031352472911e+00,-1.122532631133231762e-01,2.764486074714272434e-01,1.901019606590734767e-01,-1.422934513119327171e+00,-1.843347631771780959e+00,-1.468065739422759197e+00,-6.261002636477708098e-03,-1.618981661560015883e+00,-1.592125152649291220e+00,1.044350868181647130e-01,-1.464179489802784451e+00,1.490295794475857694e-01,-1.047113205882685277e-01,-2.568733682170444288e-01,-2.402027069120222236e-01,3.714837850013332776e-01,-1.819701471860130093e+00,-1.382362341255056970e+00,-1.035735002863291815e-01,-1.000000000000000000e+00 +-1.650019870464853655e+00,-1.806658027340098061e+00,-1.403797902110349716e+00,-6.474476174803717177e-02,-1.926903459829577114e+00,-1.415870302696234262e+00,2.319884728917572664e-02,1.186141708858362703e-01,-1.434183175589905934e+00,-2.050432041023633190e-01,-2.093555481806824714e-02,-1.936321547958794076e+00,5.434852774581955259e-02,-1.179570740375065263e+00,-1.587208761533996926e+00,-1.440860384378481740e+00,-1.644263688499265852e+00,-1.523879281006476960e+00,-2.027536844388049819e-01,-1.524534179686675817e+00,1.000000000000000000e+00 +-1.802990477740712860e+00,-1.352501645045940670e-01,2.440960711464473243e-01,-4.801656576218121808e-02,-1.361297888556253222e+00,-1.691875451030229627e+00,-1.486751210467184281e+00,4.885939370215053312e-02,-1.629492514719542484e+00,-1.713586694934849053e+00,-3.252431019550108132e-02,-1.488537331702065591e+00,1.993187636567363141e-01,-1.778658670455917745e+00,-1.727060553958040368e+00,1.165695389493317385e-01,1.687186825675073276e-01,-1.373241103838188093e-02,-1.409423765488930780e+00,-1.015870841581417716e-01,-1.000000000000000000e+00 +-2.439795576160389834e-01,1.152532094054011663e-01,2.168617152173532847e-01,1.866894323907272030e-02,-1.646879238323857852e-01,-6.997772582165057154e-02,-9.108176612808183484e-03,1.158183025451648279e-01,2.417068706899839770e-01,2.718057870858650582e-02,1.950730446043487387e-01,-1.709516556698534584e-01,2.483579463474902549e-01,2.704376787301095431e-01,-4.982668000162603777e-02,-1.441543163316200138e-01,1.534048820910890731e-01,-1.870535299139338470e+00,-1.771469223944440285e+00,-1.662769152132258510e-01,1.000000000000000000e+00 +-2.341795754986746025e-01,4.762168186027798678e-02,1.693464442342933196e-02,2.483461373386978699e-01,1.253448620658552670e-01,2.081338345781823251e-01,1.332734179988863676e-01,1.108470358722858928e-01,-6.370999672153768167e-02,-1.650066915995046068e+00,-1.334546688506071499e+00,-1.650498092965406172e-01,2.878891556507078286e-01,-1.575315876357988198e+00,-2.287549874178484710e-01,-3.197358082530338264e-02,1.652975458202473324e-01,2.154126319311222559e-03,2.234113834455967451e-01,2.111174464899304160e-01,-1.000000000000000000e+00 +-1.808980495989261694e+00,-2.212655903719411565e-01,-4.289748993112732500e-02,-1.527312149745716052e+00,-1.889086907783862901e-01,-9.630992923883216483e-02,1.303469199482651941e-01,9.399689441044666027e-02,9.766995616252455159e-02,8.890142659765526423e-02,2.541773127400676624e-01,-2.817778219832813846e-01,2.081404177732021354e-01,2.030926442196815951e-01,-1.355886793329663109e-01,-3.622955470645472520e-02,8.246131517108407083e-02,-1.909910293441634543e+00,-1.832820919420656836e+00,-1.482993850504104871e-01,1.000000000000000000e+00 +-1.786743623700731698e-01,2.333610978623791854e-01,-2.317769286988272115e-02,2.678056858207210622e-01,2.830420863340221693e-01,2.583360251014666376e-01,6.007902334857953552e-02,-1.676753537738113475e-01,-1.351090423571608967e-01,-1.439543104631452630e+00,-1.680794493945489521e+00,-2.969694657694614826e-01,1.250456463485115632e-01,-2.179542371792486666e-01,-1.459125680687157534e+00,1.568189547032813602e-01,2.312646421745751524e-01,-1.766893625688763514e+00,-8.111903637227746433e-03,7.916568950765279222e-02,-1.000000000000000000e+00 +-2.670670863779148752e-01,3.040045732407791745e-01,7.814293641534225676e-02,4.659231234598722182e-02,-1.617743354729917482e-01,-2.300903445949606940e-01,-1.621687556751581771e-01,8.252131990860941263e-03,3.992821677531311897e-01,3.517318483013078811e-01,2.383671602944574530e-01,-1.963604713128287438e-01,1.819678838763174600e-01,-1.228480995979723822e+00,-1.723203244659578193e+00,-4.365578367003825067e-03,2.155098480035778918e-01,-2.278620059362060635e-01,-1.726971638801024556e+00,-1.059862787334183848e-01,1.000000000000000000e+00 +-1.575566994330422910e-01,1.795560211713463916e-01,2.682631552317020496e-02,2.007662517788816603e-01,1.581980924416818424e-01,-1.556207542138783317e+00,-1.449804158237339102e+00,-1.210895562639316414e-01,-5.533071193417959943e-02,-8.570354810994065820e-02,-1.502045301027012503e+00,-2.784264656142161898e-01,1.310384711439979699e-01,-1.488857577924443953e+00,-8.111371166148849399e-02,-1.177031639826650322e-01,2.241191842153236746e-01,-6.670881561524841241e-02,7.480619264484354713e-02,2.173972671173703408e-01,-1.000000000000000000e+00 +-1.787231451201681143e+00,-1.874545583443503194e-01,4.154541687070615469e-02,-1.705146527264772338e+00,-3.286917219278586710e-01,-1.936103546223102434e-01,-8.827279795360024051e-02,1.179923057226739749e-02,1.704145899614943060e-01,1.593071847959947607e-01,2.207179404937528178e-01,-2.696259934960010263e-01,2.648712489501740819e-02,-1.325651556789151808e+00,-1.813693726376160775e+00,1.451873112535447818e-01,1.860007285068369376e-01,-2.763206519063196032e-01,-1.832195978436054418e+00,-3.601298690760837418e-02,1.000000000000000000e+00 +-2.100560422946932682e-01,1.484499628425742512e-01,2.444989647549606349e-01,1.609246664696919737e-01,2.301053747903976376e-01,-1.415057182688518322e+00,-1.408164698486420008e+00,-2.405783550339117838e-01,-5.833681479055863317e-02,8.179086755040124168e-02,-1.551703219796524369e+00,-1.282693526730974698e-01,3.455256040877574542e-01,-1.750968919478370633e-01,-1.687088325495360142e+00,2.791974160174996999e-01,1.056122875882148093e-01,-1.705541528252550343e+00,2.342416190795347519e-02,1.295063049478414885e-01,-1.000000000000000000e+00 +-1.423924576211661464e-01,1.921284319409686248e-01,1.468442950975537287e-01,-5.556648021727071673e-02,-2.208593480039719448e-01,-2.099378733063208546e-01,5.313501441645162443e-02,9.703649684887688531e-02,2.871007898980917350e-01,8.824986422275575559e-02,1.708047887759106542e-01,-2.352331277535596732e-01,2.526984764809652728e-01,2.687840957456584245e-01,3.921526339296696473e-03,-1.685011646696626064e+00,-1.397855565475193407e+00,-1.339423834167504879e+00,-1.674757620603984698e+00,-1.621478794819147806e+00,1.000000000000000000e+00 +-2.260360423722762302e-01,2.008593328590530802e-01,2.431177294893352436e-01,-1.309754257792349552e+00,-1.629830824440629655e+00,-5.815124031047488873e-02,2.445471168193985023e-01,-1.485328451052150278e+00,-2.366648012703968518e-01,-1.366125741782338476e+00,-1.350246501934106558e+00,-2.816137600067232793e-01,3.882081085250954633e-01,-1.569269750155042997e+00,-2.212442823205463538e-01,-1.063440726231710820e-01,8.420960607021235611e-02,-8.748164054977126958e-02,1.772677003261586420e-02,-6.235113992909069314e-02,-1.000000000000000000e+00 +-1.736992284809889098e+00,-1.702226743355233773e-01,1.679423364667675389e-01,-1.522912300366130234e+00,-3.742089925693151131e-01,-1.092724662937819002e-01,1.351180000592946961e-02,1.006382864373599334e-01,3.765244896172081357e-01,1.726545677597331740e-01,1.583330105393303544e-01,6.125501487936368950e-02,2.171387687393007693e-01,1.167521671419302631e-02,-6.763294035732590748e-02,-1.796820751677606465e+00,-1.360450205712884886e+00,-1.402203829698527304e+00,-1.772810900332412620e+00,-1.592077081909663683e+00,1.000000000000000000e+00 +-8.260132643703596567e-02,1.506323137841703852e-01,5.015301880318073779e-02,-1.291683870600369488e+00,-1.476730015821815289e+00,-1.517797963673162553e-01,-1.088438463196348543e-02,-1.496706302959756707e+00,-1.128812469775872240e-01,-1.669594973202173094e+00,-1.353208388937376716e+00,-2.855651264253485366e-01,3.092973662683150549e-01,7.599542725378864716e-02,-1.730541964489813456e+00,2.233934729433458277e-01,3.422320311481372723e-01,-1.565095979484781852e+00,7.633830631677407386e-02,1.336755402681864546e-01,-1.000000000000000000e+00 +-1.404076984799650651e-01,1.191317553918698202e-01,-5.457105978545798153e-02,1.374395884758006503e-01,-1.667339667907631606e-01,-2.253394516688803084e-01,-8.404619183367420532e-02,-4.135243822093048749e-02,3.374401550230359259e-01,1.642256742657378921e-01,2.269022987963117965e-01,-1.905198098429376208e-01,1.607784713243614882e-01,-1.335010754414554190e+00,-1.774638571230396700e+00,-1.316770162827737067e+00,-1.247768977256307421e+00,1.920813975907403326e-01,-1.758890154793151472e+00,-1.682956658738964606e+00,1.000000000000000000e+00 +-2.022386142575156864e-01,1.336806471945872765e-01,2.875705692623020671e-02,-1.565704532131195537e+00,-1.481319864862788105e+00,-1.764617757145244159e+00,-1.488746964039890708e+00,-1.681955182045430375e+00,-9.291659391132862933e-02,6.570542869799977925e-02,-1.521660326132671326e+00,-2.335841231780483029e-01,2.916632795978746362e-01,-1.702229927469582238e+00,-3.194907254166401422e-01,-1.977399998211519516e-02,1.482855204698622364e-01,-1.361706824813516403e-01,1.397701967238868526e-01,-1.785281155111982654e-02,-1.000000000000000000e+00 +-1.710675675150777897e+00,-6.889656381993650158e-02,5.336733035525358798e-02,-1.728259254603254025e+00,-4.287565833351879574e-01,-1.351761449493077416e-01,1.708338029678988235e-01,9.443669862142795024e-02,3.810947838806565402e-01,2.831015210974562324e-01,2.806288730489565619e-01,2.644478370657210586e-02,2.074396752519246934e-01,-1.368615791967153106e+00,-1.721035943584906747e+00,-1.556529571274913648e+00,-1.339529546342336896e+00,2.604816863605534660e-01,-1.604105136993718927e+00,-1.545370663450510218e+00,1.000000000000000000e+00 +-2.539133094953536784e-01,9.952600458798718797e-02,-9.406514181245290152e-02,-1.296232657332213600e+00,-1.370233898875531153e+00,-1.703616285965132482e+00,-1.464099282246143829e+00,-1.694221421276576489e+00,2.256370931205706198e-02,2.214405366647632378e-01,-1.342361032399055798e+00,-8.651427614168735736e-02,3.670801808416190304e-01,-7.240739261390924386e-02,-1.929572319215834941e+00,1.489434662442567781e-01,1.409932506741681246e-01,-1.696455953850054543e+00,3.076169633660839203e-02,1.804048154257257841e-01,-1.000000000000000000e+00 +-3.396623784028023341e-01,-1.459286752093374107e+00,-1.562384720334421218e+00,-4.378878165881100459e-02,-2.317601361455025877e-01,-1.581590712001686105e+00,3.483088885333713441e-02,-1.974632901043600619e-02,3.235861789429929081e-01,1.827132479463205450e-01,1.058161714238032985e-01,4.567541010519204914e-04,-6.143133313296494924e-02,2.801437997963560078e-01,-2.951779216041229559e-02,-2.872110181937215234e-02,2.602588034669344186e-01,-1.653150479042223786e+00,-1.858187304053193545e+00,-1.437127986318458006e-01,1.000000000000000000e+00 +-2.156700822111204396e-01,2.856154781462307835e-01,4.358355008224418237e-02,1.055124854187428074e-02,2.930327847274443731e-01,3.196338488580008907e-01,-1.228284280631395109e-01,4.776292530106697465e-02,-3.383587202693603968e-02,-1.524384588998426215e+00,-1.557843119323771619e+00,-1.150868439085395989e-01,2.526669630902841357e-01,-1.650505190739935291e+00,-1.813030026693628338e-01,-9.010045015269224633e-02,2.665444142050752419e-01,-1.706644508361277790e+00,-1.545969851106116622e+00,-1.522187528667455769e-01,-1.000000000000000000e+00 +-1.801174539362771609e+00,-1.816188763163395503e+00,-1.497895285526630538e+00,-1.812059156867410703e+00,-7.265023004259998918e-02,-1.750380778769931256e+00,4.886934369858730171e-02,-9.685963324469006042e-02,4.122079114066758976e-01,3.623485172182143721e-01,3.723119939019490232e-01,-2.074123149641814434e-01,1.542832311789590904e-01,1.811906386589723084e-01,-1.374469101163109108e-01,-4.452547192311231972e-01,6.082083527296935910e-02,-1.638050369059725675e+00,-1.777153188779318382e+00,-2.300459371093563909e-01,1.000000000000000000e+00 +-4.451531151184778823e-02,9.148420013289577934e-02,2.534058268925715729e-01,2.054960265755059190e-01,2.785636652081447395e-01,7.879173701551790698e-02,1.541296869682636655e-02,4.721268837444195232e-02,-7.916032429184548247e-02,-1.561824407894278677e+00,-1.555743657094234900e+00,-4.816314658093525658e-01,2.938611281680565401e-01,-1.650182324738711237e-01,-1.745215565765939170e+00,2.138468654552170833e-01,2.647899305847716267e-01,-1.885477440951396633e-01,-1.354105012801542607e+00,-1.185313722870132702e-01,-1.000000000000000000e+00 +-3.862363481910549012e-01,-1.484854201284331321e+00,-1.438572421643739885e+00,-4.142546019707953747e-02,-3.348652602872161377e-01,-1.639473204331644007e+00,7.197267147107569429e-02,-1.094711591033338122e-01,1.916100059435999481e-01,1.104510484539653337e-01,3.024398227715986431e-01,-2.130822796708342626e-01,9.486294496163666379e-02,-1.512413978024118810e+00,-1.735754066485621028e+00,3.041004585394775916e-01,3.318818660567996459e-01,-3.089777197151281629e-01,-1.931654961106461510e+00,-1.354386261256982082e-01,1.000000000000000000e+00 +-4.303605809054293907e-01,2.177837137677910428e-01,3.031313748889638560e-01,1.093102325839635514e-01,7.076542489851209150e-02,-1.179183095294744188e+00,-1.505863307879028001e+00,1.365853728016261648e-01,-1.305806890730770442e-01,1.147488698401981644e-01,-1.610618557095213621e+00,-1.918134292915106287e-01,-1.336499559189097130e-02,-1.718240000988179306e+00,-2.040449602330622614e-01,-1.110775901676131772e-01,1.881237390840745927e-01,-1.630215767900932144e+00,-1.695233570779625776e+00,-2.628398216005405075e-02,-1.000000000000000000e+00 +-1.677091039606229739e+00,-1.865202226684187892e+00,-1.449574026374467861e+00,-1.606683272540252228e+00,-2.477549056295799135e-01,-1.606862935006536430e+00,-1.615279137175159307e-01,8.924077723319796818e-02,-9.582226197238646881e-02,8.636847459609645061e-02,1.077270557392905337e-01,-2.009099826210908879e-01,2.933490456390652756e-02,-1.248006919167750528e+00,-1.698874983878829958e+00,5.081814598152770146e-02,1.478842899130003719e-01,-1.493291054343111601e-01,-1.871050668915237081e+00,-1.179997572996087513e-01,1.000000000000000000e+00 +-3.328088271449912705e-01,3.498802263190264883e-02,1.731632906847828668e-01,1.552086898298051520e-01,1.565348255437749581e-01,-1.435732159384214057e+00,-1.366962610615669815e+00,-2.218411404297142286e-01,-9.745524394934233781e-02,1.438923028868028253e-01,-1.542571478877929447e+00,-4.983556069812025702e-02,2.354435482550457370e-01,-2.203854683616549015e-01,-1.646878114276301819e+00,1.502885180994540937e-01,3.258911374658394200e-01,-9.419806122841692386e-02,-1.577419009343649092e+00,-1.793921236206705849e-01,-1.000000000000000000e+00 +-5.430212539709483588e-02,-1.401301184415392642e+00,-1.620363609016238993e+00,-3.140725486911737085e-03,-2.840792002816733830e-01,-1.631557432178066369e+00,-7.907571158916459897e-02,-1.672689249186963545e-02,2.820765846230589569e-01,1.924189493545946927e-02,9.424944259944431635e-02,-2.025209418751356960e-01,8.007159720548398529e-02,2.718485183061024046e-01,-3.356755891540847414e-01,-1.539747283092349317e+00,-1.283979298093514076e+00,-1.379367054633772405e+00,-1.822919127052061272e+00,-1.655120047632869129e+00,1.000000000000000000e+00 +-1.828648407826313771e-01,1.303669714280714931e-01,3.281634341282182454e-01,-1.331218717902154625e+00,-1.440335771332840631e+00,-8.438256293891646664e-02,-3.016506776942890627e-02,-1.635986432710933247e+00,-5.706716830303201760e-03,-1.519308637308587340e+00,-1.538826082544888507e+00,-1.874635769544862052e-01,1.740443531291482604e-01,-1.642323730198749665e+00,-1.818284598289054976e-01,-1.813919099043513616e-01,3.912970162390819739e-01,-1.690514193262345755e+00,-1.560177869678597284e+00,1.489094548564861564e-01,-1.000000000000000000e+00 +-1.719337784952710724e+00,-1.787163526386397194e+00,-1.493037120732656930e+00,-1.415933286885403763e+00,-1.487854202069676668e-01,-1.583751903934811356e+00,6.103681952781319503e-02,-7.524412701058486030e-02,8.375219989444812607e-02,2.142504794038084248e-01,1.438223961253209671e-01,-1.304850311421204001e-01,1.067574007228712235e-01,2.909954696904511473e-01,-1.670366864727289014e-01,-1.689081678638551631e+00,-1.528707618705964943e+00,-1.430534163439965756e+00,-1.645195206065076965e+00,-1.604056159169864548e+00,1.000000000000000000e+00 +-2.083438875860270934e-01,2.335965437908922027e-01,1.071707499091033577e-01,-1.288246750213543512e+00,-1.378517278944689561e+00,-1.707696384816015933e-01,-1.568376077712627659e-01,-1.518355268363408062e+00,-2.629407773909601675e-01,-1.565686260935592600e+00,-1.628133544842195635e+00,-2.354517478541177933e-01,2.634825782470920918e-01,-3.164200285627927212e-01,-1.728366907590010992e+00,3.490475847534628140e-01,1.841402464753418011e-01,-1.677322903246478147e-01,-1.548509155377692847e+00,-1.388963128235166933e-02,-1.000000000000000000e+00 +-8.140862605581608569e-02,-1.531952677721658107e+00,-1.579822628334136114e+00,-2.158986542727301949e-01,-4.753407251577410064e-02,-1.939691485609706056e+00,-9.736562988277902564e-02,1.229744127047200652e-01,3.433432862619288928e-01,2.159351090051743194e-01,9.719607419281767191e-02,-1.291642903895658112e-01,1.461109897296656579e-01,-1.125938608592356616e+00,-1.704452238561950717e+00,-1.517897004328281119e+00,-1.443169289655185761e+00,1.057877446144747657e-02,-1.738438363948879628e+00,-1.854476765398893834e+00,1.000000000000000000e+00 +-2.863243735617415076e-01,2.365670741337101979e-01,8.903467814116583501e-02,-1.457571714714968714e+00,-1.246015694584325262e+00,-1.673825131524844467e+00,-1.612638785923174112e+00,-1.541490244400076648e+00,-7.079531019797660751e-02,1.034955758663737568e-01,-1.377897664362565777e+00,-2.981016076976940044e-01,6.539238018431087207e-02,-1.538001995939093369e+00,-8.463110382048388836e-03,-2.875371666447258667e-02,2.202154047120651326e-01,-1.681889004787793684e+00,-1.442212170651307579e+00,-1.412194893631334713e-01,-1.000000000000000000e+00 +-1.762130690462024862e+00,-1.668951046054449217e+00,-1.674424950970456072e+00,-1.565170102148560227e+00,-2.258421072760552206e-01,-1.724891117061284174e+00,5.276235571442480093e-02,2.270031654449717884e-01,2.078060747906807226e-01,7.275412066186140447e-02,6.531493706493668450e-02,-2.285500410644309699e-01,3.294273728423363634e-02,-1.394424769471787506e+00,-1.697314211487732205e+00,-1.423639524438613524e+00,-1.392870813809807151e+00,1.297739428189970123e-02,-1.737016674483755407e+00,-1.702235737301599272e+00,1.000000000000000000e+00 +-2.829052725322153128e-01,1.053995753974671934e-01,1.243137310869227297e-02,-1.475376154478991619e+00,-1.273230705412238040e+00,-1.788930468374629879e+00,-1.512484092449806861e+00,-1.494579560309224897e+00,-1.107833022791021582e-03,1.922377008658001513e-02,-1.489411245227656666e+00,-2.360759778040930901e-01,6.220074322926733013e-02,3.836805888715498492e-02,-1.638786439357833302e+00,6.454983442961406692e-02,8.443579955664209935e-02,-4.456643119187696234e-02,-1.535275340651167886e+00,-2.510975333924962000e-02,-1.000000000000000000e+00 +-1.888353826195675322e-01,2.331116445443457141e-01,7.209972267287956260e-02,9.936601978177159178e-02,-2.998875281976747242e-01,7.736684513841683708e-02,-4.154085777495976350e-02,-1.620416913858260699e+00,-1.536378078562709693e+00,-4.970986093233982495e-02,8.054914235365649711e-02,-1.866448787992637248e+00,1.631511674540167545e-01,2.540076346857715328e-01,-3.365790456232687494e-02,-7.210663219225983689e-02,2.482153857969794930e-01,-1.883598696167930964e+00,-1.810466289958495345e+00,-4.205707354893116479e-02,1.000000000000000000e+00 +-1.884999296168834215e+00,-3.112843589472975570e-01,2.510927801806384085e-01,-1.626425934354120084e+00,2.906727097445114816e-01,1.446522529476227947e-01,1.749560835749299215e-01,-1.309041609415544094e-01,-4.164120115564299424e-02,-1.612352644191952677e+00,-1.524397432729068580e+00,-3.156040382378184628e-01,2.208343021322482225e-01,-1.509988895926045904e+00,-2.292660974459153067e-01,-8.014049356864991203e-02,3.799991368343439424e-01,-3.734193110346489819e-01,1.073939538345585615e-01,1.177208510405130526e-01,-1.000000000000000000e+00 +-1.680459216951007306e+00,-1.929987354809645228e-01,2.338483618359407656e-02,-1.613930291721018984e+00,-3.415917136958097622e-01,-1.348783215985026596e-01,1.181432289240746669e-01,-1.585137163766282553e+00,-1.534313066984771767e+00,-1.310491011825282714e-01,1.940131849865792124e-01,-1.733134803485204101e+00,9.973672008270378919e-02,2.478634840235067749e-01,-1.556240280163657952e-01,-7.091430903922907314e-02,1.834628758540778060e-01,-1.854266729031440519e+00,-1.714678789261387504e+00,4.735125599413148034e-02,1.000000000000000000e+00 +-1.735701321098774619e+00,-3.029488293090203399e-01,7.823142982724853867e-02,-1.521530706942672229e+00,1.518072634749043082e-01,1.949321976666617273e-01,1.963842715291226615e-01,5.344791979781139679e-02,-2.110863568467451212e-02,-1.562202197067320109e+00,-1.642579250325277629e+00,-2.377295785858298816e-01,2.815042624544672467e-01,-1.414518361758450027e-01,-1.803016334354505812e+00,1.813624402682965375e-01,4.385810848825134611e-01,-1.917785633493478192e+00,1.837256278364261808e-02,-5.286230436914544917e-02,-1.000000000000000000e+00 +-7.199972550916522440e-02,7.255178596017852755e-02,9.750889304507877331e-02,6.081409242016647121e-02,-2.376473500031918706e-01,-8.982997357940228356e-02,6.192461817998318879e-02,-1.572683273506855661e+00,-1.433725385605350677e+00,3.854035867080965461e-02,2.541416204131221601e-01,-1.671030333565145964e+00,2.400026694987877907e-01,-1.278116954806505667e+00,-1.724615716649997577e+00,1.107077198748998292e-01,1.780571421151442291e-01,-1.284590954348128200e-01,-1.723855140182853019e+00,-1.584402831845356241e-01,1.000000000000000000e+00 +-1.833390667392359896e+00,-2.171180825066165443e-01,1.960964068290079465e-01,-1.392986539304137628e+00,1.584083272860981051e-01,-1.397279559595808962e+00,-1.576413536407396565e+00,2.161410269269601475e-02,6.515055618245384206e-02,3.308749079517017688e-03,-1.291117871585367061e+00,-1.183363545055007710e-01,1.098549087884565512e-01,-1.478121029211044268e+00,-1.186132878675661695e-01,-1.259727635628279940e-01,3.409386167645193089e-01,1.399151801230701653e-01,1.518843333971090503e-01,-1.522919555040948003e-01,-1.000000000000000000e+00 +-1.657067455176580939e+00,-1.956006772697657103e-01,4.866738214374304039e-02,-1.520530216719676808e+00,-3.557408683673492500e-01,-4.834124711662751012e-02,-6.597629840328217421e-02,-1.446866591736235552e+00,-1.478399872957024686e+00,3.409000577232640383e-02,1.290969519790106101e-01,-1.647536979880922514e+00,5.373522734854102589e-02,-1.392181127118939710e+00,-1.613254635171154527e+00,1.703774576150753617e-01,2.226799427848425439e-01,-1.377850789387303210e-01,-1.687682883865781136e+00,-1.182185390507711903e-01,1.000000000000000000e+00 +-1.828577673095696188e+00,-1.615210861024604649e-01,1.496379005811858676e-01,-1.408848830726687096e+00,1.157878170169065607e-01,-1.186607809631150978e+00,-1.557056686931114209e+00,-3.318515883453987403e-02,6.565717225075094754e-02,2.149917478930231662e-01,-1.557595536413655557e+00,-3.593265080683860635e-01,1.885300156998934995e-01,1.078510889679324264e-01,-1.708102050101451663e+00,1.581840693686221433e-01,3.807921891383032609e-01,-1.643429295982688831e+00,-5.589858940038970603e-02,1.439733738179163058e-01,-1.000000000000000000e+00 +-2.014535912011794216e-01,1.464013503426886964e-01,1.583118228851058129e-01,-1.311144004189748191e-01,-2.933891499481025567e-01,7.171208358498094848e-02,6.713523942833959923e-02,-1.462855678228510481e+00,-1.258580727846380531e+00,-2.498970101252624421e-01,3.144182986818298553e-01,-1.585877416016334474e+00,7.929365415811465145e-02,5.093170250068383975e-01,5.087171443260374293e-02,-1.835895741151551963e+00,-1.262516885170584136e+00,-1.497584680792570122e+00,-1.802072848342316513e+00,-1.815473843169535861e+00,1.000000000000000000e+00 +-2.065909537021775932e+00,-7.713236544590557076e-02,3.292057384357651717e-01,2.336083146754882600e-01,-1.283071514341488806e+00,-2.146017387445667002e-01,9.831816996616363213e-02,-1.529687239368231699e+00,-6.557823286419892095e-02,-1.555593879643420774e+00,-1.394966934007892512e+00,-2.834226752862956666e-01,4.210734554677593722e-01,-1.763667489644399122e+00,-1.567802012234626174e-01,-1.153855943401591311e-01,2.188752837490041958e-01,3.527404622114355126e-02,1.206088262135300293e-01,1.451652686106577050e-01,-1.000000000000000000e+00 +-1.978367994037992572e+00,-2.661520016824179824e-01,4.640207350525141217e-02,-1.330826471055515992e+00,-9.127315224419021988e-02,1.120521841253903222e-01,-3.989370800105224851e-02,-1.428621944608968741e+00,-1.434193447270862842e+00,-1.710020712058683279e-01,1.776727481698182620e-01,-1.649790298552828993e+00,1.521028295464029734e-01,3.243600900035593093e-01,1.442058524107271700e-02,-1.695752680588687333e+00,-1.436336009194452590e+00,-1.408519458204670283e+00,-1.650603052771111301e+00,-1.542717696333763877e+00,1.000000000000000000e+00 +-1.707933373529028742e+00,-1.442728049153429448e-01,1.577493833657936106e-01,4.482661014397282262e-02,-1.479160225441092491e+00,-3.834935798107614868e-01,-7.472091006248304801e-02,-1.490247557780237653e+00,3.554589831091523044e-02,-1.567078881776201582e+00,-1.411662679994448499e+00,-1.907050100663965075e-01,2.008971762137442552e-01,-3.383766032115673866e-02,-1.602868897263930226e+00,1.111484426669004516e-01,9.252353490794451574e-02,-1.533010258660377101e+00,1.832494954384461527e-01,2.531780987473795097e-01,-1.000000000000000000e+00 +-6.035667714771275616e-02,6.470808836553376686e-05,1.024276970655027263e-01,-4.101435867772194876e-03,-3.071974488665177860e-01,-1.036708515513684176e-01,-1.794222268096470549e-02,-1.542565533807144673e+00,-1.255950023731022824e+00,-1.004970149235214771e-01,3.376773188277004678e-01,-1.816046997765331517e+00,1.724808629832310369e-01,-1.427182072473219598e+00,-1.552065473637629367e+00,-1.388188809964528758e+00,-1.356036369860796675e+00,1.720711068138204847e-01,-1.791030757359659109e+00,-1.734188633268321178e+00,1.000000000000000000e+00 +-1.788632517301677716e+00,-2.617386839599858406e-01,-2.936679162613395344e-02,-2.740391682109855065e-02,-1.297405165664371918e+00,-1.493482798529176536e+00,-1.514862991219524391e+00,-1.501596256478166191e+00,1.119108588338410537e-02,1.660142063933439105e-01,-1.475156599370080812e+00,-1.933276597493817639e-01,3.668878364649680868e-01,-1.640057643036256341e+00,-6.608321478067419319e-03,-1.954839037314018424e-01,7.896595294917038710e-02,-4.431425520113368988e-02,5.677358752661965796e-02,9.539055629951720405e-03,-1.000000000000000000e+00 +-1.856006504665463019e+00,-1.777447493266164913e-01,4.881178249972623756e-02,-1.568636636291946385e+00,-2.852035358681297983e-01,-1.851432584160719041e-01,1.197743117151101877e-01,-1.616994063691078232e+00,-1.383230983243273648e+00,-1.602217973295344267e-01,1.471966636387322114e-01,-1.825824796344011292e+00,4.942638947399052851e-02,-1.251995243989245887e+00,-1.694599822855320292e+00,-1.448203897775477866e+00,-1.420129063946256709e+00,1.197172510960335112e-01,-1.847869231225990561e+00,-1.900423255698547198e+00,1.000000000000000000e+00 +-1.938517563807695243e+00,-1.176138443868801620e-02,9.293952995888341007e-02,1.396608196283463110e-01,-1.335335518405849742e+00,-1.907449672753195102e+00,-1.628182913393507070e+00,-1.574695438705880024e+00,-1.678398946053656915e-01,3.380506491156565207e-01,-1.617627992223649391e+00,-2.401866425100306346e-01,2.642820025649577076e-01,-1.972760935393504111e-02,-1.830708386519253494e+00,4.779916325500892238e-02,2.844369872034545610e-01,-1.536844096857871378e+00,2.901214956634431269e-02,7.841534439478502205e-02,-1.000000000000000000e+00 +-2.696591070101692633e-01,-1.384688193113345678e+00,-1.498195562579526685e+00,-4.585590496562568646e-02,-2.210743617984119169e-01,-1.553133811849615631e+00,-4.612209217966583424e-02,-1.748276247153416119e+00,-1.405630394575534181e+00,-2.789849179701651138e-01,1.456286412021242926e-01,-1.692047959274368019e+00,2.863753688495023209e-01,2.939749539944037648e-01,-1.074634812087749969e-01,-9.361810643094208018e-02,6.991647225105473984e-02,-1.818221290983097038e+00,-1.912601633301234116e+00,-1.175312837766143148e-01,1.000000000000000000e+00 +-1.896447579353508495e+00,-1.097519778964581999e-01,1.029161177080544221e-01,-1.405474213317246646e+00,3.927025135617715490e-01,-9.727119249037760595e-02,8.051521423641112840e-02,-1.760626230279384308e-02,3.297087136498941251e-03,-1.452286395193483015e+00,-1.477747376763081988e+00,-1.297945053187743170e-01,3.227665455531983918e-01,-1.622388934463279808e+00,-1.703479678209234849e-01,-9.740909277515255593e-02,3.072525292795419971e-01,-1.848078716688827727e+00,-1.489256089766898716e+00,-4.441061523815337253e-02,-1.000000000000000000e+00 +-1.737645268732505244e+00,-1.623708628553631517e+00,-1.423996224171091063e+00,-1.405421788882911471e+00,-2.643303970591006635e-01,-1.794543486411528166e+00,-3.986729681823630855e-02,-1.596080866086237338e+00,-1.258205450819601889e+00,-3.024064760459701517e-01,8.855568852874931562e-02,-1.692147575482150001e+00,6.168322431109037052e-02,3.332373407523177566e-01,-5.444064776614818829e-02,-1.589273107450466793e-01,3.395310182394535303e-01,-1.859003577123959250e+00,-1.632951756930816067e+00,-1.503391061938186279e-01,1.000000000000000000e+00 +-2.001237531819056059e+00,-1.888309866090089606e-01,1.327341408731530226e-01,-1.470839520049634475e+00,2.117324793461772270e-01,1.710111718709355078e-01,-5.130752647826371249e-03,1.074725059185548393e-01,6.458821145140310194e-02,-1.524867715437911109e+00,-1.397109700959722423e+00,-1.599785670409428762e-01,2.758900698739424340e-01,2.391694294421903288e-03,-1.747657762294583250e+00,1.388205165123361784e-01,2.935091611745938023e-01,-9.573406562344149995e-02,-1.405575528569151800e+00,-5.505123569376621506e-02,-1.000000000000000000e+00 +-2.415894917066411518e-01,-1.339288850842404788e+00,-1.558080312315683669e+00,-8.794419585011527263e-02,-1.887423150854516951e-01,-1.603137363622819223e+00,8.521341845397489845e-02,-1.616603324363009797e+00,-1.335036905244106675e+00,-2.740610071985419283e-01,2.950184450795598767e-01,-1.871071861455992025e+00,2.602005367930751434e-01,-1.375536276457179463e+00,-1.635367593925601826e+00,2.743069787887959521e-01,2.948093318002342200e-01,-2.163009607736440798e-01,-1.647124865158788420e+00,-6.861962110242694757e-02,1.000000000000000000e+00 +-1.703216504730926006e+00,-6.387152405651808085e-02,1.390947393323525572e-01,-1.476562929876921615e+00,2.312946350175269228e-01,-1.401775340349431742e+00,-1.540237519879024797e+00,-1.737868580789893660e-02,-8.631242477821583770e-02,-1.068932328222133021e-02,-1.299781420830113943e+00,-1.957980018651323817e-01,2.200184664051651195e-01,-1.749701421790878175e+00,-1.370682515420905134e-01,-7.887318520251951348e-02,1.967623844981341019e-01,-1.802102148505244417e+00,-1.492533598037102394e+00,1.176820069900192806e-01,-1.000000000000000000e+00 +-1.668472440053598849e+00,-1.692714191205419771e+00,-1.511862118036053859e+00,-1.536036795174505531e+00,-4.130098449562970009e-01,-1.743609344136944017e+00,1.136747901042646780e-01,-1.348305275896040545e+00,-1.469801700766598440e+00,-1.698886193741123429e-01,2.087590028510214746e-01,-1.521163599292838731e+00,1.115255063517616024e-01,-1.278257121548038278e+00,-1.599898969078416666e+00,-3.734256357462203968e-02,1.714193737288485508e-01,8.378525831000405155e-02,-1.738692901858964746e+00,-2.183929967042638753e-01,1.000000000000000000e+00 +-1.835595193407608017e+00,-1.588688468422716216e-01,1.071038085108794863e-01,-1.431996774396235184e+00,3.389680600264142196e-01,-1.471000951585167416e+00,-1.513097497047408924e+00,1.292609311097401847e-01,-1.994413707680271486e-01,-4.228260372016211244e-02,-1.477577167216317600e+00,-3.847715451458932900e-01,6.221557352316370304e-02,-1.632352539781317668e-02,-1.807777450957433807e+00,1.954061683424508367e-01,1.916242989475189784e-01,-1.637500830595114976e-02,-1.480493939244007962e+00,1.073340210416912216e-01,-1.000000000000000000e+00 +-2.627949199463695540e-01,-1.311601591218620078e+00,-1.475211753932925696e+00,-1.304726359806189229e-01,-2.367698850744863193e-01,-1.608094359485233937e+00,-1.746864918809430955e-01,-1.523101588101070325e+00,-1.397629073881587303e+00,-3.822912186360122311e-01,1.458091856878317716e-01,-1.886788841133814953e+00,1.593097248506974428e-01,3.554702193506375507e-01,-1.074756851113357475e-01,-1.697499802761408327e+00,-1.554898521660907695e+00,-1.524011092956258251e+00,-1.838783882743532994e+00,-1.754451182941514809e+00,1.000000000000000000e+00 +-1.916822041755505523e+00,-1.994469698241685129e-01,-4.933632276222865709e-02,2.863965164922512296e-01,-1.423380359870673351e+00,-2.279957244996119181e-01,9.397472235863760237e-02,-1.716764114105343131e+00,-1.787351583082517070e-01,-1.414650843336660246e+00,-1.617666909739836179e+00,-2.518808571838557775e-01,1.685049104084261895e-01,-1.761630151708398762e+00,-2.776895007447031105e-01,7.729513967419639198e-02,2.379315862066027076e-01,-1.612209955935588468e+00,-1.483619705832376567e+00,-3.020913529259734720e-02,-1.000000000000000000e+00 +-1.867044884773448876e+00,-1.472673527705081753e+00,-1.649374480873115889e+00,-1.670143217194038465e+00,-3.070842398040137811e-01,-1.754939077683911108e+00,-1.423354955399841748e-01,-1.436019218204293457e+00,-1.418730579984796680e+00,1.079839128213427868e-02,4.152375627128810365e-01,-1.795347933011969488e+00,1.158722101302399332e-01,2.850322800467037232e-01,-1.398292267306494030e-01,-1.862325214132927176e+00,-1.441179708403775406e+00,-1.496897251365243431e+00,-1.622765660460842252e+00,-1.735839599488196994e+00,1.000000000000000000e+00 +-1.677393660314144386e+00,-3.394741131965792746e-01,1.944080460453487813e-01,1.723628021977686720e-01,-1.538798771158149226e+00,-6.468438635467047004e-02,4.107668666750106945e-02,-1.655328577280850588e+00,-8.153552575454094120e-02,-1.545242692158941056e+00,-1.250308347300965517e+00,-1.377305074670933382e-01,3.616201693068952228e-01,-5.447383234045161116e-02,-1.614484662310011087e+00,1.439056111517483805e-01,3.715683101161327828e-01,-4.589024722426153502e-02,-1.545033891606071963e+00,1.469928408203699521e-01,-1.000000000000000000e+00 +-2.292484754950680492e-01,-1.404350897526567721e+00,-1.578611367140718258e+00,-7.230342715986382007e-02,-2.232169484525267644e-01,-1.630610638600653806e+00,-1.399195313728806601e-02,-1.517284057248873941e+00,-1.373463738897101383e+00,2.390939499616162589e-03,4.525627012987060160e-02,-1.595028842936575320e+00,2.367456900519744889e-01,-1.294368160546527591e+00,-1.641006081856117405e+00,-1.360542163032010698e+00,-1.333418534206741279e+00,-1.075695980286662801e-01,-1.710501050537750034e+00,-1.680464972887257602e+00,1.000000000000000000e+00 +-1.824320348288306359e+00,-2.242519383694826507e-01,1.939500729578774729e-01,2.580759419678572697e-01,-1.347001387351347779e+00,-1.949872616982624818e+00,-1.565159184620138477e+00,-1.607137737383463438e+00,-1.336710862730201010e-01,9.077958480873264679e-02,-1.500475897103216383e+00,-8.793965232887804295e-02,-5.893354486171925588e-03,-1.552925293547802843e+00,-6.394711786702439449e-02,-1.428272298215615133e-01,2.401458986440296872e-01,-1.599474863735532937e+00,-1.570795285887488513e+00,4.971420756740695113e-02,-1.000000000000000000e+00 +-1.849266502460056527e+00,-1.536793408891875767e+00,-1.450331064273818971e+00,-1.662558656273171565e+00,-4.022276506934110429e-01,-1.613844321683342686e+00,4.982096749266174546e-02,-1.344866860762329974e+00,-1.254560206002134581e+00,-4.927212993065804847e-02,2.232242021749195338e-01,-1.722173281038203330e+00,1.953070056701929624e-01,-1.385138482621590006e+00,-1.547991780235024484e+00,-1.393118208201791886e+00,-1.351351286137255858e+00,9.987190245233887320e-02,-1.744067585086658490e+00,-1.654447997079727006e+00,1.000000000000000000e+00 +-1.864387728549524947e+00,-1.528811130414389763e-01,-4.480088130063339391e-02,2.433468408664729576e-01,-1.337242124972898516e+00,-1.847268380922106656e+00,-1.392291585547845933e+00,-1.696507564704244908e+00,-1.141758842819323161e-01,2.200858804252461831e-01,-1.449409253919071494e+00,-1.095331009378930492e-01,3.895191444116400392e-01,-1.685609108953693702e-02,-1.676026096460398795e+00,4.057755272416286446e-02,1.943936625867365986e-01,-3.801300302824268673e-01,-1.371802968399387090e+00,9.391816282396751869e-02,-1.000000000000000000e+00 +-2.105163915005565378e-01,5.829521326453870889e-02,-2.330801027164454420e-02,-1.343458878646294652e+00,-1.820581971638018492e+00,1.467537468396624134e-01,-8.328161426570454551e-02,-1.639913835556530364e+00,4.188109869227139015e-01,-4.448674629190724206e-02,3.122778846918302653e-01,-2.497090139123326336e-01,1.756516076350515820e-02,3.384156809903209329e-01,-9.083422385048443848e-02,-1.565818393465682468e-01,2.942333949711795626e-01,-1.752967436998053019e+00,-1.810309952758458296e+00,-4.756885371580615862e-03,1.000000000000000000e+00 +-1.697203936453344220e-01,2.164472413569353826e-01,9.900206135490280390e-02,8.897910041792289615e-02,1.450299974962855343e-01,4.101970544519639716e-01,-1.523634557246537208e-02,-1.509339160259876911e+00,-1.668266124724471222e+00,-1.582167767407107961e+00,-1.457214891393150191e+00,-2.022051936238908443e+00,1.819840730643635385e-01,-1.664136752130559005e+00,-1.705964505243858786e-01,-3.848621840533895944e-02,1.598246614759032957e-01,1.169500345506576389e-01,2.116352785084738564e-01,-2.875304415091849142e-02,-1.000000000000000000e+00 +-1.741522805904053062e+00,8.242275280928418324e-02,-6.731059268197489565e-03,1.338939803846624366e-01,-1.600336378396729486e+00,4.710828127688442002e-02,6.274461946128231937e-02,-1.459686109124986864e+00,3.554908433498816800e-01,1.550998708565357875e-01,2.400451279624569723e-01,-1.243357785412936567e-01,3.592931021600022645e-01,1.877076490277039544e-01,-4.788271790708865938e-02,-1.406023283584208750e-01,1.467205421994474679e-02,-1.861514220027873812e+00,-1.764920111008901227e+00,-2.203720929353735480e-01,1.000000000000000000e+00 +-2.979572688018531923e-01,4.381956681169807410e-03,2.399075434135144458e-01,1.491509945883924271e-01,3.286238888827819893e-01,1.205438224858945140e-01,9.462363301464880860e-02,-1.549568351713531245e+00,-1.759306043954263155e+00,-1.641338465834012883e+00,-1.680553559052160084e+00,-1.711162544065447655e+00,1.604917534636884080e-01,-1.945737205269550824e-02,-1.804900487344063276e+00,1.830046723190849600e-01,2.744771128864742504e-01,-1.716962203553597188e+00,-3.405020573273205564e-02,1.375886700854809241e-01,-1.000000000000000000e+00 +-1.955229329530519811e-01,1.478730638909200867e-01,-1.201783668794771665e-02,-1.560107922479264664e+00,-1.924336397026852774e+00,1.066808076913345615e-01,-8.443806862302799532e-02,-1.577717476909032968e+00,2.284650283079793232e-01,4.632076426126892355e-02,1.730652179581867456e-01,-1.300486417009283913e-01,1.911590199077310703e-01,-1.242333931901333788e+00,-1.526271776844993822e+00,3.141662632691351320e-02,1.167175183099673497e-01,-7.679233401081934918e-02,-1.768567765354970955e+00,-2.365405085762902759e-01,1.000000000000000000e+00 +-9.054096414312204355e-02,1.452948527338082663e-01,2.888070720707515138e-01,2.884057962486949456e-02,4.326722243748587493e-01,-1.364266619381323320e+00,-1.778410777044421742e+00,-1.739871896155328335e+00,-1.805356701043865142e+00,-1.150138933454133705e-01,-1.650812990334628472e+00,-1.537598752479355513e+00,2.521751071627383878e-01,-1.451461304705234934e+00,-7.738008865931172986e-02,-4.056494846509665908e-02,2.136542078638204167e-01,-1.842982905436176821e-01,6.353070317174616677e-02,1.409348599713760031e-01,-1.000000000000000000e+00 +-1.678331761292732871e+00,-2.896760072346654113e-01,1.000901919821304137e-01,-2.819962035219550356e-02,-1.774843789401664873e+00,-1.008161454806341673e-02,-1.379590036791146224e-01,-1.499320264123623403e+00,4.195404364322782853e-02,7.414101326892059973e-02,3.442438587840405129e-01,-1.055178104561328434e-01,1.166994867852243628e-01,-1.522151470880838575e+00,-1.667038946022956658e+00,6.227841045719036128e-02,1.199008504703146932e-01,-1.739190013501349663e-01,-1.865603480026541083e+00,-1.899886295331251751e-01,1.000000000000000000e+00 +-1.113579885006178716e-01,2.124980417005476185e-01,2.538609187701522663e-01,3.590323973709488858e-02,2.331874689876990425e-01,-1.494376707227117240e+00,-1.509753543203158799e+00,-1.741928532826658493e+00,-1.709555712420977924e+00,6.682890234843028021e-02,-1.508294795406126321e+00,-1.506992237086572928e+00,9.519961497824627750e-02,-1.369108901517802623e-01,-1.775749068029529365e+00,7.062975084623734912e-02,1.866130611863734134e-01,-1.686654783698201321e+00,-4.405234855229853141e-02,1.726240929876382835e-01,-1.000000000000000000e+00 diff --git a/qiskit_runtime/qka/aux_file/dataset_graph7.csv b/qiskit_runtime/qka/aux_file/dataset_graph7.csv new file mode 100644 index 000000000..06cee93dc --- /dev/null +++ b/qiskit_runtime/qka/aux_file/dataset_graph7.csv @@ -0,0 +1,128 @@ +-1.935744789981031500e-01,1.139791551437831785e-01,-5.926731841626675656e-03,3.009571667669095341e-01,-3.586033583511695211e-01,-8.786632376030925617e-02,-1.562259205070140322e-01,3.424416004906711275e-01,-1.600323779808836278e-02,1.431132975838603216e-01,2.564224733863315620e-01,-1.641254743615789824e-01,-1.367425919556901903e-01,1.467435020857448327e-02,1.000000000000000000e+00 +-1.000062547092753218e-01,2.430830704236164319e-03,2.442175729383090355e-01,1.268699051167839487e-01,-6.389123846329397560e-02,-8.558800399203210507e-02,7.249021145835105040e-02,4.298628554943320634e-02,-5.271419625106030932e-02,1.975425030271986404e-02,-1.593141663325880197e-01,-4.099908516596876273e-01,-1.996152652940482164e-01,5.384521498929881189e-02,-1.000000000000000000e+00 +-1.774447698492219727e+00,-4.764210619965775217e-02,-2.587953371292733840e-02,2.527078163863590299e-01,-3.506894908219360119e-01,-1.604509432277618775e+00,-1.148743176969121427e-01,3.476306938160337867e-01,5.950125588821331135e-02,-1.689561894348156068e-01,3.510138375728891758e-01,-1.285863892217228022e-01,9.889725276835834022e-02,-4.779867105539917227e-02,1.000000000000000000e+00 +-2.115847659490593113e-01,-4.378197457659226827e-02,-1.560226191263545115e+00,1.851037692264281831e-02,-5.186740647855887787e-02,-1.285080578288320674e-01,2.186092565573694302e-01,-7.563234413279935897e-02,-1.836555867319568658e-01,-1.715292381708927838e+00,-1.053609967823944532e-01,-3.007580256026237686e-01,-5.664314466742134124e-01,4.654207660509027122e-02,-1.000000000000000000e+00 +-1.059648825563055119e-01,2.083871134971554129e-01,-3.896191369327062226e-03,1.638163863683159416e-01,-3.325412330756547519e-01,-1.594674707958482429e+00,-1.889321220144114299e+00,-2.519374096022733989e-01,3.744445401896578929e-01,-1.431062364019081157e+00,2.905878641430151843e-01,1.188508934529553251e-01,4.879893502549231821e-02,-7.302283441518868379e-02,1.000000000000000000e+00 +-7.892925337285790210e-02,-1.425061922732849817e+00,-5.135339515188619419e-02,-1.063343810050886851e-01,-1.337699140567889744e+00,2.135462731273148618e-01,9.071650828203833838e-02,-1.555947836611493162e+00,-2.523557403743938998e-01,-1.186849304841148672e-01,-1.597332163655802995e-01,-1.787685024795254485e+00,1.745797844065838378e-03,2.254499206850570370e-01,-1.000000000000000000e+00 +-1.634685003002452675e+00,-3.251862351339719348e-01,-2.205331884740385773e-02,3.742916305651294850e-01,-1.476130033379834672e-01,1.032155617257237423e-01,-1.809921763154535901e+00,-1.758857849397117978e-01,4.028084449511597914e-02,-1.624796041221835630e+00,1.664756781513079820e-01,-1.071422644886549924e-01,1.558875726640141235e-01,-4.165906302948692297e-02,1.000000000000000000e+00 +-6.893588852403798040e-02,-1.502351679030673459e+00,-1.583392212954673361e+00,2.964158753223027770e-02,-1.679927090025262526e+00,1.960003535628031812e-01,1.493346616410116123e-01,-1.573801522268912567e+00,-3.857005873887964786e-01,-1.602374783816816084e+00,-1.260749596503021597e-01,-1.700132611113932102e+00,-2.914614680654354362e-01,1.977030193570133709e-01,-1.000000000000000000e+00 +-8.520846789723049430e-02,-2.651197730292323418e-02,-2.640371058762110146e-02,-3.274202584091251422e-03,-2.816433303798683840e-01,3.562988748599811573e-02,-3.048970621335784781e-01,2.403931662676393743e-01,5.712739777555907172e-02,-1.551001213505004861e+00,3.965997947137003821e-01,-1.509685871126274503e-01,-1.420543153268830849e+00,-3.946372188686265992e-02,1.000000000000000000e+00 +-1.668893144344563373e+00,1.919130586646038972e-01,1.022447000210930657e-01,5.905500448133854663e-02,-7.747440192085087995e-02,-1.740098495699613945e+00,2.704241472111564293e-01,-1.738207255835940712e-01,7.007512455348459401e-02,-1.715623810450799813e-01,1.464674195565787707e-02,-2.574836095767138455e-01,-2.205749926081212731e-01,2.136271854472032716e-01,-1.000000000000000000e+00 +-1.824841436379284199e+00,-1.748777344081649809e-01,6.115091744109193983e-03,2.462803020292880229e-01,-2.487724873459193076e-01,-1.450250498290739154e+00,-4.697050359614601822e-02,2.511430594108938763e-01,5.617082693944049077e-02,-1.450274777049656239e+00,1.015770576039708994e-01,-2.411988640947981544e-01,-1.607422220292852755e+00,-7.604821669259544809e-03,1.000000000000000000e+00 +-1.703817562252395001e+00,1.326709061849910554e-01,-1.517563722971078999e+00,1.060016902241523729e-01,-2.537611221091851110e-02,-1.625499260459003725e+00,1.413230949241252310e-01,-1.874853551714396604e-01,-1.370225761448278790e-01,-1.640928883789254389e+00,-4.872232664044925249e-02,-3.512887869343963021e-01,-1.330782873438719482e-01,1.884054816212058225e-01,-1.000000000000000000e+00 +-4.602927011417328185e-01,9.114648978546381619e-02,-1.142681042952753273e-01,1.181998566641369736e-01,-3.744886720748129005e-01,-1.683306709233135745e+00,-1.857226218067269841e+00,-1.306773969295676741e-01,8.745669333004414681e-02,1.081474503508523810e-01,3.039225988385292898e-01,-4.511980020317890583e-02,-1.668770004433653753e+00,5.705645149867304144e-02,1.000000000000000000e+00 +-1.609292721928712089e+00,-1.530845752972931129e+00,-8.278815713149145772e-02,-2.373194652547248318e-01,-1.642259528918827094e+00,-1.550507000140835645e+00,2.096919128243851516e-01,-1.647554901046019982e+00,-2.522533002550081949e-01,-9.368579713617050664e-02,6.839489592974815069e-02,-1.993235444764836606e+00,-1.704810005505085768e-01,7.884782078520123638e-02,-1.000000000000000000e+00 +-1.858420224161734247e+00,-1.734005282820396676e-01,9.726655340758569301e-02,1.248959239748611560e-01,-2.843455210772053432e-01,-2.268106966669906563e-02,-1.729137928685011705e+00,-2.638388678031160506e-01,2.428805271409836264e-01,1.609752903428374737e-01,5.216967446545667242e-01,-7.752550718208928160e-02,-1.454779407934555735e+00,-3.368056274862356314e-02,1.000000000000000000e+00 +-1.671006873242029567e+00,-1.355035901028246670e+00,-1.546876931567384483e+00,-4.882903868976315453e-02,-1.431346086050258259e+00,-1.412521778718294874e+00,2.408521092393056395e-01,-1.658598241196473033e+00,-1.382017859742168953e-01,-1.657214374755794140e+00,-2.481908330508241639e-02,-1.762757266728576244e+00,2.383848738496055497e-02,1.455584988399447055e-01,-1.000000000000000000e+00 +-1.902126388006115942e-01,1.996213562281251575e-02,1.008372071407214743e-01,-1.460752336310695121e+00,-1.718442887948363751e-01,5.289151945715515196e-02,-1.988938641605968449e-01,-1.404678157366375135e+00,-1.426704526380021543e+00,1.836450345611355395e-02,1.031901470487577022e-01,-1.214539315444648354e-01,6.219952114504016111e-02,-1.654616533531131140e+00,1.000000000000000000e+00 +-7.099264735690004324e-02,1.750364477230531568e-02,-1.062663212368153143e-01,-3.318805470065910868e-01,-2.759776562556524523e-02,-1.603913436870445519e+00,-1.353532462950584492e+00,-1.395546155641066988e-03,-1.210691504980217770e-01,-1.511115253680355153e+00,-1.480633014803731107e-01,-2.012839088420497746e-01,-1.997788643348255844e-01,1.007769414455348678e-01,-1.000000000000000000e+00 +-1.732361623182581800e+00,-2.102480727713798569e-01,4.589408489794362389e-02,-1.185027255246496658e+00,-1.496015716906844428e-01,-1.529090647151525317e+00,-3.767531196928636805e-01,-1.228746858778276163e+00,-1.513836205519304201e+00,1.311451637954682559e-01,5.498183059552660401e-01,8.708600070403887949e-03,6.129938060113643283e-02,-1.559814424680894263e+00,1.000000000000000000e+00 +-2.835840568649770910e-02,-2.146285735581612841e-01,-1.579610604303776533e+00,1.664476210172569082e-02,-5.099273752013008809e-02,-1.725949610073979512e+00,-1.467398823985591960e+00,9.482147057008569191e-02,-3.186590355189626578e-02,-4.781462003049682941e-02,-3.124998176502554736e-01,-2.756619479040776644e-01,-2.832558409756105178e-01,1.619123252324499995e-01,-1.000000000000000000e+00 +-2.367772758105469166e-01,2.386147726201319208e-01,-1.737780012137604135e-01,-1.355794095407284461e+00,-2.679290745175850752e-01,-1.644752444669923097e+00,-1.939455022430933262e+00,-1.868695451010987663e+00,-1.400941658776029719e+00,-1.688002662345677640e+00,3.606506533467307429e-01,-7.625979515244935003e-03,-4.330127823671142684e-02,-1.553362446245406980e+00,1.000000000000000000e+00 +4.032978658428024166e-02,-1.676820021646022507e+00,1.047351288964794014e-01,-1.274758592562561521e-01,-1.655047118035765985e+00,-1.346487776545435500e+00,-1.527216883896012289e+00,-1.410111606579167454e+00,-2.539982374704825852e-02,-1.713267267046358455e+00,-1.014092723436062216e-01,-1.806922107887470208e+00,-9.253453197220207205e-02,1.441451858524561147e-01,-1.000000000000000000e+00 +-1.731129921112224634e+00,-2.901473907450236034e-01,1.180327861648885340e-02,-1.348904830804334765e+00,-2.564662506036542444e-01,2.657611077804407709e-02,-1.800769790691219896e+00,-1.725256531502176394e+00,-1.582781276073582655e+00,-1.587734247659576381e+00,1.846722494212687804e-01,-8.745442992928353920e-02,2.461445513982963088e-02,-1.550950765867309844e+00,1.000000000000000000e+00 +-2.283456737923411917e-01,-1.581192929304472417e+00,-1.698892334002197302e+00,2.936243047887290847e-02,-1.475603940072048648e+00,-1.481309278271474517e+00,-1.344846732616987506e+00,-1.423359469725386628e+00,-7.429594089515155486e-02,-2.249300418856977068e-01,-8.150346857611970308e-02,-1.783421326054325684e+00,-1.956288978727144245e-01,3.423808630989662039e-01,-1.000000000000000000e+00 +-1.392029844521847359e-01,1.585516293481104011e-02,1.649686505433222983e-01,-1.455942057796049349e+00,-2.565270886891928837e-01,-2.995841363766689816e-02,-2.225188616804514574e-01,-1.304550654947434385e+00,-1.543543743723497785e+00,-1.625506112240582501e+00,2.646686934361995869e-01,3.536394651466978623e-02,-1.596770214722755821e+00,-1.718505474909242325e+00,1.000000000000000000e+00 +-1.709571844246771555e+00,7.258692646837865137e-02,-6.937956961166810710e-02,-1.759107096036754969e-02,1.023153468347885037e-01,-1.396731656103666064e-01,-1.290324032684116107e+00,1.744754247071788056e-01,-2.274937312973350501e-02,-1.542470932907623160e+00,-2.143952135731703179e-01,-2.178828468681655861e-01,-4.685982140614411429e-02,3.202479980416738092e-01,-1.000000000000000000e+00 +-1.783884668386667149e+00,-1.272375533214497589e-01,-6.483306964860351129e-02,-1.317393170180484407e+00,-1.785553028497116612e-01,-1.380977492719456912e+00,-2.398079458622670024e-01,-1.290907093889349877e+00,-1.351572903045891394e+00,-1.550242759707908036e+00,4.198473156596549960e-01,-1.242770486723988876e-01,-1.525877099216195854e+00,-1.554149498054001377e+00,1.000000000000000000e+00 +-1.734648507628440139e+00,2.192826484018607358e-01,-1.489106692551441569e+00,5.007721728629293406e-02,1.398957448036743448e-02,-2.921742187204966895e-02,-1.320288395167840356e+00,3.292664544724834830e-01,-1.795623271947691391e-01,-1.466962564866716723e-01,-2.662897463892865274e-02,-1.738436898994596969e-01,-3.817963162764551055e-02,6.503463516527510646e-02,-1.000000000000000000e+00 +-3.029885861559754590e-01,2.550953935420102736e-01,-1.103307825250942481e-01,-1.420240129037396049e+00,-4.057700325150146448e-01,-1.432037726658133003e+00,-1.742574743379801561e+00,-1.706346924550034450e+00,-1.610482645334571306e+00,2.052113966044370463e-01,2.691827908860374707e-01,-5.868454248888337749e-02,-1.688925815284796350e+00,-1.731488086207674471e+00,1.000000000000000000e+00 +-1.587169441767515288e+00,-1.449215424064308477e+00,1.056750713765475247e-01,1.005523967565410681e-01,-1.643903047784339266e+00,5.393450286841017238e-02,-1.355966372117012320e+00,-1.480728708640227875e+00,-1.192444720770064470e-01,-1.617205607701764691e+00,-2.001940707974544931e-01,-1.854276215264260674e+00,-2.120968756661331667e-01,1.249548214378189137e-01,-1.000000000000000000e+00 +-1.659231896786515836e+00,-2.734026343955329175e-01,1.614267437122218207e-01,-1.308580366437199105e+00,-2.585744813783290574e-01,-7.277887054484513674e-02,-1.964579895708931545e+00,-1.988637864745677053e+00,-1.417206261440844273e+00,-1.064945755041092068e-01,2.014194076204649519e-01,-1.354914430258996605e-01,-1.491698880763357549e+00,-1.551242130728441859e+00,1.000000000000000000e+00 +-1.720446547882567012e+00,-1.541241393438607110e+00,-1.630467902235066324e+00,-7.411202833128890943e-03,-1.654541032018014945e+00,2.623612216276595133e-01,-1.480764894592184522e+00,-1.460466737184188180e+00,-3.295613141155256387e-01,-1.335075346053341272e-01,-1.273070084023267268e-01,-2.127679633637005630e+00,-2.464723373152804542e-01,8.022582804182054605e-02,-1.000000000000000000e+00 +-2.109079585833683601e-01,-1.555892998862504095e+00,1.107885884130413306e-01,1.177215642018687169e-01,-1.903635417489270898e+00,6.149415741520453116e-02,-3.799748269154470948e-01,-1.353483645674147029e+00,-4.930015796167842856e-02,1.531146828385572256e-01,2.740872821745550247e-01,-1.725855934989520168e+00,1.813254786623140491e-01,-7.054318243692112023e-02,1.000000000000000000e+00 +-9.734287508391470389e-02,-4.678911477042982381e-03,-2.354664895714689635e-02,5.100745385274149668e-03,-1.331449935855308631e-01,-1.598625887398838596e+00,2.778746322995367390e-01,4.813444104940975410e-02,-1.625933685672424356e-01,-6.697790788327151468e-02,-1.749605469280097747e+00,2.623302391327739791e-01,-2.634792860014013027e-01,1.665935370787189895e-01,-1.000000000000000000e+00 +-1.817353774439702363e+00,-1.561445054303381763e+00,2.588251245924956534e-02,2.035376981561484999e-01,-1.766443070947268268e+00,-1.296972269680546797e+00,-2.543772414319446229e-01,-1.398205211604792941e+00,2.092399351858046930e-01,1.687208519899038306e-01,2.441626287880789281e-01,-1.858209074295685825e+00,-1.966273036325641155e-01,4.292932778109619096e-02,1.000000000000000000e+00 +2.703596192017793109e-03,-2.329838366977103892e-01,-1.380333551207818088e+00,2.044434338582787891e-01,-4.081958202166395544e-02,-1.681344439141456926e+00,1.476627032024580011e-01,-4.993020226228646330e-02,-1.875771998174172728e-01,-1.762007651436396705e+00,-1.557788011049577648e+00,2.837909499650285161e-01,-5.845342207235090792e-02,-9.376632748452173871e-02,-1.000000000000000000e+00 +-2.606686354658470095e-01,-1.408856249495505031e+00,1.880920528500927280e-01,1.835990601756801177e-02,-1.861442820461840064e+00,-1.652352721661132007e+00,-1.866726146333868464e+00,-1.805367024170868362e+00,-4.902227545481459281e-02,-1.560841106272784495e+00,2.205571318753309085e-01,-1.592737578886485927e+00,-2.845762836725848621e-02,1.370102237967169845e-01,1.000000000000000000e+00 +-9.844964916686858858e-02,-1.561368624864145183e+00,-3.914124367162109819e-02,-1.080476455056877255e-02,-1.722930338042492515e+00,-1.544141333393409976e+00,1.157126277790064411e-01,-1.727777618033043527e+00,-1.670565305629349573e-01,2.974758626022222519e-02,-1.581477342471812042e+00,-1.323120858919411447e+00,-2.100339313983373701e-01,1.448187770874282965e-01,-1.000000000000000000e+00 +-1.957458263369451723e+00,-1.694274989097745543e+00,5.454911108419973481e-02,1.805943278212934489e-01,-1.756092263705557599e+00,-8.595899367681159031e-02,-1.696067414366745130e+00,-1.712023576270820469e+00,2.060492194970154900e-01,-1.398335988329474810e+00,4.410001979147892825e-01,-1.719686354629639036e+00,-6.051566718506100340e-02,3.339095417045597619e-02,1.000000000000000000e+00 +-7.866696516093840685e-03,-1.616787244372446697e+00,-1.740116896609308572e+00,8.579584135912943221e-02,-1.439416796296014933e+00,-1.523227070758474966e+00,3.758797173705020200e-01,-1.769922243342483492e+00,-2.134407608641714915e-01,-1.517898778848180585e+00,-1.492718695300921272e+00,-1.078979009657375254e+00,-8.830119739724537342e-02,2.032437865555704604e-01,-1.000000000000000000e+00 +-2.053848989919285495e-01,-1.323498679920936505e+00,1.600594600689232427e-01,2.127530971190854658e-01,-1.812974538182991369e+00,-9.828391746720832600e-02,-3.237482397158925052e-01,-1.376107670949456407e+00,1.857257065594705159e-01,-1.536261310236082256e+00,3.252889388876294974e-01,-1.582426747967575320e+00,-1.644393972918431368e+00,-7.704793882180768771e-02,1.000000000000000000e+00 +-1.669671522325552715e+00,-4.738520888333819236e-03,-9.011259086863371293e-02,-2.387991865509341005e-01,-5.806701719645110393e-02,-1.206243669493144155e-01,3.369023832387631479e-01,-7.667184510194216540e-02,-6.384074432003430943e-02,-2.220560303315098627e-02,-1.677397347136823980e+00,2.831200735921008960e-01,-2.766936859144445426e-01,1.560540392900901796e-01,-1.000000000000000000e+00 +-1.777587007008249564e+00,-1.704171978040527335e+00,5.913101480819421552e-02,1.428869347821797309e-01,-1.901771407341925402e+00,-1.759160581014954428e+00,-2.726838542570871327e-01,-1.341989046626985482e+00,1.149581111517015558e-01,-1.541115658685354717e+00,1.677606151171127657e-01,-1.464461059124410713e+00,-1.691688504084269074e+00,-3.104441766574880454e-02,1.000000000000000000e+00 +-1.741418645816155575e+00,2.527459606765928291e-01,-1.635744431626482864e+00,-4.135823844505452651e-02,1.560523028071861273e-01,-9.996675928048688597e-02,3.331306303388317236e-01,-1.182858312212287932e-01,5.816274462819263913e-02,-1.764772416562902180e+00,-1.477910347247076261e+00,2.989494469385867159e-01,-1.051717978886456129e-01,1.921403438897218707e-01,-1.000000000000000000e+00 +-3.065731389712608168e-01,-1.342546641072029701e+00,5.964527988909403000e-02,1.711393678184557343e-01,-1.761287550914799827e+00,-1.611029149319431752e+00,-1.765853023869925487e+00,-1.888379046610273448e+00,1.002820626583308672e-01,-1.109509776410169124e-01,2.188709029554854468e-01,-1.576157910757060021e+00,-1.521945969532204845e+00,2.070355351194339347e-02,1.000000000000000000e+00 +-1.623549583779014815e+00,-1.350051115786569111e+00,-8.869050426022510522e-03,-1.874223112652556866e-01,-1.633442654667898397e+00,1.375420098898255117e-01,1.168110294924096237e-01,-1.569377840081284781e+00,-1.602564120101649947e-01,-1.325093610505566621e-01,-1.438045060031879707e+00,-1.050581181443586365e+00,-2.893936297597720642e-01,7.855108334561576588e-02,-1.000000000000000000e+00 +-1.658154207372490641e+00,-1.789134629255389797e+00,4.054766945845136861e-02,4.274914633188270363e-03,-1.797182622929081264e+00,2.559107562684073689e-02,-1.718806878310818353e+00,-1.755749373633363364e+00,7.060323412260165188e-02,-1.084276109706693719e-02,3.609575922405105497e-01,-1.714998993682882000e+00,-1.574951641335678110e+00,1.149572272760127495e-01,1.000000000000000000e+00 +-1.847961507423650840e+00,-1.473921222549971821e+00,-1.617837932133893375e+00,-7.103102034326941316e-02,-1.571069807864609036e+00,3.573476889499199727e-02,1.970289265713811022e-01,-1.739229085965009469e+00,-2.775349152229639182e-02,-1.680739634226039003e+00,-1.587025168513149342e+00,-1.236748239033638841e+00,6.935436000199549089e-02,2.040125069256005230e-01,-1.000000000000000000e+00 +-1.722904466803558410e-01,-1.411617617088636312e+00,2.916437094735713220e-04,-1.406253287690810927e+00,-1.615679981746658056e+00,-2.763953409871925445e-03,-3.950897247081973651e-01,2.430954492310867798e-01,-1.311681842017352828e+00,-2.200217028509704811e-02,1.703654951829411679e-01,-1.720433172383417197e+00,1.308044939654900291e-03,-1.417741158285068082e+00,1.000000000000000000e+00 +-6.888987672868050749e-02,-2.206762982686303332e-01,-1.750101396499676581e-02,-3.727240288538255508e-03,-1.083627958668931801e-01,-8.696675784860415093e-02,-1.319049022372315028e+00,5.257817621846899714e-02,-1.711129698146304778e-01,-1.555886570316086903e+00,-1.677814988070778224e+00,3.915202101818402447e-01,5.543780834425612869e-02,1.390389113262543719e-01,-1.000000000000000000e+00 +-1.727238257218281170e+00,-1.615530576147521602e+00,7.869056225858209186e-02,-1.423059514455122798e+00,-1.761192441644305884e+00,-1.630316956634427994e+00,-2.789120381103245006e-01,1.941822238716390814e-01,-1.518121723396330669e+00,-1.238462931609404100e-02,2.742496492662739316e-01,-1.645835021526846909e+00,3.721937042066567752e-02,-1.627505454659611495e+00,1.000000000000000000e+00 +1.470635122441772691e-02,-2.056113108150927626e-01,-1.764448694346967272e+00,1.968965126762830531e-01,-1.287913227649226389e-01,-1.238240475078797309e-02,-1.315648596190898134e+00,2.225161798906181432e-01,-1.704424821427150682e-01,-1.000365041999348586e-01,-1.533319399388676008e+00,2.921635897646819857e-01,-5.860574376450541911e-02,2.784987048414280242e-01,-1.000000000000000000e+00 +-2.494620197685982754e-01,-1.448566913248975796e+00,7.057694579524713407e-02,-1.273212303503205467e+00,-1.679047110450799574e+00,-1.588867586909664009e+00,-1.771914972690083134e+00,-2.517244837878088726e-01,-1.480580985639242897e+00,-1.669982779328623934e+00,3.614806337794052582e-01,-1.633837644043817372e+00,4.781815759110948083e-02,-1.560544856181362405e+00,1.000000000000000000e+00 +6.107929177820334288e-02,-1.534902656447697966e+00,-8.607572626113366154e-02,-1.053562469886166753e-01,-1.463402905170154700e+00,2.486780937748925002e-01,-1.327669916737666655e+00,-1.444079856760119851e+00,-2.251288416680337356e-02,-1.548686440706024792e+00,-1.576795558854126700e+00,-1.162050860905266214e+00,-1.797664644595173833e-01,8.235556289785103679e-02,-1.000000000000000000e+00 +-1.620915957675557850e+00,-1.759205929598766538e+00,6.587282363154096931e-02,-1.260937479341356138e+00,-1.825365113928211436e+00,-5.420203464332983695e-02,-1.856265590647504293e+00,-3.232448348203816324e-01,-1.506331929169810291e+00,-1.790950030579560037e+00,2.915247309809219645e-01,-1.553405508313238981e+00,1.064510860731728847e-01,-1.593081305610142051e+00,1.000000000000000000e+00 +-4.612223926753210301e-02,-1.491967959290764867e+00,-1.557513752337976953e+00,-5.320920254984169706e-02,-1.565674533919902078e+00,-6.933620921740490939e-03,-1.436743443340090298e+00,-1.483034265310440913e+00,-1.958793561744313072e-01,-5.822026887820264812e-02,-1.749475100938908234e+00,-1.367204286344534836e+00,-3.377935618256747152e-02,1.192202087137585464e-01,-1.000000000000000000e+00 +-4.031583925895292353e-01,-1.532565253996989396e+00,9.855479329093051744e-03,-1.389311833487676839e+00,-1.760960705542248483e+00,-1.210071189044274048e-01,-1.758028352391565741e-01,2.497081893375624473e-01,-1.526797317251125996e+00,-1.580968414753146645e+00,3.132766109992452530e-01,-1.604830716095155818e+00,-1.399353857373611509e+00,-1.618918810056891644e+00,1.000000000000000000e+00 +-1.752387993950347056e+00,-2.153427027813585581e-02,3.324646213538123146e-02,-1.248963934916344776e-01,6.501245133315829844e-03,-1.692417971345222094e+00,-1.397884025322287727e+00,1.653510492758513140e-01,-9.216492629239791956e-03,-1.832012590804132479e+00,-1.872767888166050554e+00,2.915105817056929394e-01,-1.079166206186459309e-01,2.912668576802653342e-01,-1.000000000000000000e+00 +-1.806184649482642346e+00,-1.773788783419828707e+00,7.595038272615574348e-02,-1.353747587535946373e+00,-1.926395246773682013e+00,-1.608648402785678888e+00,-2.940518864491442619e-02,1.536771172595196577e-01,-1.600446038430091589e+00,-1.568954173464251145e+00,2.377243129796963450e-01,-1.702954340893876317e+00,-1.540268035867445651e+00,-1.525964682609035838e+00,1.000000000000000000e+00 +-1.681853001192156416e+00,2.025248851261629568e-02,-1.507927323068977588e+00,6.015652368434671465e-02,6.787768755262589548e-02,-1.797346668678856396e+00,-1.328492471023882127e+00,1.083374242081695804e-01,-6.807194630225105181e-02,-1.536472371808336179e-01,-1.544991427545987506e+00,2.496879419231946562e-01,-2.586500999091325692e-01,2.472464564743200865e-01,-1.000000000000000000e+00 +-6.512889928647674909e-02,-1.280157641621820330e+00,8.548411526008756200e-03,-1.308195710131301981e+00,-1.759788721990152682e+00,-1.442917604990261449e+00,-1.893052923952196265e+00,-1.913558088187841955e-01,-1.400433029598109114e+00,-5.406451812426182196e-02,2.908001571900953874e-01,-1.654973814245627617e+00,-1.488252776308071290e+00,-1.629965511123137256e+00,1.000000000000000000e+00 +-1.600806204135150956e+00,-1.511861641530125588e+00,-4.237261964877586662e-02,-1.027349323790079094e-01,-1.534775695208126400e+00,-1.609318802955511662e+00,-1.415221590280331787e+00,-1.349255135992462717e+00,-1.603016048133207860e-01,-1.709813196142691449e+00,-1.498028189087612372e+00,-1.354158043503653763e+00,-1.324975882836215146e-01,2.711132403917941303e-01,-1.000000000000000000e+00 +-1.815694150620314362e+00,-1.841070891138648191e+00,-2.135129424367295248e-01,-1.451394165585441653e+00,-1.747533020562963335e+00,-1.200370342065606132e-02,-1.923975840908812129e+00,3.494210435125721936e-02,-1.425449270516573108e+00,-4.246354558705554838e-03,1.056439022865369171e-01,-1.676809418735673018e+00,-1.449035505955657666e+00,-1.531577706593846289e+00,1.000000000000000000e+00 +-1.604075385534005527e+00,-1.247560546127565617e+00,-1.672255369508738987e+00,1.065211685963571020e-01,-1.756483006388495305e+00,-1.617196120846714269e+00,-1.389449269607853088e+00,-1.586122669296846199e+00,-4.219247823196448088e-02,-1.204773546433365317e-01,-1.559777708682754449e+00,-1.272675445601217925e+00,-1.568985920919612476e-01,2.787010337220571787e-02,-1.000000000000000000e+00 +-8.034254508214096202e-02,6.880725407078074063e-02,2.094215261303540299e-01,1.446253521446881907e-01,-2.494568563956204299e-01,-1.612589456454816883e+00,-1.591140610263561961e-01,1.892714292746733307e-01,2.185783413107370288e-01,9.944979984667769168e-02,-1.197593196312220387e+00,8.774288695775299407e-02,1.763860410608001916e-01,1.613257325364919892e-01,1.000000000000000000e+00 +-1.886644372382341728e-01,-1.619309640274923801e-01,2.683354162221169431e-02,-1.413968591452252621e-01,9.808653926713208071e-02,-7.657823144474243393e-02,1.303211970396588870e-01,4.133184237415543638e-03,2.793182479116006200e-02,-1.535301780469503274e+00,-1.300594688837936885e-01,-2.232958274315878788e-01,-1.681883951636097274e+00,-2.720573586526732202e-01,-1.000000000000000000e+00 +-1.778715650654681291e+00,-1.262469228272111998e-01,6.962258263356069232e-02,1.095996415049501210e-01,-1.988037976727128342e-01,-1.175735577159129908e-01,-2.309365762557876178e-01,1.224684282068461416e-01,2.462409602188055102e-02,4.908526452607276519e-02,-1.363465905985415283e+00,-1.910421864649460011e-02,-5.795484461945044075e-02,-1.270035108333279483e-01,1.000000000000000000e+00 +-4.142652836984381248e-03,-6.013365015874117675e-02,-1.703420334445887141e+00,3.742735829120897445e-02,-4.025439783818521994e-02,1.289073532965347157e-02,3.107700852026719085e-01,-2.259629875830789780e-01,1.670993057174276208e-02,-1.019608591323673125e-01,-4.382862033882690272e-02,-4.585080608863484342e-01,-1.692172413371813899e+00,-5.792533591758292078e-02,-1.000000000000000000e+00 +-3.935154334802061782e-01,3.638872533473028192e-02,1.395259957821806951e-01,1.664661237197593568e-01,-3.103391820041769833e-01,-2.005443684678374638e-01,-1.884110053262168627e+00,-1.345063215107585031e-01,2.550919602531565022e-01,-1.361261227364313431e+00,-1.290216119102986614e+00,1.336215441564989337e-01,6.254152091303144534e-03,2.100087729950954329e-02,1.000000000000000000e+00 +-9.669110204168082645e-02,-1.775363854018477516e+00,1.069742929339808635e-01,-1.322016069705599928e-01,-1.547590907751123179e+00,4.749105067501178251e-02,1.252294878250418686e-01,-1.594537320930422553e+00,-2.024532740445836865e-01,-1.485978417205851887e+00,-1.276331802522947534e-01,-1.692798225641222842e+00,-1.719467001867377220e+00,-2.401686334724826843e-01,-1.000000000000000000e+00 +-1.818404423502115286e+00,-1.613776695434923358e-01,-2.963013171964887227e-03,1.003119539558565421e-01,-5.117550258241547745e-01,-1.840998639084066912e+00,-1.622614867210045109e+00,-1.900972555314071055e-01,1.886811273742927675e-01,-1.576598230931666178e+00,-1.384814828596028446e+00,2.764715054184119225e-02,-1.234786186862628088e-01,-2.848869597594009748e-02,1.000000000000000000e+00 +-8.409014969526823480e-02,-1.523030598750639619e+00,-1.651004473613457479e+00,2.050129427916006497e-01,-1.662422657414257543e+00,4.900743176672640172e-02,2.953259378908753563e-01,-1.727332438860938346e+00,-1.736397564513021963e-01,-1.754096263969709690e-01,-1.773807575012419369e-01,-1.803500231186919267e+00,-1.772739215935786206e+00,-3.388655478452351488e-02,-1.000000000000000000e+00 +-3.504893168320986074e-01,1.287453279306145226e-01,-9.912367241549739638e-02,5.441291793956634648e-02,-3.067496111818439619e-01,-1.847740748782476627e+00,-2.809401432452648950e-01,2.606615198081184692e-01,-8.298507598924294293e-02,-1.528360789793670182e+00,-1.243673834577912807e+00,8.332409430469578382e-02,-1.536772762431679151e+00,-2.276252096846678452e-01,1.000000000000000000e+00 +-1.669135017685616562e+00,1.878572457368295479e-01,-1.109103344102813082e-01,1.084848744675842580e-02,-8.542400125995765203e-02,-1.858098624717458680e+00,-1.107316092311189104e-02,-4.160138555491198209e-02,-2.961452456263555799e-01,-1.711925191657831302e+00,-1.727054823269065509e-01,-3.765547362240309059e-01,-1.915823137520314301e+00,-1.315770328525295851e-01,-1.000000000000000000e+00 +-1.800479249082186151e+00,-3.386831473970310924e-01,2.229926830865746057e-01,1.062297083684143884e-02,-2.752908599739183892e-01,3.925282309845710971e-02,-3.015683531867457412e-01,2.297082100678827232e-01,1.058116123622843130e-01,-1.433791716055095034e+00,-1.166489686173616480e+00,-4.472131141237700724e-02,-1.571037642506694976e+00,-4.200183994858083109e-02,1.000000000000000000e+00 +-1.615602691154683912e+00,-3.740395205227235731e-02,-1.741864658683469180e+00,-8.026571595103948464e-02,-6.875727441416802643e-02,-1.691643264385989998e+00,1.540923994518740570e-01,-6.881789309428396062e-02,-6.992666548691262030e-02,-1.090237072062237716e-01,-6.974526401931734065e-02,-2.892980571982610161e-01,-1.755724205511214553e+00,-1.814918365608780837e-01,-1.000000000000000000e+00 +-2.677256404895123065e-01,1.835013192157280426e-01,-5.410002376291028758e-02,1.960243382670116552e-01,-1.888170511251534012e-01,-5.086120994193638750e-02,-1.848774520891739437e+00,-2.360222779043457952e-01,6.986521036030590714e-02,3.710310692206895966e-02,-1.267617760230665969e+00,7.705794214828859401e-02,-1.624174807600006210e+00,7.589070361533122033e-02,1.000000000000000000e+00 +-1.483100626972129632e+00,-1.498241130023366896e+00,-1.839484030280719717e-01,-1.599276150165117594e-01,-1.677524565791744582e+00,-1.244818282776545715e+00,1.844648780999630366e-01,-1.531863635483006636e+00,-1.738348777911911669e-01,-1.734127336249420948e+00,-1.460549832600117337e-01,-1.760490064524646625e+00,-1.703402789107563597e+00,-7.417356541098861500e-02,-1.000000000000000000e+00 +-1.714556915953274085e+00,-1.098793838346878715e-02,1.397241074190134769e-01,1.188655975385968788e-01,-3.606397251872860421e-01,-1.623217860346481745e+00,-1.843379238712378632e+00,-2.198912233817513218e-01,5.596523583124383971e-02,1.984229244991772556e-01,-1.302687102631844951e+00,1.540032728930624817e-01,-1.707395301387358844e+00,8.229962282112589234e-02,1.000000000000000000e+00 +-1.552023699997773010e+00,-1.589629870687183866e+00,-1.546490125968891016e+00,-9.512728416417828847e-03,-1.623098593056410577e+00,-1.543080591010138392e+00,3.550992655544662480e-01,-1.651747859497800341e+00,2.138715259196787011e-02,-4.614252337635053880e-02,-1.099106707827380153e-01,-1.797091809321512024e+00,-1.723231873567492078e+00,-1.951141050461503434e-01,-1.000000000000000000e+00 +-1.606564658516367594e-01,1.619450152250831165e-01,1.865756135283968553e-01,-1.459656607146854412e+00,-1.715882654233442106e-01,-1.434470999144190406e+00,-1.193565190757426703e-01,-1.293034381978339198e+00,-1.521215319236361108e+00,-6.652759862985353767e-02,-1.407943939743753869e+00,8.327266281168387019e-02,-9.784064273089676522e-02,-1.498740980415620871e+00,1.000000000000000000e+00 +-1.483323083342633075e-03,6.967090893741754998e-02,6.278870422176768828e-02,-3.086306037876339337e-02,4.902401205912280957e-02,-1.521000431313429102e+00,-1.264142252532288513e+00,9.533858173133910596e-02,-1.011741668662529819e-01,3.310564112257621816e-02,-8.191935135946362689e-02,-3.186349310486205622e-01,-1.898669358964544029e+00,-4.496879339874823578e-02,-1.000000000000000000e+00 +-1.573620891016235035e+00,-1.329982948840280343e-01,2.399503310453289795e-01,-1.330488862416024265e+00,-2.765375378185703781e-01,8.808199743137788695e-02,-2.648386870994752784e-01,-1.379876580550749576e+00,-1.383938581042170268e+00,-8.501291019743140165e-02,-1.165802340721067143e+00,1.507153745526593203e-01,1.395678131681016287e-01,-1.418622966305338906e+00,1.000000000000000000e+00 +-2.414752338762555239e-01,6.263748091812453012e-02,-1.582288633664975963e+00,-2.138551415080261475e-02,-1.432209397597079270e-01,-1.564218586364576780e+00,-1.325323773803723570e+00,8.635628717131471621e-02,-9.389897569821684109e-02,-1.765087260484488496e+00,-6.942818194977197488e-02,-3.223149105900656175e-01,-1.783911969651682083e+00,-1.724922661524661005e-01,-1.000000000000000000e+00 +-2.663953337290222434e-01,2.367978735580491545e-01,-6.125756833378044608e-02,-1.600320213409630377e+00,-1.746247378962873520e-01,-3.198386486924113814e-02,-1.769758135789326392e+00,-1.706661585217408961e+00,-1.625355656722051734e+00,-1.457244178579490068e+00,-1.335913049256137963e+00,1.196030340183451696e-01,-1.642401908248225795e-01,-1.456435876411096952e+00,1.000000000000000000e+00 +-1.232307318054817469e-01,-1.646559127435431868e+00,-1.688108478516482103e-01,8.679061400071028110e-02,-1.626846068315488836e+00,-1.472854188583361923e+00,-1.365579138009763049e+00,-1.466056801914429553e+00,-1.148785375351140531e-01,-9.280445394721349717e-02,-2.086263845777263509e-02,-1.798345062543814654e+00,-1.807806298910443310e+00,-2.312401086807840955e-01,-1.000000000000000000e+00 +-1.797916284381434915e+00,-2.911877350873054437e-01,9.552965566583154766e-02,-1.199357917186429034e+00,-2.857742411791293069e-01,-1.605346495511491778e+00,-1.772284905664825549e+00,-1.665468436189193158e+00,-1.587105210501043695e+00,-1.713909513833273612e+00,-1.395969565879321861e+00,2.700363356400100612e-01,-3.133153261923687927e-02,-1.466512092594051575e+00,1.000000000000000000e+00 +-2.816570040640491157e-02,-1.490270920705007551e+00,-1.697566414689200442e+00,1.066246706088792573e-01,-1.536164302589434261e+00,-1.538328845280117152e+00,-1.356330561620534558e+00,-1.514472207116063451e+00,-1.094905748070687523e-01,-1.800177760814607675e+00,-1.216267770704610451e-01,-1.651142758533824972e+00,-1.955366473810422168e+00,-1.231567332270106924e-01,-1.000000000000000000e+00 +-2.882849109278932676e-01,1.983093618883552678e-01,-1.463093206844333360e-02,-1.322092918898141845e+00,-4.602704639099099992e-01,-1.605476279094690151e+00,-3.759189111112950510e-01,-1.369264970446639085e+00,-1.660121345876027110e+00,-1.739860774927726172e+00,-1.344866286821288837e+00,1.542177775336922019e-01,-1.668878289511315227e+00,-1.520740909468941915e+00,1.000000000000000000e+00 +-1.732333604683580441e+00,9.111658731908463260e-02,-4.803802635457201514e-02,-1.279143932227881919e-01,-2.649329842482005953e-02,8.165733683507830909e-02,-1.263156309110212039e+00,6.513957347289628386e-02,4.103908537035586002e-03,-5.453058692077440012e-02,-8.022016489145190887e-02,-1.727359390460022626e-01,-1.734239058057392757e+00,-3.904902587740946007e-01,-1.000000000000000000e+00 +-1.694631322106838534e+00,-2.318305759969209179e-01,1.452105664682052100e-01,-1.296445994927566847e+00,-9.002133292421310440e-02,-6.386742495464487954e-03,-2.279265489238869458e-01,-1.223730498180746640e+00,-1.310785738689320024e+00,-1.641340256361092376e+00,-1.230315848916744281e+00,1.856264504335765064e-01,-1.656945810802020302e+00,-1.748667142064466384e+00,1.000000000000000000e+00 +-1.450854206908660071e+00,7.828524456119041741e-02,-1.578406784248261419e+00,-2.512896826671837114e-02,-1.843895802506676229e-01,-1.191981600187149992e-01,-1.336143794401053242e+00,5.477238872298689082e-02,-2.040918138447603281e-01,-1.578286250190459761e+00,1.322295879957060549e-01,-1.209625784490078182e-01,-1.599368150749488127e+00,-1.874933429591453538e-01,-1.000000000000000000e+00 +-1.045059878192874936e-01,3.284108220258633581e-01,-1.804137053924599610e-02,-1.415434285098199352e+00,-4.356646779711194073e-01,4.788784486837571741e-02,-1.877641497993017383e+00,-1.743155189450987796e+00,-1.585436495856220152e+00,-2.573786078104545877e-02,-1.387310204173998418e+00,1.943702255628812492e-02,-1.487659000524529818e+00,-1.554790847506030582e+00,1.000000000000000000e+00 +-1.777631063812934009e+00,-1.474989866021839457e+00,-1.378492663939756557e-01,9.719130114846494251e-02,-1.544659298121808133e+00,-4.345709155336191509e-03,-1.300914268229148796e+00,-1.342089156350444235e+00,-1.443842580417471766e-02,1.101393450782136851e-01,-4.458906771815638465e-02,-1.947807337208174339e+00,-1.783904632762413067e+00,-1.539862301179337478e-01,-1.000000000000000000e+00 +-1.875221005130098373e+00,-3.027948236787822434e-02,1.577189260360888357e-01,-1.297525857832602103e+00,-4.466360637113593879e-01,-1.507951520190433659e+00,-1.781636723359887986e+00,-1.742255382993029400e+00,-1.490422196791556297e+00,-2.957936848575772396e-02,-1.097956120195445751e+00,1.158530889140248904e-01,-1.599658517211244968e+00,-1.684814893443490114e+00,1.000000000000000000e+00 +-1.413746274172867690e+00,-1.439223735272604943e+00,-1.606212425722667536e+00,2.463671345926384815e-01,-1.672731456423659946e+00,2.268676841522668242e-03,-1.316526726628725941e+00,-1.262398307739135195e+00,-1.777197614280082993e-01,-1.813590259802646987e+00,1.313756346090966254e-02,-1.618523643863773342e+00,-1.859687251220742299e+00,-2.495201522744032452e-01,-1.000000000000000000e+00 +-3.297252099680703097e-01,-1.371755504998362296e+00,-2.079422813263466507e-02,3.886685963712142033e-01,-1.856985700819249185e+00,-1.739845605796722872e+00,-2.518312292333283464e-01,-1.274371212320373870e+00,-4.745129115520065055e-02,4.382091814791814338e-02,-1.278635974363860894e+00,-1.420689039846026347e+00,-3.444506503819735888e-03,-2.982589230851817944e-02,1.000000000000000000e+00 +-2.124491174803492255e-01,-2.139723931994451100e-01,-3.322687787579704788e-02,7.088355859889972077e-02,-1.097689220635640478e-01,-1.607445440968523709e+00,4.407797109396910940e-01,-1.427730048091011839e-01,-9.409292144042250006e-03,-1.654717190782132308e+00,-1.578590319863247160e+00,3.807029358492156623e-01,-1.860152216807540171e+00,-7.885114569420309527e-02,-1.000000000000000000e+00 +-1.653082354576353996e+00,-1.504985772276845823e+00,8.862611021251846910e-02,-8.201785062421099171e-02,-1.879265445719659366e+00,-5.944675608816845508e-02,-3.709557306042644553e-01,-1.315055201264324181e+00,7.171794880851997678e-02,-5.131578626564396728e-02,-1.251328659172288083e+00,-1.482978491176007774e+00,6.440516665073076075e-02,-5.049820195005234186e-03,1.000000000000000000e+00 +-8.791229388322152616e-02,-5.836882405768635540e-02,-1.481081774714787969e+00,-6.391971646885230562e-02,-6.486649251415627093e-02,-1.506758250413897215e+00,1.770408338306008744e-01,-1.313950103499249289e-01,-1.531950314854812478e-01,6.990859955920894486e-02,-1.626637234267066745e+00,5.929477893373875652e-01,-1.546528241399860448e+00,-1.603662961096674056e-01,-1.000000000000000000e+00 +-3.396285678353226367e-01,-1.528294409412719546e+00,1.338140976218210321e-01,3.178166295579417100e-01,-1.884779252162981145e+00,7.587564002972220500e-02,-1.801052541146987984e+00,-1.802455789509802342e+00,5.629959864233883421e-02,-1.528386690144768467e+00,-1.196946559723040426e+00,-1.496014076337103482e+00,-1.459000822332437441e-02,-4.514110158813214313e-02,1.000000000000000000e+00 +-2.122826033075854713e-01,-1.742457408344193714e+00,-5.994940210951479193e-03,-7.429000399707801072e-02,-1.573847815175543285e+00,-1.427765538136147594e+00,-2.177683539857089290e-03,-1.848090509302166007e+00,-2.462976151736861463e-01,-1.507496047961274011e+00,-1.694486745172459230e+00,-1.084089586155583929e+00,-1.881101533930195080e+00,-4.045707885772964474e-01,-1.000000000000000000e+00 +-1.821486828404217828e+00,-1.785783773911563177e+00,8.694550389372397714e-02,1.018824281446641250e-01,-1.693691149603142154e+00,-1.484847003659224729e+00,-1.765063793949422033e+00,-1.802234392814523023e+00,5.522426840259311853e-02,-1.612363985088490770e+00,-1.317573593524661524e+00,-1.431744863444093685e+00,4.777887984338642646e-02,9.428109634203124534e-02,1.000000000000000000e+00 +-1.355231699667234502e-01,-1.478286893948838276e+00,-1.745691703278433149e+00,2.120014411317283631e-01,-1.397098658957899087e+00,-1.501016599025400300e+00,3.430488942349717574e-01,-1.593148440623272544e+00,-2.454628769550215583e-03,-2.170799739660791150e-01,-1.775361995629232137e+00,-1.198240303294508369e+00,-1.559487754720733532e+00,-1.240689768389830194e-01,-1.000000000000000000e+00 +-9.313034480929896497e-02,-1.548711135573551667e+00,3.748586304684338455e-02,2.709530778596377365e-01,-1.896236382869262327e+00,-1.485636556670600683e+00,-3.161759750274304448e-01,-1.164643173374209084e+00,2.586141464409828783e-02,-1.451717591710380173e+00,-1.196062416447940757e+00,-1.289322134291910560e+00,-1.532448668231899402e+00,-1.185949432151032212e-01,1.000000000000000000e+00 +-1.798587046249422450e+00,1.869445715679594888e-02,-1.044510995144790066e-01,5.572535109843471113e-03,2.050689463626149955e-01,-4.569373395860657633e-02,2.411256194966787947e-01,-1.233356642157531907e-01,-2.070072436122830095e-01,-1.795951024666027296e+00,-1.566054540405366291e+00,3.927205796742158639e-01,-1.603352638892656667e+00,-1.273094642344743677e-01,-1.000000000000000000e+00 +-1.695174531165744680e+00,-1.901057242239872380e+00,4.447986228189836078e-02,1.909527852487359345e-01,-1.747763271347229086e+00,-5.746031256144605581e-02,-3.742409088612788093e-01,-1.155721528626410866e+00,1.726663169414113241e-01,-1.451530504363050422e+00,-1.379182285647580386e+00,-1.538823892180034125e+00,-1.778675306065086392e+00,-1.389632348775922399e-01,1.000000000000000000e+00 +-1.516154332717399411e+00,2.948078711775095617e-01,-1.563462614010797669e+00,4.653471419431301492e-02,-1.000266205608066339e-01,-1.635299494656577979e-01,1.981747060035120023e-01,-2.439225329817111265e-01,-2.040020500696499428e-01,-3.313004356918086701e-02,-1.662464812462594477e+00,2.660385912486399884e-01,-1.808148641442627769e+00,-8.892180870150626082e-02,-1.000000000000000000e+00 +-1.897814348096344694e-01,-1.366965856206360463e+00,2.847573068305468691e-02,7.793816993692186113e-02,-1.847043456045497356e+00,1.384223405335286393e-02,-1.735776206540201150e+00,-1.960193423500923959e+00,2.393677744169735166e-02,4.390804650419367905e-02,-1.346637422342611190e+00,-1.525863026477373374e+00,-1.312007080002479231e+00,8.494577091091098930e-02,1.000000000000000000e+00 +-1.718634311223755873e+00,-1.610442672944327303e+00,2.038515277567406331e-02,-3.736477568872324101e-02,-1.742524571488221818e+00,1.347297168283280444e-02,2.893177315601560551e-01,-1.604696419948735020e+00,-2.371395839510001546e-02,-1.463682789949578567e+00,-1.652413527193833831e+00,-1.441831057552340978e+00,-1.703409524414448928e+00,-2.498822282863613631e-02,-1.000000000000000000e+00 +-1.637188078103448463e+00,-1.603885569860671767e+00,9.449692346724855585e-02,1.853561669661850375e-01,-1.904633031994200199e+00,-1.573753785839371311e+00,-1.746192613065815635e+00,-1.605014692948754984e+00,1.884122078598251537e-01,2.130393423176967782e-01,-1.313215595620440101e+00,-1.375569192665423213e+00,-1.609605564479541062e+00,1.032090234096018702e-02,1.000000000000000000e+00 +-1.463932539775016695e+00,-1.396927527135983116e+00,-1.597124513244271737e+00,1.700606987739440301e-01,-1.593214258358096158e+00,7.898635524713594480e-02,1.460646252191022954e-01,-1.727752340910720941e+00,-1.397253179256417455e-01,-6.391578604999410829e-02,-1.506917714065532099e+00,-1.327124410192860093e+00,-1.868621401392190240e+00,-4.814873314166542850e-02,-1.000000000000000000e+00 +-2.453302913512000472e-01,-1.381393475649339120e+00,2.820336751702119671e-02,-1.315876624107797266e+00,-1.824739999228144560e+00,-1.602121961177756182e+00,-3.705984461049904866e-01,2.088605174089669669e-01,-1.388530441999721976e+00,-1.614360672904696092e-01,-1.382764604531399932e+00,-1.468531559001070574e+00,4.112328007139420816e-02,-1.581333600149080088e+00,1.000000000000000000e+00 +2.713805008108567673e-02,-4.492060815494731962e-02,1.321227763640236974e-01,3.839966895412061376e-02,-1.796176035095834456e-01,-1.215735672995639227e-01,-1.373018785594215529e+00,3.902518928637641604e-01,-1.707055539118201293e-01,-2.122842042683150410e-01,-1.625022535456478234e+00,2.678538992717404144e-01,-1.770618558854318403e+00,-3.741509762172745557e-03,-1.000000000000000000e+00 +-1.578884567282865081e+00,-1.899409259283551643e+00,9.479623447999144248e-02,-1.121556247336875867e+00,-1.760989393365797628e+00,1.773819367306432304e-01,-1.958317720905401182e-01,1.349862208361168603e-01,-1.450112157788013389e+00,-1.357031782377540741e-02,-1.241624569528410049e+00,-1.484921610939185177e+00,1.376233665493970526e-01,-1.556604996631108184e+00,1.000000000000000000e+00 +-3.537691289734849059e-02,4.560153415536132016e-02,-1.594171868075497489e+00,3.781513593400184087e-02,-6.310239035113980599e-02,1.094511092510719319e-02,-1.370207269419601692e+00,1.199799911955920506e-02,-1.145156303849539092e-01,-1.601907832583182367e+00,-1.623516799310357195e+00,3.320668925349491429e-01,-1.861623716837563558e+00,-1.508498673571756210e-01,-1.000000000000000000e+00 +-1.393561281424530174e-01,-1.374758695615320159e+00,-7.759579169863080328e-02,-1.376195546802910563e+00,-1.938362542912492881e+00,2.884745655059433103e-02,-2.006920996823445602e+00,-1.004405064435997530e-01,-1.516342257132262672e+00,-1.596307634773844519e+00,-1.299829520439901742e+00,-1.381582888541074139e+00,-1.757494128895809860e-01,-1.272303335421770765e+00,1.000000000000000000e+00 +-1.960094070973704139e-01,-1.697496330090407568e+00,-5.048449415743674895e-02,-7.752022277990533450e-02,-1.662512304274329278e+00,1.033730136420900059e-01,-1.325909631133677991e+00,-1.454292015846018638e+00,-1.190555544626873269e-01,-8.137424270029648687e-02,-1.756864415556221193e+00,-1.241075488481948152e+00,-1.762882077075956788e+00,-2.177474284367885149e-01,-1.000000000000000000e+00 +-1.716506103836173969e+00,-1.693682981881873273e+00,1.236654366300238284e-01,-1.574135526811256902e+00,-1.664471526834966575e+00,-1.570844244304912207e+00,-1.774862866839263642e+00,-1.427638352974658076e-01,-1.373032160890863329e+00,-1.546979483717454640e+00,-1.022122703105811548e+00,-1.601236489560692533e+00,1.529056596888132558e-01,-1.520223067771249159e+00,1.000000000000000000e+00 +1.240211700589305688e-01,-1.673066155807911848e+00,-1.707600454721348804e+00,4.689498168707974951e-02,-1.699469709394447303e+00,2.441835890795615860e-01,-1.358180419249774618e+00,-1.729888665634490730e+00,-5.395068874283309723e-02,-1.573572313118200672e+00,-1.697368928119671549e+00,-1.296355045903686998e+00,-1.784341285676739997e+00,-1.336320934654017800e-01,-1.000000000000000000e+00 +-2.732278629720036323e-01,-1.400079234667832129e+00,1.347668265396879672e-01,-1.391620773220448415e+00,-1.758071744450004470e+00,-1.606464752742112978e+00,-3.402099865624137465e-01,1.174714134753892802e-01,-1.401180205989532190e+00,-1.721918597261670181e+00,-1.287308539237927096e+00,-1.591594651268959160e+00,-1.593165458194905293e+00,-1.557546955013530354e+00,1.000000000000000000e+00 +-1.598971646227494103e+00,1.236046246409897020e-01,3.431877784083394584e-02,-6.969119693131110849e-02,-8.563580320125260825e-02,-1.603697888322635379e+00,-1.329024396720724255e+00,1.682855646235556224e-01,-2.028225234038001645e-01,6.861652215427307966e-02,-1.602857773622162751e+00,3.625317017837687006e-01,-1.797304123293491429e+00,-5.321588608481897520e-02,-1.000000000000000000e+00 +-1.639644981974852200e+00,-1.854171895099718581e+00,-2.051973173553672813e-02,-1.366762673081192592e+00,-1.770892170133351984e+00,-4.244494930413263492e-03,-1.908786714563566189e-01,4.024445837550703065e-01,-1.370617000966698251e+00,-1.721377477636856446e+00,-1.252054711962235167e+00,-1.454787972043852795e+00,-1.453855134864529886e+00,-1.612771266479338106e+00,1.000000000000000000e+00 +-1.860920327172206346e+00,-3.041931909818444690e-02,-1.688612074945298192e+00,7.305155118052963914e-03,6.657171394361634242e-02,-1.711844099602422897e+00,-1.393033245878821713e+00,-8.345334352642244657e-02,5.571952925660876577e-02,-1.518301027691250749e+00,-1.576734468942531997e+00,2.081936884977613755e-01,-1.813402519878215990e+00,-1.063016573519409841e-01,-1.000000000000000000e+00 +-3.165671461358449679e-01,-1.599647375778868286e+00,1.133291167418244011e-01,-1.413005648690241367e+00,-1.761543553015034513e+00,-4.481829010622573328e-02,-1.810204724753768701e+00,-2.899966671567328347e-01,-1.566337292939382397e+00,-2.779789300881632674e-01,-1.298274720101524116e+00,-1.490768568518090698e+00,-1.663696415234289461e+00,-1.562536267185302030e+00,1.000000000000000000e+00 +-1.489405518472745715e+00,-1.564369280865593348e+00,-1.253944555836074802e-02,2.348296659373619311e-02,-1.559396690984679479e+00,-1.477378163711451586e+00,-1.423427973423906590e+00,-1.385349421852070328e+00,-1.112533298319890868e-01,8.818716506551810275e-02,-1.501800184321577802e+00,-1.270020039620027852e+00,-1.645927092564823946e+00,-1.069863646429739246e-01,-1.000000000000000000e+00 +-1.744035853108975909e+00,-1.852529322203081863e+00,-4.882570838350687448e-02,-1.300941334332897270e+00,-1.853250573941688906e+00,-1.660621744728149718e+00,-1.767231625151308050e+00,-1.626395347022064342e-01,-1.314747526695378310e+00,-5.551483382275135270e-02,-1.308293869447473945e+00,-1.485197846757616569e+00,-1.535382907086665805e+00,-1.519174727187065921e+00,1.000000000000000000e+00 +-1.722786422183899990e+00,-1.501959903195960289e+00,-1.365952932099750550e+00,-7.992587737987744134e-02,-1.527679953029977167e+00,-1.344128265453815585e+00,-1.358802884399927136e+00,-1.409152429298882403e+00,-3.089803980877695033e-01,-1.561449503771455749e+00,-1.702209857888720146e+00,-1.354103718271319812e+00,-1.798914482103099344e+00,-3.756717063139381474e-01,-1.000000000000000000e+00 diff --git a/qiskit_runtime/qka/featuremaps.py b/qiskit_runtime/qka/featuremaps.py new file mode 100644 index 000000000..dcab2ca8d --- /dev/null +++ b/qiskit_runtime/qka/featuremaps.py @@ -0,0 +1,132 @@ +# This code is part of qiskit-runtime. +# +# (C) Copyright IBM 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. + +"""The FeatureMap class.""" + +import json + +import numpy as np + +from qiskit import QuantumCircuit, QuantumRegister + + +class FeatureMap: + """Mapping data with the feature map.""" + + def __init__(self, feature_dimension, entangler_map=None): + """ + Args: + feature_dimension (int): number of features (twice the number of qubits for this + encoding) + entangler_map (list[list]): connectivity of qubits with a list of [source, target], + or None for full entanglement. Note that the order in the list is the order of + applying the two-qubit gate. + + Raises: + ValueError: If the value of ``feature_dimension`` is odd. + """ + + if isinstance(feature_dimension, int): + if feature_dimension % 2 == 0: + self._feature_dimension = feature_dimension + else: + raise ValueError("Feature dimension must be an even integer.") + else: + raise ValueError("Feature dimension must be an even integer.") + + self._num_qubits = int(feature_dimension / 2) + + if entangler_map is None: + self._entangler_map = [ + [i, j] for i in range(self._num_qubits) for j in range(i + 1, self._num_qubits) + ] + else: + self._entangler_map = entangler_map + + self._num_parameters = self._num_qubits + + def construct_circuit(self, x=None, parameters=None, q=None, inverse=False, name=None): + """Construct the feature map circuit. + + Args: + x (numpy.ndarray): data vector of size feature_dimension + parameters (numpy.ndarray): optional parameters in feature map + q (QauntumRegister): the QuantumRegister object for the circuit + inverse (bool): whether or not to invert the circuit + name (str): The name to use for the constructed ``QuantumCircuit`` object + + Returns: + QuantumCircuit: a quantum circuit transforming data x + + Raises: + ValueError: If the input parameters or vector are invalid + """ + + if parameters is not None: + if isinstance(parameters, (int, float)): + raise ValueError("Parameters must be a list.") + if len(parameters) == 1: + parameters = parameters * np.ones(self._num_qubits) + else: + if len(parameters) != self._num_parameters: + raise ValueError( + "The number of feature map parameters must be {}.".format( + self._num_parameters + ) + ) + + if len(x) != self._feature_dimension: + raise ValueError( + "The input vector must be of length {}.".format(self._feature_dimension) + ) + + if q is None: + q = QuantumRegister(self._num_qubits, name="q") + + circuit = QuantumCircuit(q, name=name) + + for i in range(self._num_qubits): + circuit.ry(-parameters[i], q[i]) + + for source, target in self._entangler_map: + circuit.cz(q[source], q[target]) + + for i in range(self._num_qubits): + circuit.rz(-2 * x[2 * i + 1], q[i]) + circuit.rx(-2 * x[2 * i], q[i]) + + if inverse: + return circuit.inverse() + else: + return circuit + + def to_json(self): + """Return JSON representation of this object. + + Returns: + str: JSON string representing this object. + """ + return json.dumps( + {"feature_dimension": self._feature_dimension, "entangler_map": self._entangler_map} + ) + + @classmethod + def from_json(cls, data): + """Return an instance of this class from the JSON representation. + + Args: + data (str): JSON string representing an object. + + Returns: + FeatureMap: An instance of this class. + """ + return cls(**json.loads(data)) diff --git a/qiskit_runtime/qka/kernel_matrix.py b/qiskit_runtime/qka/kernel_matrix.py new file mode 100644 index 000000000..c12a8ea99 --- /dev/null +++ b/qiskit_runtime/qka/kernel_matrix.py @@ -0,0 +1,138 @@ +# This code is part of qiskit-runtime. +# +# (C) Copyright IBM 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. + +"""The KernelMatrix class.""" + +import itertools + +import numpy as np + +from qiskit.compiler import transpile + + +class KernelMatrix: + """Build the kernel matrix from a quantum feature map.""" + + def __init__(self, feature_map, backend, initial_layout=None): + """ + Args: + feature_map (int): the feature map object + backend (Backend): the backend instance + initial layout (list or dict): initial position of virtual qubits on the physical + qubits of the quantum device + """ + + self._feature_map = feature_map + self._feature_map_circuit = self._feature_map.construct_circuit # the feature map circuit + self._backend = backend + self._initial_layout = initial_layout + + self.results = {} # store the results object (program_data) + + def construct_kernel_matrix(self, x1_vec, x2_vec, parameters=None): + """Create the kernel matrix for a given feature map and input data. + + With the qasm simulator or real backends, compute order 'n^2' + states Phi^dag(y)Phi(x)|0> for input vectors x and y. + + Args: + x1_vec (numpy.ndarray): NxD array of training data or test data, where N is the + number of samples and D is the feature dimension + x2_vec (numpy.ndarray): MxD array of training data or support vectors, where M + is the number of samples and D is the feature dimension + parameters (numpy.ndarray): optional parameters in feature map + + Returns: + numpy.ndarray: the kernel matrix + """ + + is_identical = False + if np.array_equal(x1_vec, x2_vec): + is_identical = True + + experiments = [] + + measurement_basis = "0" * self._feature_map._num_qubits + + if is_identical: + + my_product_list = list( + itertools.combinations(range(len(x1_vec)), 2) + ) # all pairwise combos of datapoint indices + for index_1, index_2 in my_product_list: + + circuit_1 = self._feature_map_circuit( + x=x1_vec[index_1], parameters=parameters, name="{}_{}".format(index_1, index_2) + ) + circuit_2 = self._feature_map_circuit( + x=x1_vec[index_2], parameters=parameters, inverse=True + ) + circuit = circuit_1.compose(circuit_2) + circuit.measure_all() + + experiments.append(circuit) + + program_data = self._run_circuits(experiments) + self.results["program_data"] = program_data + + mat = np.eye( + len(x1_vec), len(x1_vec) + ) # kernel matrix element on the diagonal is always 1 + for experiment, [index_1, index_2] in enumerate(my_product_list): + + counts = program_data.get_counts(experiment=experiment) + shots = sum(counts.values()) + + mat[index_1][index_2] = ( + counts.get(measurement_basis, 0) / shots + ) # kernel matrix element is the probability of measuring all 0s + mat[index_2][index_1] = mat[index_1][index_2] # kernel matrix is symmetric + + return mat + + else: + + for index_1, point_1 in enumerate(x1_vec): + for index_2, point_2 in enumerate(x2_vec): + + circuit_1 = self._feature_map_circuit( + x=point_1, parameters=parameters, name="{}_{}".format(index_1, index_2) + ) + circuit_2 = self._feature_map_circuit( + x=point_2, parameters=parameters, inverse=True + ) + circuit = circuit_1.compose(circuit_2) + circuit.measure_all() + + experiments.append(circuit) + + program_data = self._run_circuits(experiments) + self.results["program_data"] = program_data + + mat = np.zeros((len(x1_vec), len(x2_vec))) + i = 0 + for index_1, _ in enumerate(x1_vec): + for index_2, _ in enumerate(x2_vec): + + counts = program_data.get_counts(experiment=i) + shots = sum(counts.values()) + + mat[index_1][index_2] = counts.get(measurement_basis, 0) / shots + i += 1 + + return mat + + def _run_circuits(self, circuits): + """Execute the input circuits.""" + + transpiled = transpile(circuits, backend=self._backend, initial_layout=self._initial_layout) + return self._backend.run(transpiled, shots=8192).result() diff --git a/qiskit_runtime/qka/qka.json b/qiskit_runtime/qka/qka.json new file mode 100644 index 000000000..fe697433d --- /dev/null +++ b/qiskit_runtime/qka/qka.json @@ -0,0 +1,19 @@ +{ + "name": "quantum-kernel-alignment", + "description": "Quantum kernel alignment algorithm that learns, on a given dataset, a quantum kernel maximizing the SVM classification margin.", + "max_execution_time": 28800, + "version": "1.0", + "parameters": [ + {"name": "feature_map", "description": "An instance of FeatureMap in dictionary format used to map classical data into a quantum state space.", "type": "dict", "required": true}, + {"name": "data", "description": "NxD array of training data, where N is the number of samples and D is the feature dimension.", "type": "numpy.ndarray", "required": true}, + {"name": "labels", "description": "Nx1 array of +/-1 labels of the N training samples.", "type": "numpy.ndarray", "required": true}, + {"name": "initial_kernel_parameters", "description": "Initial parameters of the quantum kernel. If not specified, an array of randomly generated numbers is used.", "type": "numpy.ndarray", "required": false}, + {"name": "maxiters", "description": "Number of SPSA optimization steps. Default is 1.", "type": "int", "required": false}, + {"name": "C", "description": "Penalty parameter for the soft-margin support vector machine. Default is 1.", "type": "float", "required": false}, + {"name": "initial_layout", "description": "Initial position of virtual qubits on the physical qubits of the quantum device. Default is None.", "type": "list or dict", "required": false} + ], + "return_values": [ + {"name": "aligned_kernel_parameters", "description": "The optimized kernel parameters found from quantum kernel alignment.", "type": "numpy.ndarray"}, + {"name": "aligned_kernel_matrix", "description": "The aligned quantum kernel matrix evaluated with the optimized kernel parameters on the training data.", "type": "numpy.ndarray"} + ] +} \ No newline at end of file diff --git a/qiskit_runtime/qka/qka.py b/qiskit_runtime/qka/qka.py new file mode 100644 index 000000000..bb3e87a48 --- /dev/null +++ b/qiskit_runtime/qka/qka.py @@ -0,0 +1,518 @@ +# This code is part of qiskit-runtime. +# +# (C) Copyright IBM 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. + +"""Source code for the QKA Qiskit Runtime program.""" + +# pylint: disable=invalid-name + +import itertools +import json +import numpy as np +from numpy.random import RandomState +from qiskit import QuantumCircuit, QuantumRegister +from qiskit.compiler import transpile +from cvxopt import matrix, solvers # pylint: disable=import-error + + +class FeatureMap: + """Mapping data with the feature map.""" + + def __init__(self, feature_dimension, entangler_map=None): + """ + Args: + feature_dimension (int): number of features, twice the number + of qubits for this encoding + entangler_map (list[list]): connectivity of qubits with a list of [source, target], + or None for full entanglement. Note that the order in + the list is the order of applying the two-qubit gate. + Raises: + ValueError: If the value of ``feature_dimension`` is not an even integer. + """ + + if isinstance(feature_dimension, int): + if feature_dimension % 2 == 0: + self._feature_dimension = feature_dimension + else: + raise ValueError("Feature dimension must be an even integer.") + else: + raise ValueError("Feature dimension must be an even integer.") + + self._num_qubits = int(feature_dimension / 2) + + if entangler_map is None: + self._entangler_map = [ + [i, j] + for i in range(self._feature_dimension) + for j in range(i + 1, self._feature_dimension) + ] + else: + self._entangler_map = entangler_map + + self._num_parameters = self._num_qubits + + def construct_circuit(self, x=None, parameters=None, q=None, inverse=False, name=None): + """Construct the feature map circuit. + + Args: + x (numpy.ndarray): data vector of size feature_dimension + parameters (numpy.ndarray): optional parameters in feature map + q (QauntumRegister): the QuantumRegister object for the circuit + inverse (bool): whether or not to invert the circuit + name (str): name of circuit + + Returns: + QuantumCircuit: a quantum circuit transforming data x + Raises: + ValueError: If the input parameters or vector are invalid + """ + + if parameters is not None: + if isinstance(parameters, (int, float)): + raise ValueError("Parameters must be a list.") + if len(parameters) == 1: + parameters = parameters * np.ones(self._num_qubits) + else: + if len(parameters) != self._num_parameters: + raise ValueError( + "The number of feature map parameters must be {}.".format( + self._num_parameters + ) + ) + + if len(x) != self._feature_dimension: + raise ValueError( + "The input vector must be of length {}.".format(self._feature_dimension) + ) + + if q is None: + q = QuantumRegister(self._num_qubits, name="q") + + circuit = QuantumCircuit(q, name=name) + + for i in range(self._num_qubits): + circuit.ry(-parameters[i], q[i]) + + for source, target in self._entangler_map: + circuit.cz(q[source], q[target]) + + for i in range(self._num_qubits): + circuit.rz(-2 * x[2 * i + 1], q[i]) + circuit.rx(-2 * x[2 * i], q[i]) + + if inverse: + return circuit.inverse() + else: + return circuit + + def to_json(self): + """Return JSON representation of this object. + + Returns: + str: JSON string representing this object. + """ + return json.dumps( + {"feature_dimension": self._feature_dimension, "entangler_map": self._entangler_map} + ) + + @classmethod + def from_json(cls, data): + """Return an instance of this class from the JSON representation. + + Args: + data (str): JSON string representing an object. + + Returns: + cls: An instance of this class. + """ + return cls(**json.loads(data)) + + +class KernelMatrix: + """Build the kernel matrix from a quantum feature map.""" + + def __init__(self, feature_map, backend, initial_layout=None): + """ + Args: + feature_map: the feature map object + backend (Backend): the backend instance + initial_layout (list or dict): initial position of virtual + qubits on the physical qubits + of the quantum device + """ + + self._feature_map = feature_map + self._feature_map_circuit = self._feature_map.construct_circuit + self._backend = backend + self._initial_layout = initial_layout + + self.results = {} + + def construct_kernel_matrix(self, x1_vec, x2_vec, parameters=None): + """Create the kernel matrix for a given feature map and input data. + + With the qasm simulator or real backends, compute order 'n^2' + states Phi^dag(y)Phi(x)|0> for input vectors x and y. + + Args: + x1_vec (numpy.ndarray): NxD array of training data or test data, + where N is the number of samples + and D is the feature dimension + x2_vec (numpy.ndarray): MxD array of training data or support + vectors, where M is the number of samples + and D is the feature dimension + parameters (numpy.ndarray): optional parameters in feature map + + Returns: + numpy.ndarray: the kernel matrix + """ + + is_identical = False + if np.array_equal(x1_vec, x2_vec): + is_identical = True + + experiments = [] + + measurement_basis = "0" * self._feature_map._num_qubits + + if is_identical: + + my_product_list = list( + itertools.combinations(range(len(x1_vec)), 2) + ) # all pairwise combos of datapoint indices + + for index_1, index_2 in my_product_list: + + circuit_1 = self._feature_map_circuit( + x=x1_vec[index_1], parameters=parameters, name="{}_{}".format(index_1, index_2) + ) + circuit_2 = self._feature_map_circuit( + x=x1_vec[index_2], parameters=parameters, inverse=True + ) + circuit = circuit_1.compose(circuit_2) + circuit.measure_all() + experiments.append(circuit) + + experiments = transpile( + experiments, backend=self._backend, initial_layout=self._initial_layout + ) + program_data = self._backend.run(experiments, shots=8192).result() + + self.results["program_data"] = program_data + + mat = np.eye( + len(x1_vec), len(x1_vec) + ) # kernel matrix element on the diagonal is always 1 + for experiment, [index_1, index_2] in enumerate(my_product_list): + + counts = program_data.get_counts(experiment=experiment) + shots = sum(counts.values()) + + mat[index_1][index_2] = ( + counts.get(measurement_basis, 0) / shots + ) # kernel matrix element is the probability of measuring all 0s + mat[index_2][index_1] = mat[index_1][index_2] # kernel matrix is symmetric + + return mat + + else: + + for index_1, point_1 in enumerate(x1_vec): + for index_2, point_2 in enumerate(x2_vec): + + circuit_1 = self._feature_map_circuit( + x=point_1, parameters=parameters, name="{}_{}".format(index_1, index_2) + ) + circuit_2 = self._feature_map_circuit( + x=point_2, parameters=parameters, inverse=True + ) + circuit = circuit_1.compose(circuit_2) + circuit.measure_all() + experiments.append(circuit) + + experiments = transpile( + experiments, backend=self._backend, initial_layout=self._initial_layout + ) + program_data = self._backend.run(experiments, shots=8192).result() + + self.results["program_data"] = program_data + + mat = np.zeros((len(x1_vec), len(x2_vec))) + i = 0 + for index_1, _ in enumerate(x1_vec): + for index_2, _ in enumerate(x2_vec): + + counts = program_data.get_counts(experiment=i) + shots = sum(counts.values()) + + mat[index_1][index_2] = counts.get(measurement_basis, 0) / shots + i += 1 + + return mat + + +class QKA: + """The quantum kernel alignment algorithm.""" + + def __init__(self, feature_map, backend, initial_layout=None, user_messenger=None): + """Constructor. + + Args: + feature_map (partial obj): the quantum feature map object + backend (Backend): the backend instance + initial_layout (list or dict): initial position of virtual qubits on + the physical qubits of the quantum device + user_messenger (UserMessenger): used to publish interim results. + """ + + self.feature_map = feature_map + self.feature_map_circuit = self.feature_map.construct_circuit + self.backend = backend + self.initial_layout = initial_layout + self.num_parameters = self.feature_map._num_parameters + + self._user_messenger = user_messenger + self.result = {} + self.kernel_matrix = KernelMatrix( + feature_map=self.feature_map, backend=self.backend, initial_layout=self.initial_layout + ) + + def spsa_parameters(self): + """Return array of precomputed SPSA parameters. + + The i-th optimization step, i>=0, the parameters evolve as + + a_i = a / (i + 1 + A) ** alpha, + c_i = c / (i + 1) ** gamma, + + for fixed coefficents a, c, alpha, gamma, A. + + Returns: + numpy.ndarray: spsa parameters + """ + spsa_params = np.zeros((5)) + spsa_params[0] = 0.05 # a + spsa_params[1] = 0.1 # c + spsa_params[2] = 0.602 # alpha + spsa_params[3] = 0.101 # gamma + spsa_params[4] = 0 # A + + return spsa_params + + def cvxopt_solver(self, K, y, C, max_iters=10000, show_progress=False): + """Convex optimization of SVM objective using cvxopt. + + Args: + K (numpy.ndarray): nxn kernel (Gram) matrix + y (numpy.ndarray): nx1 vector of labels +/-1 + C (float): soft-margin penalty + max_iters (int): maximum iterations for the solver + show_progress (bool): print progress of solver + + Returns: + dict: results from the solver + """ + + if y.ndim == 1: + y = y[:, np.newaxis] + H = np.outer(y, y) * K + f = -np.ones(y.shape) + + n = K.shape[1] # number of training points + + y = y.astype("float") + + P = matrix(H) + q = matrix(f) + G = matrix(np.vstack((-np.eye((n)), np.eye((n))))) + h = matrix(np.vstack((np.zeros((n, 1)), np.ones((n, 1)) * C))) + A = matrix(y, y.T.shape) + b = matrix(np.zeros(1), (1, 1)) + + solvers.options["maxiters"] = max_iters + solvers.options["show_progress"] = show_progress + + ret = solvers.qp(P, q, G, h, A, b, kktsolver="ldl") + + return ret + + def spsa_step_one(self, lambdas, spsa_params, count): + """Evaluate +/- perturbations of kernel parameters (lambdas). + + Args: + lambdas (numpy.ndarray): kernel parameters at step 'count' in SPSA optimization loop + spsa_params (numpy.ndarray): SPSA parameters + count (int): the current step in the SPSA optimization loop + + Returns: + numpy.ndarray: kernel parameters in + direction + numpy.ndarray: kernel parameters in - direction + numpy.ndarray: random vector with elements {-1,1} + """ + + prng = RandomState(count) + + c_spsa = float(spsa_params[1]) / np.power(count + 1, spsa_params[3]) + delta = 2 * prng.randint(0, 2, size=np.shape(lambdas)[0]) - 1 + + lambda_plus = lambdas + c_spsa * delta + lambda_minus = lambdas - c_spsa * delta + + return lambda_plus, lambda_minus, delta + + def spsa_step_two(self, cost_plus, cost_minus, lambdas, spsa_params, delta, count): + """Evaluate one iteration of SPSA on SVM objective function F and + return updated kernel parameters. + + F(alpha, lambda) = 1^T * alpha - (1/2) * alpha^T * Y * K * Y * alpha + + Args: + cost_plus (float): objective function F(alpha_+, lambda_+) + cost_minus (float): objective function F(alpha_-, lambda_-) + lambdas (numpy.ndarray): kernel parameters at step 'count' in SPSA optimization loop + spsa_params (numpy.ndarray): SPSA parameters + delta (numpy.ndarray): random vector with elements {-1,1} + count(int): the current step in the SPSA optimization loop + + Returns: + float: estimate of updated SVM objective function F using average + of F(alpha_+, lambda_+) and F(alpha_-, lambda_-) + numpy.ndarray: updated values of the kernel parameters + after one SPSA optimization step + """ + + a_spsa = float(spsa_params[0]) / np.power(count + 1 + spsa_params[4], spsa_params[2]) + c_spsa = float(spsa_params[1]) / np.power(count + 1, spsa_params[3]) + + g_spsa = (cost_plus - cost_minus) * delta / (2.0 * c_spsa) + + lambdas_new = lambdas - a_spsa * g_spsa + lambdas_new = lambdas_new.flatten() + + cost_final = (cost_plus + cost_minus) / 2 + + return cost_final, lambdas_new + + def align_kernel(self, data, labels, initial_kernel_parameters=None, maxiters=1, C=1): + """Align the quantum kernel. + + Uses SPSA for minimization over kernel parameters (lambdas) and + convex optimization for maximization over lagrange multipliers (alpha): + + min_lambda max_alpha 1^T * alpha - (1/2) * alpha^T * Y * K_lambda * Y * alpha + + Args: + data (numpy.ndarray): NxD array of training data, where N is the + number of samples and D is the feature dimension + labels (numpy.ndarray): Nx1 array of +/-1 labels of the N training samples + initial_kernel_parameters (numpy.ndarray): Initial parameters of the quantum kernel + maxiters (int): number of SPSA optimization steps + C (float): penalty parameter for the soft-margin support vector machine + + Returns: + dict: the results of kernel alignment + """ + + if initial_kernel_parameters is not None: + lambdas = initial_kernel_parameters + else: + lambdas = np.random.uniform(-1.0, 1.0, size=(self.num_parameters)) + + spsa_params = self.spsa_parameters() + + lambda_save = [] + cost_final_save = [] + + for count in range(maxiters): + + lambda_plus, lambda_minus, delta = self.spsa_step_one( + lambdas=lambdas, spsa_params=spsa_params, count=count + ) + + kernel_plus = self.kernel_matrix.construct_kernel_matrix( + x1_vec=data, x2_vec=data, parameters=lambda_plus + ) + kernel_minus = self.kernel_matrix.construct_kernel_matrix( + x1_vec=data, x2_vec=data, parameters=lambda_minus + ) + + ret_plus = self.cvxopt_solver(K=kernel_plus, y=labels, C=C) + cost_plus = -1 * ret_plus["primal objective"] + + ret_minus = self.cvxopt_solver(K=kernel_minus, y=labels, C=C) + cost_minus = -1 * ret_minus["primal objective"] + + cost_final, lambda_best = self.spsa_step_two( + cost_plus=cost_plus, + cost_minus=cost_minus, + lambdas=lambdas, + spsa_params=spsa_params, + delta=delta, + count=count, + ) + + lambdas = lambda_best + + interim_result = {"cost": cost_final, "kernel_parameters": lambdas} + + self._user_messenger.publish(interim_result) + + lambda_save.append(lambdas) + cost_final_save.append(cost_final) + + # Evaluate aligned kernel matrix with optimized set of + # parameters averaged over last 10% of SPSA steps: + num_last_lambdas = int(len(lambda_save) * 0.10) + if num_last_lambdas > 0: + last_lambdas = np.array(lambda_save)[-num_last_lambdas:, :] + lambdas = np.sum(last_lambdas, axis=0) / num_last_lambdas + else: + lambdas = np.array(lambda_save)[-1, :] + + kernel_best = self.kernel_matrix.construct_kernel_matrix( + x1_vec=data, x2_vec=data, parameters=lambdas + ) + + self.result["aligned_kernel_parameters"] = lambdas + self.result["aligned_kernel_matrix"] = kernel_best + + return self.result + + +def main(backend, user_messenger, **kwargs): + """Entry function.""" + + # Reconstruct the feature map object. + feature_map = kwargs.get("feature_map") + fm = FeatureMap.from_json(feature_map) + + data = kwargs.get("data") + labels = kwargs.get("labels") + initial_kernel_parameters = kwargs.get("initial_kernel_parameters", None) + maxiters = kwargs.get("maxiters", 1) + C = kwargs.get("C", 1) + initial_layout = kwargs.get("initial_layout", None) + + qka = QKA( + feature_map=fm, + backend=backend, + initial_layout=initial_layout, + user_messenger=user_messenger, + ) + qka_results = qka.align_kernel( + data=data, + labels=labels, + initial_kernel_parameters=initial_kernel_parameters, + maxiters=maxiters, + C=C, + ) + + return qka_results diff --git a/qiskit_runtime/sample_program/__init__.py b/qiskit_runtime/sample_program/__init__.py new file mode 100644 index 000000000..0377e07d9 --- /dev/null +++ b/qiskit_runtime/sample_program/__init__.py @@ -0,0 +1,13 @@ +# This code is part of qiskit-runtime. +# +# (C) Copyright IBM 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. + +"""Package containing ``sample-program`` Qiskit quantum program.""" diff --git a/qiskit_runtime/sample_program/sample_program.json b/qiskit_runtime/sample_program/sample_program.json new file mode 100644 index 000000000..b6fd9b0e7 --- /dev/null +++ b/qiskit_runtime/sample_program/sample_program.json @@ -0,0 +1,41 @@ +{ + "name": "sample-program", + "description": "(DEPRECATED) A sample runtime program. This runtime program is deprecated, please use hello-world instead.", + "max_execution_time": 300, + "spec": { + "backend_requirements": { + "min_num_qubits": 5 + }, + "parameters": { + "$schema": "https://json-schema.org/draft/2019-09/schema", + "properties": { + "iterations": { + "type": "integer", + "minimum": 0, + "description": "Number of iterations to run. Each iteration generates a runs a random circuit." + } + }, + "required": [ + "iterations" + ] + }, + "return_values": { + "$schema": "https://json-schema.org/draft/2019-09/schema", + "description": "A string that says 'All done!'.", + "type": "string" + }, + "interim_results": { + "$schema": "https://json-schema.org/draft/2019-09/schema", + "properties": { + "iteration": { + "type": "integer", + "description": "Iteration number." + }, + "counts": { + "description": "Histogram data of the circuit result.", + "type": "object" + } + } + } + } +} diff --git a/qiskit_runtime/sample_program/sample_program.py b/qiskit_runtime/sample_program/sample_program.py new file mode 100644 index 000000000..a9e090f8d --- /dev/null +++ b/qiskit_runtime/sample_program/sample_program.py @@ -0,0 +1,53 @@ +# This code is part of qiskit-runtime. +# +# (C) Copyright IBM 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. + +"""A sample runtime program that submits random circuits for user-specified iterations.""" + +import random +from typing import Any + +from qiskit import transpile +from qiskit.circuit.random import random_circuit + + +def prepare_circuits(backend): + """Generate a random circuit. + + Args: + backend (qiskit.providers.Backend): Backend used for transpilation. + + Returns: + qiskit.QuantumCircuit: Generated circuit. + """ + circuit = random_circuit(num_qubits=5, depth=4, measure=True, seed=random.randint(0, 1000)) + return transpile(circuit, backend) + + +def main(backend, user_messenger, **kwargs) -> Any: + """Main entry point of the program. + + Args: + backend (qiskit.providers.Backend): Backend to submit the circuits to. + user_messenger (qiskit.providers.ibmq.runtime.UserMessenger): Used to communicate with the + program consumer. + kwargs: User inputs. + + Returns: + Final result of the program. + """ + iterations = kwargs.pop("iterations", 5) + for it in range(iterations): + qc = prepare_circuits(backend) + result = backend.run(qc).result() + user_messenger.publish({"iteration": it, "counts": result.get_counts()}) + + return "All done!" diff --git a/qiskit_runtime/version.py b/qiskit_runtime/version.py new file mode 100644 index 000000000..af762813a --- /dev/null +++ b/qiskit_runtime/version.py @@ -0,0 +1,5 @@ +# THIS FILE IS GENERATED FROM QISKIT_RUNTIME SETUP.PY +# pylint: disable=missing-module-docstring,invalid-name +short_version = "0.1.0" +version = "0.1.0.dev0+378fb03" +release = False diff --git a/qiskit_runtime/vqe/vqe.py b/qiskit_runtime/vqe/vqe.py new file mode 100644 index 000000000..65fe6c1b6 --- /dev/null +++ b/qiskit_runtime/vqe/vqe.py @@ -0,0 +1,1164 @@ +# This code is part of qiskit-runtime. +# +# (C) Copyright IBM 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. + +"""A generalized SPSA optimizer including support for Hessians.""" + +from abc import ABC, abstractmethod +from typing import Iterator, Optional, Union, Callable, Tuple, List, Dict, Any +import logging +from time import time +import warnings +import sys +import json +import traceback +from collections import deque + +import numpy as np +import scipy + +from qiskit.algorithms.optimizers import Optimizer, OptimizerSupportLevel, SPSA, QNSPSA +from qiskit import Aer +from qiskit.algorithms import VQE, VQEResult +from qiskit.algorithms.exceptions import AlgorithmError +from qiskit.algorithms.minimum_eigen_solvers import MinimumEigensolverResult +from qiskit.circuit import ParameterVector, QuantumCircuit +from qiskit.opflow import ( + StateFn, + CircuitSampler, + PauliExpectation, + ExpectationBase, + OperatorBase, + ListOp, + I, +) +from qiskit.providers import BaseBackend, Backend +from qiskit.utils import QuantumInstance + +from qiskit.ignis.mitigation.measurement import CompleteMeasFitter +from qiskit.providers.ibmq.runtime.utils import RuntimeDecoder + +# the overlap function +OVERLAP = Callable[[np.ndarray, np.ndarray], float] + +# parameters, loss, stepsize, number of function evaluations, accepted +CALLBACK = Callable[[np.ndarray, float, float, int, bool], None] + +logger = logging.getLogger(__name__) + + +# Suppress all warnings +warnings.simplefilter("ignore") + +# pylint: disable=invalid-name + + +# disable check for ansatzs, optimizer setter because of pylint bug +# pylint: disable=no-member +class It(ABC): + """A base class for serializable iterators.""" + + @abstractmethod + def serialize(self) -> Tuple[str, Dict[str, Any]]: + """Serialize the iterator.""" + raise NotImplementedError + + @abstractmethod + def get_iterator(self) -> Iterator[float]: + """Get the iterator.""" + raise NotImplementedError + + @staticmethod + def deserialize(serialized: Tuple[str, Dict[str, Any]]) -> "It": + """Construct the iterator from the serialized data.""" + + name, inputs = serialized + classes = {"Constant": Constant, "Powerlaw": Powerlaw, "Concatenated": Concatenated} + return classes[name](**inputs) + + +class Constant(It): + """An iterator yielding constant values.""" + + def __init__(self, value: float) -> None: + """ + Args: + value: The constant value yielded from this iterator. + """ + self.value = value + + def get_iterator(self) -> Iterator[float]: + def constant_it(): + while True: + yield self.value + + return constant_it + + def serialize(self) -> Tuple[str, Dict[str, Any]]: + return "Constant", {"value": self.value} + + +class Powerlaw(It): + r"""An iterator yielding values from a powerlaw. + + The powerlaw is + + .. math:: + + k(n) = c \left(\frac{1}{n + A}\right)^p, + + where :math:`c` is the constant coeffient (``coeff``), :math:`p` is the exponent + (``power``), :math:`A` is a constant offset (``offset``) and :math:`n` is an integer. + """ + + def __init__(self, coeff, power, offset, skip=0): + """ + Args: + coeff: The coefficient of the powerlaw. + power: The exponent in the powerlaw. + offset: The offset. + skip: How many initial values to skip in the iterator. + """ + self.coeff = coeff + self.power = power + self.offset = offset + self.skip = skip + + def serialize(self) -> Tuple[str, Dict[str, Any]]: + return ( + "Powerlaw", + {"coeff": self.coeff, "power": self.power, "offset": self.offset, "skip": self.skip}, + ) + + def get_iterator(self) -> Iterator[float]: + def powerlaw_it(): + n = 1 + while True: + if n > self.skip: + yield self.coeff / ((n + self.offset) ** self.power) + n += 1 + + return powerlaw_it + + +class Concatenated(It): + """An iterator consisting of concatenated other iterators.""" + + def __init__(self, iterators: List[It], breakpoints: List[int]) -> None: + """ + Args: + iterators: A list of iterators this iterator is made up of. + breakpoints: A list of integers specifying when to use the next iterator. + """ + self.iterators = [] + # deserialize if necessary + for iterator in iterators: + if isinstance(iterator, (list, tuple)): + self.iterators.append(self.deserialize(iterator)) + else: + self.iterators.append(iterator) + + self.breakpoints = breakpoints + + def serialize(self) -> Tuple[str, Dict[str, Any]]: + return ( + "Concatenated", + { + "iterators": [it.serialize() for it in self.iterators], + "breakpoints": self.breakpoints, + }, + ) + + def get_iterator(self) -> Iterator[float]: + iterators = [it.get_iterator()() for it in self.iterators] + breakpoints = self.breakpoints + + def concat(): + i, n = 0, 0 # n counts always up, i is at which iterator/breakpoint pair we are + while True: + if i < len(breakpoints) and n >= breakpoints[i]: + i += 1 + yield next(iterators[i]) + n += 1 + + return concat + + +class _SPSA(Optimizer): + """A generalized SPSA optimizer including support for Hessians.""" + + def __init__( + self, + maxiter: int = 100, + blocking: bool = False, + allowed_increase: Optional[float] = None, + trust_region: bool = False, + learning_rate: Optional[Union[float, Callable[[], Iterator]]] = None, + perturbation: Optional[Union[float, Callable[[], Iterator]]] = None, + resamplings: int = 1, + last_avg: int = 1, + callback: Optional[CALLBACK] = None, + # 2-SPSA arguments + second_order: bool = False, # skip_calibration: bool = False) -> None: + hessian_delay: int = 0, + lse_solver: Optional[Union[str, Callable[[np.ndarray, np.ndarray], np.ndarray]]] = None, + regularization: Optional[float] = None, + perturbation_dims: Optional[int] = None, + initial_hessian: Optional[np.ndarray] = None, + expectation: Optional[ExpectationBase] = None, + backend: Optional[Union[Backend, QuantumInstance]] = None, + ) -> None: + r""" + Args: + maxiter: The maximum number of iterations. + blocking: If True, only accepts updates that improve the loss. + allowed_increase: If blocking is True, this sets by how much the loss can increase + and still be accepted. If None, calibrated automatically to be twice the + standard deviation of the loss function. + trust_region: If True, restricts norm of the random direction to be <= 1. + learning_rate: A generator yielding learning rates for the parameter updates, + :math:`a_k`. + perturbation: A generator yielding the perturbation magnitudes :math:`c_k`. + resamplings: In each step, sample the gradient (and preconditioner) this many times. + last_avg: Return the average of the ``last_avg`` parameters instead of just the + last parameter values. + callback: A callback function passed information in each iteration step. The + information is, in this order: the parameters, the function value, the number + of function evaluations, the stepsize, whether the step was accepted. + second_order: If True, use 2-SPSA instead of SPSA. In 2-SPSA, the Hessian is estimated + additionally to the gradient, and the gradient is preconditioned with the inverse + of the Hessian to improve convergence. + hessian_delay: Start preconditioning only after a certain number of iterations. + Can be useful to first get a stable average over the last iterations before using + the preconditioner. + lse_solver: The method to solve for the inverse of the preconditioner. Per default an + exact LSE solver is used, but can e.g. be overwritten by a minimization routine. + regularization: To ensure the preconditioner is symmetric and positive definite, the + identity times a small coefficient is added to it. This generator yields that + coefficient. + perturbation_dims: The number of dimensions to perturb at once. Per default all + dimensions are perturbed simultaneously. + initial_hessian: The initial guess for the Hessian. By default the identity matrix + is used. + expectation: An expectation converter. + backend: A backend to evaluate the circuits, if the overlap function is provided as + a circuit and the objective function as operator expression. + """ + super().__init__() + + if regularization is None: + regularization = 0.01 + + if lse_solver is None: + lse_solver = np.linalg.solve + + self.history = None + + self.maxiter = maxiter + self.learning_rate = learning_rate + self.perturbation = perturbation + self.blocking = blocking + self.allowed_increase = allowed_increase + self.trust_region = trust_region + self.callback = callback + self.resamplings = resamplings + self.last_avg = last_avg + self.second_order = second_order + self.hessian_delay = hessian_delay + self.lse_solver = lse_solver + self.regularization = regularization + self.perturbation_dims = perturbation_dims + self.initial_hessian = initial_hessian + self.trust_region = trust_region + + # runtime arguments + self.grad_params = None + self.grad_expr = None + self.hessian_params = None + self.hessian_expr = None + self.gradient_expressions = None + + if backend is not None: + self._sampler = CircuitSampler(backend, caching="all") + self._expectation = expectation + else: + self._sampler = None + self._expectation = None + + self._nfev = None + self._moving_avg = None # moving average of the preconditioner + + @staticmethod + def calibrate( + loss: Callable[[np.ndarray], float], + initial_point: np.ndarray, + c: float = 0.1, + stability_constant: float = 0, + target_magnitude: Optional[float] = None, # 2 pi / 10 + alpha: float = 0.602, + gamma: float = 0.101, + modelspace: bool = False, + ) -> Tuple[Iterator[float], Iterator[float]]: + r"""Calibrate SPSA parameters with a powerseries as learning rate and perturbation coeffs. + + The powerseries are: + + .. math:: + + a_k = \frac{a}{(A + k + 1)^\alpha}, c_k = \frac{c}{(k + 1)^\gamma} + + Args: + loss: The loss function. + initial_point: The initial guess of the iteration. + c: The initial perturbation magnitude. + stability_constant: The value of `A`. + target_magnitude: The target magnitude for the first update step. + alpha: The exponent of the learning rate powerseries. + gamma: The exponent of the perturbation powerseries. + modelspace: Whether the target magnitude is the difference of parameter values + or function values (= model space). + + Returns: + tuple(generator, generator): A tuple of powerseries generators, the first one for the + learning rate and the second one for the perturbation. + """ + if target_magnitude is None: + target_magnitude = 2 * np.pi / 10 + + dim = len(initial_point) + + # compute the average magnitude of the first step + steps = 25 + avg_magnitudes = 0 + for _ in range(steps): + # compute the random directon + pert = np.array([1 - 2 * np.random.binomial(1, 0.5) for _ in range(dim)]) + delta = loss(initial_point + c * pert) - loss(initial_point - c * pert) + avg_magnitudes += np.abs(delta / (2 * c)) + + avg_magnitudes /= steps + + if modelspace: + a = target_magnitude / (avg_magnitudes ** 2) + else: + a = target_magnitude / avg_magnitudes + + # compute the rescaling factor for correct first learning rate + if a < 1e-10: + warnings.warn(f"Calibration failed, using {target_magnitude} for `a`") + a = target_magnitude + + def learning_rate(): + return powerseries(a, alpha, stability_constant) + + def perturbation(): + return powerseries(c, gamma) + + return learning_rate, perturbation + + @staticmethod + def estimate_stddev(loss: OperatorBase, initial_point: np.ndarray, avg: int = 25) -> float: + """Estimate the standard deviation of the loss function.""" + losses = [loss(initial_point) for _ in range(avg)] + return np.std(losses) + + # values_dict = {} + # for params in self.grad_params: + # values_dict.update({params[i]: [initial_point[i]] * avg}) + + # # execute at once + # sampled = self._sampler.convert(loss, params=values_dict) + # results = np.real(sampled.eval()) + + def _point_samples(self, loss, x, eps, deltas1, deltas2): + # cache gradient epxressions + if self.gradient_expressions is None: + # sorted loss parameters + sorted_params = sorted(loss.parameters, key=lambda p: p.name) + + # SPSA estimates + theta_p = ParameterVector("th+", len(loss.parameters)) + theta_m = ParameterVector("th-", len(loss.parameters)) + + # 2-SPSA estimates + x_pp = ParameterVector("x++", len(loss.parameters)) + x_pm = ParameterVector("x+-", len(loss.parameters)) + x_mp = ParameterVector("x-+", len(loss.parameters)) + x_mm = ParameterVector("x--", len(loss.parameters)) + + self.grad_expr = [ + loss.assign_parameters(dict(zip(sorted_params, theta_p))), + loss.assign_parameters(dict(zip(sorted_params, theta_m))), + ] + self.grad_params = [theta_p, theta_m] + + # catch QNSPSA case. Could be put in a method to make it a bit nicer + if self.second_order: + if self.hessian_expr is None: + self.hessian_expr = [ + loss.assign_parameters(dict(zip(sorted_params, x_pp))), + loss.assign_parameters(dict(zip(sorted_params, x_pm))), + loss.assign_parameters(dict(zip(sorted_params, x_mp))), + loss.assign_parameters(dict(zip(sorted_params, x_mm))), + ] + self.hessian_params = [x_pp, x_pm, x_mp, x_mm] + + self.gradient_expressions = ListOp(self.grad_expr + self.hessian_expr) + else: + self.gradient_expressions = ListOp(self.grad_expr) + + num_parameters = x.size + resamplings = len(deltas1) + + # SPSA parameters + theta_p_ = np.array([x + eps * delta1 for delta1 in deltas1]) + theta_m_ = np.array([x - eps * delta1 for delta1 in deltas1]) + + # 2-SPSA parameters + x_pp_ = np.array([x + eps * (delta1 + delta2) for delta1, delta2 in zip(deltas1, deltas2)]) + x_pm_ = np.array([x + eps * delta1 for delta1 in deltas1]) + x_mp_ = np.array([x - eps * (delta1 - delta2) for delta1, delta2 in zip(deltas1, deltas2)]) + x_mm_ = np.array([x - eps * delta1 for delta1 in deltas1]) + y_ = np.array([x for _ in deltas1]) + + # build dictionary + values_dict = {} + + if self.second_order: + for params, value_matrix in zip( + self.grad_params + self.hessian_params, + [theta_p_, theta_m_, x_pp_, x_pm_, x_mp_, x_mm_, y_], + ): + values_dict.update( + {params[i]: value_matrix[:, i].tolist() for i in range(num_parameters)} + ) + else: + for params, value_matrix in zip(self.grad_params, [theta_p_, theta_m_]): + values_dict.update( + {params[i]: value_matrix[:, i].tolist() for i in range(num_parameters)} + ) + + # execute at once + sampled = self._sampler.convert(self.gradient_expressions, params=values_dict) + results = np.real(sampled.eval()) + + # put results together + gradient_estimate = np.zeros(x.size) + fval_estimate = 0 + for i in range(resamplings): + self._nfev += 2 + gradient_estimate += (results[i, 0] - results[i, 1]) / (2 * eps) * deltas1[0] + fval_estimate += (results[i, 0] + results[i, 1]) / 2 + + if self.callback is not None: + if self._expectation: + # get estimation error for the function evaluations + variance = np.array( + [ + self._expectation.compute_variance(sampled_op) + for sampled_op in sampled[i][:2] + ] + ) + shots = self._sampler.quantum_instance.run_config.shots + estimation_error = np.sqrt(variance / shots) + else: + estimation_error = [0.0, 0.0] + + self.callback(self._nfev - 1, theta_p_[i, :], results[i, 0], estimation_error[0]) + self.callback(self._nfev, theta_m_[i, :], results[i, 1], estimation_error[1]) + + hessian_estimate = np.zeros((x.size, x.size)) + if self.second_order: + for i in range(resamplings): + self._nfev += 4 + diff = results[i, 2] - results[i, 3] + diff -= results[i, 4] - results[i, 5] + diff /= 2 * eps ** 2 + + rank_one = np.outer(deltas1[i], deltas2[i]) + hessian_estimate += diff * (rank_one + rank_one.T) / 2 + + return ( + gradient_estimate / resamplings, + hessian_estimate / resamplings, + fval_estimate / resamplings, + ) + + def _compute_update(self, loss, x, k, eps): + # compute the perturbations + if isinstance(self.resamplings, dict): + avg = self.resamplings.get(k, 1) + else: + avg = self.resamplings + + gradient = np.zeros(x.size) + preconditioner = np.zeros((x.size, x.size)) + + # accumulate the number of samples + deltas1 = [bernoulli_perturbation(x.size, self.perturbation_dims) for _ in range(avg)] + deltas2 = [bernoulli_perturbation(x.size, self.perturbation_dims) for _ in range(avg)] + + gradient, preconditioner, fval = self._point_samples(loss, x, eps, deltas1, deltas2) + + # update the exponentially smoothed average + if self.second_order: + smoothed = k / (k + 1) * self._moving_avg + 1 / (k + 1) * preconditioner + self._moving_avg = smoothed + + if k > self.hessian_delay: + # make the preconditioner SPD + spd_preconditioner = _make_spd(smoothed, self.regularization) + + # solve for the gradient update + gradient = np.real(self.lse_solver(spd_preconditioner, gradient)) + + return gradient, fval + + def _minimize(self, loss, initial_point): + # handle circuits case + if not callable(loss): + # sorted loss parameters + sorted_params = sorted(loss.parameters, key=lambda p: p.name) + + def loss_callable(x): + value_dict = dict(zip(sorted_params, x)) + return self._sampler.convert(loss, params=value_dict).eval().real + + else: + loss_callable = loss + + self.history = { + "loss": [], + "params": [], + "time": [], + } + + # ensure learning rate and perturbation are set + # this happens only here because for the calibration the loss function is required + if self.learning_rate is None and self.perturbation is None: + get_learning_rate, get_perturbation = self.calibrate(loss_callable, initial_point) + eta = get_learning_rate() + eps = get_perturbation() + elif self.learning_rate is None or self.perturbation is None: + raise ValueError("If one of learning rate or perturbation is set, both must be set.") + else: + if isinstance(self.learning_rate, float): + eta = constant(self.learning_rate) + else: + eta = self.learning_rate() + + if isinstance(self.perturbation, float): + eps = constant(self.perturbation) + else: + eps = self.perturbation() + + # prepare some initials + x = np.asarray(initial_point) + + if self.initial_hessian is None: + self._moving_avg = np.identity(x.size) + else: + self._moving_avg = self.initial_hessian + + self._nfev = 0 + + # if blocking is enabled we need to keep track of the function values + if self.blocking: + fx = loss_callable(x) + + self._nfev += 1 + if self.allowed_increase is None: + self.allowed_increase = 2 * self.estimate_stddev(loss_callable, x) + + logger.info("=" * 30) + logger.info("Starting SPSA optimization") + start = time() + + # keep track of the last few steps to return their average + last_steps = deque([x]) + + for k in range(1, self.maxiter + 1): + iteration_start = time() + # compute update + update, fx_next = self._compute_update(loss, x, k, next(eps)) + + # trust region + if self.trust_region: + norm = np.linalg.norm(update) + if norm > 1: # stop from dividing by 0 + update = update / norm + + # compute next parameter value + lr = next(eta) + update = update * lr + x_next = x - update + + # blocking + if self.blocking: + fx_next = loss_callable(x_next) + + self._nfev += 1 + if fx + self.allowed_increase <= fx_next: # accept only if loss improved + + self.history["loss"].append(fx_next) + self.history["params"].append(x_next) + self.history["time"].append(time()) + + # if self.callback is not None: + # self.callback(self._nfev, # number of function evals + # x_next, # next parameters + # fx_next, # loss at next parameters + # np.linalg.norm(update), # size of the update step + # False) # not accepted + + logger.info( + "Iteration %s/%s rejected in %s.", + k, + self.maxiter + 1, + time() - iteration_start, + ) + continue + fx = fx_next + + logger.info( + "Iteration %s/%s done in %s.", k, self.maxiter + 1, time() - iteration_start + ) + + # if self.callback is not None: + # self.callback(self._nfev, # number of function evals + # x_next, # next parameters + # fx_next, # loss at next parameters + # np.linalg.norm(update), # size of the update step + # True) # accepted + + self.history["loss"].append(fx_next) + self.history["params"].append(x_next) + self.history["time"].append(time()) + + # update parameters + x = x_next + + # update the list of the last ``last_avg`` parameters + if self.last_avg > 1: + last_steps.append(x_next) + if len(last_steps) > self.last_avg: + last_steps.popleft() + + logger.info("SPSA finished in %s", time() - start) + logger.info("=" * 30) + + if self.last_avg > 1: + x = np.mean(last_steps, axis=0) + + return x, loss_callable(x), self._nfev + + def get_support_level(self): + """Get the support level dictionary.""" + return { + "gradient": OptimizerSupportLevel.ignored, # could be supported though + "bounds": OptimizerSupportLevel.ignored, + "initial_point": OptimizerSupportLevel.required, + } + + def optimize( + self, + num_vars, + objective_function, + gradient_function=None, + variable_bounds=None, + initial_point=None, + ): + return self._minimize(objective_function, initial_point) + + +class _QNSPSA(_SPSA): + """Quantum Natural SPSA.""" + + def __init__( + self, + overlap_fn: Union[OVERLAP, QuantumCircuit], + maxiter: int = 100, + blocking: bool = False, + allowed_increase: Optional[float] = None, + learning_rate: Optional[Union[float, Callable[[], Iterator]]] = None, + perturbation: Optional[Union[float, Callable[[], Iterator]]] = None, + resamplings: int = 1, + callback: Optional[CALLBACK] = None, + # 2-SPSA arguments + hessian_delay: int = 0, + lse_solver: Optional[Union[str, Callable[[np.ndarray, np.ndarray], np.ndarray]]] = None, + regularization: Optional[float] = None, + perturbation_dims: Optional[int] = None, + initial_hessian: Optional[np.ndarray] = None, + expectation: Optional[ExpectationBase] = None, + backend: Optional[Union[Backend, QuantumInstance]] = None, + ) -> None: + r""" + Args: + maxiter: The maximum number of iterations. + blocking: If True, only accepts updates that improve the loss. + allowed_increase: If blocking is True, this sets by how much the loss can increase + and still be accepted. If None, calibrated automatically to be twice the + standard deviation of the loss function. + learning_rate: A generator yielding learning rates for the parameter updates, + :math:`a_k`. + perturbation: A generator yielding the perturbation magnitudes :math:`c_k`. + resamplings: In each step, sample the gradient (and preconditioner) this many times. + callback: A callback function passed information in each iteration step. The + information is, in this order: the parameters, the function value, the number + of function evaluations, the stepsize, whether the step was accepted. + hessian_delay: Start preconditioning only after a certain number of iterations. + Can be useful to first get a stable average over the last iterations before using + the preconditioner. + lse_solver: The method to solve for the inverse of the preconditioner. Per default an + exact LSE solver is used, but can e.g. be overwritten by a minimization routine. + regularization: To ensure the preconditioner is symmetric and positive definite, the + identity times a small coefficient is added to it. This generator yields that + coefficient. + perturbation_dims: The number of dimensions to perturb at once. Per default all + dimensions are perturbed simulatneously. + initial_hessian: The initial guess for the Hessian. By default the identity matrix + is used. + expectation: The Expectation converter for taking the average value of the + Observable over the ansatz state function. + backend: A backend to evaluate the circuits, if the overlap function is provided as + a circuit and the objective function as operator expression. + """ + super().__init__( + maxiter, + blocking, + allowed_increase, + trust_region=False, + learning_rate=learning_rate, + perturbation=perturbation, + resamplings=resamplings, + callback=callback, + second_order=True, + hessian_delay=hessian_delay, + lse_solver=lse_solver, + regularization=regularization, + perturbation_dims=perturbation_dims, + initial_hessian=initial_hessian, + expectation=expectation, + backend=backend, + ) + + self.overlap_fn = overlap_fn + + if not callable(overlap_fn): + sorted_overlap_params = sorted(overlap_fn.parameters, key=lambda p: p.name) + + x_pp = ParameterVector("x++", overlap_fn.num_parameters) + x_pm = ParameterVector("x+-", overlap_fn.num_parameters) + x_mp = ParameterVector("x-+", overlap_fn.num_parameters) + x_mm = ParameterVector("x--", overlap_fn.num_parameters) + y = ParameterVector("y", overlap_fn.num_parameters) + + left = overlap_fn.assign_parameters(dict(zip(sorted_overlap_params, y))) + rights = [ + overlap_fn.assign_parameters(dict(zip(sorted_overlap_params, x_pp))), + overlap_fn.assign_parameters(dict(zip(sorted_overlap_params, x_pm))), + overlap_fn.assign_parameters(dict(zip(sorted_overlap_params, x_mp))), + overlap_fn.assign_parameters(dict(zip(sorted_overlap_params, x_mm))), + ] + + self.hessian_params = [x_pp, x_pm, x_mp, x_mm, y] + self.hessian_expr = [~StateFn(left) @ StateFn(right) for right in rights] + + @staticmethod + def get_overlap(circuit, backend=None, expectation=None): + """Get the overlap function.""" + params_x = ParameterVector("x", circuit.num_parameters) + params_y = ParameterVector("y", circuit.num_parameters) + + expression = ~StateFn(circuit.assign_parameters(params_x)) @ StateFn( + circuit.assign_parameters(params_y) + ) + + if expectation is not None: + expression = expectation.convert(expression) + + if backend is None: + + def overlap_fn(values_x, values_y): + value_dict = dict( + zip(params_x[:] + params_y[:], values_x.tolist() + values_y.tolist()) + ) + return -0.5 * np.abs(expression.bind_parameters(value_dict).eval()) ** 2 + + else: + sampler = CircuitSampler(backend) + + def overlap_fn(values_x, values_y): + value_dict = dict( + zip(params_x[:] + params_y[:], values_x.tolist() + values_y.tolist()) + ) + return -0.5 * np.abs(sampler.convert(expression, params=value_dict).eval()) ** 2 + + return overlap_fn + + +class QNSPSAVQE(VQE): + def __init__( + self, + ansatz: Optional[QuantumCircuit] = None, + initial_point: Optional[np.ndarray] = None, + expectation: Optional[ExpectationBase] = None, + callback: Optional[Callable[[int, np.ndarray, float, float], None]] = None, + quantum_instance: Optional[Union[QuantumInstance, BaseBackend, Backend]] = None, + natural_spsa: bool = False, + maxiter: int = 100, + blocking: bool = True, + allowed_increase: float = 0.1, + learning_rate: Optional[float] = None, + perturbation: Optional[float] = None, + regularization: float = 0.01, + resamplings: int = 1, + hessian_delay: int = 0, + initial_hessian: Optional[np.ndarray] = None, + ) -> None: + """ + Args: + ansatz: A parameterized circuit used as Ansatz for the wave function. + initial_point: An optional initial point (i.e. initial parameter values) + for the optimizer. If ``None`` then VQE will look to the variational form for a + preferred point and if not will simply compute a random one. + expectation: The Expectation converter for taking the average value of the + Observable over the ansatz state function. When ``None`` (the default) an + :class:`~qiskit.opflow.expectations.ExpectationFactory` is used to select + an appropriate expectation based on the operator and backend. When using Aer + qasm_simulator backend, with paulis, it is however much faster to leverage custom + Aer function for the computation but, although VQE performs much faster + with it, the outcome is ideal, with no shot noise, like using a state vector + simulator. If you are just looking for the quickest performance when choosing Aer + qasm_simulator and the lack of shot noise is not an issue then set `include_custom` + parameter here to ``True`` (defaults to ``False``). + callback: a callback that can access the intermediate data during the optimization. + Four parameter values are passed to the callback as follows during each evaluation + by the optimizer for its current set of parameters as it works towards the minimum. + These are: the evaluation count, the optimizer parameters for the + variational form, the evaluated mean and the evaluated standard deviation.` + quantum_instance: Quantum Instance or Backend + """ + super().__init__( + ansatz=ansatz, + initial_point=initial_point, + quantum_instance=quantum_instance, + expectation=expectation, + ) + + self.natural_spsa = natural_spsa + self.maxiter = maxiter + self.learning_rate = learning_rate + self.perturbation = perturbation + self.allowed_increase = allowed_increase + self.blocking = blocking + self.regularization = regularization + self.resamplings = resamplings + self.hessian_delay = hessian_delay + self.initial_hessian = initial_hessian + + self._ret = VQEResult() + self._eval_time = None + self._callback = callback + + self._eval_count = 0 + + @property + def optimizer(self): # pylint: disable=arguments-differ + raise NotImplementedError( + "The optimizer is a SPSA version with batched circuits and " + "cannot be returned as a standalone." + ) + + @optimizer.setter + def optimizer(self, optimizer): + raise NotImplementedError( + "The optimizer is a SPSA version with batched circuits and " "cannot be set." + ) + + def compute_minimum_eigenvalue( + self, operator: OperatorBase, aux_operators: Optional[List[Optional[OperatorBase]]] = None + ) -> MinimumEigensolverResult: + if self.quantum_instance is None: + raise AlgorithmError( + "A QuantumInstance or Backend " "must be supplied to run the quantum algorithm." + ) + self.quantum_instance.circuit_summary = True + + if operator is None: + raise AlgorithmError("The operator was never provided.") + + self._check_operator_ansatz(operator) + + # We need to handle the array entries being Optional i.e. having value None + if aux_operators: + zero_op = I.tensorpower(operator.num_qubits) * 0.0 + converted = [] + for op in aux_operators: + if op is None: + converted.append(zero_op) + else: + converted.append(op) + + # For some reason Chemistry passes aux_ops with 0 qubits and paulis sometimes. + aux_operators = [zero_op if op == 0 else op for op in converted] + else: + aux_operators = None + + optimizer_settings = { + "maxiter": self.maxiter, + "blocking": self.blocking, + "allowed_increase": self.allowed_increase, + "learning_rate": self.learning_rate, + "perturbation": self.perturbation, + "regularization": self.regularization, + "resamplings": self.resamplings, + "hessian_delay": self.hessian_delay, + "initial_hessian": self.initial_hessian, + "expectation": self.expectation, + "callback": self._callback, + "backend": self._quantum_instance, + } + + if self.natural_spsa: + optimizer = _QNSPSA(overlap_fn=self.ansatz, **optimizer_settings) + else: + optimizer = _SPSA(**optimizer_settings) + + self._eval_count = 0 + # energy_evaluation, expectation = self.get_energy_evaluation( + # operator, return_expectation=True + # ) + + theta = ParameterVector("θ​", self.ansatz.num_parameters) + energy_expectation, expectation = self.construct_expectation( + theta, operator, return_expectation=True + ) + + start_time = time() + opt_params, opt_value, nfev = optimizer.optimize( + num_vars=len(self.initial_point), + objective_function=energy_expectation, + # gradient_function=gradient, + # variable_bounds=bounds, + initial_point=self.initial_point, + ) + eval_time = time() - start_time + + result = VQEResult() + result.optimal_point = opt_params + result.optimal_parameters = dict(zip(self._ansatz_params, opt_params)) + result.optimal_value = opt_value + result.cost_function_evals = nfev + result.optimizer_time = eval_time + result.eigenvalue = opt_value + 0j + result.eigenstate = self._get_eigenstate(result.optimal_parameters) + + logger.info( + "Optimization complete in %s seconds.\nFound opt_params %s in %s evals", + eval_time, + result.optimal_point, + self._eval_count, + ) + + # TODO delete as soon as get_optimal_vector etc are removed + self._ret = result + + if aux_operators is not None: + aux_values = self._eval_aux_ops(opt_params, aux_operators, expectation=expectation) + result.aux_operator_eigenvalues = aux_values[0] + + # return result, None + + return result, optimizer.history + + +# Code from qn-spsa/utils.py + + +def bernoulli_perturbation(dim, perturbation_dims=None): + """Get a Bernoulli random perturbation.""" + if perturbation_dims is None: + return np.array([1 - 2 * np.random.binomial(1, 0.5) for _ in range(dim)]) + + pert = np.array([1 - 2 * np.random.binomial(1, 0.5) for _ in range(perturbation_dims)]) + indices = np.random.choice(list(range(dim)), size=perturbation_dims, replace=False) + result = np.zeros(dim) + result[indices] = pert + + return result + + +def powerseries(eta=0.01, power=2, offset=0): + """Yield a series decreasing by a powerlaw.""" + + n = 1 + while True: + yield eta / ((n + offset) ** power) + n += 1 + + +def constant(eta=0.01): + """Yield a constant series.""" + + while True: + yield eta + + +def _make_spd(matrix, bias=0.01): + identity = np.identity(matrix.shape[0]) + psd = scipy.linalg.sqrtm(matrix.dot(matrix)) + return (1 - bias) * psd + bias * identity + + +class Publisher: + """Class used to publish interim results.""" + + def __init__(self, messenger): + self._messenger = messenger + + def callback(self, *args, **kwargs): + text = list(args) + for k, v in kwargs.items(): + text.append({k: v}) + self._messenger.publish(text) + + +def _parse_optimizer(kwargs): + optimizer = kwargs.get("optimizer", SPSA()) + # backwards compatibility: previously optimizers were dicts + if isinstance(optimizer, dict): + # verify the optimizer and split into name and parameters + optimizer_name = optimizer.pop("name", "SPSA") + if optimizer_name not in ["SPSA", "QN-SPSA"]: + raise ValueError( + f"Unsupported optimizer: {optimizer_name}." + "If specified via dict only SPSA and QN-SPSA are available." + ) + + optimizer_params = optimizer + + # de-serialize learning rate and perturbation if necessary + for attr in ["learning_rate", "perturbation"]: + if attr in optimizer_params.keys(): + if isinstance(optimizer_params[attr], (list, tuple)): # need to de-serialize + iterator_factory = It.deserialize(optimizer_params[attr]) + optimizer_params[attr] = iterator_factory.get_iterator() + + if optimizer_name == "SPSA": + optimizer = _SPSA(**optimizer_params) + else: + optimizer = _QNSPSA(overlap_fn=lambda: None, **optimizer_params) + + return optimizer + + +def main(backend, user_messenger, **kwargs): + """Entry function.""" + # parse inputs + mandatory = {"ansatz", "operator"} + missing = mandatory - set(kwargs.keys()) + if len(missing) > 0: + raise ValueError(f"The following mandatory arguments are missing: {missing}.") + + ansatz = kwargs["ansatz"] + operator = kwargs["operator"] + aux_operators = kwargs.get("aux_operators", None) + initial_point = kwargs.get("initial_point", None) + optimizer = _parse_optimizer(kwargs) + + shots = kwargs.get("shots", 1024) + measurement_error_mitigation = kwargs.get("measurement_error_mitigation", False) + + # set up quantum instance + if measurement_error_mitigation: + _quantum_instance = QuantumInstance( + backend, + shots=shots, + measurement_error_mitigation_shots=shots, + measurement_error_mitigation_cls=CompleteMeasFitter, + ) + else: + _quantum_instance = QuantumInstance(backend, shots=shots) + + publisher = Publisher(user_messenger) + + # verify the initial point + if initial_point == "random" or initial_point is None: + initial_point = np.random.random(ansatz.num_parameters) + elif len(initial_point) != ansatz.num_parameters: + raise ValueError("Mismatching number of parameters and initial point dimension.") + + # construct the VQE instance + if isinstance(optimizer, (SPSA, QNSPSA, _SPSA, _QNSPSA)): + vqe = QNSPSAVQE( + ansatz=ansatz, + initial_point=initial_point, + expectation=PauliExpectation(), + callback=publisher.callback, + quantum_instance=_quantum_instance, + natural_spsa=isinstance(optimizer, QNSPSA), + allowed_increase=optimizer.allowed_increase, + maxiter=optimizer.maxiter, + blocking=optimizer.blocking, + learning_rate=optimizer.learning_rate, + perturbation=optimizer.perturbation, + resamplings=optimizer.resamplings, + regularization=optimizer.regularization, + hessian_delay=optimizer.hessian_delay, + initial_hessian=optimizer.initial_hessian, + ) + result, history = vqe.compute_minimum_eigenvalue(operator, aux_operators) + else: + vqe = VQE( + ansatz=ansatz, + initial_point=initial_point, + expectation=PauliExpectation(), + callback=publisher.callback, + quantum_instance=_quantum_instance, + ) + result = vqe.compute_minimum_eigenvalue(operator, aux_operators) + history = None + + eigenvalues_list = ( + result.aux_operator_eigenvalues.tolist() + if result.aux_operator_eigenvalues is not None + else None + ) + + serialized_result = { + "optimizer_evals": result.optimizer_evals, + "optimizer_time": result.optimizer_time, + "optimal_value": result.optimal_value, + "optimal_point": result.optimal_point, + "optimal_parameters": None, # ParameterVectorElement is not serializable + "cost_function_evals": result.cost_function_evals, + "eigenstate": result.eigenstate, + "eigenvalue": result.eigenvalue, + "aux_operator_eigenvalues": eigenvalues_list, + "optimizer_history": history, + } + + user_messenger.publish(serialized_result, final=True) + + +if __name__ == "__main__": + # the code currently uses Aer instead of runtime provider + _backend = Aer.get_backend("qasm_simulator") + user_params = {} + if len(sys.argv) > 1: + # If there are user parameters. + user_params = json.loads(sys.argv[1], cls=RuntimeDecoder) + try: + main(_backend, **user_params) + except Exception: + print(traceback.format_exc()) diff --git a/qiskit_runtime/vqe/vqe_metadata.json b/qiskit_runtime/vqe/vqe_metadata.json new file mode 100644 index 000000000..b378e0219 --- /dev/null +++ b/qiskit_runtime/vqe/vqe_metadata.json @@ -0,0 +1,28 @@ +{ + "name": "vqe", + "description": "Variational Quantum Eigensolver (VQE) to find the minimal eigenvalue of a Hamiltonian.", + "max_execution_time": 18000, + "version": "1.1", + "parameters": [ + {"name": "ansatz", "description": "A parameterized quantum circuit preparing the ansatz wavefunction for the VQE. It is assumed that all qubits are initially in the 0 state.", "type": "QuantumCircuit", "required": true}, + {"name": "operator", "description": "The Hamiltonian whose smallest eigenvalue we're trying to find.", "type": "PauliSumOp", "required": true}, + {"name": "optimizer", "description": "The classical optimizer used in to update the parameters in each iteration. Can be either any of Qiskit's optimizer classes. If a dictionary, only SPSA and QN-SPSA are supported and the dictionary must specify the name and options of the optimizer, e.g. ``{'name': 'SPSA', 'maxiter': 100}``.", "type": "Union[Optimizer, dict]", "required": true}, + {"name": "initial_parameters", "description": "Initial parameters of the ansatz. Can be an array or the string ``'random'`` to choose random initial parameters.", "type": "Union[numpy.ndarray, str]", "required": true}, + {"name": "aux_operators", "description": "A list of operators to be evaluated at the final, optimized state.", "type": "List[PauliSumOp]", "required": false}, + {"name": "shots", "description": "The number of shots used for each circuit evaluation. Defaults to 1024.", "type": "int", "required": false}, + {"name": "measurement_error_mitigation", "description": "Whether to apply measurement error mitigation in form of a complete measurement fitter to the measurements. Defaults to False.", "type": "bool", "required": false}, + {"name": "initial_layout", "description": "Initial position of virtual qubits on the physical qubits of the quantum device. Default is None.", "type": "list or dict", "required": false} + ], + "return_values": [ + {"name": "optimizer_evals", "description": "The number of steps of the optimizer.", "type": "int"}, + {"name": "optimizer_time", "description": "The total time taken by the optimizer.", "type": "float"}, + {"name": "optimal_value", "description": "The smallest value found during the optimization. Equal to the ``eigenvalue`` attribute.", "type": "float"}, + {"name": "optimal_point", "description": "The optimal parameter values found during the optimization.", "type": "np.ndarray"}, + {"name": "optimal_parameters", "description": "Not supported at the moment, therefore ``None``.", "type": "NoneType"}, + {"name": "cost_function_evals", "description": "The number of cost function (energy) evaluations", "type": "int"}, + {"name": "eigenstate", "description": "The square root of sampling probabilities for each computational basis state of the circuit with optimal parameters.", "type": "dict"}, + {"name": "eigenvalue", "description": "The estimated eigenvalue.", "type": "complex"}, + {"name": "aux_operator_eigenvalues", "description": "The expectation values of the auxiliary operators at the optimal state.", "type": "np.ndarray"}, + {"name": "optimizer_history", "description": "A dictionary containing information about the optimization process: the value objective function, parameters, and a timestamp.", "type": "dict"} + ] +} diff --git a/tutorials/00_introduction.ipynb b/tutorials/00_introduction.ipynb new file mode 100644 index 000000000..b3597b348 --- /dev/null +++ b/tutorials/00_introduction.ipynb @@ -0,0 +1,344 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "2b3fb1b1", + "metadata": {}, + "source": [ + "# Qiskit Runtime" + ] + }, + { + "cell_type": "markdown", + "id": "8fb23fc2", + "metadata": {}, + "source": [ + "Qiskit Runtime is a new architecture offered by IBM Quantum that streamlines computations requiring many iterations. These experiments will execute significantly faster within this improved hybrid quantum/classical process.\n", + "\n", + "Using Qiskit Runtime, for example, a research team at IBM Quantum was able to achieve \n", + "[120x speed up](https://research.ibm.com/blog/120x-quantum-speedup) in their lithium hydride simulation. \n", + "\n", + "Qiskit Runtime allows authorized users to upload their Qiskit quantum programs for themselves or \n", + "others to use. A Qiskit quantum program, also called a Qiskit runtime program, is a piece of Python code that takes certain inputs, performs\n", + "quantum and maybe classical computation, and returns the processing results. The same or other\n", + "authorized users can then invoke these quantum programs by simply passing in the required input parameters." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "233c286a", + "metadata": {}, + "outputs": [], + "source": [ + "from qiskit import IBMQ\n", + "\n", + "IBMQ.load_account()\n", + "provider = IBMQ.get_provider(project='qiskit-runtime') # Change this to your provider." + ] + }, + { + "cell_type": "markdown", + "id": "1612dac1", + "metadata": {}, + "source": [ + "\n", + "If you don't have an IBM Quantum account, you can sign up for one on the [IBM Quantum](https://quantum-computing.ibm.com/) page." + ] + }, + { + "cell_type": "markdown", + "id": "61d9f293", + "metadata": {}, + "source": [ + "## Listing programs " + ] + }, + { + "cell_type": "markdown", + "id": "2ed6ce38", + "metadata": {}, + "source": [ + "The `provider.runtime` object is an instance of the [`IBMRuntimeService`](https://qiskit.org/documentation/stubs/qiskit.providers.ibmq.runtime.IBMRuntimeService.html#qiskit.providers.ibmq.runtime.IBMRuntimeService) class and serves as the main entry point to using the runtime service. It has three methods that can be used to find metadata of available programs:\n", + "- `pprint_programs()`: pretty prints summary metadata of available programs\n", + "- `programs()`: returns a list of `RuntimeProgram` instances\n", + "- `program()`: returns a single `RuntimeProgram` instance\n", + "\n", + "The metadata of a runtime program includes its ID, name, description, maximum execution time, backend requirements, input parameters, return values, and interim results. Maximum execution time is the maximum amount of time, in seconds, a program can run before being forcibly terminated." + ] + }, + { + "cell_type": "markdown", + "id": "2abfb988", + "metadata": {}, + "source": [ + "To print the summary metadata of the programs (by default first 20 programs are displayed):" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "a420f91c", + "metadata": {}, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": "==================================================\nqasm3-runner:\n Name: qasm3-runner\n Description: A runtime program that takes one or more circuits, converts them to OpenQASM3, compiles them, executes them, and optionally applies measurement error mitigation. This program can also take and execute one or more OpenQASM3 strings. Note that this program can only run on a backend that supports OpenQASM3.\n==================================================\nsampler:\n Name: sampler\n Description: Sample distributions generated by given circuits executed on the target backend.\n==================================================\nestimator:\n Name: estimator\n Description: Expectation value estimator. A runtime program that estimates the value of an observable for an input quantum circuit. This program is in beta mode and is only available to select accounts.\n==================================================\nsample-expval:\n Name: sample-expval\n Description: A sample expectation value program.\n==================================================\nvqe:\n Name: vqe\n Description: Variational Quantum Eigensolver (VQE) to find the minimal eigenvalue of a Hamiltonian.\n==================================================\ncircuit-runner:\n Name: circuit-runner\n Description: A runtime program that takes one or more circuits, compiles them, executes them, and optionally applies measurement error mitigation.\n==================================================\nhello-world:\n Name: hello-world\n Description: A sample runtime program.\n==================================================\nquantum-kernel-alignment:\n Name: quantum-kernel-alignment\n Description: Quantum kernel alignment algorithm that learns, on a given dataset, a quantum kernel maximizing the SVM classification margin.\n" + } + ], + "source": [ + "provider.runtime.pprint_programs()" + ] + }, + { + "cell_type": "markdown", + "id": "0a5ca5f5", + "metadata": {}, + "source": [ + "You can use the `limit` and `skip` parameters in `pprint_programs()` and `programs()` to page through the remaining programs. You can pass `detailed = True` parameter to `pprint_programs()` to view all the metadata for the programs. The program metadata once fetched, is cached for performance reasons, so you can pass `refresh = True` parameter to `pprint_programs()` or `programs()` methods in order to get the latest programs from the server. To print the metadata of the program `hello-world`:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "f8302b63", + "metadata": {}, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": "hello-world:\n Name: hello-world\n Description: A sample runtime program.\n Creation date: 2021-07-02T13:45:13Z\n Update date: 2021-07-02T13:45:13Z\n Max execution time: 300\n Input parameters:\n Properties:\n - iterations:\n Description: Number of iterations to run. Each iteration generates a runs a random circuit.\n Minimum: 0\n Type: integer\n Required: True\n Interim results:\n Properties:\n - counts:\n Description: Histogram data of the circuit result.\n Type: object\n Required: False\n - iteration:\n Description: Iteration number.\n Type: integer\n Required: False\n Returns:\n Description: A string that says 'All done!'.\n Type: string\n" + } + ], + "source": [ + "program = provider.runtime.program('hello-world')\n", + "print(program)" + ] + }, + { + "cell_type": "markdown", + "id": "ec37a66c", + "metadata": {}, + "source": [ + "As you can see from above, the program `hello-world` is a simple program that has only 1 input parameter `iterations`, which indicates how many iterations to run. For each iteration it generates and runs a random 5-qubit circuit and returns the counts as well as the iteration number as the interim results. When the program finishes, it returns the sentence `All done!`. This program can only run for 300 seconds (5 minutes), and requires a backend that has at least 5 qubits." + ] + }, + { + "cell_type": "markdown", + "id": "8097db3a", + "metadata": {}, + "source": [ + "## Invoking a runtime program " + ] + }, + { + "cell_type": "markdown", + "id": "b6d680a3", + "metadata": {}, + "source": [ + "You can use the [`IBMRuntimeService.run()`](https://qiskit.org/documentation/stubs/qiskit.providers.ibmq.runtime.IBMRuntimeService.html#qiskit.providers.ibmq.runtime.IBMRuntimeService.run) method to invoke a runtime program. This method takes the following parameters:\n", + "\n", + "- `program_id`: ID of the program to run\n", + "- `inputs`: Program input parameters. These input values are passed to the runtime program.\n", + "- `options`: Runtime options. These options control the execution environment. Currently the only available option is `backend_name`, which is required.\n", + "- `callback`: Callback function to be invoked for any interim results. The callback function will receive 2 positional parameters: job ID and interim result.\n", + "- `result_decoder`: Optional class used to decode job result." + ] + }, + { + "cell_type": "markdown", + "id": "abe247c4", + "metadata": {}, + "source": [ + "Before we run a quantum program, we may want to define a callback function that would process interim results, which are intermediate data provided by a program while its still running. \n", + "\n", + "As we saw earlier, the metadata of `hello-world` says that its interim results are the iteration number and the counts of the randomly generated circuit. Here we define a simple callback function that just prints these interim results:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "92f46214", + "metadata": {}, + "outputs": [], + "source": [ + "def interim_result_callback(job_id, interim_result):\n", + " print(f\"interim result: {interim_result}\")" + ] + }, + { + "cell_type": "markdown", + "id": "3f9e793f", + "metadata": {}, + "source": [ + "The following example runs the `hello-world` program with 3 iterations on `ibmq_montreal` and waits for its result. You can also use a different backend that supports Qiskit Runtime:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "d622e53c", + "metadata": {}, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": "job id: c618jdik2ih5ha3l6mog\ninterim result: {'iteration': 0, 'counts': {'00000': 31, '00001': 11, '10000': 10, '10001': 10, '10010': 19, '10011': 11, '10100': 4, '10101': 6, '10110': 11, '10111': 8, '11000': 16, '11001': 4, '11010': 45, '11011': 16, '11100': 8, '11101': 9, '11110': 18, '11111': 8, '00010': 104, '00011': 47, '00100': 5, '00101': 10, '00110': 25, '00111': 10, '01000': 60, '01001': 35, '01010': 260, '01011': 119, '01100': 13, '01101': 12, '01110': 39, '01111': 40}}\ninterim result: {'iteration': 1, 'counts': {'00000': 99, '00001': 64, '10000': 13, '10001': 12, '10010': 9, '10011': 4, '10100': 21, '10101': 89, '10110': 6, '10111': 19, '11000': 19, '11001': 9, '11010': 5, '11011': 5, '11100': 26, '11101': 61, '11110': 11, '11111': 17, '00010': 37, '00011': 23, '00100': 73, '00101': 13, '00110': 20, '00111': 3, '01000': 105, '01001': 83, '01010': 30, '01011': 26, '01100': 79, '01101': 11, '01110': 22, '01111': 10}}\ninterim result: {'iteration': 2, 'counts': {'00000': 30, '00001': 5, '10000': 8, '10001': 3, '10010': 1, '10011': 3, '10100': 7, '10101': 5, '10110': 3, '11000': 22, '11001': 6, '11010': 1, '11011': 3, '11100': 66, '11101': 8, '11110': 11, '11111': 7, '00010': 2, '00011': 2, '00100': 51, '00101': 5, '00110': 5, '00111': 2, '01000': 136, '01001': 16, '01010': 11, '01011': 2, '01100': 534, '01101': 31, '01110': 31, '01111': 7}}\nAll done!\n" + } + ], + "source": [ + "backend = provider.get_backend('ibmq_montreal')\n", + "program_inputs = {\n", + " 'iterations': 3\n", + "}\n", + "options = {'backend_name': backend.name()}\n", + "job = provider.runtime.run(program_id=\"hello-world\",\n", + " options=options,\n", + " inputs=program_inputs,\n", + " callback=interim_result_callback\n", + " )\n", + "print(f\"job id: {job.job_id()}\")\n", + "result = job.result()\n", + "print(result)" + ] + }, + { + "cell_type": "markdown", + "id": "1cb7b160", + "metadata": {}, + "source": [ + "The `run()` method returns a [`RuntimeJob`](https://qiskit.org/documentation/stubs/qiskit.providers.ibmq.runtime.RuntimeJob.html#qiskit.providers.ibmq.runtime.RuntimeJob) instance, which is similar to the `Job` instance returned by regular `backend.run()`. Some of the `RuntimeJob` methods:\n", + "\n", + "- `status()`: Return job status.\n", + "- `result()`: Wait for the job to finish and return the final result.\n", + "- `cancel()`: Cancel the job.\n", + "- `wait_for_final_state()`: Wait for the job to finish.\n", + "- `stream_results()`: Stream interim results. This can be used to start streaming the interim results if a `callback` function was not passed to the `run()` method. This method can also be used to reconnect a lost websocket connection.\n", + "- `job_id()`: Return the job ID.\n", + "- `backend()`: Return the backend where the job is run.\n", + "- `logs()`: Return job logs.\n", + "- `error_message()`: Returns the reason if the job failed and `None` otherwise.\n", + "\n", + "Refer to the [`RuntimeJob` API documentation](https://qiskit.org/documentation/stubs/qiskit.providers.ibmq.runtime.RuntimeJob.html#qiskit.providers.ibmq.runtime.RuntimeJob) for a full list of methods and usage. " + ] + }, + { + "cell_type": "markdown", + "id": "present-creature", + "metadata": {}, + "source": [ + "
\n", + "Note: To ensure fairness, there is a maximum execution time for each Qiskit Runtime job. If a job exceeds this time limit, it is forcibly terminated. The maximum execution time is calculated based on 1) a maximum system limit, 2) the `max_execution_time` defined by the program, and 3) the fair-share value in your hub/group/project. \n", + "
" + ] + }, + { + "cell_type": "markdown", + "id": "821630b0", + "metadata": {}, + "source": [ + "## Retrieving old jobs" + ] + }, + { + "cell_type": "markdown", + "id": "ed6efcd4", + "metadata": {}, + "source": [ + "You can use the [`IBMRuntimeService.job()`](https://qiskit.org/documentation/stubs/qiskit.providers.ibmq.runtime.IBMRuntimeService.html#qiskit.providers.ibmq.runtime.IBMRuntimeService.job) method to retrieve a previously executed runtime job. Attributes of this [`RuntimeJob`](https://qiskit.org/documentation/stubs/qiskit.providers.ibmq.runtime.RuntimeJob.html#qiskit.providers.ibmq.runtime.RuntimeJob) instance can tell you about the execution:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "4336b881", + "metadata": {}, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": "Job c618jdik2ih5ha3l6mog is an execution instance of runtime program hello-world.\nThis job ran on backend ibmq_montreal and had input parameters {'iterations': 3}\n" + } + ], + "source": [ + "retrieved_job = provider.runtime.job(job.job_id())\n", + "print(f\"Job {retrieved_job.job_id()} is an execution instance of runtime program {retrieved_job.program_id}.\")\n", + "print(f\"This job ran on backend {retrieved_job.backend()} and had input parameters {retrieved_job.inputs}\")" + ] + }, + { + "cell_type": "markdown", + "id": "5c00514e", + "metadata": {}, + "source": [ + "Similarly, you can use [`IBMRuntimeService.jobs()`](https://qiskit.org/documentation/stubs/qiskit.providers.ibmq.runtime.IBMRuntimeService.html#qiskit.providers.ibmq.runtime.IBMRuntimeService.jobs) to get a list of jobs. You can specify a limit on how many jobs to return. The default limit is 10:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "919862b8", + "metadata": {}, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": "c618jdik2ih5ha3l6mog\n" + } + ], + "source": [ + "retrieved_jobs = provider.runtime.jobs(limit=1)\n", + "for rjob in retrieved_jobs:\n", + " print(rjob.job_id())" + ] + }, + { + "cell_type": "markdown", + "id": "d6f8f1d2", + "metadata": {}, + "source": [ + "## Deleting a job" + ] + }, + { + "cell_type": "markdown", + "id": "81234913", + "metadata": {}, + "source": [ + "You can use the [`IBMRuntimeService.delete_job()`](https://qiskit.org/documentation/stubs/qiskit.providers.ibmq.runtime.IBMRuntimeService.html#qiskit.providers.ibmq.runtime.IBMRuntimeService.delete_job) method to delete a job. You can only delete your own jobs, and this action cannot be reversed. " + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "b1095852", + "metadata": {}, + "outputs": [], + "source": [ + "provider.runtime.delete_job(job.job_id())" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.1" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/tutorials/01_circuit_runner.ipynb b/tutorials/01_circuit_runner.ipynb new file mode 100644 index 000000000..8f1a3d334 --- /dev/null +++ b/tutorials/01_circuit_runner.ipynb @@ -0,0 +1,623 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "f041e8ea", + "metadata": {}, + "source": [ + "# Circuit runner program" + ] + }, + { + "cell_type": "markdown", + "id": "d16ddf88", + "metadata": {}, + "source": [ + "## `run_circuits` convenience method\n", + "\n", + "Evaluating quantum circuits is the fundamental operation on quantum computing systems. The `circuit-runner` program facilitates compiling, executing, and (possibly) post-processing circuit entirely in the cloud. The easiest way to use the `circuit-runner` is via the convenience method accessible through a provider with `runtime` access:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "fafcaba8", + "metadata": {}, + "outputs": [], + "source": [ + "from qiskit import IBMQ, QuantumCircuit\n", + "from qiskit.visualization import plot_histogram\n", + "from qiskit.quantum_info import hellinger_fidelity\n", + "from qiskit.ignis.mitigation.expval import expectation_value" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "1b07aacd", + "metadata": {}, + "outputs": [], + "source": [ + "IBMQ.load_account();" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "9b552135", + "metadata": {}, + "outputs": [], + "source": [ + "# Replace by your provider\n", + "provider = IBMQ.get_provider(project='qiskit-runtime')" + ] + }, + { + "cell_type": "markdown", + "id": "ee1e3511", + "metadata": {}, + "source": [ + "The runner is accessbile via the `run_circuits` method of the provider, and supports the same functionality that transpiling and executing a circuit in Qiskit does, as well as a few additional arguments:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "84785ee9", + "metadata": {}, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": "Execute the input circuit(s) on a backend using the runtime service.\n\n Note:\n This method uses the IBM Quantum runtime service which is not\n available to all accounts.\n\n Args:\n circuits: Circuit(s) to execute.\n\n backend_name: Name of the backend to execute circuits on.\n Transpiler options are automatically grabbed from backend configuration\n and properties unless otherwise specified.\n\n shots: Number of repetitions of each circuit, for sampling. If not specified,\n the backend default is used.\n\n initial_layout: Initial position of virtual qubits on physical qubits.\n\n layout_method: Name of layout selection pass ('trivial', 'dense',\n 'noise_adaptive', 'sabre').\n Sometimes a perfect layout can be available in which case the layout_method\n may not run.\n\n routing_method: Name of routing pass ('basic', 'lookahead', 'stochastic', 'sabre')\n\n translation_method: Name of translation pass ('unroller', 'translator', 'synthesis')\n\n seed_transpiler: Sets random seed for the stochastic parts of the transpiler.\n\n optimization_level: How much optimization to perform on the circuits.\n Higher levels generate more optimized circuits, at the expense of longer\n transpilation time.\n If None, level 1 will be chosen as default.\n\n init_qubits: Whether to reset the qubits to the ground state for each shot.\n\n rep_delay: Delay between programs in seconds. Only supported on certain\n backends (``backend.configuration().dynamic_reprate_enabled`` ). If supported,\n ``rep_delay`` will be used instead of ``rep_time`` and must be from the\n range supplied by the backend (``backend.configuration().rep_delay_range``).\n Default is given by ``backend.configuration().default_rep_delay``.\n\n transpiler_options: Additional transpiler options.\n\n measurement_error_mitigation: Whether to apply measurement error mitigation.\n\n use_measure_esp: Whether to use excited state promoted (ESP) readout for measurements\n which are the final instruction on a qubit. ESP readout can offer higher fidelity\n than standard measurement sequences. See\n `here `_.\n\n **run_config: Extra arguments used to configure the circuit execution.\n\n Returns:\n Runtime job.\n \n" + } + ], + "source": [ + "print(provider.run_circuits.__doc__)" + ] + }, + { + "cell_type": "markdown", + "id": "65f667d6", + "metadata": {}, + "source": [ + "of the possible arguments, `measurement_error_mitigation` is one that is unique to the Qiskit Runtime, and will be used below.\n", + "\n", + "We now construct an example circuit to execute via the circuit runner:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "e01c0503", + "metadata": { + "tags": [ + "nbsphinx-thumbnail" + ] + }, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": "
", + "image/svg+xml": "\n\n\n \n \n \n \n 2021-11-02T14:33:38.178360\n image/svg+xml\n \n \n Matplotlib v3.4.2, https://matplotlib.org/\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n", + "image/png": "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\n" + }, + "metadata": {}, + "execution_count": 5 + } + ], + "source": [ + "N = 6\n", + "qc = QuantumCircuit(N)\n", + "\n", + "qc.x(range(0, N))\n", + "qc.h(range(0, N))\n", + "\n", + "for kk in range(N//2,0,-1):\n", + " qc.ch(kk, kk-1)\n", + "for kk in range(N//2, N-1):\n", + " qc.ch(kk, kk+1)\n", + "qc.measure_all()\n", + "qc.draw('mpl',fold=-1)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "f36f8eb8", + "metadata": {}, + "outputs": [], + "source": [ + "exact_dist = {'000000': 0.015624999999999986,\n", + " '000001': 0.015624999999999986,\n", + " '000011': 0.031249999999999965,\n", + " '000111': 0.06249999999999992,\n", + " '100000': 0.015624999999999986,\n", + " '100001': 0.015624999999999986,\n", + " '100011': 0.031249999999999965,\n", + " '100111': 0.06249999999999992,\n", + " '110000': 0.031249999999999965,\n", + " '110001': 0.031249999999999965,\n", + " '110011': 0.06249999999999992,\n", + " '110111': 0.12499999999999982,\n", + " '111111': 0.4999999999999991}" + ] + }, + { + "cell_type": "markdown", + "id": "f8186fc9", + "metadata": {}, + "source": [ + "Having defined our circuit(s), we can now compile and execute them on a given backend via the `run_circuits` method:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "08de9773", + "metadata": {}, + "outputs": [], + "source": [ + "backend = provider.backend.ibmq_montreal" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "8356e156", + "metadata": {}, + "outputs": [], + "source": [ + "job = provider.run_circuits(qc, backend.name(), shots=2048, initial_layout=[0,1,4,7,10,12])" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "738dbad1", + "metadata": {}, + "outputs": [], + "source": [ + "res = job.result()" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "4cf377ee", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAA98AAAGXCAYAAACqZ+OYAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjQuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/Z1A+gAAAACXBIWXMAAAsTAAALEwEAmpwYAACdH0lEQVR4nOzdd3wUdf7H8dc32SSbELrU0HtvRhGFiAri6Z2oZz8PFRU90Dt7B9QD9WyniF1PrFhPQU8R8QREODCgIKB0+AHSeyQ9398f38262Wwg2WQ3JLyfj8c+YGfnM98yk5n5znznO8Zai4iIiIiIiIhETkxlZ0BERERERESkulPjW0RERERERCTC1PgWERERERERiTA1vkVEREREREQiTI1vERERERERkQhT41tEREREREQkwtT4FhEREREREYmwqDe+jTEjjTHrjDFZxpiFxpgBh5k/3hjzgC8m2xjzf8aYvwbN80djzHLf78uNMedGthQiIiIiIiIipRfVxrcx5iLgKeBBoDcwF/jcGNPiEGHvAGcAI4COwAXAkoBl9gPeBd4Cevn+fd8Y0zcCRRAREREREREpM2OtjV5ixswHllhrrwmYtgr4wFp7V4j5TwfeB9paa3eWsMx3gXrW2sEB02YAO6y1l1R0GURERERERETKKmp3vo0x8cCxwPSgn6YDJ5YQdg7wHXCzMWaTMWaVMWaCMSY5YJ5+IZb5xSGWKSIiIiIiIhJVniimdQwQC2wLmr4NGFRCTBugP5AN/BGoAzwNNAXO983TuIRlNg61QGPMCFwXdpKSko5t1qwZAAkJCcTGxnLw4EEAPB4PiYmJHDhwoDCO5ORkDh48SH5+PgA1atQgNzeXnJwcALxeL8YYMjMzAYiLiyMhIYGMjAwAYmJiqFGjRoUs49dff6WgoACA5ORksrOzyc3NBSAxMRFrLVlZWQDEx8cTFxfHr7/+CkBsbCxJSUkVsoyMjAwKe0/UrFmTzMxM8vLy8NUv+fn5ZGdnl6qOK2IZWk9aT1pPWk9aT1pPWk9aT1pPWk9aT6Wv45UrV+601jbgEBYuXNjQ4/G8DHRDg3aXxBpj9uXn579aUFDw3LHHHpsTPEM0G9/hiAEscKm1dh+AMeZ64AtjTCNrbXCj+7CstS8CLwKkpqba9PT0isyviIiIiIhIlWGM2XC4eTwez8uNGzfu3KBBgz0xMTHRe265CrHWkpOTE/fLL7/csH///j7A5cHzRPOqxU4gH2gUNL0RsLWEmC3A5sKGt89Pvn8LB2nbWsZlioiIiIiISOl1a9CgwX41vEtmjCEhISG3ZcuW+3C9t4uJWuPbWpsDLAQGB/00GDfqeSjfAk2DnvHu4Pu38ArNvDIuU0REREREREovRg3v0vHVU2zI36KclyeAK4wxVxtjOhtjnsI9v/08gDHmdWPM6wHzvw3sAl41xnQ1xpyEe1XZB9ba7b55ngJONcbcaYzpZIy5CzgFeDJKZRIRERERERE5pKg+822tfdcYUx+4F2gCLAXOtNYW3sVuETR/hjFmEG6Qte+APcDHwJ0B88w1xlwMjAMeANYAF1lr50e4OCIiIiIiIiKlEvUB16y1zwLPlvDbwBDTVgCnH2aZHwAfVET+REREREREpGTXPMmxkVz+SzeyMJLLrywaJl5EREREREQkwtT4FhERERERkSqv8N3oRyo1vkVERERERKRKSklJ6X7PPfc07tChQ5caNWr0uf3225s0b968W40aNXq3bdu26+uvv16ncN6mTZt2/+abb5IAnnvuuXrGmGPT09O9AP/85z+PGTRoUNtI5lWNbxEREREREamyPvzww3qfffbZqt27d3/fqVOnrG+++WbF/v37v7/zzjt/ufbaa1tv2LAhDqBv374HZsyYURNg9uzZNZs1a5b91VdfFX5PHjBgwIFI5lONbxEREREREamyrrvuum3t2rXLTU5OtsOHD9/TqlWr3NjYWK655po9LVu2zP7mm29qAKSlpWV88803NQHmz5+ffNNNN20N+F7ztNNOy4hkPtX4FhERERERkSqrZcuW/oe9J06cWL9Tp05datas2atmzZq9Vq9enbhjxw4PwODBgw+kp6cnb9iwIa6goMBcccUVu9PT05NXrFgRf+DAgdh+/fodjGQ+o/6qMREREREREZGKYoyxACtXroy/+eabW37yyScrTzvttAyPx0OnTp26WGsB6NatW7bX6y149NFHG/bt2/dAvXr1Co455pjcp59+ukFqampGbGxsRPOpO98iIiIiIiJS5R04cCDGGEPjxo1zAZ566qn6q1evTgycp2/fvgcmTZrUMC0t7QDASSeddGDSpEkN+/fvH9HnvUF3vkVERERERKQMXrqRhZWdh1COPfbYrBEjRmxLS0vrHBMTY//4xz/u6t27d5HnuNPS0g58+umn9QYPHpwBMHDgwAMvvvhio1NOOSWiz3sDmMJb8Eej1NRUm56eXtnZEBERERERqRTGmIXW2tRDzbN48eL1PXv23BmtPFV1ixcvPqZnz56tgqer27mIiIiIiIhIhKnxLSIiIiIiIhJhanyLiIiIiIiIRJga3yIiIiIiIiIRpsa3iIiIiIiISISp8S0iIiIiIiISYWp8i4iIiIiIiESYGt8iIiIiIiIiEeap7AyIiIiIiIhI1bGm433HRnL5bVfctzCSy68suvMtIiIiIiIiUgp//OMfW/31r39tGk6sGt8iIiIiIiIiEabGt4iIiIiIiFRZ69evjxsyZEjbunXr9kxJSek+bty4htu2bYtt1KhRj7fffrs2wL59+2JatGjRbeLEifUB3nnnndqdO3fukpyc3Ltx48Y9br755iJ3s7/44ovk3r17d6pZs2avxo0b95gwYUL9xx577JgpU6bUe+655xonJSX1PvXUU9uVJZ965ltERERERESqpPz8fM4666x2v/vd7/ZOmTJl7dq1a+NOP/30jp07d8567rnn1o8YMaL1ySefvOyWW25J6dKly8Hrr79+F0BycnLBa6+9tu7YY4/NTE9PTzzzzDM79O7d++Cf//znvStXrow/77zz2j/xxBMbrrjiij179uyJWbt2bfyJJ56YOW/evOSUlJScCRMm/FLWvKrxLSIiIiIiIlXSrFmzauzevdvz2GOPbQHo0qVLzp///OcdkydPrvfBBx+snzJlyu5TTjmlw969ez1LlixZVhj3+9///kDh//v27Zs5dOjQ3TNnzqz55z//ee+rr75a78QTT9x/7bXX7gZo3LhxfuPGjTPLm1d1OxcRERERESmFadOm0bFjR9q1a8fDDz9c7PdJkybRoEEDevXqRa9evXj55Zf9v7322mu0b9+e9u3b89prr/mnL1y4kO7du9OuXTv++te/Yq2NSlmqi7Vr18bv2LEjvmbNmr0KPxMmTGiyY8cOD8CoUaN2rlq1KvGiiy7a1bhx4/zCuP/+9781+vbt26Fu3bo9a9as2eutt95qsGvXLg/Axo0b41u3bp1d0XlV41tEREREROQw8vPzGTVqFJ9//jnLly9n8uTJLF++vNh8F110ET/88AM//PADV199NQC7d+/m/vvvZ/78+SxYsID777+fPXv2APCX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d82hS5cu5Ofns2LFCgBSUlJo1qwZ8+fPByA5OZnU1FTmzZtHdnY2AP3792flypVs374dgG7dupGdnc2qVasAaN68OY0aNSI9PR2AWrVq0adPH+bMmUNeXh4AaWlpLFu2jF27dgHQs2dPDhw4wNq1awFo1aoV9erVY9GiRQDUrVuXnj17MmvWLKy1GGM4+eSTWbx4MXv27AGgT58+7N69m/Xr12s9aT1pPWk9aT1pPWk9aT1pPWk9VYn1FEdxM2fOPGLXk0SHsdZGJyFjmgKbgZOttbMDpo8B/mSt7RgipjswAzjBWrvOGHMfcL61tlvAPP2ADsBioCbwN+BMoKe1dtWh8pSammoLN1oREREREZGKsKbjfcWmtV1RfNqRwBiz0FqbWtn5OBpE7c53WRljEoB3gVuttetKms9aO4+A7ujGmLm4u983AH+NcDZFREREREREDiuaje+dQD6uC3mgRkCoZ7SbAJ2BV40xr/qmxQDGGJMHnGmtnR4cZK3NN8akA+0rLOciIiIiIiIi5RC1AdestTnAQmBw0E+DcaOeB9sMdAd6BXyeB1b7/h8qBmOMAXrgBnITERERERERqXTR7nb+BPCGMWYB8C1wHdAU16jGGPM6gLV2mLU2Fwh+p/d2INtauzRg2ljgf8AqoBauq3kPDj+CuoiIiIiIiEhURLXxba191xhTH7gX1618Ka77+AbfLId833cJ6gAvAo2BfcD3QJq1dkH5cywiIiIiIiJSflEfcM1a+yzwbAm/DTxM7H3AfUHTbgJuqpjciYiIiIiIiFS8qD3zLSIiIiIiInK0UuNbREREREREJMLU+BYRERERERGJMDW+RURERERERCJMjW8RERERERGRCFPjW0RERERERCTC1PgWERERERERiTA1vkVEREREREQiTI1vERERERERkQhT41tEREREREQkwtT4FhEREREREYkwNb5FREREREREIkyNbxEREREREZEIU+NbREREREREJMLU+BYRERERERGJMDW+RURERERERCJMjW8RERERERGRCFPjW0RERERERCTC1PgWERERERERiTA1vkVEREREREQiTI1vERERERERkQhT41tEREREREQkwtT4FhEREREREYkwNb5FREREREREIkyNbxEREREREZEIU+NbREREREREJMLU+BYRERERERGJMDW+RURERERERCJMjW8RERERERGRCFPjW0RERERERCTC1PgWERERERERiTA1vkVEREREREQiTI1vERERERERkQhT41tEREREREQkwtT4FhEREREREYmwMjW+jTExxpiYgO+NjTFXG2NOqvisiYiIiIiIiFQPZb3z/R/gBgBjTDKQDjwKzDTGDKvgvImIiIiIiIhUC2VtfKcC//X9/zxgP9AQuAa4tQLzJSIiIiIiIlJtlLXxnQzs9f3/dOAja20urkHetgLzJSIiIiIiIlJtlLXx/X/AScaYGsAQ4Evf9HrAwYrMmIiIiIiIiEh1UdbG9xPAG8AmYDMw2zc9DfixAvMlIiIiIiJSacaNG0d8fDxxcXGcccYZxX6/9NJL8Xq9JCYmUqtWLaZOnQrAww8/TLdV4+myahzdVo3n5d1z/TF/+9vfSExMxOv10rdv36iVRY4MZWp8W2tfAPoBw4H+1toC309rgNEVnDcREREREZGoy8nJ4f7772f69Ons2bOHWbNm+RvXhR555BGysrLIzMzkhhtuYPjw4QC0bNmSd5oPZ3n7e3mi8Xk8snMGAKtWreKZZ55h0aJFZGVlsXPnTh599NGol00qT5nf822tTbfWfmStzQiY9h9r7bcVmzUREREREZHomzRpErVr12bgwIEkJyeTlpbGM888U2SeZs2a+f+/f/9+jDEAXHLJJXTzNgFgUI2OWCwH8rOYO3cutWrVonPnzgAMHjyY1157LUolkiNBmRvfxpiRxphlxpiDxpg2vml3GGMurPjsiYiIiIiIRNeKFSuoX7++/3vr1q3ZsmVLsfkuvPBC4uLieO6553j77beL/f7wzhnUivFSM9ZLWloa+/btY86cOWRlZfHJJ5+wY8eOiJZDjixlanwbY24E7gVeBEzAT78A11dctkRERERERI5s7733Hrm5uVx77bVcd911RX778sDPvLZ3PhObuHuUrVu35u677+aMM86gQYMGNGnShJiYMt8LlSqsrGv7OuAaa+1TQF7A9EVA1wrLlYiIiIiISCXp2LEju3bt8n9ft24dTZo0KXH+p556irVr1/q/L87czA1b3mdMwzPoV6O1f/rf//53MjIyOHDgAJ06daJ58+aRKYAckcra+G4JLA0xPRdILH92REREREREKtewYcPYt28fs2fPJiMjg9mzZzNy5Mgi83z55Zf+/99///0kJSUBsGHDBi7dNIkr65zAn+ocVyRm2bJlgGvMf/jhh/z973+PcEnkSOIp4/xrgT7AhqDpZwLLKyRHIiIiIiIilcjr9XLvvfcyaNAgrLUMHDiQoUOHkpaWxoABAxg/fjy33XYbP//8M8YYvF6v/5nvq666imybx+v7FvD6vgUAfNziGtoCZ555Jlu3bgXg2muvZciQIZVVRKkExlpb+pmNuRIYB9wOvABcC7TzfR9urX03EpmMlNTUVJuenl7Z2RARERERkWpkTcf7ik1ru6L4tCOBMWahtTa1svNxNCjTnW9r7avGGA/wIJAEvIEbbO2vVa3hLSIiIiIiIhItZe12jrX2JeAlY8wxQIy1dnvFZ0tERERERESk+ihz47uQtXZnRWZEREREREREpLo6bOPbGLMEONlau8cY8yNQ4kPi1toeFZk5ERERERERkeqgNHe+PwSyA/5f+hHaREREREREROTwjW9r7f0B/78vorkRERERERERqYZiyjKzMea/xpg6IabXMsb8t8JyJSIiIiIiIlKNlKnxDQwE4kNM9wIDyp0bERERERERkWqoVKOdG2P6BHztYYzZHfA9FhgCbK7IjImIiIiIiIhUF6V91Vg6bqA1C0wP8XsmcENFZUpERERERKSquObJot/vrJRcyJGutI3v1oAB1gLHAzsCfssBtltr8ys4byIiIiIiIiLVQqka39baDb7/lvUZcREREREREZGj3mEb38aY84BPrLW5vv+XyFr77wrLmYiIiIiIiEg1UZo73x8AjYHtvv+XxOIGXxMRERERERGRAIdtfFtrY0L9X0RERERERERKR41pERERERERkQgr7TPfpaJnvkVERERERESKK+0z36WhZ75FREREREREQijTM98iIiIiIiIiUnZqWIuIiIiIiIhEmN7zLSIiIiIiIhJhpbnz/QFQN+D/JX3eL02CxpiRxph1xpgsY8xCY8yAQ8x7sjFmrjFmlzEm0xjzszHm1hDz/dEYs9wYk+3799zS5EVEREREREQkGg7b+LbWxlhrtwf8v6TPYQdbM8ZcBDwFPAj0BuYCnxtjWpQQkgFMANKALsA44H5jzMiAZfYD3gXeAnr5/n3fGNP3cPkRERERERERiYZoP/N9MzDJWvuStfYna+0NwBbgL6FmttYutNa+Y61dZq1dZ619E/gCCLxbfiPwtbV2vG+Z44GZvukiIiIiIiIila7MjW9jTB9jzOvGmHTf5w1jTJ9SxMUDxwLTg36aDpxYyrR7++adFTC5X4hlflHaZYqIiIiIiIhEWmne8+1njPkT8DrwX+Az3+QTgAXGmCt8d6ZLcgzuPeDbgqZvAwYdJt1NQANffu+31j4f8HPjEpbZuIRljQBGADRt2pSZM2cC0KZNG2rWrMnixYsBqF+/Pl27dmX27NkAeDwe+vfvz6JFi9i/fz8AqampbNu2jY0bNwLQvn17EhISWLp0KQANGzakQ4cOzJkzB4CEhAT69etHeno6GRkZAPTt25dNmzaxefNmADp27EhsbCzLly93hWvcmNatWzNv3jwAEhMT6du3L/PnzyczMxOAfv36sW7dOrZu3QpAly5dyM/PZ8WKFQCkpKTQrFkz5s+fD0BycjKpqanMmzeP7OxsAPr378/KlSvZvn07AN26dSM7O5tVq1YB0Lx5cxo1akR6ejoAtWrVok+fPsyZM4e8vDwA0tLSWLZsGbt27QKgZ8+eHDhwgLVr1wLQqlUr6tWrx6JFiwCoW7cuPXv2ZNasWVhrMcZw8skns3jxYvbs2QNAnz592L17N+vXr9d60nrSetJ60nrSetJ60nrSetJ6OiLXExz+ideZM2cesetJosNYa0s/szHrgRettQ8GTb8LuNZa2+oQsU2BzcDJ1trZAdPHAH+y1nY8RGxrIBnX0P8H8Ddr7Ru+33KAq621rwfMPwx4yVqbcKjypKam2sKNVkREREREJBzXPFn0+53P3VdsnrYrik87EhhjFlprUys7H0eDsnY7bwC8F2L6+0DDw8TuBPKBRkHTGwFbDxXoe977R2vtS8ATwH0BP28NZ5kiIiIiIiIi0VLWxvfXwMAQ0wdS9DnsYqy1OcBCYHDQT4Nxo56XVgwQeEd7XgUsU0RERERERCRiDvvMtzHmvICvnwMPGWNSgf/5pp0AnEfRu9EleQJ4wxizAPgWuA5oCjzvS+t1AGvtMN/3G4B1wApffBpwK/BswDKfAmYbY+4EPgbOBU4B+pciPyIiIiIiIiIRV5oB1z4IMc0/aFmApynaKC7GWvuuMaY+cC/QBFgKnGmt3eCbJfh937G4Z7xbAXnAGuBOfI113zLnGmMuxr0D/AHfPBdZa+cftmQiIiIiIiIiUXDYxre1tkLfBW6tfZYSGunW2oFB358EnizFMj8g9EUCERERERERkUpXoQ1rERERERERESmuTO/5BjDG1AV+h+siHh/4m7X2gQrKl4iIiIiIiEi1UabGtzHmBOA/QDbutWObcc9uZwPrcc9ci4iIiIiIiEiAsnY7fxR4C0gBsoBTcXfA03EDo4mIiIiIiIhIkLI2vnsAE621FsgHEqy124A7KN2rxkRERERERESOOmVtfOcE/H8b0NL3/wzc+7pFREREREREJEhZB1xbBBwHrARmAuOMMY2Ay4AlFZs1ERERERERkeqhrHe+7wF+8f3/XmAH8DRQFxhRgfkSERERERERqTbKdOfbWpse8P8duFeOiYiIiIiIiMghlPk93wDGmLZAZ9/X5dbatRWXJREREREREZHqpazv+a4PvAKcDRT8Ntl8Cgy31u6q4PyJiIiIiIiIVHllfeb7ZaAdMADw+j5pQGvgpYrNmoiIiIiIiEj1UNZu50OA06y18wKmfWuMuRaYUXHZEhEREREREak+ynrnewfwa4jpBwF1ORcREREREREJoayN7weAJ40xKYUTfP9/3PebiIiIiIiIiAQ5bLdzY8yPgA2Y1BpYb4zZ7PueAmQBDXHPhIuIiIiIiIhIgNI88/1BxHMhIiIiIiIiUo0dtvFtrb0/GhkRERERERERqa7KOto5AMaYU4EuuO7oy6y1MysyUyIiIiIiIiLVSZka377B1T4CjgV+8U1uaoxJB8611v5SYrCIiIiIiIjIUaqso51PAPKBdtba5tba5kB737QJFZ05ERERERERkeqgrN3OBwMDrbXrCidYa9caY/4KfFWhORMRERERERGpJsp65xuKvnbsUNNEREREREREhLI3vr8CnjbGNC+cYIxpATyJ7nyLiIiIiIiIhFTWxvdfgRrAWmPMBmPMBmCNb9pfKzpzIiIiIiIiItVBWZ/53gUcDwwEOvmm/WStnVGRmRIRERERERGpTkrd+DbGxAL7gJ7W2i+BLyOWKxEREREREZFqpNTdzq21+cAGID5y2RERERERERGpfsr6zPffgYeNMcdEIjMiIiIiIiIi1VFZn/m+FWgNbDbGbAJ+DfzRWtujojImIiIiIiIiUl2UtfH9Ae6d3iYCeRERERERERGplkrV+DbGJAGPAucAcbh3et9grd0ZuayJiIiIiIiIVA+lfeb7fuAK4D/AZGAQ8FyE8iQiIiIiIiJSrZS22/l5wFXW2ncAjDFvAd8aY2J9o6CLiIiIiIiISAlKe+e7OfBN4Rdr7QIgD2gaiUyJiIiIiIiIVCelbXzHAjlB0/Io+4BtIiIiIiIiIked0jaeDfCmMSY7YJoXeMkYc7BwgrX27IrMnIiIiIiIiEh1UNrG92shpr1ZkRkRERERERERqa5K1fi21l4Z6YyIiIiIiIiIVFelfeZbRERERERERMKkxreIiIiIiIhIhKnxLSIiIiIiIhJhanyLiIiIiIiIRJga3yIiIiIiIiIRpsa3iIiIiIiISISp8S0iIiIiIiISYWp8i4iIiIiIiESYGt8iIiIiIiIiEabGt4iIiIiIiEiEqfEtIiIiIiIiEmFqfIuIiIiIiIhEmBrfIiIiIiIiIhGmxreIiIiIiIhIhKnxLSIiIiIiIhJhanyLiIiIiIiIRJga3yIiIiIiIiIRpsa3iIiIiIiISISp8S0iIiIiIiISYWp8i4iIiIiIiESYGt8iIiIiIiIiEabGt4iIiIiIiEiEqfEtIiIiIiIiEmFqfIuIiIiIiIhEmBrfIiIiIiIiIhGmxreIiIiIiIhIhKnxLSIiIiIiIhJhanyLiIiIiIiIRJga3yIiIiIiIiIRpsa3iIiIiIiISISp8S0iIiIiIiISYWp8i4iIiIiIiERY1BvfxpiRxph1xpgsY8xCY8yAQ8zbxBjztjHmZ2NMvjFmUoh5rjDG2BAfb0QLIiIiIiIiIlJKUW18G2MuAp4CHgR6A3OBz40xLUoISQB2Ag8D8w+x6INAk8CPtTarovItIiIiIlIW48aNIz4+nri4OM4444xivz/99NMkJSVhjOGWW27xT//2229JSkoiMTERr9fLpZde6v/tpJNOwuPxYIyJShlEpGJF+873zcAka+1L1tqfrLU3AFuAv4Sa2Vq73lr7V2vtJGD3IZZrrbVbAz8Vn3URERERkcPLycnh/vvvZ/r06ezZs4dZs2YxderUIvP07t2b119/nTZt2hSZ3rNnT7Zu3UpmZibr16/nvffeY9GiRQBceeWVpKenR60cIlKxPNFKyBgTDxwLPBb003TgxHIuPtEYswGIBX4ARltrvy8hHyOAEQBNmzZl5syZALRp04aaNWuyePFiAOrXr0/Xrl2ZPXs2AB6Ph/79+7No0SL2798PQGpqKtu2bWPjxo0AtG/fnoSEBJYuXQpAw4YN6dChA3PmzAEgISGBfv36kZ6eTkZGBgB9+/Zl06ZNbN68GYCOHTsSGxvL8uXLAWjcuDGtW7dm3rx5rqCJifTt25f58+eTmZkJQL9+/Vi3bh1bt7prDl26dCE/P58VK1YAkJKSQrNmzZg/33UeSE5OJjU1lXnz5pGdnQ1A//79WblyJdu3bwegW7duZGdns2rVKgCaN29Oo0aN/Dv8WrVq0adPH+bMmUNeXh4AaWlpLFu2jF27dgHu4HHgwAHWrl0LQKtWrahXr57/AFK3bl169uzJrFmzsNZijOHkk09m8eLF7NmzB4A+ffqwe/du1q9fr/Wk9aT1pPWk9aT1pPWk9VQl1tOPP/5IjRo1AEhPT+ekk07i0UcfpVatWv711KFDB/Ly8sjKymL37t1kZ2cXW09z5szBWkt6ejpdu3alf//+/vW0fft2racj6O8J+nI4M2fOPGLXk0SHsdZGJyFjmgKbgZOttbMDpo8B/mSt7XiY+E+BndbaK4Km9wM6AIuBmsDfgDOBntbaVYdaZmpqqtXVQxERERGpSLfccguffvqpv2F83XXXMXfuXJYsWVJs3nbt2jF06FAef/xx/7T58+czcOBAsrKyuOCCC3jvvfeKxBhjiNY5vJTONU8W/X7nc/cVm6ftiuLTjgTGmIXW2tTKzsfRoMqPdm6tnWetfc1a+4O19hvgImANcEMlZ01ERERExG/atGl07NiRdu3a8fDDDxf7ffbs2fTp04eTTjqJN954g4ULF/LZZ5/xwQcf0K9fP7p27UqPHj2KxFxxxRW0bt2aXr160atXL3744YcolUZEyiqaje+dQD7QKGh6I6DCntG21uYD6UD7ilqmiIiIiEhpdezY0d/dF2DdunU0btyYUaNG8fnnn7N8+XImT57s785cqEWLFkyaNMk/yFqfPn1o2rQpn332Ga+//jrLli1j2rRpAOzdu9cf9+ijj/LDDz/www8/0KtXr4iXT0TCE7XGt7U2B1gIDA76aTBu1PMKYdzwjz1wA7mJiIiIiETVsGHD2LdvH7NnzyYjI4PZs2dz2mmn0a5dO9q0aUN8fDwXX3wxU6ZMKRLXqlUrsrOzyc3NBVyjff369Zx11lm0b+/uKzVt2hSAHTt2RLdQIlJu0e52/gRwhTHmamNMZ2PMU0BT4HkAY8zrxpjXAwOMMb2MMb2AWkA93/cuAb+PNcYMMca08c33Cq7x/Xx0iiQiIiIi8huv18u9997LoEGDqFu3Lv3796dt27YsXryYe+65B3AN67vvvps1a9bwz3/+E6/XC8BXX33Fe++9x6WXXkqXLl248MIL+eMf/wjA8ccfT0yMO33v2LEjAwcOBOCee+6hR48e3HTTTf5Bu0TkyBPVxre19l3gRuBe3Kjk/YEzrbUbfLO08H0Cfe/7DAD+4Pv/ZwG/1wFeBH7CjZyeAqRZaxdEogwiIiIiIoczduxYcnJyyM3N5csvvwTg97//PePHjwdgwIABjBo1CmstBQUFZGVlAXDnnXfy5z//mbfffpvMzEzefPNN/zKnTJlC+/btmTdvHgUFBcycOZOHHnqIn3/+me+++47du3fzj3/8I/qFFZFSidqrxgpZa58Fni3ht4EhppnDLO8m4KYKyZyIiIiISASkpKT4X4MFsGnTJlJSUkodv3//fs466yzGjx/PCSec4J/epEkTwL0668orr+Sxx4Lf6isiR4oqP9q5iIiIiMiR7rjjjmPVqlWsW7eOnJwc3nnnHc4+++xSxebk5HDuuecybNgwzj///CK/bdnihjmy1vLxxx/TrVu3Cs+7iFQMNb5FRERERCLM4/EwceJEhgwZQufOnbnwwgvp2rUrY8aMYerUqQB89913NGvWjPfff59rr72Wrl27AvDee+8xe/ZsJk2aVOyVYn/605/o3r073bt3Z+fOndx7772VVUQROQxjra3sPFSa1NRUm56eXtnZEBERERGRKuyaJ4t+v/O5+4rN03ZF8WlHAmPMQmttamXn42gQ9We+RURERETECW60Abx0Y7RzISLRoG7nIiIiIiIiIhGmxreIiIiIiIhIhKnbuYiIiIjIEWRNx/uKTTtSnxcWkdLTnW8RERERERGRCFPjW0RERERERCTC1PgWERERERERiTA1vkVEREREREQiTI1vERERERERkQhT41tEREREREQkwtT4FhEREREREYkwNb5FREREREREIkyNbxEREREREZEIU+NbREREREREJMLU+BYRERERERGJMDW+RURERERERCJMjW8RERERERGRCFPjW0RERERERCTC1PgWERERkagaN24c8fHxxMXFccYZZxT7ff/+/bRo0YK4uDiSk5OZM2cOACNHjiQxMdH/Mcbw7rvv8ssvvxSZHhMTQ+/evaNdLBGRQ1LjW0RERESiJicnh/vvv5/p06ezZ88eZs2axdSpU4vMM2LECJKTk8nNzWX48OFceumlADz77LNkZmaSmZnJm2++icfj4aKLLqJp06b+6ZmZmXi9XoYPH14ZxRMRKZEa3yIiIiISNZMmTaJ27doMHDiQ5ORk0tLSeOaZZ4rMM2PGDG688UYAHnnkETZt2kRBQUGReR5//HGOP/74Ysv/4osvyMnJYdSoURErg4hIONT4FhEREZGoWbFiBfXr1/d/b926NVu2bCkyT0ZGhr/buNfrJTY2llWrVhWZ57vvvuPWW28ttvx//OMf9OrVi5gYneaKyJHFU9kZEBEREREpi1deeYXY2FjOPffcYr99++23vPLKK5WQKxGRQ9MlQRERERGJmo4dO7Jr1y7/93Xr1tGkSZMi8yQnJ/P9998DkJWVRX5+Pu3bt/f//swzz5CWllZs2e+99x7WWi677LII5V5EJHxqfIuIiIhI1AwbNox9+/Yxe/ZsMjIymD17NiNHjiwyz6mnnsqTTz4JwO23305KSoq/G3leXh6LFy/m7rvvLrbsJ554gn79+kW8DCIi4VC3cxERERGJGq/Xy7333sugQYOw1jJw4ECGDh1KWloaAwYMYPz48bz44ot069aNuLg4EhIS+PTTT/3xEydOJDExkYEDBxZb9sKFC5kyZUoUSyMiUnq68y3iM23aNDp27Ei7du14+OGHi/2enZ3NRRddRLt27ejbty/r16/3/7ZkyRL69etH165d6d69O1lZWQCcccYZ9OzZk65du3LdddeRn58freKIiBxVtA+vWsaOHUtOTg65ubl8+eWXANx999188MEHtGvXjueff55NmzaRm5tLRkYGAwcO9K/DiRMn0rVr15DrsEOHDtxxxx1ahyJyRFLjWwTIz89n1KhRfP755yxfvpzJkyezfPnyIvO88sor1K1bl9WrV3PTTTdxxx13AK7722WXXcbzzz/PsmXLmDlzJnFxcYB79mzx4sUsXbqUHTt28P7770e9bCIi1Z324VWf1qGIHA3U+BYBFixYQLt27WjTpg3x8fFcfPHFxbqtTZkyhcsvvxyA888/n6+++gprLdOnT6dHjx707NkTgPr16xMbGwtArVq1AHdikJOTgzEmiqUSETk6aB9e9WkdisjRQI1vOSqNGzeO+Ph44uLiOOOMM9i8eTPNmzf3/16/fn0efPBB4uLiSE5OZs6cOWzevJktW7Zg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iaFfv35FYnJycrjqqqv8V6rbt2/vj9u1axennHIKycnJXH/99cXiRowYQYcOHejUqRMffvhhRMosIlKVTZkyhaVLl9KgQYMi+++EhARmzJhBbm4uTzzxBNZa/va3vzF69Gjatm3LE088wYYNG7juuuvo0aMHMTEx1K5dm5NOOont27dzzjnnMHz4cM444wzatWtHnz59KCgo8A88BG4ffvLJJzNx4kT++Mc/kpmZSZs2bbj22mvLvA+/5sniHzlyhDs2wapVq6hbty7GGHr06FEkpk6dOsTHx5OYmMirtyWyZ8sy/299zijaYwLwj/VSGBs41svAgQMBeP7558nKyiIzM7PYWC/BvTCkegk1xsTIkSOLzHPqqacWeTtOSkoKMTExbNiwgbS0NG688UZ/wxsgJiaG5s2b89RTTwHw0ksv+XtMilQHanxHUfCBND8/n1GjRvH555+zfPly3nrrLZo0aVLkQPrKK6/w008/sWbNGhYsWED79u3Jz8/nzjvvZNKkSUyYMIGVK1cSExPjH+Tn2muvZfPmzfz+978nKSmJ1atX06tXL8Dd0fn73/8OuAOm1+ulS5cu5OTkMH78eBo2bMjKlStZvnw5J598cljlXNPxviIfEZEjRajRwIOfQ01OTub7778HfusCecIJJ7Br1y6+++47LrjgAjp27Mj+/fv9++/Jkyfj9XpZs2YNZ511Fm+++Sb5+fkcPHiQ+vXrc9lll/G3v/2Nzp0787///Y9du3Zx//33c+mll7J06VLq16/PRx99RJ06dfj111+56aabuPTSS1mzZg3du3dn69atgNuHjx492p+3goICMjIyaNasWYXsw4P339qHV47yjCJdu3Ztxo8fzyWXXBJy2S+//DKZmZlc+WgmdZt0LfJbuOte283RKdQYE0OHDi3ydpwXX3yxyNtx3njjDQCuuuoqsrKyePrpp0lMTCQxMdE/EOTkyZN54IEHSExM5N///jfvv/9+pZVRpKKp8R0loQ6kjz/+OO3ataNNmzbEx8djjCE/P7/IgfSjjz5i+fLl/O1vf+O8884jLy+Pxx57jPbt23PhhReSnJzMwYMHMcaQkJDA8OHDeeONN8jLy+Oyyy7jwQcf5KKLLiIjIwNrLTVq1KB///488MADXHfddRw8eJB9+/Zx66238q9//Yu77roLcFcedaVRRKqbcO/UXH75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\n", + "text/plain": [ + "
" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "plot_histogram([res.get_counts(), exact_dist], legend=['raw', 'exact'], figsize=(15,6))" + ] + }, + { + "cell_type": "markdown", + "id": "6c9901a9", + "metadata": {}, + "source": [ + "The overall fidelity between the ideal and raw distributions is:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "3dfd52a7", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "0.839082737475692" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "hellinger_fidelity(res.get_counts(), exact_dist)" + ] + }, + { + "cell_type": "markdown", + "id": "b0707ef3", + "metadata": {}, + "source": [ + "## The circuit-runner program\n", + "\n", + "The `run_circuits` method executes a `circuit-runner` for you internally. It is also possible to directly call this program via the `program_id`. First, let's print the program description:" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "23de57a7", + "metadata": {}, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": "circuit-runner:\n Name: circuit-runner\n Description: A runtime program that takes one or more circuits, compiles them, executes them, and optionally applies measurement error mitigation.\n Creation date: 2021-07-02T13:46:10Z\n Update date: 2021-07-02T13:46:10Z\n Max execution time: 14400\n Input parameters:\n Properties:\n - seed_transpiler:\n Type: integer\n Description: Sets random seed for the stochastic parts of the transpiler.\n Required: False\n - translation_method:\n Enum: ['unroller', 'translator', 'synthesis']\n Type: string\n Description: Name of translation pass ('unroller', 'translator', 'synthesis').\n Required: False\n - transpiler_options:\n Type: object\n Description: Additional compilation options.\n Required: False\n - shots:\n Default: 1024\n Description: Number of repetitions of each circuit, for sampling. Default: 1024.\n Type: integer\n Required: False\n - circuits:\n Description: A circuit or a list of QuantumCircuits.\n Type: ['object', 'array']\n Required: True\n - init_qubits:\n Type: boolean\n Description: Whether to reset the qubits to the ground state for each shot.\n Required: False\n - layout_method:\n Enum: ['trivial', 'dense', 'noise_adaptive', 'sabre']\n Type: string\n Description: Name of layout selection pass ('trivial', 'dense', 'noise_adaptive', 'sabre')\n Required: False\n - initial_layout:\n Description: Initial position of virtual qubits on physical qubits.\n Type: ['object', 'array']\n Required: False\n - rep_delay:\n Type: number\n Description: Delay between programs in seconds.\n Required: False\n - routing_method:\n Enum: ['basic', 'lookahead', 'stochastic', 'sabre']\n Type: string\n Description: Name of routing pass ('basic', 'lookahead', 'stochastic', 'sabre').\n Required: False\n - optimization_level:\n Min: 0\n Type: integer\n Default: 1\n Description: How much optimization to perform on the circuits (0-3). Higher levels generate more optimized circuits. Default is 1.\n Max: 3\n Required: False\n - measurement_error_mitigation:\n Type: boolean\n Default: False\n Description: Whether to apply measurement error mitigation. Default is False.\n Required: False\n Interim results:\n none\n Returns:\n Description: Circuit execution results in a RunnerResult object.\n Type: object\n" + } + ], + "source": [ + "program = provider.runtime.program('circuit-runner')\n", + "print(program)" + ] + }, + { + "cell_type": "markdown", + "id": "19df4562", + "metadata": {}, + "source": [ + "To run the program, we need the custom circuit runner results class, `RunnerResult`, from the IBM Quantum package:" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "204aad90", + "metadata": {}, + "outputs": [], + "source": [ + "from qiskit.providers.ibmq import RunnerResult\n", + "\n", + "# Specify the program inputs here.\n", + "program_inputs = {\n", + " 'circuits': qc,\n", + " 'shots': 2048,\n", + " 'optimization_level': 0,\n", + " 'initial_layout': [0,1,4,7,10,12],\n", + " 'measurement_error_mitigation': False\n", + "}\n", + "# Specify the backend.\n", + "options = {'backend_name': backend.name()}\n", + "\n", + "# Send circuits to the cloud for execution by the circuit-runner program.\n", + "job2 = provider.runtime.run(program_id=\"circuit-runner\",\n", + " options=options,\n", + " inputs=program_inputs,\n", + " result_decoder=RunnerResult\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "9955d4d3", + "metadata": {}, + "outputs": [], + "source": [ + "res2 = job2.result()" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "55c59d81", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "0.794535071278472" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "hellinger_fidelity(res2.get_counts(), exact_dist)" + ] + }, + { + "cell_type": "markdown", + "id": "b35132ff", + "metadata": {}, + "source": [ + "## Measurement error mitigation in the Cloud\n", + "\n", + "One of the unique options for the Qiskit Runtime circuit-runner is the ability to correct for measurement errors automatically in the cloud. To enable this, just set `measurement_error_mitigation=True` in the `run_circuits` method or `measurement_error_mitigation: True` in the `program_inputs` for the `circuit-runner` program." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "b2c1990e", + "metadata": {}, + "outputs": [], + "source": [ + "job3 = provider.run_circuits(qc, backend, shots=2048, initial_layout=[0,1,4,7,10,12], \n", + " measurement_error_mitigation=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "e0b476e8", + "metadata": {}, + "outputs": [], + "source": [ + "res3 = job3.result()" + ] + }, + { + "cell_type": "markdown", + "id": "adb9712b", + "metadata": {}, + "source": [ + "The mitigated results are returned as quasiprobabilities; a distribution that may contain negative values but nevertheless sums to one. These can be accessed similar to counts, using the `get_quasiprobabilities` method:" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "fb8676ac", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'000000': 0.015020697020227713,\n", + " '000001': 0.01503968527760154,\n", + " '000010': -0.00012627678196427706,\n", + " '000011': 0.030574525060850996,\n", + " '000100': 0.000659834155234161,\n", + " '000101': -0.001063355958793566,\n", + " '000110': -0.0004775999777916211,\n", + " '000111': 0.06031549309142881,\n", + " '001000': 0.0003777367742875846,\n", + " '001001': -0.00018911714421758157,\n", + " '001011': -0.0005497404302188685,\n", + " '001111': 0.002569807761895156,\n", + " '010000': 0.0009733012442038954,\n", + " '010001': 0.0011091309301552864,\n", + " '010011': 0.0028983164662437686,\n", + " '010101': 0.0006953842032121027,\n", + " '010111': 0.0062289131505383925,\n", + " '011011': 0.0005211534611385364,\n", + " '011101': 1.8085574676836907e-05,\n", + " '011110': 0.00010457485981474027,\n", + " '011111': 0.009008930948687141,\n", + " '100000': 0.020757733894230336,\n", + " '100001': 0.013550397423734102,\n", + " '100010': 0.00022053106121490537,\n", + " '100011': 0.038644305848702515,\n", + " '100100': 0.0003334666950873213,\n", + " '100101': -6.858956101837708e-05,\n", + " '100110': -0.0005943967601188724,\n", + " '100111': 0.05970117602149442,\n", + " '101000': -0.0004924095356996335,\n", + " '101001': 0.0008826058626688284,\n", + " '101011': -0.0013611362001075304,\n", + " '101101': 0.001737025111214627,\n", + " '101110': 0.00023664577533407804,\n", + " '101111': 0.0011200992316059554,\n", + " '110000': 0.03567791385094045,\n", + " '110001': 0.030199327570111845,\n", + " '110010': 0.0012285103619610381,\n", + " '110011': 0.07161663042226933,\n", + " '110100': 0.00021431875863761756,\n", + " '110101': 0.0019190227680874988,\n", + " '110110': 0.0006237863586354301,\n", + " '110111': 0.12786449020370413,\n", + " '111000': 0.0020301695201953808,\n", + " '111001': 0.0029065130997377527,\n", + " '111010': 0.00025079508226566666,\n", + " '111011': 0.022803871645841876,\n", + " '111100': 0.006655111831611918,\n", + " '111101': 0.0024129361534944403,\n", + " '111110': 0.002880228496461319,\n", + " '111111': 0.4123394393204907}" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "quasi = res3.get_quasiprobabilities()\n", + "quasi_binary = quasi.binary_probabilities()\n", + "quasi_binary" + ] + }, + { + "cell_type": "markdown", + "id": "240bc428", + "metadata": {}, + "source": [ + "Quasiprobabilities can be directly used to compute things like expectation values:" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "fd8b7452", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Raw expectation value: 0.2158203125\n", + "Mitigated expectation value: 0.32066344764961213\n", + "Exact expectation value: 0.4374999999999999\n" + ] + } + ], + "source": [ + "print(\"Raw expectation value:\", expectation_value(res3.get_counts())[0])\n", + "print(\"Mitigated expectation value:\", expectation_value(quasi_binary)[0])\n", + "print(\"Exact expectation value:\", expectation_value(exact_dist)[0])" + ] + }, + { + "cell_type": "markdown", + "id": "5d22a386", + "metadata": {}, + "source": [ + "It is also possible to compute the closest true probability distribution (in terms of the Euclidean norm):" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "c5daccf0", + "metadata": {}, + "outputs": [], + "source": [ + "nearest_probs = quasi.nearest_probability_distribution().binary_probabilities()" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "e5c7f20e", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAA/AAAAGSCAYAAABE/Cp2AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjQuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/Z1A+gAAAACXBIWXMAAAsTAAALEwEAmpwYAACjhklEQVR4nOzdd3gVVf7H8fdJbiohJAQCpFCS0EFaEAuGSLdhQ2QtWEAXsKy6LrIrUuw/dRUVXNe22FHRFWwsyAqBFemggIYWhNANJYT05Pz+uMk1N7kBAqnweT3PfZI7d84958zMnZnvnDNnjLUWEREREREREandvGq6ACIiIiIiIiJyYgrgRUREREREROoABfAiIiIiIiIidYACeBEREREREZE6QAG8iIiIiIiISB2gAF5ERERERESkDnDUdAGqSqNGjWzLli1ruhgiIiIiIiI1YtWqVb9ZaxufYJ5wh8PxBtAJNfDWtEJgfX5+/qgePXrs9zTDGRvAt2zZkpUrV9Z0MURERERERGqEMebXE83jcDjeaNq0afvGjRsf8vLystVRLvGssLDQHDhwoMPevXvfAIZ4mkdXWERERERERM5enRo3bpyu4L3meXl52caNGx/B2RvC8zzVWB4RERERERGpXbwUvNceReui3DhdAbyIiIiIiIhIHaAAXkRERERERKQOOGMHsRMREREREZGKuWMqPary+1+/j1VV+f1nOrXAi4iIiIiISK2Wl5dX00WoFRTAi4iIiIiISK0TGRnZ+eGHH27apk2bDvXq1es+bty4ZtHR0Z3q1avXLTY2tuM777wTUjxvRERE58WLFwcC/OMf/2hojOmxcuVKf4AXXnihUf/+/WNrqBqVSgG8iIiIiIiI1Eqffvppw6+//nrzwYMH17Rr1y578eLFyenp6WvGjx+/+49//GOrX3/91QegV69eR7/99tv6AElJSfWjoqJyFixYUPw+6KKLLjpak/WoLArgRUREREREpFYaPXr0vri4uLygoCB7++23H2rZsmWet7c3d9xxx6EWLVrkLF68uB5AQkJCxuLFi+sDLFu2LOj+++/fW+J9/X79+mXUZD0qiwJ4ERERERERqZVatGjhuvl92rRpYe3atetQv379rvXr1++6ZcuWgAMHDjgABgwYcHTlypVBv/76q09hYaG59dZbD65cuTIoOTnZ9+jRo97nn39+Zs3VovJoFHoRERERERGplYwxFmDTpk2+DzzwQIsvvvhiU79+/TIcDgft2rXrYK0FoFOnTjn+/v6Fzz77bHivXr2ONmzYsLBRo0Z5L7/8cuP4+PgMb2/vGq1HZVELvIiIiIiIiNRqR48e9TLG0LRp0zyAF198MWzLli0BJefp1avX0RkzZoQnJCQcBbjwwguPzpgxI7x3795nxP3voABeREREREREarkePXpk33nnnfsSEhLaN27cuMtPP/0U0K1bN7f72hMSEo4eO3bMa8CAARkAiYmJR48dO+Z18cUXnxH3vwOY4i4HZ5r4+Hi7cuXKmi6GiIiIiIhIjTDGrLLWxh9vnnXr1m3v0qXLb9VVJjmxdevWNerSpUtLT5+pBV5ERERERESkDlAALyIiIiIiIlIHKIAXERERERERqQMUwIuIiIiIiIjUAQrgRUREREREROoABfAiIiIiIiIidYACeBEREREREZE6QAG8iIiIiIiISB2gAF5ERERERETqrM2bN/sGBgZ2y8/PL3eewMDAbhs3bvStxmJ5lJyc7GuM6ZGXl3dK6R2VXB4RERERERGpo7a2ndyjKr8/Nnnyqsr+ztatW+dmZmauKX5/7rnnth0+fHjaAw888FvxtJKfn44HHnggYuvWrX6zZ89OqYzvqyi1wIuIiIiIiIjUAQrgRUREREREpNaJjIzs/MgjjzRp06ZNh4CAgG7Dhg1rsXPnTkdCQkLrevXqdbvgggvaHDhwwLtkt/R77rknctWqVUF//etfmwcGBnYbMWJEcwBjTI/169f7Aezdu9e7b9++cUFBQd06derU/t57743o0aNH2+J8b7vttuimTZueExQU1K1jx47t586dGwQwa9as4JdffrnpV199FRoYGNitbdu2HQDS0tK8hw0b1qJx48bnhIeHn3PvvfdGFHfnz8/P584774wKDQ3tEhUV1fmzzz5rcDrLRAG8iIiIiIiI1Epz5swJXbBgwaaNGzeu//bbb0MGDRrU+qmnnko9cODA2sLCQp5++unwkvO//PLLu3r06JHx1FNP7cjMzFzzzjvv7Cj9naNGjWoRGBhYuGfPnnVvv/12yscff9yo5Oc9e/Y8tnbt2g2HDh1aM3To0IM33XRTbGZmphk6dGj6Pffcs/eyyy47lJmZuSY5OXkjwPDhw1s6HA62bt26fs2aNRu/++67Bi+88EIjgOeff77x/PnzG6xYsWLjqlWrNn7++eehp7M8FMCLiIiIiIhIrTR69Oj90dHR+a1atcrr2bNnRrdu3Y5deOGFWYGBgfaKK644vG7dusCKfF9+fj5z584NeeKJJ3bVr1+/sEePHtnDhg37reQ8Y8eOPdi0adMCHx8fpkyZsi83N9esW7fO39P37dy507Fo0aIGr7322o7g4ODCyMjI/LvvvnvfrFmzGgJ89tlnoWPGjNkfFxeX16RJk4KHHnpo76kvDQ1iJyIiIiIiIrVUs2bNXMO1+/v7FzZp0sQ11HxAQEBhZmamd0W+b/fu3Y6CggITExPj+t7o6OjckvNMnDixyXvvvdfowIEDPgDHjh3z3r9/v8fYecuWLb75+fmmWbNmXYqnWWtN06ZNcwH27dvn07x5c9f3x8bG5lSkvKWpBV5ERERERM56c+fOpW3btsTFxfH000+XO9+nn36KMYaVK1cCkJaWxsUXX0xQUBB33323a77MzEwuu+wy2rVrR8eOHRk/fnyV10GcjDG2vM8iIiLyvb29bUpKik/xtJ07d7oeLzd37tygadOmNZ05c+a2w4cPrz169OjaoKCgAmutx++OiYnJ8/X1tQcPHlx79OjRtUePHl2bkZGxZsuWLRsAwsPD83bs2OH6/m3btvmdTt0UwIuIiIiIyFmtoKCAu+66i2+++YaNGzfy4YcfsnHjxjLzHT16lBdffJFevXq5pvn7+/PYY4/x3HPPlZn/wQcf5JdffmHNmjX873//45tvvqnSeohT48aN88sLlB0OB4MGDTr88MMPRxw9etRrzZo1/p988klY8edHjhzxdjgctmnTpnl5eXnmwQcfbHbs2DFXK3+TJk3yU1NTfQsKCgBo0aJF3oUXXnjkzjvvjD548KBXQUEBGzZs8Pvqq6+CAK655ppD//znP8O3bt3qc+DAAe9nnnmm6enUTQG8iIiIiIic1ZYvX05cXBwxMTH4+voyfPhwZs+eXWa+Rx55hIceegh//99vh65Xrx69e/d2mwYQGBjIxRdfDICvry/du3cnNTW1aisiANx33337vvzyy9Dg4OCut956a3Tpz19//fUdR48e9W7WrFmXm2++udVVV1110NfX1wJce+21R/r06ZPeoUOHztHR0Z39/f0Li7vDA4wYMeIgQGhoaNcOHTq0B/j444+35+bmmvbt23cKCQnpOnTo0Nhdu3b5ADzwwAMHEhMT03v06NGxa9euHYYMGXLodOpmirsCnGni4+NtcbcWERERERGR8syaNYu5c+fyxhtvAPDuu++ybNkypk2b5ppn9erVPPHEE3z66ackJiby3HPPER8f7/p8xowZrFy50i1NscOHD9O9e3e+/fZbYmJiqr5CRYwxq6y18cebZ926ddu7dOny2/HmOdONGTMmct++fT6fffbZ9pouC8C6desadenSpaWnz9QCLyIiIiIichyFhYU88MAD/P3vf69w2vz8fP7whz9w7733VmvwLuVbs2aN/7JlywIKCwv57rvvAmfOnNno6quvPlzT5ToZGoVeRERERETOapGRkezcudP1PjU1lcjISNf7o0ePsn79ehITEwHYu3cvQ4YMYc6cOW6t8J7ceeedtG7dmvvuu68qii6nID093evmm2+OOXDggE/Dhg3zR48eve/GG288XNPlOhkK4EVERERE5KzWs2dPNm/eTEpKCpGRkcycOZMPPvjA9XmDBg347bffe5l76kLvyYQJEzhy5Iira77UDn369MncsWPH+poux6lQAC8iIiIiImc1h8PBtGnTGDRoEAUFBdx+++107NiRiRMnEh8fz5AhQ46bvmXLlqSnp5Obm8vnn3/OvHnzCA4O5oknnqBdu3Z0794dgLvvvptRo0ZVR5XkDKVB7ERERERERM5AJzmI3bbOnTsf8vLyOjMDwzqmsLDQ/PTTT6FdunTxOGCCBrETERERERE5e60/cOBAg8LCQlPTBTnbFRYWmgMHDjQAyu3ery70IiIiIiIiZ6n8/PxRe/fufWPv3r2dUANvTSsE1ufn55d7n4UCeBERERERkbNUjx499gPHv8lfag1dYRERERERERGpAxTAi4iIiIiIiNQBCuBFRERERERE6gAF8CIiIiIiIiJ1gAJ4ERERERERkTpAAbyIiIiIiIhIHaAAXkRERERERKQOUAAvIiIiIiIiUgcogBcRERERERGpAxTAi4iIiIiIiNQBCuBFRERERERE6gBHTRdARERERESkrtjadnKZabHJZaeJVAW1wIuIiIiIiIjUAQrgRUREREREROoABfAiIiIiIiIidUC1B/DGmLHGmBRjTLYxZpUx5qKTTNfbGJNvjFlf1WUUERERERERqW2qNYA3xlwPvAg8CXQDvge+McY0P0G6UOAdYEGVF1JERERERESkFqruFvgHgBnW2tettT9ba+8B9gBjTpDuTeBtYGlVF1BERERERESkNqq2AN4Y4wv0AOaV+mgecMFx0o0FmgCPV13pRERERERERGq36nwOfCPAG9hXavo+oL+nBMaYzsAk4DxrbYEx5rgZGGPuBO4EiIiIYOHChQDExMRQv3591q1bB0BYWBgdO3YkKSkJAIfDQe/evVm9ejXp6ekAxMfHs2/fPnbu3AlA69at8fPzY/165y344eHhtGnThiVLlgDg5+fH+eefz8qVK8nIyACgV69epKamsmvXLgDatm2Lt7c3GzduBKBp06a0atWKpUudHQsCAgLo1asXy5YtIysrC4Dzzz+flJQU9u7dC0CHDh0oKCggOTkZgMjISKKioli2bBkAQUFBxMfHs3TpUnJycgDo3bs3mzZtYv/+/QB06tSJnJwcNm/eDEB0dDRNmjRh5cqVAAQHB9O9e3eWLFlCfn4+AAkJCWzYsIG0tDQAunTpwtGjR9m2bRsALVu2pGHDhqxevRqA0NBQunTpwqJFi7DWYoyhT58+rFu3jkOHDgHQvXt3Dh48yPbt27WetJ60nrSetJ60nrSetJ60nrSe6sR68qGshQsX1tr1JGcWY62tnoyMiQB2AX2stUklpk8EbrTWti01vx+wBnjKWvtu0bTJwFBrbacT5RcfH2+LN3wREREREZHKsLXt5DLTYpPLTqsNjDGrrLXxNV0OqTzV2QL/G1CAszt8SU2AvR7mbwa0B/5ljPlX0TQvwBhj8oFLrbWlu+OLiIiIiIiInJGq7R54a20usAoYUOqjAThHoy9tF9AZ6Fri9Sqwpeh/T2lEREREREREzkjV2QIP8DzwrjFmOfA/YDQQgTMwxxjzDoC1doS1Ng9we+a7MWY/kGOt1bPgRURERERE5KxSrQG8tfYjY0wYMAFnF/n1OLvC/1o0y3GfBy8iIiIiIiJytqruFnista8Ar5TzWeIJ0k4GJld6oURERERERERquWq7B15ERERERERETp0CeBEREREREZE6QAG8iIiIiIiISB2gAF5ERERERESkDlAALyIiIiIiIlIHKIAXERERERERqQMUwIuIiIiIiIjUAQrgRURERETkrPL444/j6+uLj48PgwcPLvP5DTfcgL+/PwEBAQQHBzNnzhy3z9dk7SRu0xTuSP3ANe2qq67C398ff39/WrRoweHDh6u6GnIWUgAvIiIiIiJnjdzcXKZMmcK8efM4dOgQixYtKhOgP/PMM2RnZ5OVlcU999zD7bff7vb53bs/oYl3fdf7lStX8tVXX7F7926ys7MpLCzkwQcfrJb6yNlFAbyIiIiIiJw1ZsyYQYMGDUhMTCQoKIiEhASmT5/uNk9UVJTr//T0dIwxrvfPHviWcEcQLXwauqWx1nLo0CGys7PJzc0lNja2aisiZyUF8CIiIiIictZITk4mLCzM9b5Vq1bs2bOnzHzDhg3Dx8eHf/zjH3zwgbOr/N69e3n38Apei7zBbd74+HiGDBlCXFwcgYGBBAQE8Ne//rVqKyJnJQXwIiIiIiIipXz88cfk5eXxxz/+kdGjRwNw+eWXM6xBNxo7gtzmTUlJ4bvvvmPjxo1kZGSQk5PDmDFjaqLYcoZz1HQBREREREREqkvbtm15++23Xe9TUlJo1qxZufO/+OKL+Pj4ALB582bWph/l3cPLKcBicN4P3/7ldBo3bkz79u0BuOKKK1i8eHGV1kPOTmqBFxERERGRs8aIESM4cuQISUlJZGRkkJSUxNixY93mmT9/vuv/KVOmEBgYCMCRI0dIbjOR5DYTOde/BYmBrZkWcR0dO3Zkx44d/PbbbxQWFrJgwQLatWtXrfWSs4MCeBEREREROWv4+/szYcIE+vfvT2hoKL179+bKK68kISGBhx9+GIC//OUvrsfITZs2zXUPfHlGjhxJr169iIyMJDAwEGstb731VnVUR84yxlpb02WoEvHx8XblypU1XQwRERERETmDbG07ucy02OSy02oDY8wqa218TZdDKo9a4EVERERERETqAAXwIiIiIiIiInWAAngRERERERGROkABvIiIiIiIiEgdoABeREREREREpA5QAC8iIiIiIiJSByiAFxEREREREakDFMCLiIiIiIiI1AEK4EVERERERETqAAXwIiIiIiIiInWAAngRERERERGROkABvIiIiIiIiEgd4KjpAoiIiIiIiNRGd0wtO218tZdC5HdqgRcRERERERGpAxTAi4iIiIiIiNQBCuBFRERERERE6gAF8CIiIiIiIiJ1gAJ4ERERERERkTpAAbyIiIiIiIhIHaAAXkRERERERKQOUAAvIiIiIiIiUgcogBcRERERERGpAxTAi4iIiIiIiNQBCuBFRERERERE6gAF8CIiIiIiIiJ1gAJ4ERERERERkTpAAbyIiIiIiIhIHaAAXkRERERERKQOUAAvIiIiIiIiUgcogBcRERERERGpAxTAi4iIiIiIiNQBCuBFRERERERE6gAF8CIiIiIiIiJ1gAJ4ERERERERkTpAAbyIiIiIiIhIHaAAXkRERERERKQOUAAvIiIiIiIiUgcogBcRERERERGpAyoUwBtjvIwxXiXeNzXGjDLGXFj5RRMRERERERGRYhVtgf8KuAfAGBMErASeBRYaY0aczBcYY8YaY1KMMdnGmFXGmIuOM28fY8z3xpg0Y0yWMeYXY8yDFSyziIiIiIiISJ1X0QA+Hvhv0f/XAOlAOHAHcMLA2hhzPfAi8CTQDfge+MYY07ycJBnAS0AC0AF4HJhijBlbwXKLiIiIiIiI1GkVDeCDgMNF/w8E/m2tzcMZ1MeeRPoHgBnW2tettT9ba+8B9gBjPM1srV1lrZ1prd1grU2x1r4H/Acot9VeRERERERE5ExU0QB+B3ChMaYeMAiYXzS9IZB5vITGGF+gBzCv1EfzgAtOJnNjTLeieRdVoMwiIiIiIiIidZ6jgvM/D7yLs2v7r0BS0fQE4KcTpG0EeAP7Sk3fB/Q/XkJjTCrQuKi8U6y1r5Yz353AnQAREREsXLgQgJiYGOrXr8+6desACAsLo2PHjiQlOYvvcDjo3bs3q1evJj09HYD4+Hj27dvHzp07AWjdujV+fn6sX78egPDwcNq0acOSJUsA8PPz4/zzz2flypVkZGQA0KtXL1JTU9m1axcAbdu2xdvbm40bNwLQtGlTWrVqxdKlSwEICAigV69eLFu2jKysLADOP/98UlJS2Lt3LwAdOnSgoKCA5ORkACIjI4mKimLZsmUABAUFER8fz9KlS8nJyQGgd+/ebNq0if379wPQqVMncnJy2Lx5MwDR0dE0adKElStXAhAcHEz37t1ZsmQJ+fn5ACQkJLBhwwbS0tIA6NKlC0ePHmXbtm0AtGzZkoYNG7J69WoAQkND6dKlC4sWLcJaizGGPn36sG7dOg4dOgRA9+7dOXjwINu3b9d60nrSetJ60nrSetJ60nrSetJ6qnXr6WQtXLiw1q4nObMYa23FEhgTD0QD8621GUXTLgMOW2v/d5x0EcAuoI+1NqnE9InAjdbatsdJ2wpn9/3zgP8D/mStffd45YyPj7fFG76IiIiIiEhF3TG17LTx/5hcZlpsctlptYExZpW1Nr6myyGVp6It8FhrV+Icfb7ktK9OIulvQAHQpNT0JsDeE+SZUvTvT8aYJsBknD0BRERERERERM4KFb0HvvgxcBuMMZnGmJiiaQ8ZY4YdL521NhdYBQwo9dEAnKPRnywvwK8iZRYRERERERGp6yoUwBtj7gMmAK8BpsRHu4G7T+IrngduNcaMMsa0N8a8CEQArxZ9/zvGmHdK5HePMeZyY0zrotdInI+re68i5RYRERERERGp6yrahX40cIe19itjzOMlpq8GOp4osbX2I2NMGM6LAM2A9cCl1tpfi2Yp/Tx4b5z3vLcE8oGtwHiKAn4RERERERGRs0VFA/gWOIPu0vKAgJP5AmvtK8Ar5XyWWOr9VGBqRQooIiIiIiIiciaq6D3w24DuHqZfCmw8/eKIiIiIiIiIiCcVbYF/DphmjAnEeQ/8+caYm4FxwO2VXTgRERERERERcapQAG+t/ZcxxgE8CQTifJTbbuBea+1HVVA+EREREREREeHUngP/OvC6MaYR4GWt3V/5xRIRERERERGRkiocwBez1v5WmQURERERERERkfKdMIA3xvwI9LHWHjLG/ATY8ua11p5TmYUTEREREREREaeTaYH/FMgp8X+5AbyIiIiIiIiIVI0TBvDW2ikl/p9cpaUREREREREREY8q9Bx4Y8x/jTEhHqYHG2P+W2mlEhERERERERE3FQrggUTA18N0f+Ci0y6NiIiIiIiIiHh0UqPQG2O6l3h7jjHmYIn33sAgYFdlFkxEREREREREfneyj5FbiXPwOgvM8/B5FnBPZRVKRERERERERNydbADfCjDANuBc4ECJz3KB/dbagkoum4iIiIiIiIgUOakA3lr7a9G/Fb1nXkREREREREQqwQkDeGPMNcAX1tq8ov/LZa39rNJKJiIiIiIiIiIuJ9MCPwtoCuwv+r88FueAdiIiIiIiIiJSyU4YwFtrvTz9LyIiIiIiIiLVRwG5iIiIiIiISB1wsvfAnxTdAy8iIiIiIiJSNU72HviToXvgRURERERERKpIhe6BFxEREREREZGaoeBcREREREREpA7Qc+BFRERERERE6gA9B15ERERERESkDtBz4EVERERERETqAAXkIiIiIiIiInVAhQN4Y0x3Y8w7xpiVRa93jTHdq6JwIiIiIiIiIuJUoQDeGHMjsAJoBnxd9GoCLDfG3FT5xRMREREREREROLlB7Ep6AnjEWvtkyYnGmL8CjwPvVVbBREREREREROR3Fe1C3xj42MP0T4Dw0y+OiIiIiIiIiHhS0QD+OyDRw/REYNHpFkZEREREREREPDthF3pjzDUl3n4DPGWMiQd+KJp2HnANMLnSSyciIiIiIiIiwMndAz/Lw7Q7i14lvQy8ctolEhEREREREZEyThjAW2v1rHgRERERERGRGqbgXERERERERKQOqOhj5DDGhAKXAM0B35KfWWsfraRyiYiIiIiIiEgJFQrgjTHnAV8BOTgfKbcLaFb0fjugAF5ERERERESkClS0C/2zwPtAJJAN9MXZEr8S+L/KLZqIiIiIiIiIFKtoAH8OMM1aa4ECwM9auw94CD1GTkRERERERKTKVDSAzy3x/z6gRdH/GUBEpZRIRERERERERMqo6CB2q4GewCZgIfC4MaYJcBPwY+UWTURERERERESKVbQF/mFgd9H/E4ADwMtAKHBnJZZLREREREREREqoUAu8tXZlif8P4HycnIiIiIiIiIhUsQo/Bx7AGBMLtC96u9Fau63yiiQiIiIiIiIipVX0OfBhwJvAEKDw98nmS+B2a21aJZdPRERERERERKj4PfBvAHHARYB/0SsBaAW8XrlFExEREREREZFiFe1CPwjoZ61dWmLa/4wxfwS+rbxiiYiIiIiIiEhJFW2BPwAc8zA9E1D3eREREREREZEqUtEA/lFgqjEmsnhC0f9/L/pMRERERERERKrACbvQG2N+AmyJSa2A7caYXUXvI4FsIBznPfIiIiIiIiIiUslO5h74WVVeChERERERERE5rhMG8NbaKdVREBEREREREREpX0VHoQfAGNMX6ICza/0Ga+3CyiyUiIiIiIiIiLirUABfNGDdv4EewO6iyRHGmJXA1dba3eUmFhEREREREZFTVtFR6F8CCoA4a220tTYaaF007aXKLpyIiIiIiIiIOFU0gB8A3GWtTSmeYK3dBtxb9NkJGWPGGmNSjDHZxphVxpiLjjPvNcaYecaYA8aYo8aYZcaYIRUss4iIiIiIiEidV9EAHtwfKXe8aWUYY64HXgSeBLoB3wPfGGOal5OkD/Bf4LKi+b8G/n28oF9ERERERETkTFTRAH4B8LIxJrp4QlHwPbXosxN5AJhhrX3dWvuztfYeYA8wxtPM1to/WWufttYut9ZuKRoRfxVwVQXLLSIiIiIiIlKnVTSAvxeoB2wzxvxqjPkV2Fo07d7jJTTG+OIc/G5eqY/mARdUoAz1gUMVmF9ERERERESkzqvoY+TSgHOBRKBd0bSfrbXfnkTaRoA3sK/U9H1A/5PJ3BhzFxAFvFvO53cCdwJERESwcOFCAGJiYqhfvz7r1q0DICwsjI4dO5KUlASAw+Ggd+/erF69mvT0dADi4+PZt28fO3fuBKB169b4+fmxfv16AMLDw2nTpg1LliwBwM/Pj/PPP5+VK1eSkZEBQK9evUhNTWXXrl0AtG3bFm9vbzZu3AhA06ZNadWqFUuXLgUgICCAXr16sWzZMrKysgA4//zzSUlJYe/evQB06NCBgoICkpOTAYiMjCQqKoply5YBEBQURHx8PEuXLiUnJweA3r17s2nTJvbv3w9Ap06dyMnJYfPmzQBER0fTpEkTVq5cCUBwcDDdu3dnyZIl5OfnA5CQkMCGDRtIS0sDoEuXLhw9epRt27YB0LJlSxo2bMjq1asBCA0NpUuXLixatAhrLcYY+vTpw7p16zh0yHn9pXv37hw8eJDt27drPWk9aT1pPWk9aT1pPWk9aT1pPdW69XSyFi5cWGvXk5xZjLUndfs6xhhvIBvoYq3dWOGMjIkAdgF9rLVJJaZPBG601rY9QfprcQbu11trvzhRfvHx8bZ4wxcREREREamoO6aWnTb+H5PLTItNLjutNjDGrLLWxtd0OaTynHQXemttAfAr4HuKef2G83FzTUpNbwLsPV5CY8xQnMH7iJMJ3kVERERERETONBW9B/4x4GljTKOKZmStzcU5AF3px80NwDkavUfGmGE4g/dbrbWzKpqviIiIiIiIyJmgovfAPwi0AnYZY1KBYyU/tNaec4L0zwPvGmOWA/8DRgMRwKsAxph3ir5nRNH74TiD9weBJGNM06LvybXWHqxg2UVERERERETqrIoG8LNwPvPdnEpm1tqPjDFhwASgGbAeuNRa+2vRLKWfBz+6qIxTi17FFuEcSE9ERERERETkrHBSAbwxJhB4Fufz131wPvP9HmvtbxXN0Fr7CvBKOZ8lHu+9iIiIiIiIyNnqZO+BnwLcCnwFfIjzsW//qKIyiYiIiIiIiEgpJ9uF/hpgpLV2JoAx5n3gf8YY76LR6UVERERERESkCp1sC3w0sLj4jbV2OZCPcwA6EREREREREaliJxvAewO5pablU/FB8ERERERERETkFJxsAG6A94wxOSWm+QOvG2MyiydYa4dUZuFERERERERExOlkA/i3PUx7rzILIiIiIiIiIiLlO6kA3lp7W1UXRERERERERETKd7L3wIuIiIiIiIhIDVIALyIiIiIiIlIHKIAXERERERERqQMUwIuIiIiIiIjUAQrgRUREREREROoABfAiIiIiIiIidYACeBEREREREZE6QAG8iIiIiIiISB2gAF5ERERERESkDlAALyIiIiIiIlIHKIAXERERERERqQMUwIuIiIiIiIjUAQrgRUREREREROoABfAiIiIiIiIidYACeBEREREREZE6QAG8iIiIiIiISB2gAF5ERERERESkDlAALyIiIiIiIlIHKIAXERERESnl8ccfx9fXFx8fHwYPHlzm85dffpnAwECMMfz5z392Tf/f//5HYGAgAQEB+Pv7c8MNN7g+e++99/D398fHx4cuXbpQWFhYLXURkTOHAngRERERkRJyc3OZMmUK8+bN49ChQyxatIg5c+a4zdOtWzfeeecdYmJi3KZ36dKFvXv3kpWVxfbt2/n4449ZvXo1AKNHj2batGnk5OSwe/duHn/88Wqrk4icGRTAi4iIiIiUMGPGDBo0aEBiYiJBQUEkJCQwffp05s6dS9u2bYmLi2PJkiUMHToUY4wrXVJSEgkJCTRs2JBZs2Zx9OhRAJKTkznnnHPIzMzkpZde4pNPPmH48OF8+OGHLFiwgO7du9O1a1d69+7Nli1baqraIlIHKIAXERERESkhOTmZsLAw1/tWrVqxe/du7rrrLr755hs2btzIhx9+yMaNG93SNW/enBkzZjBw4EBuuOEG2rRpwzXXXEOPHj246aabCAkJYe7cudx3331ER0dz8OBBxowZw/vvv8/atWu54YYb1CovIselAF5ERERE5AQyMzOJi4sjJiYGX19fhg8fzuzZs93madmyJeeccw7h4eF88MEHrFq1iq+//prc3FyaNm0KQEREBOHh4WRkZABgjCE9PR2AI0eOEBERUb0VE5E6xVHTBRARERERqU3atm3L22+/7XqfkpJCUFAQ0dHRrmlRUVEsW7bsuN/TvXt3IiIi+Ne//sXNN9/MsWPHWL58Obm5uRw8eJCGDRvy2muvcemllxIQEEBwcDA//PBDldVLROo+tcCLiIiIiJQwYsQIjhw5QlJSEhkZGSQlJXkcid6TFStWkJOTAzgD/+3bt3PBBRfQtWtXvL29GTJkCG+++SYfffQRw4cP54UXXuDrr78mNTWV2267jQceeKAqqyYidZwCeBERERGREvz9/ZkwYQL9+/cnNDSU3r17c9VVV/Hpp5/y8MMPAzBnzhxeeeUVtm7dygsvvIC/vz8ACxYs4OOPP+aGG26gQ4cODBs2jGuvvZb09HTCw8NJS0ujT58+NGvWjNGjR7Nu3Tp69eoFwPXXX8/3339fY/UWkdpPAbyIiIiISCmTJk0iNzeXvLw85s+fT8+ePQkNDWXUqFHk5uayadMmfvrpJ6y1FBYWkp2dDcD48eO5+eab+eCDD8jKyuK9994jNzeXq6++mvvuu4+8vDzy8vL48ccfCQsL48iRI2zatAmA+fPn0759+5qstojUcroHXkRERETkBBwOB9OmTWPQoEEUFBRw++2307FjRyZOnEh8fDxDhgxhxYoVXH311Rw6dIgvvviCSZMmsWHDBj7++GOSkpJIS0tjxowZgPNRdV27duX111/n2muvxcvLi9DQUN56662araiI1GrGWlvTZagS8fHxduXKlTVdDBERERERqaPumFp22vh/TC4zLTa57LTawBizylobX9PlkMqjLvQiIiIiIiIidYACeBEREREREZE6QAG8iIiIiIiISB2gQexERERERE6Rp3ukX7+vukshImcLtcCLiIiIiIiI1AFqgRcRERERqURb204uM622jlIuInWLWuBFRERERERE6gAF8CIiIiIiIiJ1gAJ4ERERERERkTpAAbyIiIiIiIhIHaAAXkRERERERKQOUAAvIiIiIiIiUgcogBcRERERERGpAxTAi4iIiIiIiNQBCuBFRERERERE6gAF8CIiIiIiIiJ1gAJ4ERERERERkTpAAbyIiIiIiIhIHaAAXkRERERERKQOqPYA3hgz1hiTYozJNsasMsZcdJx5mxljPjDG/GKMKTDGzKjGooqIiIiIiIjUGtUawBtjrgdeBJ4EugHfA98YY5qXk8QP+A14GlhWLYUUERERERERqYWquwX+AWCGtfZ1a+3P1tp7gD3AGE8zW2u3W2vvtdbOAA5WYzlFREREREREapVqC+CNMb5AD2BeqY/mARdUVzlERERE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IiIicrIUwIuU4umEFnRSKyIiIiIiNUvPgRcRkbPS3Llzadu2LXFxcTz99NNlPs/JyeH6668nLi6OXr16sX37dgCWL19O165d6dq1K126dOHf//63K82LL75Ip06d6NixI1OnTq2mmoiIiMjZQgG8iIic8R5//HF8fX3x8fFh8ODBFBQUcNddd/HNN9+wceNG3n//fZo1a4aPjw9BQUEsWbKEN998k/3797Nr1y7WrFlDbGwsDz30EJ06deLxxx8nOTmZ5ORkrrnmGowx9OvXj9dff53ly5ezbt06vvzyS7Zs2VLTVRcREZEziAJ4ERE5o+Xm5jJlyhTmzZvHoUOHWLRoEX//+9+Ji4sjJiYGX19fjDEUFBSQl5fH7bffzg033MDs2bMZN24chw4dIjMzk+DgYJ555hm8vLy4/PLLycrK4ueffyY8PBxjDImJifTq1YvAwEAcDgd9+vThs88+q+nqSx1SFb1CDh8+zNChQ2nXrh3t27dn6dKl1VUdqWNKX+gsLT09nebNm7td6AT417/+RUBAgOv10EMPudI4HA78/f0JCAigXr161VYXkTOZAngRETmjzZgxgwYNGpCYmEhQUBAJCQm8//77REdHu+ZJSUmhY8eOADzzzDOkpqaya9cuOnfujL+/Pw6Hg8DAQNf8y5Yto2PHjnTu3JkBAwYQFBTE0KFDWbx4MWlpaWRmZvL111+zc+fOaq+v1E2le4V8+OGHbNy40W2eN998k9DQULZs2cL999/vCpQ6derEypUrWbt2LXPnzuWPf/wj+fn5APzpT39i8ODB/PLLL6xbt4727dtXe92k9vN0oXPOnDlu89x5550EBQW5XegEuOKKKzh06BBZWVksXbqUZ599luzsbFe6devWkZWVxbFjx6q1TiJnKgXwIiJyRktOTiYsLMz1vlWrVhw+fNhtnuzsbBo3bgyAv78/3t7e5ObmAs6gyd/fn927d3P33Xfj7+9Pr1692LBhAytWrGDWrFn079+f9u3b89BDDzFw4EAGDx5M165d8fb2rrZ6St22fPlyt14hw4cPZ/bs2W7zzJ49m1tuuQWAoUOHsmDBAqy1rl4f4NyWjTEAHDlyhKSkJEaOHAmAr68vISEh1VcpqTM8XeicPn262zzffvst9913H/D7hc7CwkIaNWqEv78/4GylF5GqpQBe3FRF970TfaeISHVzOBxurePWWlcAXyw8PJydO3cycuRIMjIyqF+/Pm+99ZZb8B8dHe3aLwKMHDmSVatWkZSURGhoKG3atKmW+kjdU7q78q5du9x6hYSFhfHkk0+6dVfetWsX33//PQEBAdSvX5+DBw9y7733As5eIX5+fsTExHDw4EEcDgcpKSk0btyY2267jW7dujFq1Ci1gopHni507tmzx22ejIwMunXrBvx+oXPz5s3A7xc6+/Tpw1/+8hdXQG+MoVu3bgQGBnLTTTdVU21EzmwK4M9ilT2o08qVKxk6dCgbN27kmmuuYeDAgSfVJVBEpCq1bduWtLQ01/uUlBRiYmLYvHkzKSkprpb24gA+OzubgoIChg8fzttvvw3ArFmzGDx4ML6+vrz55puu7skPPfSQawA7gP379wOwY8cOPvvsM1cXU5GSPHVXXrFihds8b731Fj4+PmW6Kw8YMMDVXTkyMpJp06aRnZ3NoUOHCAkJISwsjPz8fLKzs8nPz2f16tWMGTOGH374gaSkJKKjo3UBXirdyJEjyc7O5osvvmD69OmuC51Lly4lMzOTFStW8Omnn/Lyyy/XbEFFzgAK4M9SVTGoU2FhIVOmTOGdd96hcePGJCUllflOT10CRUSq0ogRI1xdiTMyMkhKSuLuu+9m2rRpDBo0iPbt29O+fXs+/vhjJk6cyNChQ4mMjGTUqFH88ssvxMbG8vzzzzN06FDS09MpKCigS5cudO3alddff52LL76YRo0aAXDttdfSoUMHrrjiCqZPn67uyuKRp+7KCxcudOsV8tNPP9GnTx/g9+7KERERHDt2DH9/f/Lz8zl69Cjw+/3zS5cu5ZNPPgFgzpw5REVFERUVRa9evXjzzTdp3749559//kndP68L8GcXTxc6mzVr5jZPUFAQa9asAX6/0Nm6dWu3eS6//HJ8fHxc98/Hx8cD0LFjR3r16sV//vOfqqyGyFlBAfxZqioGdZo0aRLWWu644w7++c9/0qdPnzLfGRUVxa5du6qvoiJy1vP392fChAn079+f0NBQevfuzZVXXsnTTz/Nddddx9atW1m8eDHp6ek89dRTLFy4kHfffRd/f3/atWvHrl27+Omnn7j11lsZN24c48aNY8OGDcybN4/8/Hz+9a9/ufJavHgxGzduZN26da5WeZHSPHVXzszMdOsVkpWV5bo1o7i78nnnncfbb7/N1VdfjY+PD0eOHKFly5bMnTuX2NhYYmJiXEHYqFGjiI6OZufOnXzwwQfMnj2b3bt3M2/ePG655RY+/vhjnnvuOQIDA9m/fz8BAQG0bNmSAwcO4OPjQ/v27XUB/izi6ULn2LFj3ebp27cvU6dOBWDcuHFERkbi5eVFUlKSa9C6//3vf6Snp9OrVy/279/P7t27AWfvpFWrVnHeeedVa71EzkQK4M9SVTGoU25uLrGxsaxYsYKnnnqK6OjoMt8pIlITJk2aRG5uLnl5ecyfPx+ApKQknnjiCQBCQkJITU0lLy+PjIwMEhMTAXj11VfJzs4mKyuLzMxMnnrqKdd3hoeHU1hYSPPmzau9PnLmMca49Qrx8vIiNjaWiRMnulozhw4dyoEDB/j8889p164dL730Etu3b+e1115jxYoVdO3alb/97W+Ac5yHvLw8hg0bxq233sqiRYvIyclh/vz55OTk0KRJE8aNGwfAzp07iYmJwcfHh88++4yAgAB69+6tC/BnkfIudCYkJPDwww8D8Nprr5Geno6Pjw9vvfUW7777LgAffPABISEhBAQEMGDAAMaNG0fbtm3ZuHEjsbGxBAQE0Lx5c8477zwmTJhQk9UUOSMogBeXyhrUqX379gQFBXHo0KEy35mamkpkZGSV10VERKS2Kq+78qWXXsqmTZvYunUrISEhrFmzhkcffdQ1pkynTp0YNGgQYWFh/Pzzz9xzzz04HA5+/vlnrr32WtauXctbb70FQJcuXQDnM7rz8/OJi4vj66+/dl2cqlevHtZaV2tp8VMVxo8fT05OjsfngMuZrbIvdCYmJpKVlUVWVhbZ2dmu7xSR06MA/ixVFYM6xcXFkZaWxq+//sovv/zCwYMHy3znzJkzGTJkSPVXWETkFG1tO7nMS+R0nE535W+//ZaGDRsCzu7K+fn5WGvdLpYDdOjQAfi9B11ISIhrnvz8fHbv3k1ISAjBwcGuNO3bt2f//v20adPG1f2+mC7An51O9elE8+fPp0ePHnTu3JkePXrw3//+15Vm8ODBdOnShY4dOzJ69GgKCgqqqzoiZwRHTRdAasaIESO46667SEpKonv37iQlJTFz5kx8fHwYNGgQBQUFrkGdCgsLWb16tWtQp8GDBxMbG0tYWBgPPvggn376KQUFBbz88sukpaUxYMAA/v73v3P77beX+c7bb7/ddV+9iJw55s6dy5/+9CcKCgoYNWoU48ePd/s8JyeHESNGsGrVKsLCwvjoo49o2bIl8+fPZ/z48eTm5uLr68uzzz5L3759AXj44Yd55513OHToEBkZGdVSjzumlp02vuwkkdNSsruytZbExERXd+WLLrqIJ554gtdee41OnTrh4+ODn58fX375JQC//PILmzdvJiAgAGMMXbt2JT8/n82bNxMZGel69Ncrr7zCsWPHmDFjBuC8IPD2229z/vnnu1rZv/rqK1JSUoiOjsbhcPDrr79y+PBhnnzySXr27Om6AB8ZGcnMmTP54IMPamqRSQ0oHshw/vz5REVF0bNnT4YMGeK6OATOWypDQ0PZsmULM2fO5KGHHuKjjz6iUaNGfPHFF0RERLB+/XoGDRrkugXj448/Jjg4GGstQ4cO5ZNPPmH48OE1VU2ROkct8GepqhjUaePGjUyePJnt27czYsQI13eW7BJYfB+ViNRdp/oISi8vL9LS0lyPrQJcJ3l9+/Zl/fr19OvXj169egFwxRVXsHz58hqsqUjVOdXuynfffTchXgGsb/4QP0WPI2hTOiHbMpk2bRr16tWjVatWBAQE8M9//pPmzZsza9YsCgoKGD9+PGlpaURHR/P8888zZcoU+vbty5IlS1xPVUhMTMTb25sxY8bgcDjc7skfNmyYLsCfZZYvX+5xIMM7puJ6PfnSbHb73wI4x2hYsGAB1lq6detGREQE4ByBPisri5ycHABXr4/8/Hxyc3MxxtRA7UTqLgXwZ7GqGNTJ03eKSO1TOggvLT09nebNm7sF4QAbNmzgkUceIS8vj9atW7s9gvLFF1+kQYMGbNiwgd9++63MIyhvuukmnnjiCa6//noyMjJcJ3nHjh1j+vTprFmzhtDQUA4cOMCzzz7LeeedV+YxRiJnuxEjRnC0MIflmdvJKMxlRdYObmzQ0+1i+eWXX87UqVN59NFHSUpKIjIyksDAQJ577jl+++03HnzwQR555BEAbr75ZjZs2MDatWtp0qQJF154oSsvXYA/u+3ateuEAxlmHtlFUIhzHofDQYMGDdxu0QT49NNP6d69O35+fq5pgwYNIjw8nPr16zN06NAqrIXImUcBvIjIWSY3N5cpU6Ywb948Dh06xKJFi1yjXBe78847CQoKcgvCAb755hvq1avHH/7wBxwOh+sRlMHBwUyfPp3Vq1dTr149HA4Hzz77rNsjKLt06cLYsWMJCgrC29vbdZL3/fffExwczMaNG+nevTsDBw50jbUhIu78/f25q+FFjEh9lx5b/o94/2gG1G93UqOFjxw5kuzsbF5++WUCAgIICAhgw4YNru9etWqV6/nwIpVhw4YNPPTQQ/zzn/90m/6f//yHPXv2kJOT43Z/vIicmO6Br0Knek9oWloaF198MevXr6d+/fr89a9/daVNTExkz549+Pn5sWPHDkJDQwkPD3dLO3ToUFasWMGtt97KtGnTXPkVpw0ICABg3rx5hIeHn7AeHu8J/cfkMtNik8tOE5HaZ8aMGTRo0MDVqyYhIYHp06e7DTD57bff8uSTTwLwzDPPMG3aNAoLC9mzZ4+rNQ+cj6D85Zdf2L9/P8HBwbRv357s7GyaN2/O22+/zV/+8he3R1B6kpCQwOHDh/nTn/7E/PnzGTBgAPn5+VW3AETqEI/H4EaJ3Nso0W1aUlKS6//iHnSlffvtt2UHYbzmE0h2do3Py8s7zdLKmSQyMtLjQIbbSswT2CCSjMM7gSjy8/M5cuSI6zHFqampXH311bzzzjvExsaW+X5/f3+uvPJKZs+ezYABA6q4NiJnDrXAV5LTuSc0NTWV5cuX06pVKwYPHoyPjw9paWlMmTKFG264gQkTJmCMwcfHh8WLF/OXv/yF0aNHc8kll3D48GG3+0n9/f157LHHeO655wBo2rQp/v7+rnK+//77rF27lrVr155U8C4iZ57k5GTXCRY4g/Diga+KZWRk0K1bN+D3Uaw3b97s8fscDgfWWo4cOcKSJUuw1rJ3714OHDjgmqf4EZSPP/44b731Frm5udx4440ALFmyBGMMe/bsoWvXrhw4cID9+/e79pXWWi699FK8vb0xxtCuXTsA18VOYwwOhwN/f386dOhAbm6uRjmWanGqt6Js3ryZ0NBQjDGcc845bmkaNWpEQECAa3vOzy//4pdIVSo5kGF5TxJq0WkIm5f//nSivn37Yozh8OHDXHbZZTz99NNut2VkZGS4jjf5+fl89dVXrn26iJwcBfCVwFN31OJ7QosH/jDGUFBQUOae0OHDh5OXl+caOGbhwoW8+uqrdOrUiejoaJYtW4aXl3M15eXl0axZMyZMmFDu/aT16tWjd+/e+Pv78/3337sF7yIip6u8R1CmpqYyZswY1xMngoOD8fLycnsE5VtvvcWUKVM455xzqF+/PklJSbz33nuMGjWKl19+mezsbAIDA/H19aVnz56ufWV+fj6NGzfm5Zdf5oILLmDv3r3A7xcsn3zySUaPHk1mZiZHjhzhwQcf5OOPP2bdunWsX7+eAwcO8Mknn9TUIpMz1OncitKgQQOeeOIJ/vCHP5T53rVr17rGmDly5AjLZz94ymUsOdhY8UvkZJU3kOGqbyby63rntt6m10hyMtOIi4vj+eefdz1qbtq0aWzZsoVHH32Url270rVrV/bv38+xY8cYMmQI55xzDl27diU8PJzRo0fXZDVF6hwF8JWgZHfUoKAg1z2hJQf+SElJcY3eWvKe0F9++YXQ0FBiY2Px9vbm/PPPd6U9cuQI69evd7U6AcTExLBv3z6SkpL47rvvGDNmTJn7SQFX2pdeesmtrLfddhtdu3blsccew1pbDUtHpG6oipa09957D39/f3x8fOjSpQuFhYXVUpcT8RSElx4sLigoiDVr1gC4gvDWrVu7nl+9Z88e8vPzSUpK4u6772batGnMmzePpk2b0qlTJzIzM/Hy8mLo0KGuR1CuXr2awsJCdu3aRUREBAkJCYwfP578/Hxeeuklzj33XNLT08nMzOSxxx7D4XCwc+dOcnNz+frrr9m/fz9t27Ytc8GyuOzZ2dnk5+djjNEox1LlPB37p0+f7jbPt99+y3333Qf8fuwvLCwkPDycsWPHum5FKSkqKgr4fXtG267UIE8DGfa45FFadHK2xDt8/Ol36yds2bKF5cuXExMTA8CECRM4duyYq9dncc/PJk2asGLFCn788UfWr1/Pyy+/jMOhO3pFKkIBfCXw1B318OHDbvNkZ2fTuHFj4PfuqLm5uWzdutUtbfPmzV1pn3/+eTp16kReXp7r5PPDDz/E4XAQERHB8uXLXQPTlFactmHDhq5p77//Pj/99BOLFy9m8eLF5aYVOdtUVUva6NGjmTZtGjk5OezevZvHH3+8WupzIsVBeFJSEhkZGSQlJTF27Fjmzp1L27ZtiYuLIyIigqlTpwIwbtw4IiMjycvL45ZbbqF+/fp8/fXX/Pzzz67HRd51113s27cPh8PBmDFjSE9PZ9++fcyfPx+Hw0GPHj0IDg6msLCQtLQ0kpOTmTdvHjk5Odx2221kZWXx888/Y60lJiaGQYMG8fzzz+NwOGjdujVr1qxh8uTJeHl5lblgCfDGG28QFBREQEAAzz77LKBRjqVqVfatKCU1atTItT2fO+TZyi24SC1zqhfQwbmf9/HxwdfX1/UUJYBrrrkGf39//P39ufrqq6ulHiLVRQF8FSluOSpmrXUF8MXCw8M5evQoAIWFhRQUFODv74/D4WDFihUcPnyYCy64wK2lPDIyEoBmzZqRkJDA8uXLXWmLTyQ++ugjV9qSitPWr1+fG264Qc9XFilSFS1pa9euJS8vj1GjRuHl5cXw4cP58MMPq6tKx+Xv78+ECRPo378/oaGh9O7dm8svv5yrr76afv36sXHjRlegXXIU6zfffJPQ0FDS09Nd3/Xf//6XadOmERwcTIMGDUhJSeGuu+7irrvuYu3atbRu3ZoNGzawbt06tm3bRsuWLSksLMRayx//+EfXcvv111/JycnB4XDQvXv3Ctdp1KhRHDx4kLy8PJ5//nlAoxxL3fXbb7+5tuf1C5+v6eKInNDWtpPLvE7G6VxAnzNnDv/9738xxlBYWMgjjzxCbm4u//73v/n6669JTU1lx44dzJ49G4fDcdLB/1VXXeUK/lu0aEF25uHTXj4ilUkBfCUo757QkgN/AK4AvuQ9obt37yYtLY2UlBSCgoLYvn07MTExbN26lfT0dKZPn05hYSGFhYU0aNCA1NRUCgoKuO666/jggw/o1KmTK21xK/3nn3/O0aNHeeWVV+jTpw85OTk0aNCA3377DXDeS//ll1/SqVOnal5SIrVTeS1pJVukDx8+XKYlbcOGDVx//fXExcUxZ84ct5HWn3jiCfLz82nbti3/+c9/aNu2LQcPHuTw4cMMHTqUdu3a0b59e5YuXVrt9QWYNGkSubm55OXlMX/+fJYvX05CQgKvvPIKvr6+3Hjjjdx3333k5eWRkZFBYmIis2fP5pZbbiE/P5+8vDzCwsLIz8/n6NGjDB8+nB07dpCbm8vAgQO58cYb+fnnn+nVqxeBgYE4HA66du3K3l9TXSd3G96dT8i+fLeLnb6+vq6RsIv3lS1btnTNU/qCZUkhISFcdtllfPDBB65pJUc5FqlMp3Mrysko3p63rv7gxDOf4aqihfbXX38lKioKPz8//Pz8eO2116qlLmeqaWlJtN/0GO02PcZtqe8BuB1DJ0+eXGYd5eTkcMEFF5Cfn8/FF19MaGgoUVFRTJ8+naeeeoqWLVvi4+PDRx99xKZNm3j00UdZtGgRO3fupGvXrlxzzTXk5+czb948Dh8+jLWW+++/n8WLF9O8eXMaNWrEvffeS0BAAAMHDiwT/CclJZGWlsa3337L5MmTyc3NZeXKlXz11Vfs3r2b7OxsCgsLWT7n1MehEKkKCuArgafuqMX3hBYP/NG+fXs+/vhjJk6c6HZPaFhYGGlpaaxZs4ZGjRq50tarVw8vLy+MMXh7ewMwZswYOnbsiMPh4NVXX8Xb25vnnnuO9evX07RpU1d5li5dSkhICIGBgYSFheHj48Pu3bsZNGiQa9CQyMhI7rjjjppaZFJNquKkp3i074CAAOrVq3fGltla6/YkicLCQrZt2+Y2z6xZswgNDWXLli107NjRNbjaxo0bWbZsGcHBwcydO5exY8e67n//05/+xODBg/nll19Yt24d7du3r/Ayqgq7du1yG7cjKiqKXbt2lTuPw+GgQYMGpKWllZv2o9WdmPXFYm5+Io3bnslk6dq95Nh8lmduJ6MwlxVZO7gz9EK3i50+Pj78+OOPwO9d96+88krXc+FLX7Dcu3cvO3bsAJwB0ty5c4mNjdUox1LlyrsVpaS+ffuWuRWleGBaT/bu3cvatWuB37fnBuFtq6oKdcLpttB6CtLAuW769etHTk4OaWlpDBo0qNrrVp6qOA6Cc1kGBgbSpEmTSi1vfn4uL6ct4l+RN7My7iGWZ/3KvPSf3Y6hU6dOxcfHx20dvfnmm6SlpWGM4c033+Siiy5i69atbN26lZkzZ+Ll5cUVV1wBOHtU3XLLLaxbtw6Hw8EHH3yAtRZfX19X77nAwEC++uor+vbty/bt29m8eTPz5s0jPz+f1NRUt95z06dPJyEhgeDgYBISEmjQoAEzZswAnMf/Q4cOkZ2dTW5uLsFhZR+BJ1KTFMBXAk/dUa+88kqefvpprrvuOrZu3crixYtJT0/nqaeeYuHChbz77rv4+/vz2WefYYzhyJEjbN26lezsbIwxtG3blvHjx1NYWMgXX3wBwN///ncKCgqYN28eP//8M5s3b2b79u2u+0m9vb2ZM2cO27dv5+DBg2RkZPDZZ5/h5eVFvXr1WLVqFT/++CMbNmzgxRdfdF0YkDNTVZ30AKxbt46srCyOHTtWLWUueRX/oosuKlPmnJwcevfuzfz58+nSpQvvvvuuq8xPPfUUBQUFNGvWjM8//5xjx46RnZ3Nueee63rMWHJycpmWtMDAQLcnSQQEBDBz5kzg95a0pUuXcssttwDOVvviwdVmz57NlVdeSWZmJq1atSIuLo6kpCQaNGhAUlISI0eOBJytzSEhIZW6DCtbydGrd6fBuDdOPm1ok/Z06fsQ37w6kLn/HExYVHe6+EUyIvVdemz5P+L9oxkc3IHAwEDi4+Np3749d999Nzk5OXh5efHaa6/x7rvvMnLkSNdJ3sKFCzly5Ihrf9ejRw8mTZrE9OnTXY/emjp1qkY5lipX3rE/ISHBNdjXa6+9Rnp6ututKMUcDgdvvfUWP/30k2t7/u2337jwwgsJCAggJCSEkJAQEm84u8erOZ1bnKZPn05kZCSNGjWiX79+GGOYMWMGO3bsYMeOHfzrX/8iPT2dDh06EBcXd1LB8OHDh13jE/j7+9OnTx+P5fbUIl1SeUF4bm4uEydOdF30XbBgAXPmzGHWrFl4eXnh5eVFSEgIaWlpFToOxsXF0bBhQ+rXrw9Q5jg4adKkU15Hm1fMoL6XH+fVa0mQly89A5rz6qElbsfQvLw810Cvxevo888/Jysri4CAAEaMGMGPP/6Ij48P+/fvZ/DgwezcuZNZs2ZhjGH79u20aNHClefSpUvx8vIiICDANc3hcJCRkYHD4SAgIIC2bdty6NAhIiIi8Pb2dus9t2zZMn744Qd69erF9u3badiwIcnJycyfPx9/f3/i4uIICAggICCATon3M/uFc/ns2S7Merojq7459WUlUhkUwFeS0t1RAZKSklw7/JCQEFJTU926oxYrvh+0+DVkyBC3tJdccgnWWo9p8/PzXekLCgrKPJ+zd+/eZGdnV3p9T/XABOVfHW7Tpg1eXl569F0lKe+kp2Qw/MUXX5Q56cnKyuLOO+8kPz+fkJAQ1zNdZ8yY4QqGBw8ezGeffUbz5s1xOBx4e3vTsmVLOnbsyIsvvkhcXBzGGIwxtGnTBoBly5YRGhqKn58fvr6+BAYGltk+ZsyYgTGGAQMG0LBhQ6Kiopg2bRqjRo1iz5497Nixgx9//NE1LkRxmd944w1+/fVXBgwYwIMPPshnn31GgwYNePLJJ11X8d944w3Gjh1LQUEBfn5+/Pe//2XdunWsXbuWffv2cfjwYbeWtMTERLdW5U6dOrFo0SLg95a03bt3Ex0dzeOPP87bb79NQUEB/fr1Y9euXfTq1QsfHx/eeOMNwsPD+eyzz9iyZQs7duzg0ksvpVu3bowaNYp+/fqV+T2UXFb+/v5cc801Vb69REZGunVlT01NdY2bUSywQSQZh53z5Ofnc+TIEcLCwo6btu15I7n6z6u4/J4k/AJDuapBF35p8wjJbR7h7egRAKxfv560tDS2bt3K448/7jr5zs7OJjExEX9/fz755BO3/WTx/m7Xrl1u0zdt2kSLFi0qdZTj09nfefotwO/7Oz8/vwrtK2ti26iI02k5PJ1l5Snt3LlzXY9xdTgcHrvvXn/99QQGBmKMwcfHx7Wcx44d63oCjK+vL9dccw07d+7k4osvpkOHDq593ekc+z0dvzt16sSxY8fIysoiOzubH3/8EYdv5R4TK/v4fbLb5Knmm5ycTFZWlivfgwcPsmfPHqZOnepaR2lpaa4xRkoGaT/88ANbt24lNjaWtWvXkp+fz9dff81DDz3kGncoJCSEffv2sXz5ckJCQrj44ovp2LEjd9xxh8cL2cHBwWzZsoWsrCwOHjzIqlWr+GWp+5VNTy3S3x5NPqmL0R06dMBaS9euXXnvvfcoKCjgpZde4o9//CMRERGubT0vL49nnnmGdevWsXPnTlq2bMn69evLPQ6+/fbbeHt7U1BQgLW2zHFw7ty5/PDDD6e0TR3Zl0yo9+/jwET5hHCgIMPtGFrcw6rkOtq+fTsABQUFrl5dxY1LGzdudB2vrbU88MAD7N+/33UBfdGiRRhjSE9PdwXhOTk5rlvAQkJCiIuLA5yBfXR0NBdffDH5+fn069ePvLw8brjhBu6//34eeughANLS0njnnXdwOBzMmzePVq1akZ2dzdJ/38elY//LNX9ZxzV/WUvqL3NZk5V6SstKpDIogK8FTnXgj4qm9XRiVfJgUvzszhMp78BU0oladj/66COaNm3KhAkTXCcB99xzT5mR8U+nzJVV31NRU+UumfbJJ58kLCzMlfaHH35g7dq1bl3asrKyaNCgAfD7AfXpp58mNzeXgoICJkyYwJVXXkl+fj4zZ85k5syZeHt7s2fPHq699loOHz7Mzp07ue666ygsLOSHH37g//7v/9i6dStTpkwhMjKSzZs3c9NNN+Hn5+cakX3w4MFkZWXx6aefum0fX3/9Nfn5+a7tY8uWLaxcuZLmzZvzj3/8g9zcXLy9vUlOTmbOnDmuMn/44Yf4+voSExPD0KFDWbBgAaGhoSxYsIDhw4e7uuFt376dSy+9FGMMU6dOdbWsJycnc9ttt9G3b1/q16+PMYbU1FS+/PJLV0vabbfd5joBKdmSlpubyyOPPOJqLfnuu+9cJ0GvvPIKd999N++99x7e3t788MMPWGtZvXo1a9asYd++fSxatKjM76Hkstq2bRtffvklc+bMqdLt6r///a9bV/aZM2eWuSDYotMQNi93dmWfNWuW6+LOkCFDmDlzJjk5OaSkpLB582bOPfdcALKO7gcg49AOtv/4GUPqdy63jJWpNuzvPv30U4+/Bfh9f5efn1+hfWVVbBuno2S+AwcOZMqUKTz++OO0bNmSefPmMWLEiCpfVp7S3nDDDdx111389a9/ZcaMGRQUFHjsvnvkyBGsta7uu5MnT2bdunV8++23vPnmm2zbto2IiAi++OILFixYQPv27dmyZQvJycn87W9/Y+PGjWWW84mWe/GFg7i4OFfgUay4pbR47IxiSR/eznuPhPPp/53e2DWnE1iW17r73nvv4evrS8uWLXnrrbf48ssv+XX9HLcyn87vaOvWrWRlZbl+CytXrmTv3r38/e9/57nnniMnJwdjDElJSW69zGbNmkV+fj5+fn5MmjSJRx99lICAAJYvX87//vc/rLU8+uijGGNo1KgRo0eP5pNPPqGgoIDvv/+eDz74gO7du/P9999zxx13UFhY6DqeFN+2mJmZSWFhYZnHVHpqkX738DK34++GDRsYNmwY4H4xet++fQQGBrqCcF9fX1atWuV6ctF//vMf19hI3t7evPDCCzgcDlq2bMmxY8cICQnxeBy8/vrrmT59Oo0aNXI9brPkcfDnn3/GGFNt50nFGjRoQG5uLklJSVhrycrKomHDhixatAhrLdOmTcPHx4djx45x7bXXMm7cOCIiIvjss89o1aqVa0DU22+/ndzcXBo2bEhubi6ff/656ze0detWHnnkEZ588kmMMXTu3JmCggJ+/PFH17JKS0tzXZQODw9nwIABtG7dml69erEvZQk+fkEAFBbkUViQhx7uKDWqZMvFmfTq0aOHrY1GvVD2taXNpDKvyk6bk5NjHQ6H/e677+zRo0etv7+//eyzz2xMTIzdunWrzcnJseecc47dsGHDCfO9cNg/bYhXgCu/3oExtndgjFuasLAw+89//tNaa21WVpY1xtiCggI7cOBAO2DAAFe+DRs2tFFRUa58Fy9ebP38/E67zJVZ3+pc1pWZ1tvb2zZr1syVdtSoUdbX19decMEFrjTe3t527NixrvcOh8NeeOGFtlGjRtbPz8/m5eXZsLAwGxAQYENCQuyTTz5pV6xY4ZrX29vbvvTSS27rOCgoyHp5ebny9fb2tn5+fm5lDgsLs/Xr17dPPfWUW9rmzZvbgIAAV1pfX1/r5eVlr732Wrcy+vn52aeeesr1vnXr1rZdu3Z29OjR1lprY2JibExMjO3SpYt99913XWW++uqrrY+Pj/373/9uvb29bWxsrK1Xr5719vYus5xjY2PdltWTTz5pn3zySbdlPnDgQDtu3DgbFhbmWlYDBgywcXFxbvP6+PjYcePG2T179tjmzZu76tuzZ08bFhZW7u+hWNOmTe1jjz1W5dvVP/7xD9u6dWsbExNjH3/8cWuttd0GPmIHjJxtR71g7a3PZNlWXYba2NhY27NnT7t161ZXHo8//riNiYmxbdq0sV9//bW11vm7adKqtw1p0t42jDjHXjLm27Nqf9e+fXvrcDjK/S0sXrzYAqe0r6ysbaMyl7PD4bBBQUGufPv372+DgoLK/PYre1l5Suvj4+P6/Ranveqqq9zSDhgwwJ577rl24MCBrt9vw4YN7VVXXeX2+x04cKBt2LBhmeXs5eVlJ02a5LacO3fubKOjo8ss95LL94Jrp9t25//RWmvthx9+aIcNG2attXbDhg32nHPOsdnZ2Xbbtm02JibG5ufn21EvWHvZ3YvsVQ+ssqFNO57WeYOn7fnCgFZudfD29raTJk1yW1bTpk2zUVFRduDAga4yh4WF2UmTJpUpc5MmTWz8ZU+5lbmyf0cl16+11gYGBlpfX1+3Y8qAAQNskyZNbGBgoGv9+vr62vr169t7773Xent7W2utNcbYP/zhD7Zx48bWWucx5ZdffrH169e3l156qSvfuLg426hRI7thwwabk5Nj/f39LWDPPffcMsu5U58HbCufMFd9hzfobqMdoXbgwIGuMpd3/A0ODrYNGjRwlbl+/frW19fXxsfHu+Vbv359t7StW7e29erVs5dddpm11v04eOWVV9qOHTtaa63t1auXDQ4Odq3r4x0HT2c/eY5fhFt9AwMDy/0NxsbGWofDYQHr7e1tExISrJ+fnzXGuOri4+NjAVuvXj372GOP2bCwMPvdd99ZY4w1xrjSXnTRRTY+Pt4GBwdbX19fC7jOK++55x4bFRVlO3bsaLt27Wp9fHzskSNHbEREhPX29rajR4+2I0eOtH5+fvbAgQP2tttus+Hh4bblOdfa2/+ebxtGdLEO33r2nL7jTvo3WBsAK20tiM30qryXWuDPEp66Uz/99NNu9ycNHz78pEZq9tRVan9+hts85T37ds+ePdSrV8+Vb1hYGC1btvSY7+mUuTLrW1E1Ve7Sadu0acP+/ftdaXfs2OG6Ml0sICCAjRs3Ar/f13348GF8fX3x9vZ2dWkrKCggNzeX6Oho4uPjAeetH9HR0fznP/9xrePvvvuOzMxM1/1jMTEx1KtXD29vb7cyp6enk5GRwU033eS2feTn55Ofn+9KWzz43K+//upK63A4yMnJ4aabbnKV2dfXl/DwcLdB5g4dOkRoaCiAq8yhoaG0bt2aGTNmEBISwpYtW0hNTSU4OJiJEye6Ledbb72Vn3/++bgt0kOGDGHu3LmEhYW5WqRjYmIwxri1SOfl5XH11VfTtGlTmjdvjpeXF5s3b+bXX38lNDT0uL+HJUuWcODAAXx8fKp8uzp06BCbNm1i69atrp4HPS55lBadnPV2+PjT79ZP2LJlC8uXLycmJsaVx8MPP8zWrVtJTk7mkksucU2/4t7FDB2/kWv+so7INv3KlK0q1Jb93f79+/Hz8zvubwE45X1lZWwbp6P0co6IiCAvL8+Vb2xsLPXq1XPLtyqWlae0xhi3fR3gsftuenq6a1/ToEEDQkJCSElJcev66+Pjw+HDh92W82+//Yavry9ffvml23K+8MIL8ff3P+5y/3X9bFqf6xw7o7j1z1rn2BnDhw/Hz8/PNXZG8eNem8Um4Fev4WmvM0/b8468w251ADhw4IDbsjpRL6fiMoeFhXHgwAHi4m9yK/Pp/I68vLzIz893tYwWP76z5Ppt0aIFubm53HTTTW63OHXp0oWsrCwyMzPx8/Nz9aLq2bMn9erV45tvvsEYw9KlS93ur05NTSU7OxsvLy/XsjHG0K5dO2bPno2vry9ZWVls376dTZs2kfLjv0+47PMpcNuujDGu5Vxs//79BAQEkJ2d7dom8/PzXWMZFefr7e3NsWPH+Pe//+06DoLztoysrCzX9xUfB9evX8/PP/+Mw+Fg2bJlpKenExERccLj4MnuN1r3GMHRwpxTGqA0NDSUXbt28c9//pPExESMMVx++eUEBgYSFBTkGqegcePGxMfHk5GRwfr1613nGZMmTXLdInXhhRcSGBjI5s2bXeOpFD9SuWTvuby8PPbt28eFF15IWFgYu3fv5s9//jPe3t4kJibSq1cvIiMjXYOnJvzhLby8vLnmL2v5w+RUDuxYzqac/SdcLiJVRQH8WcLTY7L27t17wlGnq8KxY8fc8g0KCvKY7+mUuSbrW1PlLp32ggsucHUbLb6vu3v37m4Dz3Xo0IF169YBvx9QwXm/d1ZWFunp6a5RWP39/UlPT2f37t2u9Lt37+a8884DnL157r33XtdzvUuW2cvLy1XmvXv3kpeXx4033khUVJRbHUJDQ8nLy3OVOSsri6ioKPbt2+dKm5OTQ2BgIFFRUa4yR0ZGcumll5KUlMTBgwdJS0sjPT2dfv368fPPP7vKvG3bNlJSUqhfv75rWYWEhBAbG0tqaqpbmVu0aMEFF1zgepLEsGHD6NixI90HTWTgqDncMRVWZo5k++4sUlJSeP75513dDf39/Rk2bBgdOnRg8ODBrnECAF5++WXX/a7Z2dlER0eX+3vYu3cvAwcOdN37Vxd/DzWhNtXXWlvub+FEjrevrKxt43SUXs7FF/tKB78nm+/pLCtPaStjkM29e/cyd+5cLr/8ctdyzsjI4Nprr+Wiiy7iwIEDbvk6HA638RY8LffMI7sICjn5pzlUtZMNLIuD++IyFxYWcvToUVfavXv3snr1ai6//HKCQtz37afDy8uLmJgYVq5cSWhoKP7+/jRu3JgNGzbw8MMPs3fvXrZs2YKPjw+tWrVyu8Wpb9++eHt7u4K0li1bup5E8tprrzF06FAKCwvZt28fH3/8MdnZ2eTn5zNu3Dg6dOjAjh07XPU7ePAgrVu3dlsnLVq0oFu3bmxa9qZbmRs0acuhgkzX+9S8wzTwch/LwN/f37WcS16Mjo2NLdOlPCIigoMHD7rSFt/q9eabbzJu3DiaNWvG3r17ue666/j+++/LHAdHjhxJQUGB69GmoaGh3HLLLSc8Dp7sNujw9eeuhhed0gClxXUZOXIkS5Ys4ZFHHqFVq1bExMQQEBDAyJEjOXToEIcPH+bjjz/m2LFjzJ8/n+DgYOD3MahiYmL49NNPCQoKonnz5q7bM3Nycrj22mtd41DEx8ezZ88e7rnnHr777juysrIICwvj6aefdo3nsmjRInJycujfvz+ff/45vv7Brrr6BYTQLO5iko5tOeFyEakqCuClwjwdmMIdQW7zlPfs22bNmrmCMXAeEEs/N1cqh8PhIDg4mK+//to1QnKPHj3cWlcHDRrkCvKLT3oiIyPp2rUr3t7eNGzYkN27dxMTE0PDhg1Zs2YNsbGxBAQEUFhYSLNmzZgwYQJHjx6loKCA22+/nejoaHJyclzlyMzMdB1oMzMz6dSpEz4+Plx00UWA+/YRGRlJkyZNXGX29vbm/PPPZ/fu3dx555107NiRoKAggoKC3Mo8ZMgQUlJS6N27N40bNyY9PZ2JEydy9dVXM3PmTGJiYvDz82PhwoVccMEFnHPOOa4Wi6ysLFJTUz0OctayZcsTtkh37vsgwcHBrhbp4mdBl2yRbtCggev30K5dO4wx/Pzzz5x33nluo9+X/D0UL6sBAwbwzDPPVOq2cbpOZ9yOuuZ09nfh4eHl/hZKqui+srZuGyEhIa6xIMD5NIfST1moimXlKW1QUBAZGe4tvHl5eW5pW7ZsSXBwMNu2bXMNynj48GFatWrFzp07Xcu5YcOGjB8/HnBeKLj22mu58cYb3Xqg1BWnE1ger5dT8bKKjIzk5ptvPql8K3LeEBQUxO23305eXh6BgYGukeUffvhhOnXqRFxcHFOmTHEbLDAyMpJevXoBzkeQNWzYkD179tCnTx927tzJ9ddfz7Fjx2jSpAnh4eG0atWKP//5z/j5+XHjjTcyZcoUfv75Z1cwfeTIES666CIOHTrk6hVWPIhdo6jubnXx1CJ9Rf3OboN9tmzZkg0bNgC4XYweMmQIXl5e9OvXj5SUFMD5yMKNGzdy8803uwZNLCgo4JtvvuHNN98kIiKC/v37U69evXKPg8U9wg4cOIDD4SAzM/OkjoMn695Giac8QGlmZibWOgdrnjhxIpGRkYSFhbFv3z4KCwt57LHHmDBhAk/ObsV9r9fjmkfSSC+MZuxzZQdV7dq1K6mpqa6W/+TkZC699FLumAoj/55Hu/hradiyP+984VyHJzOeS1bGAXKyDjvzys1iV/J8YnwbnfKyEjldCuDPEm3bti3zmKymTZuecNRpTzwdmG5s0NNtnvKefTt27Fh++eUXUlJSXAfEmJgYj/meTpkrs74VVVPl9pS2Xbt2DBgwwDVCsq+vL/Xq1WPUqFHk5uYye/Zsvv/+e7eTniFDhriu9D/88MNcffXV7N69m3vvvZfly5dz+PBhNm7cSL169QgICMBaS8+ePQkKCuLPf/4zkydPpqCggDVr1jB37lzy8/NJTEwkIiKCzp07Ex0dzVVXXVXu9nHo0CEuvvhiFixYADhHl77xxhuZMWMGGRkZPPTQQ+zbt4+//vWvfPDBByQmJroeM5aSkkKPHj3YsmULEydOpGPHjtx+++1ERkbSsmVLvv76a7799lsaN27M9u3bOeecc1xdKWNiYk7593Cqz4Iu7/dQclkVd1+sq7+HmlBb9nfl/RZK5uvl5VWj28bpKJ2vj48PhYWF/Pjjj67fQteuXd3yrYpl5Slt//79ycjIcN3CApTbfTcpKYl33nmHzp07k56ezqRJk5g5cyadOnUiPDycoKAgzj33XNq0acOvv/5K+/bteeCBBzwu5+LbgI633E/laQ6V5XQCy/J6Oe3YscO1TbZr185jmSv7vKFnz55kZGS48vPx8fF4i9PMmTOZMGECAwcO5NChQ1x00UWMHz+eJ554goceeoiUlBT8/f3Jy8vD4XDw2muvcfnll/PAAw9w5ZVX0qVLF9577z369+/PI488wt69ewFo3749AQEBRERE0KNHD3pc8qhb3p5apEc2vICkpCTuuusucnNzsda6nn5Q+mL0xRdf7Bocb8qUKQwdOpSwsDDef/99/Pz8OHbsGImJifztb3/j3HPP5cILL+SDDz4o9zhYskfYe++9x/79+yv1OFjZevbsecqDql599dU0aNCAgQMH0rp1a3x8fBg+fDgrv36Eb14dSEiT9lx88wfkZKYRFxfn1nuu9LKaPn063t7eZKbv4evpF/PpM+cw+4WeRLYdQN+gNmXKLVJtqvume2AskAJkA6uAi04wf5+i+bKBbcDok8lHg9i5y8rKsg6Hwy5atMg1yNGnn35qW7VqZbdt2+YarGT9+vUnle+9DftYB17WGy97YUAru6XNJHvRRRfZv/3tb9Zaaw8dOmQjIyOtw+Gw9erVs999953r+/r27WsB63A47COPPOLKt3nz5tbLy8sC1svLy954442nXObKrm91LevqSPvKK6+c1EBloaGh1hhjvb29bf/+/a211rZo0cKGhITYNm3a2I8//thGRkZab29vC7gGy+nSpYtt0qSJBSxgW7VqZc85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\n", + "text/plain": [ + "
" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "plot_histogram([res3.get_counts(),nearest_probs], legend=['raw', 'mitigated'], figsize=(15,6))" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "32e72f51", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "0.9255895675305238" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "hellinger_fidelity(nearest_probs, exact_dist)" + ] + }, + { + "cell_type": "markdown", + "id": "a59a1f7c", + "metadata": {}, + "source": [ + "### Additional information on measurement mitigation\n", + "\n", + "From the results object, it is also possible to determine the execution time of the mitigation process (not including calibration time), on the per experiment level:" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "6ffd5b04", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "0.008146638981997967" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "res3.results[0].header.measurement_mitigation_time" + ] + }, + { + "cell_type": "markdown", + "id": "c1973f47", + "metadata": {}, + "source": [ + "It is also possible to view the final measurment mapping that shows which physical qubit measurements correspond to classical bit values:" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "1251442e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'0': 0, '1': 1, '4': 2, '7': 3, '10': 4, '12': 5}" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "res3.results[0].header.final_measurement_mapping" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d8f2a61e", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.4" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/tutorials/02_uploading_program.ipynb b/tutorials/02_uploading_program.ipynb new file mode 100644 index 000000000..34e647007 --- /dev/null +++ b/tutorials/02_uploading_program.ipynb @@ -0,0 +1,654 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "55f13cc7", + "metadata": {}, + "source": [ + "# Uploading a Qiskit runtime program" + ] + }, + { + "cell_type": "markdown", + "id": "4ff8d2da", + "metadata": {}, + "source": [ + "
\n", + "Note: Qiskit Runtime allows authorized users to upload runtime programs. Access to the Qiskit Runtime service may not mean you have access to upload a runtime program.\n", + "
" + ] + }, + { + "cell_type": "markdown", + "id": "2077b996", + "metadata": {}, + "source": [ + "Here we provide an overview on how to construct and upload a runtime program. A runtime program is a piece of Python code that lives in the cloud and can be invoked by passing in just its parameters. Runtime programs are private by default, which means only you can see and access your programs. Some authorized users can also mark their programs as public, making them visible and accessible by everyone." + ] + }, + { + "cell_type": "markdown", + "id": "cf42076e", + "metadata": {}, + "source": [ + "## Constructing a runtime program" + ] + }, + { + "cell_type": "markdown", + "id": "e282eccc", + "metadata": {}, + "source": [ + "Below is a template of a runtime program. You can find the template file in the \n", + "[`qiskit-ibmq-provider`](https://github.com/Qiskit/qiskit-ibmq-provider/blob/master/qiskit/providers/ibmq/runtime/program/program_template.py) repository." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "1206f612", + "metadata": {}, + "outputs": [], + "source": [ + "import sys\n", + "import json\n", + "\n", + "from qiskit.providers.ibmq.runtime import UserMessenger, ProgramBackend\n", + "\n", + "\n", + "def program(backend: ProgramBackend, user_messenger: UserMessenger, **kwargs):\n", + " \"\"\"Function that does classical-quantum calculation.\"\"\"\n", + " # UserMessenger can be used to publish interim results.\n", + " user_messenger.publish(\"This is an interim result.\")\n", + " return \"final result\"\n", + "\n", + "\n", + "def main(backend: ProgramBackend, user_messenger: UserMessenger, **kwargs):\n", + " \"\"\"This is the main entry point of a runtime program.\n", + "\n", + " The name of this method must not change. It also must have ``backend``\n", + " and ``user_messenger`` as the first two positional arguments.\n", + "\n", + " Args:\n", + " backend: Backend for the circuits to run on.\n", + " user_messenger: Used to communicate with the program user.\n", + " kwargs: User inputs.\n", + " \"\"\"\n", + " # Massage the input if necessary.\n", + " result = program(backend, user_messenger, **kwargs)\n", + " # Final result can be directly returned\n", + " return result\n" + ] + }, + { + "cell_type": "markdown", + "id": "bb4cfa8b", + "metadata": {}, + "source": [ + "Each runtime program must have a `main()` function, which serves as the entry point to the program. This function must have `backend` and `user_messenger` as the first two positional arguments:\n", + "\n", + "- `backend` is an instance of [`ProgramBackend`](https://qiskit.org/documentation/stubs/qiskit.providers.ibmq.runtime.ProgramBackend.html#qiskit.providers.ibmq.runtime.ProgramBackend) and has a [`run()`](https://qiskit.org/documentation/stubs/qiskit.providers.ibmq.runtime.ProgramBackend.run.html#qiskit.providers.ibmq.runtime.ProgramBackend.run) method that can be used to submit circuits.\n", + "- `user_messenger` is an instance of [`UserMessenger`](https://qiskit.org/documentation/stubs/qiskit.providers.ibmq.runtime.UserMessenger.html#qiskit.providers.ibmq.runtime.UserMessenger) and has a [`publish()`](https://qiskit.org/documentation/stubs/qiskit.providers.ibmq.runtime.UserMessenger.publish.html#qiskit.providers.ibmq.runtime.UserMessenger.publish) method that can be used to send interim and final results to the program user. This method takes a parameter `final` that indicates whether it's a final result. However, it is recommended to return the final result directly from the `main()` function. Currently only final results are stored after a program execution finishes." + ] + }, + { + "cell_type": "markdown", + "id": "705ccef3", + "metadata": {}, + "source": [ + "There are several runtime programs in the `qiskit_runtime` directory in this repository. `qiskit_runtime/hello_world/hello_world.py` is one of them. It is a sample runtime program that submits random circuits for user-specified iterations:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "181916df", + "metadata": {}, + "outputs": [], + "source": [ + "\"\"\"A sample runtime program that submits random circuits for user-specified iterations.\"\"\"\n", + "\n", + "import random\n", + "\n", + "from qiskit import transpile\n", + "from qiskit.circuit.random import random_circuit\n", + "\n", + "\n", + "def prepare_circuits(backend):\n", + " \"\"\"Generate a random circuit.\n", + "\n", + " Args:\n", + " backend: Backend used for transpilation.\n", + "\n", + " Returns:\n", + " Generated circuit.\n", + " \"\"\"\n", + " circuit = random_circuit(num_qubits=5, depth=4, measure=True,\n", + " seed=random.randint(0, 1000))\n", + " return transpile(circuit, backend)\n", + "\n", + "\n", + "def main(backend, user_messenger, **kwargs):\n", + " \"\"\"Main entry point of the program.\n", + "\n", + " Args:\n", + " backend: Backend to submit the circuits to.\n", + " user_messenger: Used to communicate with the program consumer.\n", + " kwargs: User inputs.\n", + " \"\"\"\n", + " iterations = kwargs.pop('iterations', 5)\n", + " for it in range(iterations):\n", + " qc = prepare_circuits(backend)\n", + " result = backend.run(qc).result()\n", + " user_messenger.publish({\"iteration\": it, \"counts\": result.get_counts()})\n", + "\n", + " return \"All done!\"\n" + ] + }, + { + "cell_type": "markdown", + "id": "1281b655", + "metadata": {}, + "source": [ + "## Data serialization" + ] + }, + { + "cell_type": "markdown", + "id": "9edb1023", + "metadata": {}, + "source": [ + "Runtime programs live in the cloud, and JSON is the standard way of passing data to and from cloud services. Therefore, when a user invokes a runtime program, the input parameters must first be serialized into the JSON format and then deserialized once received by the server. By default, this serialization and deserialization is done automatically using the [`RuntimeEncoder`](https://qiskit.org/documentation/stubs/qiskit.providers.ibmq.runtime.RuntimeEncoder.html#qiskit.providers.ibmq.runtime.RuntimeEncoder) and [`RuntimeDecoder`](https://qiskit.org/documentation/stubs/qiskit.providers.ibmq.runtime.RuntimeDecoder.html#qiskit.providers.ibmq.runtime.RuntimeDecoder) classes.\n" + ] + }, + { + "cell_type": "markdown", + "id": "8b4aeb2b", + "metadata": {}, + "source": [ + "### Custom classes" + ] + }, + { + "cell_type": "markdown", + "id": "1d7fbdf5", + "metadata": {}, + "source": [ + "`RuntimeEncoder` and `RuntimeDecoder` only support types commonly used in Qiskit, such as complex numbers and numpy arrays. If your program uses custom Python classes for input or output, these two methods only have partial support for that. \n", + "\n", + "Your custom class should have the following methods:\n", + "\n", + "- a `to_json()` method that returns a JSON string representation of the object\n", + "- a `from_json()` class method that accepts a JSON string and returns the corresponding object. \n", + "\n", + "When `RuntimeEncoder` serializes a Python object, it checks whether the object has a `to_json()` method. If so, it calls the method to serialize the object. `RuntimeDecoder`, however, does _not_ invoke `from_json()` to convert the data back because it doesn't know how to import your custom class. Therefore the deserialization needs to be done explicitly. " + ] + }, + { + "cell_type": "markdown", + "id": "c3e2d6e2", + "metadata": {}, + "source": [ + "Here is an example of serializing and deserializing a custom class. First we define the class `MyCustomClass`:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "ca9047fc", + "metadata": {}, + "outputs": [], + "source": [ + "import json\n", + "\n", + "class MyCustomClass:\n", + " \n", + " def __init__(self, foo, bar):\n", + " self._foo = foo\n", + " self._bar = bar\n", + " \n", + " def to_json(self):\n", + " \"\"\"Convert this instance to a JSON string.\"\"\"\n", + " return json.dumps({\"foo\": self._foo, \"bar\": self._bar})\n", + " \n", + " @classmethod\n", + " def from_json(cls, json_str):\n", + " \"\"\"Return a MyCustomClass instance based on the input JSON string.\"\"\"\n", + " return cls(**json.loads(json_str))" + ] + }, + { + "cell_type": "markdown", + "id": "31f967a4", + "metadata": {}, + "source": [ + "Note that it has the `to_json()` method that converts a `MyCustomClass` instance to a JSON string, and a `from_json()` class method that converts a JSON string back to a `MyCustomClass` instance." + ] + }, + { + "cell_type": "markdown", + "id": "2fdf8941", + "metadata": {}, + "source": [ + "Here is how one would use `MyCustomClass` as an **input** to your program:" + ] + }, + { + "cell_type": "markdown", + "id": "258fcd02", + "metadata": {}, + "source": [ + "```\n", + "program_inputs = {\n", + " 'my_obj': MyCustomClass(\"my foo\", \"my bar\")\n", + "}\n", + "\n", + "options = {\"backend_name\": \"ibmq_qasm_simulator\"}\n", + "job = provider.runtime.run(program_id=\"some-program\",\n", + " options=options,\n", + " inputs=program_inputs\n", + " )\n", + "```" + ] + }, + { + "cell_type": "markdown", + "id": "dc86356b", + "metadata": {}, + "source": [ + "Since `MyCustomClass` has a `to_json()` method, the method is automatically called to convert the instance to a JSON string when `provider.runtime.run()` is invoked. \n", + "\n", + "Your program can then use the `from_json()` method to restore the JSON string back to a `MyCustomClass` instance:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "681a1798", + "metadata": {}, + "outputs": [], + "source": [ + "def main(backend, user_messenger, **kwargs):\n", + " \"\"\"Main entry point of the program.\"\"\"\n", + " my_obj_str = kwargs.pop('my_obj')\n", + " my_obj = MyCustomClass.from_json(my_obj_str)" + ] + }, + { + "cell_type": "markdown", + "id": "b71c40bc", + "metadata": {}, + "source": [ + "Similarly, if you pass a `MyCustomClass` instance as an **output** of your program, it is automatically converted to a JSON string (via the `to_json()` method):" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "f4cf18b4", + "metadata": {}, + "outputs": [], + "source": [ + "def main(backend, user_messenger, **kwargs):\n", + " \"\"\"Main entry point of the program.\"\"\"\n", + " return MyCustomClass(\"this foo\", \"that bar\")" + ] + }, + { + "cell_type": "markdown", + "id": "8e0fdb4a", + "metadata": {}, + "source": [ + "Now when the user of this program calls `job.result()`, they will receive a JSON string rather than a `MyCustomClass` instance. The user can convert the string back to `MyCustomClass` themselves:" + ] + }, + { + "cell_type": "markdown", + "id": "abf149cc", + "metadata": {}, + "source": [ + "```\n", + "output_str = job.result()\n", + "output = MyCustomClass.from_json(output_str)\n", + "```" + ] + }, + { + "cell_type": "markdown", + "id": "32cd0c7e", + "metadata": {}, + "source": [ + "Alternatively, you can provide a decoder for the users. Your decoder class should inherit [`ResultDecoder`](https://qiskit.org/documentation/stubs/qiskit.providers.ibmq.runtime.ResultDecoder.html#qiskit.providers.ibmq.runtime.ResultDecoder) and overwrites the `decode()` method:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "a2f8564d", + "metadata": {}, + "outputs": [], + "source": [ + "from qiskit.providers.ibmq.runtime import ResultDecoder\n", + "\n", + "class MyResultDecoder(ResultDecoder):\n", + "\n", + " @classmethod\n", + " def decode(cls, data):\n", + " data = super().decoded(data) # Perform any preprocessing.\n", + " return MyCustomClass.from_json(data)" + ] + }, + { + "cell_type": "markdown", + "id": "3d0de7d0", + "metadata": {}, + "source": [ + "Your user can then use this `MyResultDecoder` to decode the result of your program:\n", + "\n", + "```\n", + "output = job.result(decoder=MyResultDecoder)\n", + "```" + ] + }, + { + "cell_type": "markdown", + "id": "ecdda3bf", + "metadata": {}, + "source": [ + "## Testing your runtime program" + ] + }, + { + "cell_type": "markdown", + "id": "2a18f49e", + "metadata": {}, + "source": [ + "You can test your runtime program using a local simulator or a real backend before uploading it. Simply import and invoke the `main()` function of your program and pass the following parameters:\n", + "\n", + "- the `backend` instance you want to use\n", + "- a new `UserMessenger` instance.\n", + "- program input parameters that are serialized and then deserialized using the correct encoder and decoder. While this may seem redundant, it is to ensure input parameters can be passed to your program properly once it's uploaded to the cloud.\n" + ] + }, + { + "cell_type": "markdown", + "id": "f197d48e", + "metadata": {}, + "source": [ + "The following example tests the `hello-world` program we saw earlier. It uses the `qasm_simulator` from Qiskit Aer as the test backend. It serializes and deserializes input data using `RuntimeEncoder` and `RuntimeDecoder`, which are the default en/decoders used by runtime." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "12c32ba7", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{\"iteration\": 0, \"counts\": {\"01000\": 4, \"00000\": 12, \"00011\": 872, \"01011\": 136}}\n", + "{\"iteration\": 1, \"counts\": {\"01000\": 6, \"00000\": 19, \"00011\": 871, \"01011\": 128}}\n", + "{\"iteration\": 2, \"counts\": {\"00001\": 1024}}\n" + ] + }, + { + "data": { + "text/plain": [ + "'All done!'" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import sys\n", + "sys.path.insert(0, '..') # Add qiskit_runtime directory to the path\n", + "\n", + "from qiskit_runtime.hello_world import hello_world\n", + "from qiskit import Aer\n", + "from qiskit.providers.ibmq.runtime.utils import RuntimeEncoder, RuntimeDecoder\n", + "from qiskit.providers.ibmq.runtime import UserMessenger\n", + "\n", + "inputs = {\"iterations\": 3}\n", + "\n", + "backend = Aer.get_backend('qasm_simulator')\n", + "user_messenger = UserMessenger()\n", + "serialized_inputs = json.dumps(inputs, cls=RuntimeEncoder)\n", + "deserialized_inputs = json.loads(serialized_inputs, cls=RuntimeDecoder)\n", + "\n", + "hello_world.main(backend, user_messenger, **deserialized_inputs)" + ] + }, + { + "cell_type": "markdown", + "id": "501a66f3", + "metadata": {}, + "source": [ + "## Defining program metadata" + ] + }, + { + "cell_type": "markdown", + "id": "f98640c1", + "metadata": {}, + "source": [ + "Program metadata helps users to understand how to use your program. It includes:\n", + "\n", + "- `name`: Name of the program.\n", + "- `max_execution_time`: Maximum amount of time, in seconds, a program can run before being forcibly terminated.\n", + "- `description`: Describes the program.\n", + "- `spec`: Detailed information about the program, which includes the following attributes:\n", + " - `backend_requirements`: Describes the backend attributes needed to run the program.\n", + " - `parameters`: Describes the program input parameters as a JSON schema\n", + " - `return_values`: Describes the return values as a JSON schema\n", + " - `interim_results`: Describes the interim results as a JSON schema\n", + "\n", + "When uploading a program, you must specify at least `name`, `max_execution_time`, and `description`. It is strongly encouraged to also specify `parameters`, `return_values`, and `interim_results` within `spec` if the program has them." + ] + }, + { + "cell_type": "markdown", + "id": "55db2a39", + "metadata": {}, + "source": [ + "Below shows the metadata JSON file of the `hello-world` program as an example:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "3cdce276", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{\n", + " \"name\": \"hello-world\",\n", + " \"description\": \"A sample runtime program.\",\n", + " \"max_execution_time\": 300,\n", + " \"spec\": {\n", + " \"backend_requirements\": {\n", + " \"min_num_qubits\": 5\n", + " },\n", + " \"parameters\": {\n", + " \"$schema\": \"https://json-schema.org/draft/2019-09/schema\",\n", + " \"properties\": {\n", + " \"iterations\": {\n", + " \"type\": \"integer\",\n", + " \"minimum\": 0,\n", + " \"description\": \"Number of iterations to run. Each iteration generates a runs a random circuit.\"\n", + " }\n", + " },\n", + " \"required\": [\n", + " \"iterations\"\n", + " ]\n", + " },\n", + " \"return_values\": {\n", + " \"$schema\": \"https://json-schema.org/draft/2019-09/schema\",\n", + " \"description\": \"A string that says 'All done!'.\",\n", + " \"type\": \"string\"\n", + " },\n", + " \"interim_results\": {\n", + " \"$schema\": \"https://json-schema.org/draft/2019-09/schema\",\n", + " \"properties\": {\n", + " \"iteration\": {\n", + " \"type\": \"integer\",\n", + " \"description\": \"Iteration number.\"\n", + " },\n", + " \"counts\": {\n", + " \"description\": \"Histogram data of the circuit result.\",\n", + " \"type\": \"object\"\n", + " }\n", + " }\n", + " }\n", + " }\n", + "}\n", + "\n" + ] + } + ], + "source": [ + "import os\n", + "\n", + "hello_world_json = os.path.join(os.getcwd(), \"../qiskit_runtime/hello_world/hello_world.json\")\n", + "\n", + "with open(hello_world_json, 'r') as file:\n", + " data = file.read()\n", + "\n", + "print(data)" + ] + }, + { + "cell_type": "markdown", + "id": "43dd6a77", + "metadata": {}, + "source": [ + "## Uploading a program" + ] + }, + { + "cell_type": "markdown", + "id": "d1865c58", + "metadata": {}, + "source": [ + "You can use the [`IBMRuntimeService.upload_program()`](https://qiskit.org/documentation/stubs/qiskit.providers.ibmq.runtime.IBMRuntimeService.html#qiskit.providers.ibmq.runtime.IBMRuntimeService.upload_program) method to upload your program. In the example below, the program data lives in the file `hello_world.py`, and its metadata, as described above, is in `hello_world.json`. " + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "864ab085", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "hello-world-nQ9dgRjGEe\n" + ] + } + ], + "source": [ + "import os\n", + "from qiskit import IBMQ\n", + "\n", + "IBMQ.load_account()\n", + "provider = IBMQ.get_provider(project='qiskit-runtime') # Substitute with your provider.\n", + "\n", + "hello_world_data = os.path.join(os.getcwd(), \"../qiskit_runtime/hello_world/hello_world.py\")\n", + "hello_world_json = os.path.join(os.getcwd(), \"../qiskit_runtime/hello_world/hello_world.json\")\n", + "\n", + "program_id = provider.runtime.upload_program(\n", + " data=hello_world_data,\n", + " metadata=hello_world_json\n", + ")\n", + "print(program_id)" + ] + }, + { + "cell_type": "markdown", + "id": "45ffd6c8", + "metadata": {}, + "source": [ + "`upload_program()` returns a program ID, which uniquely identifies the program. It is derived from the program name, usually with a randomly-generated suffix. Program ID is needed for users to invoke the program" + ] + }, + { + "cell_type": "markdown", + "id": "chubby-infection", + "metadata": {}, + "source": [ + "## Updating a program" + ] + }, + { + "cell_type": "markdown", + "id": "measured-catalyst", + "metadata": {}, + "source": [ + "You can use the [`IBMRuntimeService.update_program()`](https://qiskit.org/documentation/stubs/qiskit.providers.ibmq.runtime.IBMRuntimeService.update_program.html#qiskit.providers.ibmq.runtime.IBMRuntimeService.update_program) method to update the source code and/or metadata of a program:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "southern-farmer", + "metadata": {}, + "outputs": [], + "source": [ + "provider.runtime.update_program(program_id=program_id, description=\"A new description.\")" + ] + }, + { + "cell_type": "markdown", + "id": "becoming-badge", + "metadata": {}, + "source": [ + "This method allows you to make changes to your program while retaining the same program ID." + ] + }, + { + "cell_type": "markdown", + "id": "f841041c", + "metadata": {}, + "source": [ + "## Deleting a program" + ] + }, + { + "cell_type": "markdown", + "id": "a20cd296", + "metadata": {}, + "source": [ + "You can use the [`IBMRuntimeService.delete_program()`](https://qiskit.org/documentation/stubs/qiskit.providers.ibmq.runtime.IBMRuntimeService.html#qiskit.providers.ibmq.runtime.IBMRuntimeService.delete_program) method to delete a program. Only the person who uploaded the program can delete it. \n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.1" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/tutorials/images/chip.png b/tutorials/images/chip.png new file mode 100644 index 000000000..0fa4a271f Binary files /dev/null and b/tutorials/images/chip.png differ diff --git a/tutorials/images/subgraphs.png b/tutorials/images/subgraphs.png new file mode 100644 index 000000000..7ccd9e3e2 Binary files /dev/null and b/tutorials/images/subgraphs.png differ diff --git a/tutorials/qka.ipynb b/tutorials/qka.ipynb new file mode 100644 index 000000000..c55b47081 --- /dev/null +++ b/tutorials/qka.ipynb @@ -0,0 +1,634 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Quantum Kernel Alignment with Qiskit Runtime\n", + "\n", + "
\n", + "\n", + "**Classification with Support Vector Machines**
\n", + "Classification problems are widespread in machine learning applications. Examples include credit card risk, handwriting recognition, and medical diagnosis. One approach to tackling classification problems is the support vector machine (SVM) [1,2]. This supervised learning algorithm uses labeled data samples to train a model that can predict to which class a test sample belongs. It does this by finding a separating hyperplane maximizing the margin between data classes. Often, data is not linearly separable in the original space. In these cases, the kernel trick is used to implicitly encode a transformation of the data into a higher-dimensional feature space, through the inner product between pairs of data points, where the data may become separable.\n", + "\n", + "**Quantum Kernels**
\n", + "Quantum computers can be used to encode classical data in a quantum-enhanced feature space. In 2019, IBM introduced an algorithm called the quantum kernel estimator (QKE) for computing quantum kernels [3]. This algorithm uses quantum circuits with data provided classically and offers an efficient way to evaluate inner products between data in a quantum feature space. For two data samples $\\theta$ and $\\theta'$, the kernel matrix is given as\n", + "\n", + "$$\n", + " K(\\theta, \\theta') = \\lvert\\langle 0^n \\rvert U^\\dagger(\\theta) U(\\theta') \\lvert 0^n \\rangle \\rvert^2,\n", + "$$\n", + "\n", + "where $U(\\theta)$ prepares the quantum feature state. Quantum kernels used in a classification framework inherit the convex optimization program of the SVM and avoid common limitations of variational quantum classifiers. A key observation of this paper was that a necessary condition for a computational advantage requires quantum circuits for the kernel that are hard to simulate classically. More recently, IBM proved that quantum kernels can offer superpolynomial speedups over any classical learner on a learning problem based on the hardness of the discrete logarithm problem [4]. This means that quantum kernels can someday offer quantum advantage on suitable problems. \n", + "\n", + "\n", + "**Quantum Kernels that Exploit Structure in Data**
\n", + "An important approach in the search for practical quantum advantage in machine learning is to identify quantum kernels for learning problems that have underlying structure in the data. We've taken a step in this direction in our recent paper [5], where we introduced a broad class of quantum kernels that exploit group structure in data. Examples of learning problems for data with group structure could include learning permutations or classifying translations. We call this new class of kernels _covariant quantum kernels_ as they are related to covariant quantum measurements. The quantum feature map is defined by a unitary representation $D(\\theta)$ of a group $G$ for some element $\\theta \\in G$, and a fiducial reference state $\\lvert\\psi\\rangle = V\\lvert0^n\\rangle$ prepared by a unitary circuit $V$. The kernel matrix is given as\n", + "\n", + "$$\n", + " K(\\theta, \\theta') = \\vert\\langle 0^n \\rvert V^\\dagger D^\\dagger(\\theta) D(\\theta') V \\lvert 0^n \\rangle \\rvert^2. \\qquad (1)\n", + "$$\n", + "\n", + "In general, the choice of the fiducial state is not known _a priori_ and can significantly impact the performance of the classifier. Here, we use a method called quantum kernel alignment (QKA) to find a good fiducial state for a given group.\n", + "\n", + "**Aligning Quantum Kernels on a Dataset**
\n", + "In practice, SVMs require a choice of the kernel function. Sometimes, symmetries in the data can inform this selection, other times it is chosen in an ad hoc manner. Kernel alignment is one approach to learning a kernel on a given dataset by iteratively adapting it to have high similarity to a target kernel informed from the underlying data distribution [6]. As a result, the SVM with an aligned kernel will likely generalize better to new data than with an unaligned kernel. Using this concept, we introduced in [5] an algorithm for quantum kernel alignment, which provides a way to learn a quantum kernel from a family of kernels. Specifically, the algorithm optimizes the parameters in a quantum circuit to maximize the alignment of a kernel while converging to the maximum SVM margin. In the context of covariant quantum kernels, we extend Eq. $(1)$ to\n", + "\n", + "$$\n", + " K_\\lambda(\\theta,\\theta') = \\lvert\\langle 0^n \\rvert V^\\dagger_\\lambda D^\\dagger(\\theta) D(\\theta') V_\\lambda \\lvert 0^n \\rangle \\rvert^2, \\qquad (2)\n", + "$$\n", + "\n", + "and use QKA to learn a good fiducial state parametrized by $\\lambda$ for a given group. \n", + "\n", + "\n", + "**Covariant Quantum Kernels on a Specific Learning Problem**
\n", + "Let's try out QKA on a learning problem. In the following, we'll consider a binary classification problem we call _labeling cosets with error_ [5]. In this problem, we will use a group and a subgroup to form two cosets, which will represent our data classes. We take the group $G = SU(2)^{\\otimes n}$ for $n$ qubits, which is the special unitary group of $2\\times2$ matrices and has wide applicability in nature, for example, the Standard Model of particle physics and in many condensed matter systems. We take the graph-stabilizer subgroup $S_{\\mathrm{graph}} \\in G$ with $S_{\\mathrm{graph}} = \\langle \\{ X_i \\otimes_{k:(k,i) \\in \\mathcal{E}} Z_k \\}_{i \\in \\mathcal{V}} \\rangle$ for a graph $(\\mathcal{E},\\mathcal{V})$ with edges $\\mathcal{E}$ and vertices $\\mathcal{V}$. Note that the stabilizers fix a stabilizer state such that $D_s \\lvert \\psi\\rangle = \\lvert \\psi\\rangle$. This observation will be useful a bit later. \n", + "\n", + "To generate the dataset, we write the rotations of the group as $D(\\theta_1, \\theta_2, 0)=\\exp(i \\theta_1 X) \\exp(i \\theta_2 Z) \\in SU(2)$, so that each qubit is parametrized by the first two Euler angles (the third we set to zero). Then, we draw randomly two sets of angles $\\mathbf{\\theta}_\\pm \\in [-\\pi/4, \\pi/4]^{2n}$ for the $n$-qubit problem. From these two sets, we construct a binary classification problem by forming two left-cosets (representing the two classes) with those angles, $C_\\pm = D(\\mathbf{\\theta}_\\pm) S_{\\mathrm{graph}}$ where $D(\\mathbf{\\theta}_\\pm) = \\otimes_{k=1}^n D(\\theta_\\pm^{2k-1}, \\theta_\\pm^{2k}, 0)$. Note that the elements of the cosets can again be written in terms of Euler angles. We build training and testing sets by randomly drawing elements from $C_\\pm$ such that the dataset has samples $i=1,...,m$ containing the first two Euler angles for each qubit $\\mathbf{\\theta}_{y_i} = (\\theta_{y_i}^{1}, \\theta_{y_i}^{2}, \\theta_{y_i}^{3}, \\theta_{y_i}^{4}, ..., \\theta_{y_i}^{2n-1}, \\theta_{y_i}^{2n})$ and labels $y_i \\in \\{-1,1\\}$ that indicate to which coset a sample belongs.\n", + "\n", + "Next, we select a fiducial state. A natural candidate is the stabilizer state we encountered above. Why? Because this is a subgroup invariant state, $D_s\\lvert\\psi\\rangle = \\lvert\\psi\\rangle$, which causes the data for a given coset to be mapped to a unique state: $D(\\mathbf{\\theta}_\\pm)D_s \\lvert\\psi\\rangle = D(\\mathbf{\\theta}_\\pm) \\lvert\\psi\\rangle$. This means the classifier only needs to distinguish the _two_ states $D(\\mathbf{\\theta}_\\pm) \\lvert\\psi\\rangle \\langle \\psi\\rvert D^\\dagger(\\mathbf{\\theta}_\\pm)$ for every element of the coset. In this tutorial, we will add a small Gaussian error with variance $0.01$ to the Euler angles of the dataset. This noise will perturb these two states, but if the variance is sufficiently small, we expect the states will still be classified correctly. Let's consider a parametrized version of the stabilizer state, associated with the coupling graph $(\\mathcal{E},\\mathcal{V})$ given by the device connectivity, as our fiducial state and then use kernel alignment to find its optimal parameters. Specifically, we'll replace the initial layers of Hadamards in the graph state with $y$-rotations by an angle $\\lambda$,\n", + "\n", + "$$\n", + "\\lvert \\psi_\\lambda\\rangle = V_\\lambda \\lvert 0^n\\rangle = \\prod_{(k,t) \\in \\mathcal{E}} CZ_{k,t} \\prod_{k \\in \\mathcal{V}} \\exp\\left(i \\frac{\\lambda}{2} Y_k\\right)\\lvert 0^n\\rangle,\n", + "$$\n", + "\n", + "where $CZ=\\mathrm{diag}(1,1,1,-1)$. Then, given two samples from our dataset, $\\mathbf{\\theta}$ and $\\mathbf{\\theta}'$, the kernel matrix is evaluated as in Eq. $(2)$. If we initialize the kernel with $\\lambda \\approx 0$, we expect the quantum kernel alignment algorithm to converge towards the optimal $\\lambda = \\pi/2$ and the classifier to yield 100\\% test accuracy.\n", + "\n", + "Let's define two specific problem instances to test these ideas out. We'll be using the quantum device `ibmq_montreal`, with coupling map shown below:\n", + "\n", + "
\n", + "\n", + "
\n", + "\n", + "We'll pick two different subgraphs, one for 7 qubits and one for 10, to define our problem instances. Using these subgraphs, we'll generate the corresponding datasets as described above, and then align the quantum kernel with QKA to learn a good fiducial state.\n", + "\n", + "
\n", + "\n", + "
\n", + "\n", + "**Speeding up Algorithms with Qiskit Runtime**
\n", + "QKA is an iterative quantum-classical algorithm, in which quantum hardware is used to execute parametrized quantum circuits for evaluating the quantum kernel matrices with QKE, while a classical optimizer tunes the parameters of those circuits to maximize the alignment. Iterative algorithms of this type can be slow due to latency between the quantum and classical calculations. Qiskit Runtime is a new architecture that can speed up iterative algorithms like QKA by co-locating classical computations with the quantum hardware executions. In this tutorial, we'll use QKA with Qiskit Runtime to learn a good quantum kernel for the _labeling cosets with error_ problem defined above.\n", + "\n", + "
\n", + "\n", + "**References**
\n", + "[1] B. E. Boser, I. M. Guyon, and V. N. Vapnik, Proceedings of the Fifth Annual Workshop on Computational Learning Theory, COLT ’92 (Association for Computing Machinery, New York, NY, USA, 1992) pp. 144-152 [link](https://doi.org/10.1145/130385.130401)
\n", + "[2] V. Vapnik, The Nature of Statistical Learning Theory, Information Science and Statistics (Springer New York, 2013) [link](https://books.google.com/books?id=EqgACAAAQBAJ)
\n", + "[3] V. Havlíček, A. D. Córcoles, K. Temme, A. W. Harrow, A. Kandala, J. M. Chow, and J. M. Gambetta, Nature 567, 209-212 (2019) [link](https://doi.org/10.1038/s41586-019-0980-2)
\n", + "[4] Y. Liu, S. Arunachalam, and K. Temme, arXiv:2010.02174 (2020) [link](https://arxiv.org/abs/2010.02174)
\n", + "[5] J. R. Glick, T. P. Gujarati, A. D. Córcoles, Y. Kim, A. Kandala, J. M. Gambetta, K. Temme, arXiv:2105.03406 (2021) [link](https://arxiv.org/abs/2105.03406)
\n", + "[6] N. Cristianini, J. Shawe-taylor, A. Elisseeff, and J. Kandola, Advances in Neural Information Processing Systems 14 (2001) [link](https://proceedings.neurips.cc/paper/2001/file/1f71e393b3809197ed66df836fe833e5-Paper.pdf)
\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Load your IBM Quantum account and get the quantum backend\n", + "\n", + "We'll be using the 27-qubit device `ibmq_montreal` for this tutorial." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "import sys\n", + "sys.path.insert(0, '..') # Add qiskit_runtime directory to the path\n", + "\n", + "from qiskit import IBMQ\n", + "IBMQ.load_account()\n", + "provider = IBMQ.get_provider(project='qiskit-runtime') # Change this to your provider.\n", + "backend = provider.get_backend('ibmq_montreal')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Invoke the Quantum Kernel Alignment program\n", + "\n", + "Before executing the runtime program for QKA, we need to prepare the dataset and configure the input parameters for the algorithm.\n", + "\n", + "### 1. Prepare the dataset\n", + "\n", + "First, we load the dataset from the `csv` file and then extract the labeled training and test samples. Here, we'll look at the 7-qubit problem, shown above in subfigure a). A second dataset is also available for the 10-qubit problem in b)." + ] + }, + { + "cell_type": "code", + "execution_count": 65, + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "\n", + "df = pd.read_csv('../qiskit_runtime/qka/aux_file/dataset_graph7.csv',sep=',', header=None) # alterative problem: dataset_graph10.csv\n", + "data = df.values" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's take a look at the data to see how it's formatted. Each row of the dataset contains a list of Euler angles, followed by the class label $\\pm1$ in the last column. For an $n$-qubit problem, there are $2n$ features corresponding to the first two Euler angles for each qubit (recall discussion above). The rows alternate between class labels." + ] + }, + { + "cell_type": "code", + "execution_count": 66, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " 0 1 2 3 4 5 6 \\\n", + "0 -0.193574 0.113979 -0.005927 0.300957 -0.358603 -0.087866 -0.156226 \n", + "1 -0.100006 0.002431 0.244218 0.126870 -0.063891 -0.085588 0.072490 \n", + "2 -1.774448 -0.047642 -0.025880 0.252708 -0.350689 -1.604509 -0.114874 \n", + "3 -0.211585 -0.043782 -1.560226 0.018510 -0.051867 -0.128508 0.218609 \n", + "\n", + " 7 8 9 10 11 12 13 14 \n", + "0 0.342442 -0.016003 0.143113 0.256422 -0.164125 -0.136743 0.014674 1.0 \n", + "1 0.042986 -0.052714 0.019754 -0.159314 -0.409991 -0.199615 0.053845 -1.0 \n", + "2 0.347631 0.059501 -0.168956 0.351014 -0.128586 0.098897 -0.047799 1.0 \n", + "3 -0.075632 -0.183656 -1.715292 -0.105361 -0.300758 -0.566431 0.046542 -1.0 \n" + ] + } + ], + "source": [ + "print(df.head(4))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now, let's explicitly construct the training and test samples (denoted `x`) and their labels (denoted `y`)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": false + }, + "outputs": [], + "source": [ + "import numpy as np\n", + "\n", + "# choose number of training and test samples per class:\n", + "num_train = 10\n", + "num_test = 10\n", + "\n", + "# extract training and test sets and sort them by class label\n", + "train = data[:2*num_train, :]\n", + "test = data[2*num_train:2*(num_train+num_test), :]\n", + "\n", + "ind=np.argsort(train[:,-1])\n", + "x_train = train[ind][:,:-1]\n", + "y_train = train[ind][:,-1]\n", + "\n", + "ind=np.argsort(test[:,-1])\n", + "x_test = test[ind][:,:-1]\n", + "y_test = test[ind][:,-1]\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 2. Configure the QKA algorithm\n", + "\n", + "The first task is to set up the feature map and its entangler map, which specifies the arrangement of $CZ$ gates in the fiducial state. We will choose this to match the connectivity of the problem subgraph, pictured above. We also initialize the fiducial state parameter $\\lambda$ with `initial_point`." + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "metadata": {}, + "outputs": [], + "source": [ + "from qiskit_runtime.qka import FeatureMap\n", + "\n", + "d = np.shape(data)[1]-1 # feature dimension is twice the qubit number\n", + "\n", + "em = [[0,2],[3,4],[2,5],[1,4],[2,3],[4,6]] # we'll match this to the 7-qubit graph \n", + "# em = [[0,1],[2,3],[4,5],[6,7],[8,9],[1,2],[3,4],[5,6],[7,8]] # we'll match this to the 10-qubit graph\n", + "\n", + "fm = FeatureMap(feature_dimension=d, entangler_map=em) # define the feature map\n", + "initial_point = [0.1] # set the initial parameter for the feature map" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's print out the circuit for the feature map (the circuit for the kernel will be a feature map for one data sample composed with an inverse feature map for a second sample). The first part of the feature map is the fiducial state, which is prepared with a layer of $y$ rotations followed by $CZ$s. Then, the last two layers of $z$ and $x$ rotations in the circuit denote the group representation $D(\\theta)$ for a data sample $\\theta$. Note that a single-qubit rotation is defined as $RP(\\phi) = \\exp(- i [\\phi/2] P)$ for $P \\in {X, Y, Z}$." + ] + }, + { + "cell_type": "code", + "execution_count": 64, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
     ┌──────────┐   ┌──────────────┐ ┌────────────┐                                                 \n",
+       "q_0: ┤ RY(-0.1) ├─■─┤ RZ(-0.38383) ├─┤ RX(3.3378) ├─────────────────────────────────────────────────\n",
+       "     ├──────────┤ │ └──────────────┘ └────────────┘┌──────────────┐ ┌──────────────┐                \n",
+       "q_1: ┤ RY(-0.1) ├─┼────────────────────────■───────┤ RZ(-0.11811) ├─┤ RX(-0.20449) ├────────────────\n",
+       "     ├──────────┤ │                        │       └──────────────┘ └┬────────────┬┘┌─────────────┐ \n",
+       "q_2: ┤ RY(-0.1) ├─■────────■───────────────┼──────────────■──────────┤ RZ(3.4802) ├─┤ RX(0.15495) ├─\n",
+       "     ├──────────┤          │               │              │         ┌┴────────────┤ ├─────────────┴┐\n",
+       "q_3: ┤ RY(-0.1) ├─■────────┼───────────────┼──────────────■─────────┤ RZ(0.34764) ├─┤ RX(-0.54085) ├\n",
+       "     ├──────────┤ │        │               │                        ├─────────────┤ ├──────────────┤\n",
+       "q_4: ┤ RY(-0.1) ├─■────────┼───────────────■──────────────■─────────┤ RZ(0.34312) ├─┤ RX(-0.14015) ├\n",
+       "     ├──────────┤          │        ┌─────────────┐       │        ┌┴─────────────┴┐└──────────────┘\n",
+       "q_5: ┤ RY(-0.1) ├──────────■────────┤ RZ(0.51497) ├───────┼────────┤ RX(-0.029293) ├────────────────\n",
+       "     ├──────────┤                   └─────────────┘       │        └┬──────────────┤┌─────────────┐ \n",
+       "q_6: ┤ RY(-0.1) ├─────────────────────────────────────────■─────────┤ RZ(-0.42725) ├┤ RX(0.44115) ├─\n",
+       "     └──────────┘                                                   └──────────────┘└─────────────┘ 
" + ], + "text/plain": [ + " ┌──────────┐ ┌──────────────┐ ┌────────────┐ \n", + "q_0: ┤ RY(-0.1) ├─■─┤ RZ(-0.38383) ├─┤ RX(3.3378) ├─────────────────────────────────────────────────\n", + " ├──────────┤ │ └──────────────┘ └────────────┘┌──────────────┐ ┌──────────────┐ \n", + "q_1: ┤ RY(-0.1) ├─┼────────────────────────■───────┤ RZ(-0.11811) ├─┤ RX(-0.20449) ├────────────────\n", + " ├──────────┤ │ │ └──────────────┘ └┬────────────┬┘┌─────────────┐ \n", + "q_2: ┤ RY(-0.1) ├─■────────■───────────────┼──────────────■──────────┤ RZ(3.4802) ├─┤ RX(0.15495) ├─\n", + " ├──────────┤ │ │ │ ┌┴────────────┤ ├─────────────┴┐\n", + "q_3: ┤ RY(-0.1) ├─■────────┼───────────────┼──────────────■─────────┤ RZ(0.34764) ├─┤ RX(-0.54085) ├\n", + " ├──────────┤ │ │ │ ├─────────────┤ ├──────────────┤\n", + "q_4: ┤ RY(-0.1) ├─■────────┼───────────────■──────────────■─────────┤ RZ(0.34312) ├─┤ RX(-0.14015) ├\n", + " ├──────────┤ │ ┌─────────────┐ │ ┌┴─────────────┴┐└──────────────┘\n", + "q_5: ┤ RY(-0.1) ├──────────■────────┤ RZ(0.51497) ├───────┼────────┤ RX(-0.029293) ├────────────────\n", + " ├──────────┤ └─────────────┘ │ └┬──────────────┤┌─────────────┐ \n", + "q_6: ┤ RY(-0.1) ├─────────────────────────────────────────■─────────┤ RZ(-0.42725) ├┤ RX(0.44115) ├─\n", + " └──────────┘ └──────────────┘└─────────────┘ " + ] + }, + "execution_count": 64, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from qiskit.tools.visualization import circuit_drawer\n", + "circuit_drawer(fm.construct_circuit(x=x_train[0], parameters=initial_point), \n", + " output='text', fold=200)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Next, we set the values for the SVM soft-margin penalty `C` and the number of SPSA iterations `maxiters` we use to align the quantum kernel." + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "C = 1 # SVM soft-margin penalty\n", + "maxiters = 10 # number of SPSA iterations" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Finally, we decide how to map the virtual qubits of our problem graph to the physical qubits of the hardware. For example, in the 7-qubit problem, we can directly map the virtual qubits `[0, 1, 2, 3, 4, 5, 6]` to the physical qubits `[10, 11, 12, 13, 14, 15, 16]` of the device. This allows us to avoid introducing SWAP gates for qubits that are not connected, which can increase the circuit depth. " + ] + }, + { + "cell_type": "code", + "execution_count": 54, + "metadata": {}, + "outputs": [], + "source": [ + "initial_layout = [10, 11, 12, 13, 14, 15, 16] # see figure above for the 7-qubit graph\n", + "# initial_layout = [9, 8, 11, 14, 16, 19, 22, 25, 24, 23] # see figure above for the 10-qubit graph" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 3. Set up and run the program\n", + "\n", + "We're almost ready to run the program. First, let's take a look at the program metadata, which includes a description of the input parameters and their default values." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "quantum-kernel-alignment:\n", + " Name: quantum-kernel-alignment\n", + " Description: Quantum kernel alignment algorithm that learns, on a given dataset, a quantum kernel maximizing the SVM classification margin.\n", + " Version: 1\n", + " Creation date: 2021-05-06T14:56:53Z\n", + " Max execution time: 28800\n", + " Input parameters:\n", + " - feature_map:\n", + " Description: An instance of FeatureMap in dictionary format used to map classical data into a quantum state space.\n", + " Type: dict\n", + " Required: True\n", + " - data:\n", + " Description: NxD array of training data, where N is the number of samples and D is the feature dimension.\n", + " Type: numpy.ndarray\n", + " Required: True\n", + " - labels:\n", + " Description: Nx1 array of +/-1 labels of the N training samples.\n", + " Type: numpy.ndarray\n", + " Required: True\n", + " - initial_kernel_parameters:\n", + " Description: Initial parameters of the quantum kernel. If not specified, an array of randomly generated numbers is used.\n", + " Type: numpy.ndarray\n", + " Required: False\n", + " - maxiters:\n", + " Description: Number of SPSA optimization steps. Default is 1.\n", + " Type: int\n", + " Required: False\n", + " - C:\n", + " Description: Penalty parameter for the soft-margin support vector machine. Default is 1.\n", + " Type: float\n", + " Required: False\n", + " - initial_layout:\n", + " Description: Initial position of virtual qubits on the physical qubits of the quantum device. Default is None.\n", + " Type: list or dict\n", + " Required: False\n", + " Interim results:\n", + " none\n", + " Returns:\n", + " - aligned_kernel_parameters:\n", + " Description: The optimized kernel parameters found from quantum kernel alignment.\n", + " Type: numpy.ndarray\n", + " - aligned_kernel_matrix:\n", + " Description: The aligned quantum kernel matrix evaluated with the optimized kernel parameters on the training data.\n", + " Type: numpy.ndarray\n" + ] + } + ], + "source": [ + "print(provider.runtime.program('quantum-kernel-alignment'))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We see that this program has several input parameters, which we'll configure below. To run the program, we'll set up its two main components: `inputs` (the input parameters from the program metadata) and `options` (the quantum backend). We'll also define a callback function so that the intermediate results of the algorithm will be printed as the program runs. Note that each step of the algorithm for the settings we've selected here takes approximately 11 minutes." + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "def interim_result_callback(job_id, interim_result):\n", + " print(f\"interim result: {interim_result}\\n\")" + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "metadata": { + "scrolled": false, + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "c2at64rhejjp7co0uc9g\n", + "interim result: {'cost': 10.870283985111303, 'kernel_parameters': array([0.24000164])}\n", + "\n", + "interim result: {'cost': 10.333206932017534, 'kernel_parameters': array([0.42813036])}\n", + "\n", + "interim result: {'cost': 9.080271557433964, 'kernel_parameters': array([0.62392269])}\n", + "\n", + "interim result: {'cost': 7.651520327865867, 'kernel_parameters': array([0.76737064])}\n", + "\n", + "interim result: {'cost': 6.6448212932491355, 'kernel_parameters': array([0.91182299])}\n", + "\n", + "interim result: {'cost': 5.958753300709191, 'kernel_parameters': array([1.03833353])}\n", + "\n", + "interim result: {'cost': 5.265464439204466, 'kernel_parameters': array([1.11397698])}\n", + "\n", + "interim result: {'cost': 4.899536249549028, 'kernel_parameters': array([1.15494826])}\n", + "\n", + "interim result: {'cost': 4.848342921952558, 'kernel_parameters': array([1.1975977])}\n", + "\n", + "interim result: {'cost': 4.743013044149239, 'kernel_parameters': array([1.221689])}\n", + "\n" + ] + } + ], + "source": [ + "program_inputs = {\n", + " 'feature_map': fm,\n", + " 'data': x_train,\n", + " 'labels': y_train,\n", + " 'initial_kernel_parameters': initial_point,\n", + " 'maxiters': maxiters,\n", + " 'C': C,\n", + " 'initial_layout': initial_layout\n", + "}\n", + "\n", + "options = {'backend_name': backend.name()}\n", + "\n", + "job = provider.runtime.run(program_id=\"quantum-kernel-alignment\",\n", + " options=options,\n", + " inputs=program_inputs,\n", + " callback=interim_result_callback,\n", + " )\n", + "\n", + "print(job.job_id())\n", + "result = job.result()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 4. Retrieve the results of the program\n", + "\n", + "Now that we've run the program, we can retrieve the output, which is the aligned kernel parameter and the aligned kernel matrix. Let's also plot this kernel matrix (we'll subtract off the diagonal to show the contrast between the remaining entries). The kernel matrix is expected to have a block-diagonal structure. This reflects the fact that the kernel maps the input data effectively to just two states (modulo the small noise we added to the data; recall the discussion above). That is, data in the same coset (same class label) have a larger overlap than do data from different cosets." + ] + }, + { + "cell_type": "code", + "execution_count": 62, + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "aligned_kernel_parameters: [1.221689]\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "print(f\"aligned_kernel_parameters: {result['aligned_kernel_parameters']}\")\n", + "\n", + "from matplotlib import pyplot as plt\n", + "from pylab import cm\n", + "plt.rcParams['font.size'] = 20\n", + "plt.imshow(result['aligned_kernel_matrix']-np.identity(2*num_train), cmap=cm.get_cmap('bwr', 20))\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Use the results of the program to test an SVM on new data\n", + "\n", + "Equipped with the aligned kernel and its optimized parameter, we can use the `sklearn` package to train an SVM and then evaluate its classification accuracy on new test points. Note that a second kernel matrix built from the test points is needed for the SVM decision function." + ] + }, + { + "cell_type": "code", + "execution_count": 63, + "metadata": { + "scrolled": true, + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "accuracy test: 1.0\n" + ] + } + ], + "source": [ + "from qiskit_runtime.qka import KernelMatrix\n", + "from sklearn.svm import SVC\n", + "from sklearn import metrics\n", + "\n", + "# train the SVM with the aligned kernel matrix:\n", + "\n", + "kernel_aligned = result['aligned_kernel_matrix']\n", + "model = SVC(C=C, kernel='precomputed')\n", + "model.fit(X=kernel_aligned, y=y_train)\n", + "\n", + "# test the SVM on new data:\n", + "\n", + "km = KernelMatrix(feature_map=fm, backend=backend, initial_layout=initial_layout)\n", + "kernel_test = km.construct_kernel_matrix(x1_vec=x_test, x2_vec=x_train, parameters=result['aligned_kernel_parameters'])\n", + "labels_test = model.predict(X=kernel_test)\n", + "accuracy_test = metrics.balanced_accuracy_score(y_true=y_test, y_pred=labels_test)\n", + "print(f\"accuracy test: {accuracy_test}\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "

Version Information

Qiskit SoftwareVersion
Qiskit0.25.0
Terra0.17.0
Aer0.8.0
Ignis0.6.0
Aqua0.9.0
IBM Q Provider0.13.0
System information
Python3.7.10 (default, Feb 26 2021, 10:16:00) \n", + "[Clang 10.0.0 ]
OSDarwin
CPUs4
Memory (Gb)16.0
Mon May 03 13:47:57 2021 EDT
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "

This code is a part of Qiskit

© 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.

" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import qiskit.tools.jupyter\n", + "%qiskit_version_table\n", + "%qiskit_copyright" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python [conda env:envruntime2]", + "language": "python", + "name": "conda-env-envruntime2-py" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.8" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/tutorials/sample_expval_program/qiskit_runtime_expval_program.ipynb b/tutorials/sample_expval_program/qiskit_runtime_expval_program.ipynb new file mode 100644 index 000000000..53c723b7f --- /dev/null +++ b/tutorials/sample_expval_program/qiskit_runtime_expval_program.ipynb @@ -0,0 +1,823 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "33be72af", + "metadata": {}, + "source": [ + "# Custom Expectation Value Program for the Qiskit Runtime\n", + "\n", + "\n", + "

\n", + "Paul Nation\n", + "

\n", + "

\n", + "IBM Quantum Partners Technical Enablement Team\n", + "

\n", + "\n", + "Here we will show how to make a program that takes a circuit, or list of circuits, and computes the expectation values of one or more diagonal operators." + ] + }, + { + "cell_type": "markdown", + "id": "118e72f0", + "metadata": {}, + "source": [ + "## Prerequisites\n", + "\n", + "- You must have Qiskit 0.32+ installed.\n", + "- You must have an IBM Quantum Experience account with the ability to upload a Runtime program. You can upload a program if you have access to more than just the open hub/group/project (ibm-q/open/main)." + ] + }, + { + "cell_type": "markdown", + "id": "6368c90a", + "metadata": {}, + "source": [ + "## Background\n", + "\n", + "The primary method by which information is obtained from quantum computers is via expectation values. Indeed, the samples that come from executing a quantum circuit multiple times, once converted to probabilities, can be viewed as just a finite sample approximation to the expectation value for the projection operators corresponding to each bitstring. More practically, many quantum algorithms require computing expectation values over Pauli operators, e.g. Variational Quantum Eigensolvers, and thus having a runtime program that computes these quantities is of fundamental importance. Here we look at one such example, where an user passes one or more circuits and expectation operators and gets back the computed expectation values, and possibly error bounds.\n", + "\n", + "### Expectation value of a diagonal operator\n", + "\n", + "Consider a generic observable given by the tensor product of diagonal operators over $N$ qubits $O = O_{N-1}\\dots O_{0}$ where the subscript indicates the qubit on which the operator acts. Then for a set of observed $M$ bitstrings $\\{b_{0}, \\dots b_{M-1}\\}$, where $M \\leq 2^N $, with corresponding approximate probabilites $p_{m}$ the expectation value is given by\n", + "\n", + "$$\n", + "\\langle O\\rangle \\simeq \\sum_{m=0}^{M-1} p_{m}\\prod_{n=0}^{N-1}O_{n}[b_{m}[N-n-1], b_{m}[N-n-1]],\n", + "$$\n", + "\n", + "where $O_{n}[b_{m}[N-n-1], b_{m}[N-n-1]]$ is the diagonal element of $O_{n}$ specified by the $N-n-1$th bit in bitstring $b_{m}$. The reason for the complicated indexing in `b_{m}` is because Qiskit uses least-sginificant bit indexing where the zeroth element of the bit-strings is given by the right-most bit.\n", + "\n", + "Here we will use built-in routines to compute these expectation values. However, it is not hard to do yourself, with plenty of examples to be found." + ] + }, + { + "cell_type": "markdown", + "id": "42df9e62", + "metadata": {}, + "source": [ + "## Main program\n", + "\n", + "Here we define our main function for the expectation value runtime program. As always, our program must start with the `backend`, and `user_messenger` arguements, followed by the actual inputs we pass to the program. Here our options are quite simple:\n", + "\n", + "- `circuits`: A single QuantumCircuit or list of QuantumCircuits to be executed on the target backend.\n", + "\n", + "\n", + "- `expectation_operators`: The operators we want to evaluate. These can be strings of diagonal Pauli's, eg, `ZIZZ`, or custom operators defined by dictionarys. For example, the projection operator on the all ones state of 4 qubits is `{'1111': 1}`.\n", + "\n", + "\n", + "- `shots`: Howe many times to sample each circuit.\n", + "\n", + "\n", + "- `transpiler_config`: A dictionary that passes additional arguments on to the transpile function, eg. `optimization_level`.\n", + "\n", + "\n", + "- `run_config`: A dictionary that passes additional arguments on to `backend.run()`.\n", + "\n", + "\n", + "- `skip_transpilation`: A flag to skip transpilation altogether and just run the circuits. This is useful for situations where you need to transpile parameterized circuits once, but must bind parameters multiple times and evaluate. \n", + "\n", + "\n", + "- `return_stddev`: Flag to return bound on standard deviation. If using measurement mitigation this adds some overhead to the computation.\n", + "\n", + "\n", + "- `use_measurement_mitigation`: Use M3 measurement mitigation and compute expecation value and standard deviation bound from quasi-probabilities.\n", + "\n", + "At the top of the cell below you will see a commented out `%%writefile sample_expval.py`. We will use this to convert the cell to a Python module named `sample_expval.py` to upload." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "06b48e43", + "metadata": {}, + "outputs": [], + "source": [ + "#%%writefile sample_expval.py\n", + "import mthree\n", + "from qiskit import transpile\n", + "\n", + "# The entrypoint for our Runtime Program\n", + "def main(backend, user_messenger,\n", + " circuits,\n", + " expectation_operators='',\n", + " shots = 8192,\n", + " transpiler_config={},\n", + " run_config={},\n", + " skip_transpilation=False,\n", + " return_stddev=False,\n", + " use_measurement_mitigation=False,\n", + " ):\n", + " \n", + " \"\"\"Compute expectation \n", + " values for a list of operators after\n", + " executing a list of circuits on the target backend.\n", + " \n", + " Parameters:\n", + " backend (ProgramBackend): Qiskit backend instance.\n", + " user_messenger (UserMessenger): Used to communicate with the program user.\n", + " circuits: (QuantumCircuit or list): A single list of QuantumCircuits.\n", + " expectation_operators (str or dict or list): Expectation values to evaluate.\n", + " shots (int): Number of shots to take per circuit.\n", + " transpiler_config (dict): A collection of kwargs passed to transpile().\n", + " run_config (dict): A collection of kwargs passed to backend.run().\n", + " skip_transpilation (bool): Skip transpiling of circuits, default=False.\n", + " return_stddev (bool): Return upper bound on standard devitation,\n", + " default=False. \n", + " use_measurement_mitigation (bool): Improve resulting using measurement\n", + " error mitigation, default=False.\n", + " \n", + " Returns:\n", + " array_like: Returns array of expectation values or a list of (expval, stddev)\n", + " tuples if return_stddev=True.\n", + " \"\"\"\n", + " \n", + " # transpiling the circuits using given transpile options\n", + " if not skip_transpilation:\n", + " trans_circuits = transpile(circuits, backend=backend,\n", + " **transpiler_config)\n", + " # Make sure everything is a list\n", + " if not isinstance(trans_circuits, list):\n", + " trans_circuits = [trans_circuits]\n", + " # If skipping set circuits -> trans_circuits\n", + " else:\n", + " if not isinstance(circuits, list):\n", + " trans_circuits = [circuits]\n", + " else:\n", + " trans_circuits = circuits\n", + "\n", + " # If we are given a single circuit but requesting multiple expectation\n", + " # values, then set flag to make multiple pointers to same result.\n", + " duplicate_results = False\n", + " if isinstance(expectation_operators, list):\n", + " if len(expectation_operators) and len(trans_circuits) == 1:\n", + " duplicate_results = True\n", + " \n", + " # If doing measurement mitigation we must build and calibrate a\n", + " # mitigator object. Will also determine which qubits need to be\n", + " # calibrated.\n", + " if use_measurement_mitigation:\n", + " # Get an the measurement mappings at end of circuits\n", + " meas_maps = mthree.utils.final_measurement_mapping(trans_circuits)\n", + " # Get an M3 mitigator\n", + " mit = mthree.M3Mitigation(backend)\n", + " # Calibrate over the set of qubits measured in the transpiled circuits.\n", + " mit.cals_from_system(meas_maps)\n", + "\n", + " # Compute raw results\n", + " result = backend.run(trans_circuits, shots=shots, **run_config).result()\n", + " raw_counts = result.get_counts()\n", + "\n", + " # When using measurement mitigation we need to apply the correction and then\n", + " # compute the expectation values from the computed quasi-probabilities.\n", + " if use_measurement_mitigation:\n", + " quasi_dists = mit.apply_correction(raw_counts, meas_maps,\n", + " return_mitigation_overhead=return_stddev)\n", + " \n", + " if duplicate_results:\n", + " quasi_dists = mthree.classes.QuasiCollection(\n", + " [quasi_dists]*len(expectation_operators))\n", + " # There are two different calls depending on what we want returned.\n", + " if return_stddev:\n", + " return quasi_dists.expval_and_stddev(expectation_operators)\n", + " return quasi_dists.expval(expectation_operators)\n", + " \n", + " # If the program didn't return in the mitigation loop above it means\n", + " # we are processing the raw_counts data. We do so here using the\n", + " # mthree utilities\n", + " if duplicate_results:\n", + " raw_counts = [raw_counts]*len(expectation_operators)\n", + " if return_stddev:\n", + " return mthree.utils.expval_and_stddev(raw_counts, expectation_operators)\n", + " return mthree.utils.expval(raw_counts, expectation_operators)" + ] + }, + { + "cell_type": "markdown", + "id": "edadd3f8", + "metadata": {}, + "source": [ + "## Local testing\n", + "\n", + "Here we test with a local \"Fake\" backend that mimics the noise properties of a real system and a 4-qubit GHZ state." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "2a25b3ac", + "metadata": {}, + "outputs": [], + "source": [ + "from qiskit import QuantumCircuit\n", + "from qiskit.test.mock import FakeSantiago\n", + "from qiskit.providers.ibmq.runtime import UserMessenger\n", + "msg = UserMessenger()\n", + "backend = FakeSantiago()" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "6339a144", + "metadata": {}, + "outputs": [], + "source": [ + "qc = QuantumCircuit(4)\n", + "qc.h(2)\n", + "qc.cx(2, 1)\n", + "qc.cx(1, 0)\n", + "qc.cx(2, 3)\n", + "qc.measure_all()" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "3966f447", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([0.974614 , 1. , 0.02428596])" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "main(backend, msg,\n", + " qc,\n", + " expectation_operators=['ZZZZ', 'IIII', 'IZZZ'],\n", + " transpiler_config={'optimization_level':3, 'layout_method': 'sabre',\n", + " 'routing_method': 'sabre'},\n", + " run_config={},\n", + " skip_transpilation=False,\n", + " return_stddev=False,\n", + " use_measurement_mitigation=True\n", + " )" + ] + }, + { + "cell_type": "markdown", + "id": "fafe3295", + "metadata": {}, + "source": [ + "If we have done our job correctly, the above should print out two expectation values close to one and a final expectation value close to zero." + ] + }, + { + "cell_type": "markdown", + "id": "f1fa15d4", + "metadata": {}, + "source": [ + "## Program metadata\n", + "\n", + "Next we add the needed program data to a dictionary for uploading with our program." + ] + }, + { + "cell_type": "code", + "execution_count": 297, + "id": "cdb8037d", + "metadata": {}, + "outputs": [], + "source": [ + "meta = {\n", + " \"name\": \"sample-expval\",\n", + " \"description\": \"A sample expectation value program.\",\n", + " \"max_execution_time\": 1000,\n", + " \"spec\": {}\n", + "}\n", + "\n", + "meta[\"spec\"][\"parameters\"] = {\n", + " \"$schema\": \"https://json-schema.org/draft/2019-09/schema\",\n", + " \"properties\": {\n", + " \"circuits\": {\n", + " \"description\": \"A single or list of QuantumCircuits.\",\n", + " \"type\": [\n", + " \"array\",\n", + " \"object\"\n", + " ]\n", + " },\n", + " \"expectation_operators\": {\n", + " \"description\": \"One or more expectation values to evaluate.\",\n", + " \"type\": [\n", + " \"string\",\n", + " \"object\",\n", + " \"array\"\n", + " ]\n", + " },\n", + " \"shots\": {\n", + " \"description\": \"Number of shots per circuit.\",\n", + " \"type\": \"integer\"\n", + " },\n", + " \"transpiler_config\": {\n", + " \"description\": \"A collection of kwargs passed to transpile.\",\n", + " \"type\": \"object\"\n", + " },\n", + " \"run_config\": {\n", + " \"description\": \"A collection of kwargs passed to backend.run. Default is False.\",\n", + " \"type\": \"object\",\n", + " \"default\": False\n", + " },\n", + " \"return_stddev\": {\n", + " \"description\": \"Return upper-bound on standard deviation. Default is False.\",\n", + " \"type\": \"boolean\",\n", + " \"default\": False\n", + " },\n", + " \"use_measurement_mitigation\": {\n", + " \"description\": \"Use measurement mitigation to improve results. Default is False.\",\n", + " \"type\": \"boolean\",\n", + " \"default\": False\n", + " }\n", + " },\n", + " \"required\": [\n", + " \"circuits\"\n", + " ]\n", + "}\n", + "\n", + "meta[\"spec\"][\"return_values\"] = {\n", + " \"$schema\": \"https://json-schema.org/draft/2019-09/schema\",\n", + " \"description\": \"A list of expectation values and optionally standard deviations.\",\n", + " \"type\": \"array\"\n", + "}" + ] + }, + { + "cell_type": "markdown", + "id": "09521947", + "metadata": {}, + "source": [ + "## Upload the program\n", + "\n", + "We are now in a position to upload the program. To do so we first uncomment and excute the line `%%writefile sample_expval.py` giving use the `sample_expval.py` file we need to upload. " + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "935a21d6", + "metadata": {}, + "outputs": [], + "source": [ + "from qiskit import IBMQ\n", + "IBMQ.load_account();" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "d93c23d6", + "metadata": {}, + "outputs": [], + "source": [ + "provider = IBMQ.get_provider(group='deployed')" + ] + }, + { + "cell_type": "code", + "execution_count": 276, + "id": "59a3e697", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": "'sample-expval-KLGD4Kbn97'" + }, + "execution_count": 276, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "program_id = provider.runtime.upload_program(data='sample_expval.py', metadata=meta)\n", + "program_id" + ] + }, + { + "cell_type": "markdown", + "id": "58430b3e", + "metadata": {}, + "source": [ + "### Delete program if needed" + ] + }, + { + "cell_type": "code", + "execution_count": 255, + "id": "4ec662be", + "metadata": {}, + "outputs": [], + "source": [ + "#provider.runtime.delete_program(program_id)" + ] + }, + { + "cell_type": "markdown", + "id": "8542a282", + "metadata": {}, + "source": [ + "## Wrapping the runtime program\n", + "\n", + "As always, it is best to wrap the call to the runtime program with a function (or possibly a class) that makes input easier and does some validation." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "c685ef09", + "metadata": {}, + "outputs": [], + "source": [ + "def expectation_value_runner(backend,\n", + " circuits,\n", + " expectation_operators='',\n", + " shots = 8192,\n", + " transpiler_config={},\n", + " run_config={},\n", + " skip_transpilation=False,\n", + " return_stddev=False,\n", + " use_measurement_mitigation=False):\n", + " \n", + " \"\"\"Compute expectation values for a list of operators after\n", + " executing a list of circuits on the target backend.\n", + " \n", + " Parameters:\n", + " backend (Backend or str): Qiskit backend instance or name.\n", + " circuits: (QuantumCircuit or list): A single or list of QuantumCircuits.\n", + " expectation_operators (str or dict or list): Expectation values to evaluate.\n", + " shots (int): Number of shots to take per circuit.\n", + " transpiler_config (dict): A collection of kwargs passed to transpile().\n", + " run_config (dict): A collection of kwargs passed to backend.run().\n", + " return_stddev (bool): Return upper bound on standard devitation,\n", + " default=False. \n", + " skip_transpilation (bool): Skip transpiling of circuits, default=False.\n", + " use_measurement_mitigation (bool): Improve resulting using measurement\n", + " error mitigation, default=False.\n", + " \n", + " Returns:\n", + " array_like: Returns array of expectation values or a list of (expval, stddev)\n", + " pairs if return_stddev=True.\n", + " \"\"\"\n", + " if not isinstance(backend, str):\n", + " backend = backend.name()\n", + " options = {'backend_name': backend}\n", + " \n", + " if isinstance(circuits, list) and len(circuits) != 1:\n", + " if isinstance(expectation_operators, list):\n", + " if len(circuits) != 1 and len(expectation_operators) == 1:\n", + " expectation_operators = expectation_operators*len(circuits)\n", + " elif len(circuits) != len(expectation_operators): \n", + " raise ValueError('Number of circuits must match number of expectation \\\n", + " values if more than one of each')\n", + " inputs = {}\n", + " inputs['circuits'] = circuits\n", + " inputs['expectation_operators'] = expectation_operators\n", + " inputs['shots'] = shots\n", + " inputs['transpiler_config'] = transpiler_config\n", + " inputs['run_config'] = run_config\n", + " inputs['return_stddev'] = return_stddev\n", + " inputs['skip_transpilation'] = skip_transpilation\n", + " inputs['use_measurement_mitigation'] = use_measurement_mitigation\n", + " \n", + " return provider.runtime.run('sample-expval', options=options, inputs=inputs)" + ] + }, + { + "cell_type": "markdown", + "id": "766a8961", + "metadata": {}, + "source": [ + "### Trying it out\n", + "\n", + "Because we made our program public anyone can try it out. Lets do so here with our previously made GHZ state and running on the simulator." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "b9f2a955", + "metadata": {}, + "outputs": [], + "source": [ + "backend = provider.backend.ibmq_qasm_simulator\n", + "\n", + "all_zeros_proj = {'0000': 1}\n", + "all_ones_proj = {'1111': 1}\n", + "job = expectation_value_runner(backend, qc, [all_zeros_proj, all_ones_proj, 'ZZZZ'])" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "beb8550b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([0.50012207, 0.49987793, 1. ])" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "job.result()" + ] + }, + { + "cell_type": "markdown", + "id": "29268312", + "metadata": {}, + "source": [ + "The first two projectors should be nearly $0.50$ as they tell use the probability of being in the all zeros and ones states, respectively, which should be 50/50 for our GHZ state. The final expectation value of `ZZZZ` should be one since this is a GHZ over an even number of qubits. It should go close to zero for an odd number." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "5494d586", + "metadata": {}, + "outputs": [], + "source": [ + "qc2 = QuantumCircuit(3)\n", + "qc2.h(2)\n", + "qc2.cx(2, 1)\n", + "qc2.cx(1, 0)\n", + "qc2.measure_all()" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "2c326f9d", + "metadata": {}, + "outputs": [], + "source": [ + "all_zeros_proj = {'000': 1}\n", + "all_ones_proj = {'111': 1}\n", + "job2 = expectation_value_runner(backend, qc2, [all_zeros_proj, all_ones_proj, 'ZZZ'])" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "58d9a637", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([0.50415039, 0.49584961, 0.00830078])" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "job2.result()" + ] + }, + { + "cell_type": "markdown", + "id": "05ff6e0d", + "metadata": {}, + "source": [ + "## Quantum Volume as an expectation value\n", + "\n", + "Here we formulate QV as an expectation value of a projector onto the heavy-output elements on a distribution. We can then use our expectation value routine to compute whether a given circuit has passed the QV metric.\n", + "\n", + "QV is defined in terms of heavy-ouputs of a distribution. Heavy-outputs are those bit-strings that are those that have probabilities above the median value of the distribution. Below we define the projection operator onto the set of bit-strings that are heavy-outputs for a given distribution." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "778b6a77", + "metadata": {}, + "outputs": [], + "source": [ + "def heavy_projector(qv_probs):\n", + " \"\"\"Forms the projection operator onto the heavy-outputs of a given probability distribution.\n", + "\n", + " Parameters:\n", + " qv_probs (dict): A dictionary of bitstrings and associated probabilities.\n", + "\n", + " Returns:\n", + " dict: Projector onto the heavy-set.\n", + " \"\"\"\n", + " median_prob = np.median(list(qv_probs.values()))\n", + " heavy_strs = {}\n", + " for key, val in qv_probs.items():\n", + " if val > median_prob:\n", + " heavy_strs[key] = 1\n", + " return heavy_strs" + ] + }, + { + "cell_type": "markdown", + "id": "1c05e1b2", + "metadata": {}, + "source": [ + "Now we generate 10 QV circuits as our dataset." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "20fab8af", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "from qiskit.quantum_info import Statevector\n", + "from qiskit.circuit.library import QuantumVolume\n", + "# Generate QV circuits\n", + "N = 10\n", + "qv_circs = [QuantumVolume(5) for _ in range(N)]" + ] + }, + { + "cell_type": "markdown", + "id": "927a0946", + "metadata": {}, + "source": [ + "Next, we have to determine the heavy-set of each circuit from the ideal answer, and then pass this along to our heavy-set projector function that we defined above." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "73d822cc", + "metadata": {}, + "outputs": [], + "source": [ + "ideal_probs = [Statevector.from_instruction(circ).probabilities_dict() for circ in qv_circs]\n", + "heavy_projectors = [heavy_projector(probs) for probs in ideal_probs]" + ] + }, + { + "cell_type": "markdown", + "id": "33559bc5", + "metadata": {}, + "source": [ + "QV circuits have no meaasurements on them so need to add them:" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "bf818575", + "metadata": {}, + "outputs": [], + "source": [ + "circs = [circ.measure_all(inplace=False) for circ in qv_circs]" + ] + }, + { + "cell_type": "markdown", + "id": "dd9579fb", + "metadata": {}, + "source": [ + "With a list of circuits and projection operators we now need only to pass both sets to our above expection value runner targeting the desired backend. We will also set the best transpiler arguments to give us a sporting chance of getting some passing scores." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "8d692921", + "metadata": {}, + "outputs": [], + "source": [ + "backend = provider.backend.ibmq_manila" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "id": "5c169661", + "metadata": {}, + "outputs": [], + "source": [ + "job3 = expectation_value_runner(backend, circs, heavy_projectors,\n", + " transpiler_config={'optimization_level':3, 'layout_method': 'sabre',\n", + " 'routing_method': 'sabre'})" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "id": "98f6efd9", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([0.52392578, 0.60400391, 0.57189941, 0.57897949, 0.7734375 ,\n", + " 0.65844727, 0.56225586, 0.73706055, 0.69030762, 0.61193848])" + ] + }, + "execution_count": 37, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "qv_scores = job3.result()\n", + "qv_scores" + ] + }, + { + "cell_type": "markdown", + "id": "947b5149", + "metadata": {}, + "source": [ + "A passing QV score is one where the value of the heavy-set projector is above $2/3$. So let us see who passed:" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "id": "3f6394d6", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([False, False, False, False, True, False, False, True, True,\n", + " False])" + ] + }, + "execution_count": 38, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "qv_scores > 2/3" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "id": "0a8fe223", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "

This code is a part of Qiskit

© 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.

" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from qiskit.tools.jupyter import *\n", + "%qiskit_copyright" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5c482e31", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/tutorials/sample_expval_program/sample_expval.py b/tutorials/sample_expval_program/sample_expval.py new file mode 100644 index 000000000..72ba1edd6 --- /dev/null +++ b/tutorials/sample_expval_program/sample_expval.py @@ -0,0 +1,91 @@ +import mthree +from qiskit import transpile + +# The entrypoint for our Runtime Program +def main(backend, user_messenger, + circuits, + expectation_operators='', + shots = 8192, + transpiler_config={}, + run_config={}, + skip_transpilation=False, + return_stddev=False, + use_measurement_mitigation=False, + ): + + """Compute expectation values for a list of operators after + executing a list of circuits on the target backend. + + Parameters: + backend (ProgramBackend): Qiskit backend instance. + user_messenger (UserMessenger): Used to communicate with the program user. + circuits: (QuantumCircuit or list): A single list of QuantumCircuits. + expectation_operators (str or dict or list): Expectation values to evaluate. + shots (int): Number of shots to take per circuit. + transpiler_config (dict): A collection of kwargs passed to transpile(). + run_config (dict): A collection of kwargs passed to backend.run(). + skip_transpilation (bool): Skip transpiling of circuits, default=False. + return_stddev (bool): Return upper bound on standard devitation, + default=False. + use_measurement_mitigation (bool): Improve resulting using measurement + error mitigation, default=False. + + Returns: + array_like: Returns array of expectation values or a list of (expval, stddev) + tuples if return_stddev=True. + """ + + # transpiling the circuits using given transpile options + if not skip_transpilation: + trans_circuits = transpile(circuits, backend=backend, + **transpiler_config) + + if not isinstance(trans_circuits, list): + trans_circuits = [trans_circuits] + # If skipping set circuits -> trans_circuits + else: + if not isinstance(circuits, list): + trans_circuits = [circuits] + else: + trans_circuits = circuits + + # If we are given a single circuit but requesting multiple expectation values + # Then set flag to make multiple pointers to same result. + duplicate_results = False + if isinstance(expectation_operators, list): + if len(expectation_operators) and len(trans_circuits) == 1: + duplicate_results = True + + if use_measurement_mitigation: + # Get an the measurement mappings at end of circuits + meas_maps = mthree.utils.final_measurement_mapping(trans_circuits) + # Get an M3 mitigator + mit = mthree.M3Mitigation(backend) + # Calibrate over the set of qubits measured in the transpiled circuits. + mit.cals_from_system(meas_maps) + + # Compute raw results + result = backend.run(trans_circuits, shots=shots, **run_config).result() + raw_counts = result.get_counts() + + # When using measurement mitigation we need to apply the correction and then + # compute the expectation values from the computed quasi-probabilities. + if use_measurement_mitigation: + quasi_dists = mit.apply_correction(raw_counts, meas_maps, + return_mitigation_overhead=return_stddev) + + if duplicate_results: + quasi_dists = mthree.classes.QuasiCollection([quasi_dists]*len(expectation_operators)) + # There are two different calls depending on what we want returned. + if return_stddev: + return quasi_dists.expval_and_stddev(expectation_operators) + return quasi_dists.expval(expectation_operators) + + # If the program didn't return in the mitigation loop above it means + # we are processing the raw_counts data. We do so here using the + # mthree utilities + if duplicate_results: + raw_counts = [raw_counts]*len(expectation_operators) + if return_stddev: + return mthree.utils.expval_and_stddev(raw_counts, expectation_operators) + return mthree.utils.expval(raw_counts, expectation_operators) diff --git a/tutorials/sample_vqe_program/qiskit_runtime_vqe_program.ipynb b/tutorials/sample_vqe_program/qiskit_runtime_vqe_program.ipynb new file mode 100644 index 000000000..4c3ab084a --- /dev/null +++ b/tutorials/sample_vqe_program/qiskit_runtime_vqe_program.ipynb @@ -0,0 +1,1381 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "98d4d396", + "metadata": {}, + "source": [ + "# Creating Custom Programs for Qiskit Runtime\n", + "\n", + "

\n", + "Paul Nation\n", + "

\n", + "

\n", + "IBM Quantum Partners Technical Enablement Team\n", + "

\n", + "\n", + "Here we will demonstrate how to create, upload, and use a custom Program for Qiskit Runtime. As the utility of the Runtime execution engine lies in its ability to execute many quantum circuits with low latencies, this tutorial will show how to create your own Variational Quantum Eigensolver (VQE) program from scratch." + ] + }, + { + "cell_type": "markdown", + "id": "96b86d47", + "metadata": {}, + "source": [ + "## Prerequisites\n", + "\n", + "- You must have Qiskit 0.30+ installed.\n", + "- You must have an IBM Quantum account with the ability to upload a runtime program. You have this ability if you belong to more than one provider." + ] + }, + { + "cell_type": "markdown", + "id": "46817643", + "metadata": {}, + "source": [ + "## Current limitations\n", + "\n", + "The runtime execution engine currently has the following limitations that must be kept in mind:\n", + "\n", + "- The Docker images used by the runtime include only Qiskit and its dependencies, with few exceptions. One exception is the inclusion of the `mthree` measurement mitigation package.\n", + "\n", + "\n", + "- For security reasons, the runtime cannot make internet calls outside of the environment.\n", + "\n", + "\n", + "- Your runtime program name must not contain an underscore`_`, otherwise it will cause an error when you try to execute it.\n", + "\n", + "As Qiskit Runtime matures, these limitations will be removed." + ] + }, + { + "cell_type": "markdown", + "id": "98e4b3ac", + "metadata": {}, + "source": [ + "## Simple VQE\n", + "\n", + "VQE is an hybrid quantum-classical optimization procedure that finds the lowest eigenstate and eigenenergy of a linear system defined by a given Hamiltonian of Pauli operators. For example, consider the following two-qubit Hamiltonian:\n", + "\n", + "\n", + "$$\n", + "H = A X_{1}\\otimes X_{0} + A Y_{1}\\otimes Y_{0} + A Z_{1}\\otimes Z_{0},\n", + "$$\n", + "\n", + "where $A$ is numerical coefficient and the subscripts label the qubits on which the operators act. The zero index being farthest right is the ordering used in Qiskit. The Pauli operators tell us which measurement basis to to use when measuring each of the qubits.\n", + "\n", + "We want to find the ground state (lowest energy state) of this Hamiltonian, and the associated eigenvector. To do this we must start at a given initial state and iteratively vary the parameters that define this state using a classical optimizer, such that the computed energies of subsequent steps are nominally lower than those previously. The parameterized state of the system is defined by an ansatz quantum circuit that should have non-zero support in the direction of the ground state. Because in general we do not know the solution, the choice of ansatz circuit can be highly problem-specific with a form dictated by additional information. For further information about variational algorithms, we point the reader to [Nature Reviews Physics volume 3, 625 (2021)](https://doi.org/10.1038/s42254-021-00348-9).\n", + "\n", + "\n", + "Thus we need at least the following inputs to create our VQE quantum program:\n", + "\n", + "1. A representation of the Hamiltonian that specifies the problem.\n", + "\n", + "\n", + "2. A choice of parameterized ansatz circuit, and the ability to pass configuration options, if any.\n", + "\n", + "\n", + "However, the following are also beneficial inputs that users might want to have:\n", + "\n", + "3. Add the ability to pass an initial state.\n", + "\n", + "\n", + "4. Vary the number of shots that are taken.\n", + "\n", + "\n", + "5. Ability to select which classical optimizer is used, and set configuraton values, if any. \n", + "\n", + "\n", + "6. Ability to turn on and off measurement mitigation.\n" + ] + }, + { + "cell_type": "markdown", + "id": "d15ad0da", + "metadata": {}, + "source": [ + "## Specifying the form of the input values\n", + "\n", + "All inputs to runtime programs must be serializable objects. That is to say, whatever you pass into a runtime program must be able to be converted to JSON format. It is thus beneficial to keep inputs limited to basic data types and structures unless you have experience with custom object serialization, or they are common Qiskit types such as ``QuantumCircuit`` etc that the built-in `RuntimeEncoder` can handle. Fortunately, the VQE program described above can be made out of simple Python components.\n", + "\n", + "First, it is possible to represent any Hamiltonian using a list of values with each containing the numerical coefficeint for each term and the string representation for the Pauli operators. For the above example, the ground state energy with $A=1$ is $-3$ and we can write it as:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "78b7a519", + "metadata": {}, + "outputs": [], + "source": [ + "H = [(1, 'XX'), (1, 'YY'), (1, 'ZZ')]" + ] + }, + { + "cell_type": "markdown", + "id": "fca6d7c5", + "metadata": {}, + "source": [ + "Next we have to provide the ability to specify the parameterized Ansatz circuit. Here we will take advange of the fact that many ansatz circuits are pre-defined in the Qiskit Circuit Library. Examples can be found in the [N-local circuits section](https://qiskit.org/documentation/apidoc/circuit_library.html#n-local-circuits).\n", + "\n", + "We would like the user to be able to select between ansatz options such as: `NLocal`, `TwoLocal`, and `EfficientSU2`. We could have the user pass the whole ansatz circuit to the program; however, in order to reduce the size of the upload we will pass the ansatz by name. In the runtime program, we can take this name and get the class that it corresponds to from the library using, for example, " + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "421f2f51", + "metadata": {}, + "outputs": [], + "source": [ + "import qiskit.circuit.library.n_local as lib_local\n", + "\n", + "ansatz = getattr(lib_local, 'EfficientSU2')" + ] + }, + { + "cell_type": "markdown", + "id": "48c7ebae", + "metadata": {}, + "source": [ + "For the ansatz configuration, we will pass a simple `dict` of values." + ] + }, + { + "cell_type": "markdown", + "id": "a592ac05", + "metadata": {}, + "source": [ + "### Optionals \n", + "\n", + "- If we want to add the ability to pass an initial state, then we will need to add the ability to pass a 1D list/ NumPy array. Because the number of parameters depends on the ansatz and its configuration, the user would have to know what ansatz they are doing ahead of time.\n", + "\n", + "\n", + "- Selecting a number of shots requires simply passing an integer value.\n", + "\n", + "\n", + "- Here we will allow selecting a classical optimizer by name from those in SciPy, and a `dict` of configuration parameters. Note that for execution on an actual system, the noise inherent in today's quantum systems makes having a stochastic optimizer crucial to success. SciPy does not have such a choice, and the one built into Qiskit is wrapped in such a manner as to make it difficult to use elsewhere. As such, here we will use an SPSA optimizer written to match the style of those in SciPy. This function is given in [Appendix A](#Appendix-A)." + ] + }, + { + "cell_type": "markdown", + "id": "8c5964c1", + "metadata": {}, + "source": [ + "- Finally, for measurement error mitigation we can simply pass a boolean (True/False) value." + ] + }, + { + "cell_type": "markdown", + "id": "00df4e79", + "metadata": {}, + "source": [ + "## Main program\n", + "\n", + "We are now in a position to start building our main program. However, before doing so we point out that it makes the code cleaner to make a separate fuction that takes strings of Pauli operators that define our Hamiltonian and convert them to a list of circuits with single-qubit gates that change the measurement basis for each qubit, if needed. This function is given in [Appendix B](#Appendix-B)." + ] + }, + { + "cell_type": "markdown", + "id": "3ddbadd8", + "metadata": {}, + "source": [ + "### Required signature\n", + "\n", + "Every runtime program is defined via the `main` function, and must have the following input signature:\n", + "\n", + "```\n", + "main(backend, user_message, *args, **kwargs)\n", + "```\n", + "\n", + "where `backend` is the backend that the program is to be executed on, and `user_message` is the class by which interim (and possibly final) results are communicated back to the user. After these two items, we add our program-specific arguments and keyword arguments." + ] + }, + { + "cell_type": "markdown", + "id": "28ba84cc", + "metadata": {}, + "source": [ + "### The main VQE program\n", + "\n", + "Here is the main program for our sample VQE. What each element of the function does is written in the comments before the element appears." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "96bb7811", + "metadata": {}, + "outputs": [], + "source": [ + "# Grab functions and modules from dependencies\n", + "import numpy as np\n", + "import scipy.optimize as opt\n", + "from scipy.optimize import OptimizeResult\n", + "import mthree\n", + "\n", + "# Grab functions and modules from Qiskit needed\n", + "from qiskit import QuantumCircuit, transpile\n", + "import qiskit.circuit.library.n_local as lib_local\n", + "\n", + "# The entrypoint for our Runtime Program\n", + "def main(backend, user_messenger,\n", + " hamiltonian,\n", + " ansatz='EfficientSU2',\n", + " ansatz_config={},\n", + " x0=None,\n", + " optimizer='SPSA',\n", + " optimizer_config={'maxiter': 100},\n", + " shots = 8192,\n", + " use_measurement_mitigation=False\n", + " ):\n", + " \n", + " \"\"\"\n", + " The main sample VQE program.\n", + " \n", + " Parameters:\n", + " backend (ProgramBackend): Qiskit backend instance.\n", + " user_messenger (UserMessenger): Used to communicate with the\n", + " program user.\n", + " hamiltonian (list): Hamiltonian whose ground state we want to find.\n", + " ansatz (str): Optional, name of ansatz quantum circuit to use,\n", + " default='EfficientSU2'\n", + " ansatz_config (dict): Optional, configuration parameters for the\n", + " ansatz circuit.\n", + " x0 (array_like): Optional, initial vector of parameters.\n", + " optimizer (str): Optional, string specifying classical optimizer,\n", + " default='SPSA'.\n", + " optimizer_config (dict): Optional, configuration parameters for the\n", + " optimizer.\n", + " shots (int): Optional, number of shots to take per circuit.\n", + " use_measurement_mitigation (bool): Optional, use measurement mitigation,\n", + " default=False.\n", + " \n", + " Returns:\n", + " OptimizeResult: The result in SciPy optimization format. \n", + " \"\"\"\n", + " \n", + " # Split the Hamiltonian into two arrays, one for coefficients, the other for\n", + " # operator strings\n", + " coeffs = np.array([item[0] for item in hamiltonian], dtype=complex)\n", + " op_strings = [item[1] for item in hamiltonian]\n", + " # The number of qubits needed is given by the number of elements in the strings\n", + " # the defiune the Hamiltonian. Here we grab this data from the first element.\n", + " num_qubits = len(op_strings[0])\n", + " \n", + " # We grab the requested ansatz circuit class from the Qiskit circuit library\n", + " # n_local module and configure it using the number of qubits and options\n", + " # passed in the ansatz_config.\n", + " ansatz_instance = getattr(lib_local, ansatz)\n", + " ansatz_circuit = ansatz_instance(num_qubits, **ansatz_config)\n", + " \n", + " # Here we use our convenence function from Appendix B to get measurement circuits\n", + " # with the correct single-qubit rotation gates.\n", + " meas_circs = opstr_to_meas_circ(op_strings)\n", + " \n", + " # When computing the expectation value for the energy, we need to know if we\n", + " # evaluate a Z measurement or and identity measurement. Here we take and X and Y\n", + " # operator in the strings and convert it to a Z since we added the rotations\n", + " # with the meas_circs.\n", + " meas_strings = [string.replace('X', 'Z').replace('Y', 'Z') for string in op_strings]\n", + " \n", + " # Take the ansatz circuits, add the single-qubit measurement basis rotations from\n", + " # meas_circs, and finally append the measurements themselves.\n", + " full_circs = [ansatz_circuit.compose(mcirc).measure_all(inplace=False) for mcirc in meas_circs]\n", + " \n", + " # Get the number of parameters in the ansatz circuit.\n", + " num_params = ansatz_circuit.num_parameters\n", + " \n", + " # Use a given initial state, if any, or do random initial state.\n", + " if x0:\n", + " x0 = np.asarray(x0, dtype=float)\n", + " if x0.shape[0] != num_params:\n", + " raise ValueError('Number of params in x0 ({}) does not match number \\\n", + " of ansatz parameters ({})'. format(x0.shape[0],\n", + " num_params))\n", + " else:\n", + " x0 = 2*np.pi*np.random.rand(num_params)\n", + " \n", + " # Because we are in general targeting a real quantum system, our circuits must be transpiled\n", + " # to match the system topology and, hopefully, optimize them.\n", + " # Here we will set the transpiler to the most optimal settings where 'sabre' layout and\n", + " # routing are used, along with full O3 optimization.\n", + "\n", + " # This works around a bug in Qiskit where Sabre routing fails for simulators (Issue #7098)\n", + " trans_dict = {}\n", + " if not backend.configuration().simulator:\n", + " trans_dict = {'layout_method': 'sabre', 'routing_method': 'sabre'}\n", + " trans_circs = transpile(full_circs, backend, optimization_level=3, **trans_dict)\n", + " \n", + " # If using measurement mitigation we need to find out which physical qubits our transpiled\n", + " # circuits actually measure, construct a mitigation object targeting our backend, and\n", + " # finally calibrate our mitgation by running calibration circuits on the backend.\n", + " if use_measurement_mitigation:\n", + " maps = mthree.utils.final_measurement_mapping(trans_circs)\n", + " mit = mthree.M3Mitigation(backend)\n", + " mit.cals_from_system(maps)\n", + " \n", + " # Here we define a callback function that will stream the optimizer parameter vector\n", + " # back to the user after each iteration. This uses the `user_messenger` object.\n", + " # Here we convert to a list so that the return is user readable locally, but\n", + " # this is not required.\n", + " def callback(xk):\n", + " user_messenger.publish(list(xk))\n", + " \n", + " # This is the primary VQE function executed by the optimizer. This function takes the \n", + " # parameter vector as input and returns the energy evaluated using an ansatz circuit\n", + " # bound with those parameters.\n", + " def vqe_func(params):\n", + " # Attach (bind) parameters in params vector to the transpiled circuits.\n", + " bound_circs = [circ.bind_parameters(params) for circ in trans_circs]\n", + " \n", + " # Submit the job and get the resultant counts back\n", + " counts = backend.run(bound_circs, shots=shots).result().get_counts()\n", + " \n", + " # If using measurement mitigation apply the correction and\n", + " # compute expectation values from the resultant quasiprobabilities\n", + " # using the measurement strings.\n", + " if use_measurement_mitigation:\n", + " quasi_collection = mit.apply_correction(counts, maps)\n", + " expvals = quasi_collection.expval(meas_strings)\n", + " # If not doing any mitigation just compute expectation values\n", + " # from the raw counts using the measurement strings.\n", + " # Since Qiskit does not have such functionality we use the convenence\n", + " # function from the mthree mitigation module.\n", + " else:\n", + " expvals = mthree.utils.expval(counts, meas_strings)\n", + " \n", + " # The energy is computed by simply taking the product of the coefficients\n", + " # and the computed expectation values and summing them. Here we also\n", + " # take just the real part as the coefficients can possibly be complex,\n", + " # but the energy (eigenvalue) of a Hamiltonian is always real.\n", + " energy = np.sum(coeffs*expvals).real\n", + " return energy\n", + " \n", + " # Here is where we actually perform the computation. We begin by seeing what\n", + " # optimization routine the user has requested, eg. SPSA verses SciPy ones,\n", + " # and dispatch to the correct optimizer. The selected optimizer starts at\n", + " # x0 and calls 'vqe_func' everytime the optimizer needs to evaluate the cost\n", + " # function. The result is returned as a SciPy OptimizerResult object.\n", + " # Additionally, after every iteration, we use the 'callback' function to\n", + " # publish the interm results back to the user. This is important to do\n", + " # so that if the Program terminates unexpectedly, the user can start where they\n", + " # left off.\n", + " \n", + " # Since SPSA is not in SciPy need if statement\n", + " if optimizer == 'SPSA':\n", + " res = fmin_spsa(vqe_func, x0, args=(), **optimizer_config,\n", + " callback=callback)\n", + " # All other SciPy optimizers here\n", + " else:\n", + " res = opt.minimize(vqe_func, x0, method=optimizer,\n", + " options=optimizer_config, callback=callback)\n", + " # Return result. OptimizeResult is a subclass of dict.\n", + " return res" + ] + }, + { + "cell_type": "markdown", + "id": "8ab3432a", + "metadata": {}, + "source": [ + "## Local testing\n", + "\n", + "
\n", + "Important: You need to execute the code blocks in Appendices A and B before continuing.\n", + "
\n", + "\n", + "We can test whether our routine works by simply calling the `main` function with a backend instance, a `UserMessenger`, and sample arguments." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "c146a02e", + "metadata": {}, + "outputs": [], + "source": [ + "from qiskit.providers.ibmq.runtime import UserMessenger\n", + "msg = UserMessenger()" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "ea291c28", + "metadata": {}, + "outputs": [], + "source": [ + "# Use the local Aer simulator\n", + "from qiskit import Aer\n", + "backend = Aer.get_backend('qasm_simulator')" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "3f071e0f", + "metadata": { + "scrolled": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[1.419780432710152, 2.3984284215892018, 1.1306533554149105, 1.8357672762510684, 5.414120644000338, 6.107301966755861, -0.013391355872252708, 5.615586607539193, 4.211781149943555, 1.792388243059789, 4.203949657158362, 0.1038271369149637, 2.4220098073658884, 4.617958787629208, 2.9969591661895865, 1.5490655190231735]\n", + "[2.1084925021737537, 3.0871404910528035, 0.4419412859513089, 2.52447934571467, 4.725408574536736, 5.418589897292259, -0.7021034253358543, 6.3042986770027944, 3.523069080479953, 1.1036761735961873, 3.5152375876947604, 0.7925392063785653, 3.11072187682949, 5.30667085709281, 3.685671235653188, 0.8603534495595718]\n", + "[1.7365578685005831, 3.459075124725974, 0.8138759196244794, 2.8964139793878405, 4.353473940863566, 5.046655263619089, -1.0740380590090248, 5.932364043329624, 3.1511344468067826, 1.475610807269358, 3.8871722213679307, 1.1644738400517358, 2.73878724315632, 4.934736223419639, 4.057605869326359, 1.2322880832327423]\n", + "[1.7839871181735734, 3.4116458750529834, 0.766446669951489, 2.84898472971485, 4.306044691190576, 5.094084513292079, -1.0266088093360346, 5.884934793656634, 3.198563696479773, 1.5230400569423481, 3.8397429716949403, 1.1170445903787456, 2.6913579934833294, 4.887306973746649, 4.105035118999349, 1.2797173329057325]\n", + "[1.122687940285629, 4.072945052940928, 1.4277458478394336, 2.1876855518269056, 3.6447455133026314, 5.755383691180024, -1.687907987223979, 6.546233971544579, 2.5372645185918286, 2.1843392348302926, 4.501042149582885, 1.7783437682666903, 3.352657171371274, 4.226007795858704, 4.766334296887294, 0.618418155017788]\n" + ] + }, + { + "data": { + "text/plain": [ + " fun: -1.72705078125\n", + " message: 'Optimization terminated successfully.'\n", + " nfev: 10\n", + " nit: 5\n", + " success: True\n", + " x: array([ 1.12268794, 4.07294505, 1.42774585, 2.18768555, 3.64474551,\n", + " 5.75538369, -1.68790799, 6.54623397, 2.53726452, 2.18433923,\n", + " 4.50104215, 1.77834377, 3.35265717, 4.2260078 , 4.7663343 ,\n", + " 0.61841816])" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Execute the main routine for our simple two-qubit Hamiltonian H, and perform 5 iterations of the SPSA solver.\n", + "main(backend, msg, H, optimizer_config={'maxiter': 5})" + ] + }, + { + "cell_type": "markdown", + "id": "1460bc50", + "metadata": {}, + "source": [ + "Having executed the above, we see that there are 5 parameter arrays returned, one for each callback, along with the final optimization result. The parameter arrays are the interim results, and the `UserMessenger` object prints these values to the cell output. The output itself is the answer we obtained, expressed as a SciPy `OptimizerResult` object." + ] + }, + { + "cell_type": "markdown", + "id": "d5d0151f", + "metadata": {}, + "source": [ + "## Program metadata\n", + "\n", + "Program metadata is essentially the docstring for a runtime program. It describes overall program information such as the program `name`, `description`, and the `max_execution_time` the program is allowed to run, as well as details the inputs and the outputs the program expects. At a bare minimum the values described above are required" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "2b3aa8f2", + "metadata": {}, + "outputs": [], + "source": [ + "meta = {\n", + " \"name\": \"sample-vqe\",\n", + " \"description\": \"A sample VQE program.\",\n", + " \"max_execution_time\": 100000,\n", + " \"spec\": {}\n", + "}" + ] + }, + { + "cell_type": "markdown", + "id": "be4e4dd2", + "metadata": {}, + "source": [ + "It is important to set the `max_execution_time` high enough so that your program does not get terminated unexpectedly. Additionally, one should make sure that interim results are sent back to the user so that, if something does happen, the user can start where they left off.\n", + "\n", + "It is, however, good form to detail the parameters and return types, as well as interim results. That being said, if making a runtime intended to be used by others, this information would also likely be mirrored in the signature of a function or class that the user would interact with directly; end users should not directly call runtime programs. We will see why below. Nevertheless, let us add to our metadata. First, the `parameters` section details the inputs the user is able to pass:" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "d10c1b1a", + "metadata": {}, + "outputs": [], + "source": [ + "meta[\"spec\"][\"parameters\"] = {\n", + " \"$schema\": \"https://json-schema.org/draft/2019-09/schema\",\n", + " \"properties\": {\n", + " \"hamiltonian\": {\n", + " \"description\": \"Hamiltonian whose ground state we want to find.\", \n", + " \"type\": \"array\"\n", + " },\n", + " \"ansatz\": {\n", + " \"description\": \"Name of ansatz quantum circuit to use, default='EfficientSU2'\",\n", + " \"type\": \"string\",\n", + " \"default\": \"EfficientSU2\"\n", + " },\n", + " \"ansatz_config\": {\n", + " \"description\": \"Configuration parameters for the ansatz circuit.\",\n", + " \"type\": \"object\"\n", + " },\n", + " \"optimizer\": {\n", + " \"description\": \"Classical optimizer to use, default='SPSA'.\",\n", + " \"type\": \"string\",\n", + " \"default\": \"SPSA\"\n", + " },\n", + " \"x0\": {\n", + " \"description\": \"Initial vector of parameters. This is a numpy array.\", \n", + " \"type\": \"array\"\n", + " },\n", + " \"optimizer_config\": {\n", + " \"description\": \"Configuration parameters for the optimizer.\", \n", + " \"type\": \"object\"\n", + " },\n", + " \"shots\": {\n", + " \"description\": \"The number of shots used for each circuit evaluation.\",\n", + " \"type\": \"integer\"\n", + " },\n", + " \"use_measurement_mitigation\": {\n", + " \"description\": \"Use measurement mitigation, default=False.\",\n", + " \"type\": \"boolean\",\n", + " \"default\": False\n", + " }\n", + " },\n", + " \"required\": [\n", + " \"hamiltonian\"\n", + " ]\n", + "}" + ] + }, + { + "cell_type": "markdown", + "id": "0ee9bb79", + "metadata": {}, + "source": [ + "Next, the `return_values` section tells about the return types:" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "404e6d1b", + "metadata": {}, + "outputs": [], + "source": [ + "meta[\"spec\"][\"return_values\"] = {\n", + " \"$schema\": \"https://json-schema.org/draft/2019-09/schema\",\n", + " \"description\": \"Final result in SciPy optimizer format\",\n", + " \"type\": \"object\"\n", + "}" + ] + }, + { + "cell_type": "markdown", + "id": "affde208", + "metadata": {}, + "source": [ + "and finally let us specify what comes back when an interim result is returned:" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "99a56745", + "metadata": {}, + "outputs": [], + "source": [ + "meta[\"spec\"][\"interim_results\"] = {\n", + " \"$schema\": \"https://json-schema.org/draft/2019-09/schema\",\n", + " \"description\": \"Parameter vector at current optimization step. This is a numpy array.\", \n", + " \"type\": \"array\"\n", + "}" + ] + }, + { + "cell_type": "markdown", + "id": "c55bbf04", + "metadata": {}, + "source": [ + "## Uploading the program\n", + "\n", + "We now have all the ingredients needed to upload our program. To do so we need to collect all of our code in one file, here called `sample_vqe.py` for uploading. This limitation will be removed in later versions of Qiskit Runtime. Alternatively, if the entire code is contained within a single Jupyter notebook cell, this can be done using the magic function\n", + "\n", + "```\n", + "%%writefile my_program.py\n", + "```\n", + "\n", + "To actually upload the program we need to get a Provider from our IBM Quantum account:" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "9166cb5a", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from qiskit import IBMQ\n", + "IBMQ.load_account()" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "c2da132b", + "metadata": {}, + "outputs": [], + "source": [ + "provider = IBMQ.get_provider(group='deployed')" + ] + }, + { + "cell_type": "markdown", + "id": "cf539336", + "metadata": {}, + "source": [ + "### Program upload\n", + "\n", + "The call to `program_upload` takes the target Python file as `data` and the metadata as inputs." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "46ce62a5", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": "'sample-vqe-G3YBjmvlPr'" + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "program_id = provider.runtime.upload_program(data='sample_vqe.py', metadata=meta)\n", + "program_id" + ] + }, + { + "cell_type": "markdown", + "id": "0d73dc6e", + "metadata": {}, + "source": [ + "Here the `upload_program()` method returns a `program_id`, which is how you should reference your program." + ] + }, + { + "cell_type": "markdown", + "id": "375651ac", + "metadata": {}, + "source": [ + "### Program information\n", + "\n", + "We can query the program for information and see that our metadata is correctly being attached:" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "0e08a4d0", + "metadata": {}, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": "sample-vqe-G3YBjmvlPr:\n Name: sample-vqe\n Description: A sample VQE program.\n Creation date: 2021-11-10T17:10:18.903742Z\n Update date: 2021-11-10T17:10:18.903742Z\n Max execution time: 100000\n Input parameters:\n Properties:\n - ansatz:\n Default: EfficientSU2\n Description: Name of ansatz quantum circuit to use, default='EfficientSU2'\n Type: string\n Required: False\n - ansatz_config:\n Description: Configuration parameters for the ansatz circuit.\n Type: object\n Required: False\n - hamiltonian:\n Description: Hamiltonian whose ground state we want to find.\n Type: array\n Required: True\n - optimizer:\n Default: SPSA\n Description: Classical optimizer to use, default='SPSA'.\n Type: string\n Required: False\n - optimizer_config:\n Description: Configuration parameters for the optimizer.\n Type: object\n Required: False\n - shots:\n Description: The number of shots used for each circuit evaluation.\n Type: integer\n Required: False\n - use_measurement_mitigation:\n Default: False\n Description: Use measurement mitigation, default=False.\n Type: boolean\n Required: False\n - x0:\n Description: Initial vector of parameters. This is a numpy array.\n Type: array\n Required: False\n Interim results:\n Description: Parameter vector at current optimization step. This is a numpy array.\n Type: array\n Returns:\n Description: Final result in SciPy optimizer format\n Type: object\n" + } + ], + "source": [ + "prog = provider.runtime.program(program_id)\n", + "print(prog)" + ] + }, + { + "cell_type": "markdown", + "id": "70a9e564", + "metadata": {}, + "source": [ + "### Deleting a program\n", + "\n", + "If you make a mistake and need to delete and/or re-upload the program, you can run the following, passing the `program_id`:" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "ec56e36b", + "metadata": {}, + "outputs": [], + "source": [ + "#provider.runtime.delete_program(program_id)" + ] + }, + { + "cell_type": "markdown", + "id": "4025eb01", + "metadata": {}, + "source": [ + "## Running the program\n", + "\n", + "### Specify parameters\n", + "\n", + "To run the program we need to specify the `options` that are used in the runtime environment (not the program variables). At present, only the `backend_name` is required." + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "2efca1b3", + "metadata": {}, + "outputs": [], + "source": [ + "backend = provider.backend.ibmq_qasm_simulator\n", + "options = {'backend_name': backend.name()}" + ] + }, + { + "cell_type": "markdown", + "id": "c8b0b300", + "metadata": {}, + "source": [ + "The `inputs` dictionary is used to pass arguments to the `main` function itself. For example:" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "381794fa", + "metadata": {}, + "outputs": [], + "source": [ + "inputs = {}\n", + "inputs['hamiltonian'] = H\n", + "inputs['optimizer_config']={'maxiter': 10}" + ] + }, + { + "cell_type": "markdown", + "id": "8443e1ae", + "metadata": {}, + "source": [ + "### Execute the program\n", + "\n", + "We now can execute the program and grab the result." + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "3b419193", + "metadata": {}, + "outputs": [], + "source": [ + "job = provider.runtime.run(program_id, options=options, inputs=inputs)" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "0addcac0", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'fun': -1.93994140625,\n", + " 'x': array([-1.28984461, 3.73974929, 3.52327612, 1.74979783, 3.13519544,\n", + " 2.43577395, 1.30425595, 0.04847941, 6.17766827, 1.92879213,\n", + " 1.95707213, 2.8097762 , 1.95108352, 1.20067124, 7.01868106,\n", + " 4.36507161]),\n", + " 'nit': 10,\n", + " 'nfev': 20,\n", + " 'message': 'Optimization terminated successfully.',\n", + " 'success': True}" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "job.result()" + ] + }, + { + "cell_type": "markdown", + "id": "9e6e0ec0", + "metadata": {}, + "source": [ + "A few things need to be pointed out. First, we did not get back any interim results, and second, the return object is a plain dictionary. This is because we did not listen for the return results, and we did not tell the job how to format the return result." + ] + }, + { + "cell_type": "markdown", + "id": "917857da", + "metadata": {}, + "source": [ + "### Listening for interim results\n", + "\n", + "To listen for interm results we need to pass a callback function to `provider.runtime.run` that stores the results. The callback takes two arguments `job_id` and the returned data:" + ] + }, + { + "cell_type": "code", + "execution_count": 70, + "id": "358bdc84", + "metadata": {}, + "outputs": [], + "source": [ + "interm_results = []\n", + "def vqe_callback(job_id, data):\n", + " interm_results.append(data)" + ] + }, + { + "cell_type": "markdown", + "id": "9ed2dabd", + "metadata": {}, + "source": [ + "Executing again we get:" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "34b61c28", + "metadata": {}, + "outputs": [], + "source": [ + "job2 = provider.runtime.run(program_id, options=options, inputs=inputs, callback=vqe_callback)" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "8d331f69", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'fun': -2.635986328125,\n", + " 'x': array([ 1.39625003, 3.10967996, 2.46291361, -0.09150619, 1.89013366,\n", + " 0.48872864, 5.60656903, 1.12770301, 4.04603538, 2.85551118,\n", + " 0.45677689, 3.46054286, 4.10740117, 4.163728 , 1.53949656,\n", + " 3.46634995]),\n", + " 'nit': 10,\n", + " 'nfev': 20,\n", + " 'message': 'Optimization terminated successfully.',\n", + " 'success': True}" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "job2.result()" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "e83bf7ba", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[[1.1839280526666394, 2.391820224610454, 2.7491281736833244, 0.5771768054969294, 2.349087960882593, 0.20251406828095217, 5.3527505036344865, 1.80726551800796, 2.8686317344166947, 2.4545878612072003, -0.04047464122825306, 4.2780676963333795, 3.27599724292225, 3.5527489679560844, 2.1472927005219273, 3.1637626657075555], [1.1855194978035488, 2.3902287794735444, 2.750719618820234, 0.5755853603600198, 2.3506794060195024, 0.20092262314404263, 5.351159058497577, 1.8088569631448694, 2.870223179553604, 2.452996416070291, -0.04206608636516258, 4.27647625119647, 3.2775886880591596, 3.554340413092994, 2.148884145658837, 3.165354110844465], [1.0411904999135912, 2.534557777363502, 2.8950486167101914, 0.7199143582499773, 2.206350408129545, 0.05659362525408518, 5.206830060607619, 1.664527965254912, 3.0145521774435617, 2.5973254139602484, 0.10226291152479487, 4.420805249086427, 3.133259690169202, 3.6986694109829514, 2.004555147768879, 3.0210251129545074], [1.005580093753927, 2.5701681835231662, 2.9306590228698557, 0.7555247644096416, 2.241960814289209, 0.020983219094420913, 5.242440466767284, 1.7001383714145764, 3.050162583603226, 2.561715007800584, 0.13787331768445915, 4.456415655246091, 3.0976492840095378, 3.663059004823287, 2.0401655539285435, 3.0566355191141716], [1.07047876838977, 2.6350668581590093, 2.8657603482340126, 0.8204234390454845, 2.177062139653366, 0.08588189373026392, 5.307339141403126, 1.6352396967787333, 2.985263908967383, 2.496816333164741, 0.20277199232030216, 4.521314329881934, 3.162547958645381, 3.7279576794591303, 1.9752668792927004, 2.9917368444783285], [1.3994411335364108, 2.96402922330565, 3.1947227133806533, 0.4914610738988439, 2.5060245048000067, -0.2430804714163767, 5.636301506549767, 1.3062773316320926, 3.3142262741140236, 2.8257786983113817, -0.12619037282633846, 4.192351964735293, 3.4915103237920215, 3.3989953143124896, 2.304229244439341, 3.3206992096249692], [1.325020213130704, 3.0384501437113567, 3.1203017929749466, 0.5658819943045507, 2.5804454252057134, -0.16865955101066996, 5.710722426955474, 1.231856411226386, 3.3886471945197303, 2.751357777905675, -0.2006112932320452, 4.117931044329586, 3.417089403386315, 3.4734162347181963, 2.2298083240336344, 3.395120130030676], [1.031941029864989, 2.7453709604456416, 2.8272226097092314, 0.2728028110388356, 2.2873662419399983, 0.12441963225504513, 6.003801610221189, 1.524935594492101, 3.6817263777854454, 2.45827859463996, 0.09246789003366987, 3.8248518610638707, 3.71016858665203, 3.7664954179839114, 1.9367291407679192, 3.102040946764961], [1.4127118235825624, 3.126141754163215, 2.446451815991658, -0.10796798267873797, 1.9065954482224248, 0.5051904259726187, 5.623030816503616, 1.1441648007745275, 4.062497171503019, 2.8390493883575334, 0.47323868375124345, 3.444081067346297, 4.090939380369604, 4.147266211701485, 1.5559583470503457, 3.4828117404825343], [1.3962500340466297, 3.1096799646272824, 2.4629136055275906, -0.09150619314280523, 1.890133658686492, 0.4887286364366859, 5.606569026967683, 1.1277030112385948, 4.046035381967086, 2.855511177893466, 0.4567768942153107, 3.46054285688223, 4.107401169905537, 4.163728001237418, 1.539496557514413, 3.4663499509466016]]\n" + ] + } + ], + "source": [ + "print(interm_results)" + ] + }, + { + "cell_type": "markdown", + "id": "43f69e8a", + "metadata": {}, + "source": [ + "### Formatting the returned results\n", + "\n", + "In order to format the return results into the desired format, we need to specify a decoder. This decoder must have a `decode` method that gets called to do the actual conversion. In our case `OptimizeResult` is a simple sub-class of `dict` so the formatting is simple." + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "ccdc7af3", + "metadata": {}, + "outputs": [], + "source": [ + "from qiskit.providers.ibmq.runtime import ResultDecoder\n", + "from scipy.optimize import OptimizeResult\n", + "\n", + "class VQEResultDecoder(ResultDecoder):\n", + " @classmethod\n", + " def decode(cls, data):\n", + " data = super().decode(data) # This is required to preformat the data returned.\n", + " return OptimizeResult(data)" + ] + }, + { + "cell_type": "markdown", + "id": "5e0f0aae", + "metadata": {}, + "source": [ + "We can then use this when returning the job result:" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "951eed95", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + " fun: -0.645751953125\n", + " message: 'Optimization terminated successfully.'\n", + " nfev: 20\n", + " nit: 10\n", + " success: True\n", + " x: array([ 5.72140052, 2.29687026, 4.13837683, 3.22216958, 4.76184762,\n", + " 1.20943004, 5.74244574, 2.22665936, 4.34308411, 3.8390838 ,\n", + " -0.50949471, 2.15587397, 3.19045035, 5.82751179, 1.95972168,\n", + " 3.75821819])" + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "job3 = provider.runtime.run(program_id, options=options, inputs=inputs)\n", + "job3.result(decoder=VQEResultDecoder)" + ] + }, + { + "cell_type": "markdown", + "id": "5ca7d039", + "metadata": {}, + "source": [ + "## Simplifying program execution with wrapping functions\n", + "\n", + "While runtime programs are powerful and flexible, they are not the most friendly things to interact with. Therefore, if your program is intended to be used by others, it is best to make wrapper functions and/or classes that simplify the user experience. Moreover, such wrappers allow for validation of user inputs on the client side, which can quickly find errors that would otherwise be raised later during the execution process - something that might have taken hours waiting in queue to get to.\n", + "\n", + "Here we will make two helper routines. First, a job wrapper that allows us to attach and retrieve the interim results directly from the job object itself, as well as decodes for us so that the end user need not worry about formatting the results themselves." + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "id": "c3460583", + "metadata": {}, + "outputs": [], + "source": [ + "class RuntimeJobWrapper():\n", + " \"\"\"A simple Job wrapper that attaches interm results directly to the job object itself\n", + " in the `interm_results attribute` via the `_callback` function.\n", + " \"\"\"\n", + " def __init__(self):\n", + " self._job = None\n", + " self._decoder = VQEResultDecoder\n", + " self.interm_results = []\n", + " \n", + " def _callback(self, job_id, xk):\n", + " \"\"\"The callback function that attaches interm results:\n", + " \n", + " Parameters:\n", + " job_id (str): The job ID.\n", + " xk (array_like): A list or NumPy array to attach.\n", + " \"\"\"\n", + " self.interm_results.append(xk)\n", + " \n", + " def __getattr__(self, attr):\n", + " if attr == 'result':\n", + " return self.result\n", + " else:\n", + " if attr in dir(self._job):\n", + " return getattr(self._job, attr)\n", + " raise AttributeError(\"Class does not have {}.\".format(attr))\n", + " \n", + " def result(self):\n", + " \"\"\"Get the result of the job as a SciPy OptimizerResult object.\n", + " \n", + " This blocks until job is done, cancelled, or errors.\n", + " \n", + " Returns:\n", + " OptimizerResult: A SciPy optimizer result object.\n", + " \"\"\"\n", + " return self._job.result(decoder=self._decoder)" + ] + }, + { + "cell_type": "markdown", + "id": "0f336b6d", + "metadata": {}, + "source": [ + "Next, we create the actual function we want users to call to execute our program. To this function we will add a series of simple validation checks (not all checks will be done for simplicity), as well as use the job wrapper defined above to simply the output." + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "id": "aa386027", + "metadata": {}, + "outputs": [], + "source": [ + "import qiskit.circuit.library.n_local as lib_local\n", + "\n", + "def vqe_runner(backend, hamiltonian,\n", + " ansatz='EfficientSU2', ansatz_config={},\n", + " x0=None, optimizer='SPSA',\n", + " optimizer_config={'maxiter': 100},\n", + " shots = 8192,\n", + " use_measurement_mitigation=False):\n", + " \n", + " \"\"\"Routine that executes a given VQE problem via the sample-vqe program on the target backend.\n", + " \n", + " Parameters:\n", + " backend (ProgramBackend): Qiskit backend instance.\n", + " hamiltonian (list): Hamiltonian whose ground state we want to find.\n", + " ansatz (str): Optional, name of ansatz quantum circuit to use, default='EfficientSU2'\n", + " ansatz_config (dict): Optional, configuration parameters for the ansatz circuit.\n", + " x0 (array_like): Optional, initial vector of parameters.\n", + " optimizer (str): Optional, string specifying classical optimizer, default='SPSA'.\n", + " optimizer_config (dict): Optional, configuration parameters for the optimizer.\n", + " shots (int): Optional, number of shots to take per circuit.\n", + " use_measurement_mitigation (bool): Optional, use measurement mitigation, default=False.\n", + " \n", + " Returns:\n", + " OptimizeResult: The result in SciPy optimization format. \n", + " \"\"\"\n", + " options = {'backend_name': backend.name()}\n", + " \n", + " inputs = {}\n", + " \n", + " # Validate Hamiltonian is correct\n", + " num_qubits = len(H[0][1])\n", + " for idx, ham in enumerate(hamiltonian):\n", + " if len(ham[1]) != num_qubits:\n", + " raise ValueError('Number of qubits in Hamiltonian term {} does not match {}'.format(idx,\n", + " num_qubits))\n", + " inputs['hamiltonian'] = hamiltonian\n", + " \n", + " # Validate ansatz is in the module\n", + " ansatz_circ = getattr(lib_local, ansatz, None)\n", + " if not ansatz_circ:\n", + " raise ValueError('Ansatz {} not in n_local circuit library.'.format(ansatz))\n", + " \n", + " inputs['ansatz'] = ansatz\n", + " inputs['ansatz_config'] = ansatz_config\n", + " \n", + " # If given x0, validate its length against num_params in ansatz:\n", + " if x0:\n", + " x0 = np.asarray(x0)\n", + " ansatz_circ = ansatz_circ(num_qubits, **ansatz_config)\n", + " num_params = ansatz_circ.num_parameters\n", + " if x0.shape[0] != num_params:\n", + " raise ValueError('Length of x0 {} does not match number of params in ansatz {}'.format(x0.shape[0],\n", + " num_params))\n", + " inputs['x0'] = x0\n", + " \n", + " # Set the rest of the inputs\n", + " inputs['optimizer'] = optimizer\n", + " inputs['optimizer_config'] = optimizer_config\n", + " inputs['shots'] = shots\n", + " inputs['use_measurement_mitigation'] = use_measurement_mitigation\n", + " \n", + " rt_job = RuntimeJobWrapper()\n", + " job = provider.runtime.run(program_id, options=options, inputs=inputs, callback=rt_job._callback)\n", + " rt_job._job = job\n", + " \n", + " return rt_job\n", + " " + ] + }, + { + "cell_type": "markdown", + "id": "d4c8982f", + "metadata": {}, + "source": [ + "We can now execute our runtime program via this runner function:" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "id": "8e81851d", + "metadata": {}, + "outputs": [], + "source": [ + "job4 = vqe_runner(backend, H, optimizer_config={'maxiter': 15})" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "id": "9bf5499c", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + " fun: -1.349853515625\n", + " message: 'Optimization terminated successfully.'\n", + " nfev: 30\n", + " nit: 15\n", + " success: True\n", + " x: array([0.09925502, 1.40473727, 1.61291267, 3.45519813, 2.65167136,\n", + " 4.4163485 , 1.98523376, 5.94459488, 6.46103911, 1.76878845,\n", + " 1.96124064, 3.31830748, 2.06192779, 4.28293342, 3.2448137 ,\n", + " 1.63457609])" + ] + }, + "execution_count": 36, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "job4.result()" + ] + }, + { + "cell_type": "markdown", + "id": "ef281bc5", + "metadata": {}, + "source": [ + "The interim results are now attached to the job `interm_results` attribute and, as expected, we see that the length matches the number of iterations performed." + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "id": "958dbc84", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "15" + ] + }, + "execution_count": 37, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "len(job4.interm_results)" + ] + }, + { + "cell_type": "markdown", + "id": "dd78b903", + "metadata": {}, + "source": [ + "## Conclusion\n", + "\n", + "We have demonstrated how to create, upload, and use a custom Qiskit Runtime by creating our own VQE solver from scratch. This tutorial was meant to touch upon every aspect of the process for a real-world example. Within the current limitations of the runtime environment, this example should enable readers to develop their own single-file runtime program. This program is also a good starting point for exploring additional flavors of VQE runtime. For example, it is straightforward to vary the number of shots per iteration, increasing shots as the number of iterations increases. Those looking to go deeper can consider implimenting an [adaptive VQE](https://doi.org/10.1038/s41467-019-10988-2), where the ansatz is not fixed at initialization." + ] + }, + { + "cell_type": "markdown", + "id": "0a7a50d4", + "metadata": {}, + "source": [ + "## Appendix A\n", + "\n", + "Here we code a simple simultaneous perturbation stochastic approximation (SPSA) optimizer for use on noisy quantum systems. Most optimizers do not handle fluctuating cost functions well, so this is a needed addition for executing on real quantum hardware." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "9d8788af", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "from scipy.optimize import OptimizeResult\n", + "\n", + "def fmin_spsa(func, x0, args=(), maxiter=100,\n", + " a=1.0, alpha=0.602, c=1.0, gamma=0.101,\n", + " callback=None):\n", + " \"\"\"\n", + " Minimization of scalar function of one or more variables using simultaneous\n", + " perturbation stochastic approximation (SPSA).\n", + "\n", + " Parameters:\n", + " func (callable): The objective function to be minimized.\n", + "\n", + " ``fun(x, *args) -> float``\n", + "\n", + " where x is an 1-D array with shape (n,) and args is a\n", + " tuple of the fixed parameters needed to completely \n", + " specify the function.\n", + "\n", + " x0 (ndarray): Initial guess. Array of real elements of size (n,), \n", + " where ‘n’ is the number of independent variables.\n", + " \n", + " maxiter (int): Maximum number of iterations. The number of function\n", + " evaluations is twice as many. Optional.\n", + "\n", + " a (float): SPSA gradient scaling parameter. Optional.\n", + "\n", + " alpha (float): SPSA gradient scaling exponent. Optional.\n", + "\n", + " c (float): SPSA step size scaling parameter. Optional.\n", + " \n", + " gamma (float): SPSA step size scaling exponent. Optional.\n", + "\n", + " callback (callable): Function that accepts the current parameter vector\n", + " as input.\n", + "\n", + " Returns:\n", + " OptimizeResult: Solution in SciPy Optimization format.\n", + "\n", + " Notes:\n", + " See the `SPSA homepage `_ for usage and\n", + " additional extentions to the basic version implimented here.\n", + " \"\"\"\n", + " A = 0.01 * maxiter\n", + " x0 = np.asarray(x0)\n", + " x = x0\n", + "\n", + " for kk in range(maxiter):\n", + " ak = a*(kk+1.0+A)**-alpha\n", + " ck = c*(kk+1.0)**-gamma\n", + " # Bernoulli distribution for randoms\n", + " deltak = 2*np.random.randint(2, size=x.shape[0])-1\n", + " grad = (func(x + ck*deltak, *args) - func(x - ck*deltak, *args))/(2*ck*deltak)\n", + " x -= ak*grad\n", + " \n", + " if callback is not None:\n", + " callback(x)\n", + "\n", + " return OptimizeResult(fun=func(x, *args), x=x, nit=maxiter, nfev=2*maxiter, \n", + " message='Optimization terminated successfully.',\n", + " success=True)" + ] + }, + { + "cell_type": "markdown", + "id": "40b4d8c2", + "metadata": {}, + "source": [ + "## Appendix B\n", + "\n", + "This is a helper function that converts the Pauli operators in the strings that define the Hamiltonian operators into the appropriate measurements at the end of the circuits. For $X$ operators this involves adding an $H$ gate to the qubits to be measured, whereas a $Y$ operator needs $S^{+}$ followed by a $H$. Other choices of Pauli operators require no additional gates prior to measurement." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "15654ed8", + "metadata": {}, + "outputs": [], + "source": [ + "def opstr_to_meas_circ(op_str):\n", + " \"\"\"Takes a list of operator strings and makes circuit with the correct post-rotations for measurements.\n", + "\n", + " Parameters:\n", + " op_str (list): List of strings representing the operators needed for measurements.\n", + "\n", + " Returns:\n", + " list: List of circuits for measurement post-rotations\n", + " \"\"\"\n", + " num_qubits = len(op_str[0])\n", + " circs = []\n", + " for op in op_str:\n", + " qc = QuantumCircuit(num_qubits)\n", + " for idx, item in enumerate(op):\n", + " if item == 'X':\n", + " qc.h(num_qubits-idx-1)\n", + " elif item == 'Y':\n", + " qc.sdg(num_qubits-idx-1)\n", + " qc.h(num_qubits-idx-1)\n", + " circs.append(qc)\n", + " return circs" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "715a3fec", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "

This code is a part of Qiskit

© 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.

" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from qiskit.tools.jupyter import *\n", + "%qiskit_copyright" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f8141d8a", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/tutorials/sample_vqe_program/sample_vqe.py b/tutorials/sample_vqe_program/sample_vqe.py new file mode 100644 index 000000000..684eba51f --- /dev/null +++ b/tutorials/sample_vqe_program/sample_vqe.py @@ -0,0 +1,251 @@ +# This code is part of qiskit-runtime. +# +# (C) Copyright IBM 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. + +# Grab functions and modules from dependencies +import numpy as np +import scipy.optimize as opt +from scipy.optimize import OptimizeResult +import mthree + +# Grab functions and modules from Qiskit needed +from qiskit import QuantumCircuit, transpile +import qiskit.circuit.library.n_local as lib_local + + +# The entrypoint for our Runtime Program +def main(backend, user_messenger, + hamiltonian, + ansatz='EfficientSU2', + ansatz_config={}, + x0=None, + optimizer='SPSA', + optimizer_config={'maxiter': 100}, + shots = 8192, + use_measurement_mitigation=False + ): + + """ + The main sample VQE program. + + Parameters: + backend (ProgramBackend): Qiskit backend instance. + user_messenger (UserMessenger): Used to communicate with the program user. + hamiltonian (list): Hamiltonian whose ground state we want to find. + ansatz (str): Optional, name of ansatz quantum circuit to use, default='EfficientSU2' + ansatz_config (dict): Optional, configuration parameters for the ansatz circuit. + x0 (array_like): Optional, initial vector of parameters. + optimizer (str): Optional, string specifying classical optimizer, default='SPSA'. + optimizer_config (dict): Optional, configuration parameters for the optimizer. + shots (int): Optional, number of shots to take per circuit. + use_measurement_mitigation (bool): Optional, use measurement mitigation, default=False. + + Returns: + OptimizeResult: The result in SciPy optimization format. + """ + + # Split the Hamiltonian into two arrays, one for coefficients, the other for + # operator strings + coeffs = np.array([item[0] for item in hamiltonian], dtype=complex) + op_strings = [item[1] for item in hamiltonian] + # The number of qubits needed is given by the number of elements in the strings + # the defiune the Hamiltonian. Here we grab this data from the first element. + num_qubits = len(op_strings[0]) + + # We grab the requested ansatz circuit class from the Qiskit circuit library + # n_local module and configure it using the number of qubits and options + # passed in the ansatz_config. + ansatz_instance = getattr(lib_local, ansatz) + ansatz_circuit = ansatz_instance(num_qubits, **ansatz_config) + + # Here we use our convenence function from Appendix B to get measurement circuits + # with the correct single-qubit rotation gates. + meas_circs = opstr_to_meas_circ(op_strings) + + # When computing the expectation value for the energy, we need to know if we + # evaluate a Z measurement or and identity measurement. Here we take and X and Y + # operator in the strings and convert it to a Z since we added the rotations + # with the meas_circs. + meas_strings = [string.replace('X', 'Z').replace('Y', 'Z') for string in op_strings] + + # Take the ansatz circuits, add the single-qubit measurement basis rotations from + # meas_circs, and finally append the measurements themselves. + full_circs = [ansatz_circuit.compose(mcirc).measure_all(inplace=False) for mcirc in meas_circs] + + # Get the number of parameters in the ansatz circuit. + num_params = ansatz_circuit.num_parameters + + # Use a given initial state, if any, or do random initial state. + if x0: + x0 = np.asarray(x0, dtype=float) + if x0.shape[0] != num_params: + raise ValueError('Number of params in x0 ({}) does not match number of ansatz parameters ({})'. format( + x0.shape[0], num_params)) + else: + x0 = 2*np.pi*np.random.rand(num_params) + + # Because we are in general targeting a real quantum system, our circuits must be transpiled + # to match the system topology and, hopefully, optimize them. + # Here we will set the transpiler to the most optimal settings where 'sabre' layout and + # routing are used, along with full O3 optimization. + + # This works around a bug in Qiskit where Sabre routing fails for simulators (Issue #7098) + trans_dict = {} + if not backend.configuration().simulator: + trans_dict = {'layout_method': 'sabre', 'routing_method': 'sabre'} + trans_circs = transpile(full_circs, backend, optimization_level=3, **trans_dict) + + # If using measurement mitigation we need to find out which physical qubits our transpiled + # circuits actually measure, construct a mitigation object targeting our backend, and + # finally calibrate our mitgation by running calibration circuits on the backend. + if use_measurement_mitigation: + maps = mthree.utils.final_measurement_mapping(trans_circs) + mit = mthree.M3Mitigation(backend) + mit.cals_from_system(maps) + + # Here we define a callback function that will stream the optimizer parameter vector + # back to the user after each iteration. This uses the `user_messenger` object. + # Here we convert to a list so that the return is user readable locally, but + # this is not required. + def callback(xk): + user_messenger.publish(list(xk)) + + # This is the primary VQE function executed by the optimizer. This function takes the + # parameter vector as input and returns the energy evaluated using an ansatz circuit + # bound with those parameters. + def vqe_func(params): + # Attach (bind) parameters in params vector to the transpiled circuits. + bound_circs = [circ.bind_parameters(params) for circ in trans_circs] + + # Submit the job and get the resultant counts back + counts = backend.run(bound_circs, shots=shots).result().get_counts() + + # If using measurement mitigation apply the correction and + # compute expectation values from the resultant quasiprobabilities + # using the measurement strings. + if use_measurement_mitigation: + quasi_collection = mit.apply_correction(counts, maps) + expvals = quasi_collection.expval(meas_strings) + # If not doing any mitigation just compute expectation values + # from the raw counts using the measurement strings. + # Since Qiskit does not have such functionality we use the convenence + # function from the mthree mitigation module. + else: + expvals = mthree.utils.expval(counts, meas_strings) + + # The energy is computed by simply taking the product of the coefficients + # and the computed expectation values and summing them. Here we also + # take just the real part as the coefficients can possibly be complex, + # but the energy (eigenvalue) of a Hamiltonian is always real. + energy = np.sum(coeffs*expvals).real + return energy + + # Here is where we actually perform the computation. We begin by seeing what + # optimization routine the user has requested, eg. SPSA verses SciPy ones, + # and dispatch to the correct optimizer. The selected optimizer starts at + # x0 and calls 'vqe_func' everytime the optimizer needs to evaluate the cost + # function. The result is returned as a SciPy OptimizerResult object. + # Additionally, after every iteration, we use the 'callback' function to + # publish the interm results back to the user. This is important to do + # so that if the Program terminates unexpectedly, the user can start where they + # left off. + + # Since SPSA is not in SciPy need if statement + if optimizer == 'SPSA': + res = fmin_spsa(vqe_func, x0, args=(), **optimizer_config, callback=callback) + # All other SciPy optimizers here + else: + res = opt.minimize(vqe_func, x0, method=optimizer, options=optimizer_config, callback=callback) + # Return result. OptimizeResult is a subclass of dict. + return res + + +def opstr_to_meas_circ(op_str): + """Takes a list of operator strings and makes circuit with the correct post-rotations for measurements. + + Parameters: + op_str (list): List of strings representing the operators needed for measurements. + + Returns: + list: List of circuits for measurement post-rotations + """ + num_qubits = len(op_str[0]) + circs = [] + for op in op_str: + qc = QuantumCircuit(num_qubits) + for idx, item in enumerate(op): + if item == 'X': + qc.h(num_qubits-idx-1) + elif item == 'Y': + qc.sdg(num_qubits-idx-1) + qc.h(num_qubits-idx-1) + circs.append(qc) + return circs + + +def fmin_spsa(func, x0, args=(), maxiter=100, + a=1.0, alpha=0.602, c=1.0, gamma=0.101, + callback=None): + """ + Minimization of scalar function of one or more variables using simultaneous + perturbation stochastic approximation (SPSA). + + Parameters: + func (callable): The objective function to be minimized. + + ``fun(x, *args) -> float`` + + where x is an 1-D array with shape (n,) and args is a + tuple of the fixed parameters needed to completely + specify the function. + + x0 (ndarray): Initial guess. Array of real elements of size (n,), + where ‘n’ is the number of independent variables. + + maxiter (int): Maximum number of iterations. The number of function + evaluations is twice as many. Optional. + + a (float): SPSA gradient scaling parameter. Optional. + + alpha (float): SPSA gradient scaling exponent. Optional. + + c (float): SPSA step size scaling parameter. Optional. + + gamma (float): SPSA step size scaling exponent. Optional. + + callback (callable): Function that accepts the current parameter vector + as input. + + Returns: + OptimizeResult: Solution in SciPy Optimization format. + + Notes: + See the `SPSA homepage `_ for usage and + additional extentions to the basic version implimented here. + """ + A = 0.01 * maxiter + x0 = np.asarray(x0) + x = x0 + + for kk in range(maxiter): + ak = a * (kk+1.0+A)**-alpha + ck = c * (kk+1.0)**-gamma + # Bernoulli distribution for randoms + deltak = 2*np.random.randint(2, size=x.shape[0])-1 + grad = (func(x + ck*deltak, *args) - func(x - ck*deltak, *args))/(2*ck*deltak) + x -= ak*grad + + if callback is not None: + callback(x) + + return OptimizeResult(fun=func(x, *args), x=x, nit=maxiter, nfev=2*maxiter, + message='Optimization terminated successfully.', + success=True) diff --git a/tutorials/vqe.ipynb b/tutorials/vqe.ipynb new file mode 100644 index 000000000..b95c691be --- /dev/null +++ b/tutorials/vqe.ipynb @@ -0,0 +1,705 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# VQE\n", + "\n", + "The Variational Quantum Eigensolver (VQE) is a central algorithm in many applications from e.g. quantum chemistry or optimization.\n", + "This tutorial shows you how to run the VQE as a Qiskit Runtime program. We'll start off by defining the algorithm settings, such as the Hamiltonian and ansatz, and then run a VQE both locally, on your machine, and remotely, using the Qiskit Runtime.\n", + "\n", + "**Note:** You can find tutorials on solving more comprehensive problems, such as finding the ground state of the lithium hydride molecule, using the VQE (and Qiskit Runtime) within [the tutorials of Qiskit Nature](https://github.com/Qiskit/qiskit-nature/tree/main/docs/tutorials)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## System Hamiltonian" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's start by defining the operator of which we want to determine the ground state. Here we'll chose a simple diagonal Hamiltonian $\\hat H$ acting with Pauli-Z operators on the first two qubits\n", + "\n", + "$$\n", + "\\hat H = \\hat Z_0 \\otimes \\hat Z_1.\n", + "$$\n", + "\n", + "We can construct this Hamiltonian with Qiskit's `opflow` module:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from qiskit.opflow import Z, I\n", + "\n", + "num_qubits = 4\n", + "hamiltonian = (Z ^ Z) ^ (I ^ (num_qubits - 2))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This Hamiltonian has a ground state energy of -1." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "target_energy = -1" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Parameterized Ansatz Circuit" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Next, we choose a parameterized quantum circuit $\\hat U(\\theta)$ to prepare the ansatz wavefunction\n", + "\n", + "$$\n", + "|\\psi(\\theta)\\rangle = \\hat U(\\theta)|0\\rangle.\n", + "$$\n", + "\n", + "We'll use the `EfficientSU2` circuit from Qiskit's circuit library, which is a hardware efficient, heuristic ansatz with alternating rotation and entanglement layers." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from qiskit.circuit.library import EfficientSU2\n", + "\n", + "# the rotation gates are chosen randomly, so we set a seed for reproducibility\n", + "ansatz = EfficientSU2(num_qubits, reps=1, entanglement='linear', insert_barriers=True) \n", + "ansatz.draw('mpl', style='iqx')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Solve with the VQE" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now that we have the problem and ansatz specified we can use the Variational Quantum Eigensolver (VQE) to solve for the minimal eigenvalue of our Hamiltonian.\n", + "\n", + "The VQE requires a classical optimization routine, along with an initial point, to calculate the parameter updates." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "from qiskit.algorithms.optimizers import SPSA\n", + "\n", + "optimizer = SPSA(maxiter=50)\n", + "\n", + "np.random.seed(10) # seed for reproducibility\n", + "initial_point = np.random.random(ansatz.num_parameters)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To access intermediate information we can pass a callback into the VQE. The callback is given the current number of function evaluations, the current parameters, function values and standard deviation in the expectation evaluation. " + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "intermediate_info = {\n", + " 'nfev': [],\n", + " 'parameters': [],\n", + " 'energy': [],\n", + " 'stddev': []\n", + "}\n", + "\n", + "def callback(nfev, parameters, energy, stddev):\n", + " intermediate_info['nfev'].append(nfev)\n", + " intermediate_info['parameters'].append(parameters)\n", + " intermediate_info['energy'].append(energy)\n", + " intermediate_info['stddev'].append(stddev)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Local VQE\n", + "\n", + "Before running the Qiskit Runtime VQE program, let's first simulate this system locally using Qiskit's `VQE` class." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "from qiskit.providers.basicaer import QasmSimulatorPy # local simulator\n", + "from qiskit.algorithms import VQE\n", + "\n", + "local_vqe = VQE(ansatz=ansatz,\n", + " optimizer=optimizer,\n", + " initial_point=initial_point,\n", + " quantum_instance=QasmSimulatorPy(),\n", + " callback=callback)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/jul/opt/miniconda3/envs/stable/lib/python3.7/site-packages/qiskit/utils/run_circuits.py:564: UserWarning: Option max_credits is not used by this backend\n", + " return backend.run(circuits, **backend_options, **noise_config, **run_config)\n" + ] + } + ], + "source": [ + "local_result = local_vqe.compute_minimum_eigenvalue(hamiltonian)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Eigenvalue: -1.0\n", + "Target: -1\n" + ] + } + ], + "source": [ + "print('Eigenvalue:', local_result.eigenvalue)\n", + "print('Target:', target_energy)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "With the information from the callback, we can for instance compute the average of the estimation errors in the expectation evaluations. For an exact (statevector) simulation this would be 0, but for a shot-based readout as we have on real hardware, this will be a small finite error." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Mean error: 0.013757962876791466\n" + ] + } + ], + "source": [ + "print('Mean error:', np.mean(intermediate_info['stddev']))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Runtime VQE: ``VQEProgram``\n", + "\n", + "To run the VQE using Qiskit Runtime, we only have to do very few changes from the local VQE run and mainly have to replace the `VQE` class by the `VQEProgram` class. Both follow the same `MinimumEigensolver` interface and thus share the `compute_minimum_eigenvalue` method to execute the algorithm and return the same type of result object. Merely the signature of the initializer differs sligthly.\n", + "\n", + "We start by choosing the provider with access to the Qiskit Runtime service and the backend to execute the circuits on. \n", + "\n", + "**Note:** To run this tutorial, replace the provider by your provider." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "from qiskit import IBMQ\n", + "\n", + "IBMQ.load_account()\n", + "provider = IBMQ.get_provider(project='qiskit-runtime') # replace by your designated provider" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We're using the IBM Montreal device, but you can replace this with another device that support the Qiskit Runtime." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "backend = provider.get_backend('ibmq_montreal')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's not forget to reset the callback!" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "intermediate_info = {\n", + " 'nfev': [],\n", + " 'parameters': [],\n", + " 'energy': [],\n", + " 'stddev': []\n", + "}\n", + "\n", + "def callback(nfev, parameters, energy, stddev):\n", + " intermediate_info['nfev'].append(nfev)\n", + " intermediate_info['parameters'].append(parameters)\n", + " intermediate_info['energy'].append(energy)\n", + " intermediate_info['stddev'].append(stddev)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Since the Qiskit Runtime VQE runs on real hardware, we might want to do measurement error mitigation. For this purpose the `VQEProgram` supports a boolean flag `measurement_error_mitigation` that can be set to `True` to enable error mitigation with a complete measurement fitter." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "measurement_error_mitigation = True" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [], + "source": [ + "from qiskit_nature.runtime import VQEProgram\n", + "\n", + "# in this first release, the optimizer must be specified as dictionary\n", + "optimizer = {'name': 'SPSA',\n", + " 'maxiter': 50}\n", + "\n", + "runtime_vqe = VQEProgram(ansatz=ansatz,\n", + " optimizer=optimizer,\n", + " initial_point=initial_point,\n", + " provider=provider,\n", + " backend=backend,\n", + " shots=1024,\n", + " measurement_error_mitigation=measurement_error_mitigation,\n", + " callback=callback)" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [], + "source": [ + "runtime_result = runtime_vqe.compute_minimum_eigenvalue(hamiltonian)" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Eigenvalue: -0.9756263578485963\n", + "Target: -1\n" + ] + } + ], + "source": [ + "print('Eigenvalue:', runtime_result.eigenvalue)\n", + "print('Target:', target_energy)" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Mean error: (0.012858397132816049+0j)\n" + ] + } + ], + "source": [ + "print('Mean error:', np.mean(intermediate_info['stddev']))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Additionally to final results, the `VQEProgram` also returns the history of the optimization, such that we can visualize the " + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [], + "source": [ + "history = runtime_result.optimizer_history\n", + "\n", + "loss = history['loss']\n", + "timestamps = history['time']\n", + "\n", + "runtimes = np.concatenate(([0], np.diff(timestamps)))\n", + "runtimes_in_min = runtimes / 60" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "From this, we can extract the times per iteration and statistics on it:" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Total time taken: 23.09min\n", + "\n", + "Median time of the iterations: 27.12s\n", + "Average time per iteration: 27.71s\n", + "Standard deviation: 5.21s\n" + ] + } + ], + "source": [ + "print(f'Total time taken: {np.sum(runtimes_in_min):.2f}min\\n')\n", + "\n", + "# note that the median and average might differ, since the device get's calibrated every hour\n", + "# resulting in some iteration times that are much larger than the rest\n", + "print(f'Median time of the iterations: {np.median(runtimes):.2f}s')\n", + "print(f'Average time per iteration: {np.mean(runtimes):.2f}s')\n", + "print(f'Standard deviation: {np.std(runtimes):.2f}s')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "or plot the loss against the total time taken:" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "scrolled": false + }, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAu8AAAH8CAYAAABhKUH5AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjQuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8rg+JYAAAACXBIWXMAAAsTAAALEwEAmpwYAACH/UlEQVR4nOzdd3yV5f3/8dcne0F22EkYSRhOCEM27tlWrbuCe9VabR1t7bD99teh1mpbrdU6a9W2rrrrIoALZSoCCXtDEhIC2ck51++PcxJDCJBAknOSvJ+Px3mcc677uu/7c7gDfM6Vz31d5pxDRERERESCX0igAxARERERkdZR8i4iIiIi0kUoeRcRERER6SKUvIuIiIiIdBFK3kVEREREuggl7yIiIiIiXYSSdxGRdmZmmWbmzOyyNu6XZ2YrW9FvvZk9eajxBTMze9LM1gc6DhGRYKXkXUSkFcws3cwe9ifONWZWaGYvm9lxQRBbfzO7y8yOaWX/hi8XDQ+vmZWY2Vud8XnMbKQ/3syOPtfhMLNz/H8+1x+gz2h/n980az/CzP5pZlvNrNbMtpjZM2Y2soVjTG92PZo/ruuIzyciXVNYoAMQEQl2ZjYJeNP/9lFgBdAPuAz4yMy+65z7a5NdNgDRQF0HhZQDeJu87w/8AlgPLGnDcf4FvA6EAtnADcBsMxvrnPuyXSJt2Uh88ebhi7mpqwmegaU3gF3AxcBf99PnEv/zMw0NZnYO8BxQAjwGrAMGA1cC3zazC5xz/23hWA8Cn7bQPv9QgheR7knJu4jIAZhZIvACUAVMcs6tabLtD8D/gD+b2WLn3KcAzrd0dXVHxeScq2mnQy1xzjVNOucBbwHX40vkO51zrqO+8LSZc67GzP4DXGVm6c65jU23m1kIcCGw2Dm33N82FPgHsBaY4pwrbtL/fmAe8IyZHeWcW9fslB86557vuE8kIt1BsIxuiIgEq2uBvsBtTRN3AOdcFTDL//bnDe0t1bybWZyZ3Wtm6/xlN0X+GvepBzq5mU01s91m9pqZRfrbGmvezWw68Lm/+xNNSi3uOoTPOs//PLTJ+e8yM9dCXJf5z5PZpG29mb1tZpPN7DMzqzaztWY2s+l+wH/8b2c3ifcy//Z9at792x82s2+b2VdmVmVm8xvKhMzsajNb5T/fHDMb0kK8Y83sTTMr8+//oZnNaMWfyTOAARe1sG0Gvt96PNOk7TYgBrimaeIO4H9/LRDn7yci0mZK3kVEDuwsfKPo/25po3/09EPgRDOLOsBx/gp8D3gZ36j274Ei4Oj97WBmJ+EbCf8fcM5+RtxX8PUXh0eAS/2Plw4Qy/5k+p9LD2HfBoPx/abiXeCH/mM9aWaj/NvnAn/yv/4NX8c79yDHnQj8Ed+o9l34Sode99eD34Lvz/duYALwZNMdzWwavi8mScCvgDuASOAd/5efA5mHrwzqkha2XYKvfOm5Jm1nAeudc/Na6I9zbi6+UqGzWtjcy8xSWniEHiRGEelBVDYjInJgI4H8g5SqLAWmAVnA/mrFzwQedc79oDUnNbOz8I1Q/xu43Dnnaamfc26Hmb2FLyn9pGkZTCvEmFkKvpr3LOA+f/sLbThGc9nANH+Sipn9G9gEXA7c6pxb6y/PuQl41zmX18rjDgdGNPz2w8xKgb/h+9xZzrkyf3sY8GMzG+acW21m5u/3IXCSv6QJM3sYWIzvC8TE/Z3UOefM7Fn/MUc5577y7x8FnAt84Jzb5m+LxzcS31I9e1NfAN8ws17OuT1N2h/xP5obARx0FiIR6Rk08i4icmC9gD0H6dOwvdcB+pQB481swMFOaGbfBl4EngIu21/i3g5+hm/0fzu+EebhwA+dc4eTvBc0JO4AzrkiIB/Yp5SljWY3K1tquInzpYbEvVl7w/mOxjdK/yyQ3DCaDfTG99uB8WYWc5BzN3whajr6fqb/GE2/LDVc/0P9efl/wEktPDYiIuKnkXcRkQPbw4GTcppsLzxAn9vwJeMbzWwx8DbwD+dcfrN+6cDzwCvOuWsPId62eMx/rihgOnAzsE99exu1lGiWAontfNyGhH3Tftobzpftf37sAMdOBir3t9E5t9x/zS4yszv9o/eX4LuJuWl5Umu+xDVsd0Bxs/Zlzrn3DrKviPRwSt5FRA5sOTDazCIPUDpzFFALbNnfQZxz//GXi3wTOBlf2cjtZnaZc+7ZJl134KuxPsXMjnPOfdIun6Jlq5ski6+bWR1wj5nNc84taAh9P/vurw57f78lsEMN8iDHPdj5Gn7D/CNg4X76FrXi/M8AfwAmmtly4HTg5aZlL865MjPbiu/n4UCOAjY752pbcV4Rkb2obEZE5MBewzcyfV5LG/2zrUwBXvfPPrNfzrntzrm/OefOxTfCvhb4ZbNuNfhuZlwOvGmtW3jpcEfLG/wW2A38uklbKYCZJTTrm3EY52mveFujodRmj3Puvf08WjOt53P4vihcDHwbiGDvkpkGrwGDzWxySwcxsyn4bgz+T0vbRUQORsm7iMiB/Q1fTfjdZja46QYziwaewDfjyN37O4CZhfpvZmzknNuFb/GehOb9/aO5p+IrFXnHzIYfJMYK//NhlaY453YDD+Mb9T/W39yQ/DZOaWlmsXw9ReahaJd4W2khsBr4gZntU85iZqmtOYj/ptQPgPPxffZifLMANXcvvhKcv5lZcrNzJeP78y0D/tKGzyAi0khlMyIiB+CcK/XfQPomsNjMGlZY7YtvhdXBwHedcwdaBbMXsMXMXsQ3M81uYBK+BL3FJM5/3pPxTaH4nplNaWFRnwZr8I2QX29m5fhqr5c555a17dMCvmkcfwj8GF+i+g6+LxGPmdk9+Eafr8BXapJ+CMcH3ywvHnwzuCTgqx2ff4DPd8icc14zuxLfPQbLzexxYDO+WWGm4Suvac187+AbaT8JSAEeamlBKf8MNzPxjdR/aWZ/xzc1ZCZwFRAPXLCfzzrZP1tOc8udc4taGaOIdHNK3kVEDsI595GZHQX8BF9C2x/fv58OOL4V0x1WAg/iS/y+AYTjG3W/FXjgAOfdYWYn4psJ5n1/Ar9PXb1zrs7MLsVX9vKg//i/BNqcvDvntpvZP4FZZpbtnCsws7OBh4D/w/dbiPvxfVl4oq3Hb/K5rsb35/kovvr5y/H9mbQ759xcM5uAb3adG/DNErMd3+JWf2/DoV7CN598DC2XzDSc70UzG43vC9DVQBq+33RXAaOdc/ub9vG7/kdzfwCUvIsIAOaf8lZERNrAzE7ANxo/Fzi9pVFYkQb+0fgngaedc5cFNhoR6cpU8y4icgicc+/jq30+AXjcvxiQSIucc0/jG4mfZWb/L9DxiEjXpZF3EREREZEuQiPvIiIiIiJdhJJ3EREREZEuQsm7iIiIiEgXoaki2yAlJcVlZmYGOgwRERER6eYWLlxY7JzbZyE5Je9tkJmZyYIFCwIdhoiIiIh0c2a2oaV2lc2IiIiIiHQRSt5FRERERLoIJe8iIiIiIl2EkncRERERkS5CybuIiIiISBeh2Wa6gNp6LxFh+p4lIiI9w+7duyksLKSuri7QoYh0iPDwcNLS0ujdu3eb91XyHuQWbSzlu/9cxN9n5TKqf3ygwxEREelQu3fvZseOHQwYMIDo6GjMLNAhibQr5xxVVVVs2bIFoM0JvIZzg9ygxBicg+ufWURZpUYgRESkeyssLGTAgAHExMQocZduycyIiYlhwIABFBYWtnl/Je9BLrVXJA9eMpptZVXc8u8leL0u0CGJiIh0mLq6OqKjowMdhkiHi46OPqTSMCXvXcCYjER+duZIPlhZyF9mrw50OCIiIh1KI+7SExzqz7mS9y7i0gkZnH3sAP74XgFzCooCHY6IiIiIBICS9y7CzPjN2UeS06cX339+MZtKKgMdkoiIiByCyy67jDPPPLNVfZ988kni4uL2u339+vWYGQsWLGiv8DpNW/4c5GtK3ruQ6IhQHv7OGDxex/X/XEh1nSfQIYmIiIjfli1buOaaaxg4cCAREREMGDCAq6++ms2bN+/V74EHHuCZZ55pl3MOGjSIbdu2ccwxxwCQl5eHmVFcXHzA/RqS/oZHfHw8EyZM4LXXXmuXuJraX0zt+efQWrW1taSkpPB///d/LW7/61//SkxMDGVlZQB4vV7+/Oc/c8wxxxAdHU3v3r05/vjjeeutt/bar+EztvRYuXJlu34GJe9dTGZKLPedfwzLtuzmF//9KtDhiIiICLBu3Tpyc3NZtmwZTz31FKtXr+aZZ57hq6++YuzYsaxfv76xb3x8PAkJCe1y3tDQUPr27UtY2KHN/v3222+zbds25s+fz7hx4zj33HNZtmxZu8R2MO3559BaERERXHrppTz55JM4t+8kII899hjf/va3iY/3Tc998cUX87Of/Yxrr72Wr776ik8++YSxY8dy5pln8te//nWf/b/66iu2bdu21yMrK6t9P4RzTo9WPsaMGeOCxT1vr3QZd7zunpu/IdChiIiItJvly5cHOoRDctppp7n+/fu7ioqKvdorKipc//793emnn97YNmvWLHfGGWc0vp8zZ44bP368i42Ndb1793Zjx451X375pXPOuSeeeMLFxsY29i0pKXETJ050J598sisvL3fr1q1zgPv8888bXzd9zJo1q8V4m+7XYPfu3Q5wf/rTn/bbxznnAPef//xnrz4vvPCCO/HEE110dLQbMWKEe+edd/ba3lJMzf8cpk2b5q677jr3gx/8wCUmJrqUlBR3//33u+rqanfDDTe4+Ph4N2jQIPf000/vFc/mzZvdBRdc4BISElxCQoI7/fTTXUFBwX6v1bJlyxzgPvjgg73alyxZ4gA3Z84c55xz//rXvxzgXn755X2O8f3vf99FRES4TZs2Oeecmz17tgNcUVHRfs/bkgP9vAMLXAv5aNCPvJvZDWa2zsyqzWyhmU05QN9zzOwdMysysz1mNt/MvtGsz2Vm5lp4RHX8p2k/t5yUzZSsFH7+6ld8sXlXoMMRERHpsUpKSnj77bf57ne/S0xMzF7bYmJiuOGGG3jrrbcoLS3dZ9/6+nq++c1vMnnyZJYuXcr8+fO5+eabCQ0N3afv1q1bmTp1KgMHDuS1114jNjZ2r+2DBg3ixRdfBL4eAX7ggQda9Rnq6up49NFHAd/qn2115513ctNNN7F06VLGjh3LhRdeSHl5eZtj+uc//0mvXr2YP38+P/rRj7j55pv51re+RXZ2NgsWLGDWrFlcddVVbNu2DYDKykpmzJhBVFQUc+bM4ZNPPqFfv36ceOKJVFa2fH/gqFGjGD9+PI8//vhe7Y899hhZWVlMnTq1MZasrCy+9a1v7XOM2267jdra2sbP1pmCeoVVM7sAeAC4AfjQ//yWmY10zm1sYZdpwAfAT4ES4BLgZTOb7pyb16RfJTC06Y7OueoO+AgdJjTEeODCYznrzx9y/TOLeO17k0mKjQh0WCIiIu3ul699xfKtuzv1nCP79+YXZ41qVd9Vq1bhnGPEiBEtH2vkSJxzrFq1inHjxu21bffu3ezatYuzzjqLoUN9qcnw4cP3Ocbq1as56aSTOOWUU3jooYcICdl3/DU0NJSkpCQA0tLSSElJOWjsU6dOJSQkhKqqKrxeL4MHD+b8888/6H7N3XLLLZx11lkA/OY3v+Hpp59myZIlTJ48uU0xjRo1irvuuguAH/zgB/zud78jPDyc73//+wD8/Oc/5/e//z0fffQR3/72t3n++edxzvHEE080Tr34t7/9jbS0NF5//fX9fparrrqKm266ib/85S/Ex8dTU1PDP//5T26//fbGPgUFBfu9pgMGDKB3797k5+fv1Z6ZmbnX+4SEhH3ueThcwT7y/gPgSefco865Fc657wHbgOtb6uyc+75z7nfOuc+cc6udc78EFgLf2rer29700aGfooMkxUbw0CWjKdpTw/efX4xHCziJiIgErYiIfQfZkpKSuOyyyzjllFM444wzuO+++9i4ce/xydraWiZPnszpp5/Oww8/3GLifqieffZZFi9ezKuvvkpWVhaPP/54Y7LdFkcddVTj6/79+wMc0uqhTY9jZqSlpXHkkUc2toWHh5OYmNh47IULF7Ju3Tp69epFXFwccXFxxMfHU1paypo1a/Z7ngsvvJDQ0FCee+45AF555RV2797NrFmz2hRv82s6e/ZslixZ0viYN2/efvY8dEE78m5mEcAY4N5mm94BJrbhUL2A5r+nijazDUAosAT4mXNu8SGGGlBHD0rgl98cxY9f+pL73yvghyfnBDokERGRdtXaEfBAycrKwsxYvnw5Z5999j7bly9fTlhYGIMHD25x/yeeeIKbb76Zt99+m1dffZU777yTV155hVNOOQXwJawnn3wyb775Jhs2bCAjI6PdYh84cCBZWVlkZWURFxfHeeedx/Lly0lJSWn8kuCa3Ni5vxVBm5baNIyAe73eNsfTvGTHzFpsazi21+vlmGOO4fnnn9/nWAf6EhIXF8f555/P448/znXXXcdjjz3GGWecQd++fRv7ZGdns2LFihb337JlC7t37yY7O3uv9sGDB7fqNx6HI5hH3lPwJdc7mrXvAPru231fZvZdYCDwjybN+cAVwDeBi4Bq4CMza/FWYDO7xswWmNmCoqLgXBzpwrGDOD93IH/+YDXvLW/+xyUiIiIdKSkpiVNPPZWHHnponzrryspKHnzwQc4+++zGGUxacvTRR3PHHXeQl5fH9OnTeeqppxq3mRlPPvkkkydPZsaMGfuMzDfVMBLs8bR9Oulp06YxcuRIfvWrXwGQmpoK0FhfDrBkyZI2H/dwYjqY0aNHs3r1alJSUhg2bNhej4P9BuGqq67i888/5/XXX+f999/nqquu2mv7JZdcwqpVq3jllVf22ffuu+8mKiqKCy64oD0/TqsEc/J+WMzsXOAe4GLn3IaGdufcJ865p5xzS/x18BcAa4DvtXQc59wjzrlc51xuww9xsDEzfvXNIxjVvze3/HsJ64srAh2SiIhIj/Lggw/i8Xg48cQT+eCDD9i0aRN5eXmcdNJJhIeH86c//anF/datW8ePfvQjPv74YzZs2MDs2bP54osvGDly5F79QkJCeOqpp5g4cSLTp0/fbwKfkZGBmfHGG29QVFREeXl5mz7HD3/4Qx555BE2bdpEdHQ0EyZM4Pe//z1fffUVH3/8MbfeemubjtceMR3IJZdcQp8+ffjmN7/JnDlzWLduHXPnzuWHP/whq1atOuC+xx13HCNHjmTmzJn07duX0047ba/t5513HhdccAGXXXYZDz/8MOvWrWP58uX86Ec/4i9/+Qt///vfSU5O3mufwsJCtm/fvtejtra23T4vBHfyXgx4gD7N2vsAB6xRN7Nv4xttn+mcO+BqA845D7AAaOdJODtXVLhvAacQM657ZiFVtVrASUREpLMMHjyYBQsWMGrUKC699FIyMzOZMWMGISEhLFmyZK9yjKZiYmIoKCjgvPPOIzs7m1mzZnHJJZdwxx137NO3aQK/vxH4AQMG8Mtf/pI777yTPn36cOONN7bpc5x55plkZmY2LmLUMCPL2LFjufbaa/n1r3/dpuO1R0wHEhMTw9y5cxkyZAjnnXcew4cPZ9asWZSWlpKYmHjQ/a+88kpKS0u57LLL9pnhx8x49tln+dWvfsVf//pXRowYwahRo7j//vuZM2cOl1xyyT7HGzVqFP369dvrMXfu3Hb7vADWtI4p2JjZfGCpc+6aJm0FwIvOuR/vZ5/zgaeAWc65f7fiHIYveV/qnLviQH1zc3NdsC8/PDu/kCue/JzvjM/g/751RKDDERERaZMVK1bsd4aPruYvf/kLP/zhD/nPf/7DN77xjYPvIEGvoKCAGTNmMG3aNJ555pnDvnn4QD/vZrbQOZfbvD2YR94B7gMuM7OrzGyEmT0A9AceBjCzp83s6YbOZnYh8E/gR8BcM+vrfyQ16fMLMzvFzIaY2THAY8BRDcfs6mbkpHHxuHT+9fkmCnd3qdkvRUREupUbb7yRf/zjH3z11VdUVVUFOhxpB9nZ2cyePZvs7OxDqv9vD0E72wyAc+5fZpaMb972fsAy4PQmNezpzXa5Dt9nut//aDAHmO5/nQA8gu+m1zJgMTDVOfdZu3+AALl26lCe+2wjj3+0nh+dtu9csSIiItI5DmXOdAlu2dnZjXPRB0JQJ+8AzrmHgIf2s236gd7vZ59bgFvaI7ZglZ4cw+lH9uOfn27ghhlD6R3V9pXSRERERCT4BHvZjByi66YNZU9NPc/N3/90UiIiIiLStSh576aOGBDP5GEpPPbhOmrqNfOMiIiISHeg5L0bu3baEAr31PDfxVsDHYqIiIiItAMl793Y5GEpjOrfm4fnrsHrDd4pQUVERESkdZS8d2NmxrXThrK2qIJ3V+wIdDgiIiIicpiUvHdzpx/Rl0FJ0Tw8Zw3BvCCXiIiIiByckvduLiw0hKunDGHxxl18vr400OGIiIiIyGFQ8t4DnDdmEEmxEfxtzppAhyIiItLtmNkBH5dddlnAYsvMzOTee+8N2Pml/QX9Ik1y+KIjQpl1XCZ/fK+A/O17yOnbK9AhiYiIdBvbtm1rfP36669z9dVX79UWHR3dpuPV1tYSERHRbvEFs570WduLRt57iJnHZRAdHsojc9cGOhQREZFupW/fvo2PhISEvdoqKiqYOXMmffv2JTY2ltGjR/P666/vtX9mZiZ33XUXV1xxBQkJCVxyySUAPP7446SnpxMTE8NZZ53FQw89hJntte9rr73GmDFjiIqKYvDgwdx5553U1tYCMH36dDZs2MBtt93W+FuA/amtreWOO+5g4MCBxMTEMHbsWP73v/81bs/Ly8PMeP/99xk/fjwxMTHk5uayaNGivY7z8ccfM23aNGJiYhgwYADXX389u3fvbtw+ffp0rr/+em699VZSU1OZNGkSAG+88QY5OTlERUUxdepUnn/+ecyM9evXU1FRQe/evXnhhRf2Ote7775LeHg4O3b0rEk5lLz3EImxEVwwdhD/XbKFrbuqAh2OiIhIj1BeXs5pp53Gu+++y9KlSzn33HM555xzWLly5V797rvvPoYPH86CBQv4zW9+wyeffMJVV13Fd7/7XZYsWcI3vvENfvGLX+y1z//+9z8uueQSbrzxRr766isef/xxXnjhBX7yk58A8NJLLzFw4EB+/vOfs23btr1+G9Dc5Zdfzpw5c3j22WdZtmwZs2bN4qyzzmLp0qV79fvxj3/M7373OxYtWkRycjKXXHJJ44QYX375JSeffDLf+MY3WLp0KS+99BJLlizhiiuu2OsYzzzzDM455s2bx9NPP83GjRs555xzOOOMM1i6dCk33XQTt99+e2P/2NhYLrroIh5//PG9jvP4449z5pln0qdPn1Zeje7BNANJ6+Xm5roFCxYEOoxDtrm0kmn35HH5xEx+eubIQIcjIiKyjxUrVjBixIh92jdcOnOftl6nnUrSxRfjrapi0zXX7rM9/uyzSTjnbOpLS9ly0/f32Z540YX0Pv106rZtY+vtd+y1LeMfTx9S/C+88ALnnXfeAWd4mzBhAmeeeSY//elPAd/I+5FHHslrr73W2Oeiiy6itLSUt99+u7Htmmuu4dFHH2089tSpUznppJP42c9+1tjnlVde4Tvf+Q579uzBzMjMzOTGG2/k1ltv3W88a9asISsri/Xr15Oent7Y/q1vfYv+/fvz0EMPkZeXx4wZM3j77bc55ZRTAPjoo4+YPHkymzZtYuDAgcycOZPw8HAee+yxxmMsWbKEY489lh07dpCWlsb06dMpKSnhiy++aOzz4x//mFdeeYUVK1Y0tv3mN7/hzjvvZN26dWRmZrJgwQImTJjAhg0bGDBgAKWlpfTv35///Oc/nHnmmfu/IEFufz/vAGa20DmX27xdI+89yMDEGM46qh/PfbaRssq6QIcjIiLS7VVUVHD77bczcuRIEhMTiYuLY8GCBWzcuHGvfrm5e+doK1euZNy4cXu1jR8/fq/3Cxcu5P/9v/9HXFxc4+Piiy+moqKC7du3tzrGRYsW4Zxj5MiRex3rjTfeYM2avSe7OOqooxpf9+/fH4DCwsLGeJ555pm9jtFQFtP0OGPGjNnns44dO/aAnzU3N5cjjzySp556CoBnn32WpKQkTjvttFZ/zu5CN6z2MNdOG8orS7byzPwNfHfGsECHIyIi0ioHGgkPiY4+4PawxMQDbg/v1++QR9oP5tZbb+Xtt9/m3nvvJSsri5iYGGbOnNlYl94gNja2zcf2er384he/4LzzzttnW2pqapuOY2Z8/vnnhIeH77Wt+c22Tbc31NB7vd7G56uuuopbbrlln3MMGDCg8fWhfFaAq666igceeICf/OQnPP7448yaNYvQ0NBDOlZXpuS9hxnRrzfTslN54qN1XDl5MFHhPe+HXkREpLN8+OGHzJw5k3PPPReA6upq1qxZQ3Z29gH3Gz58OJ9//vlebZ999tle70ePHs3KlSsZNmz/g3ERERF4PJ4DnuvYY4/FOcf27duZMWPGAfseyOjRo/nqq68OGE9Lhg8fzn//+9+92pp/VoBLLrmE2267jb/85S8sWrSI559//pBj7cpUNtMDXTdtKMXltby4aHOgQxEREenWsrOzefnll1m0aBFffvkl3/nOd6iurj7ofjfddBPvvPMO99xzD6tWreKxxx7j5Zdf3qvPz3/+c5599ll+/vOfs2zZMlauXMkLL7yw182emZmZzJs3jy1btlBcXLzfGC+55BIuu+wyXnjhBdauXcuCBQu49957eemll1r9We+44w4+++wzrrvuOhYvXszq1at5/fXXufbafe9HaOq6665jzZo13HrrreTn5/PSSy/xt7/9DWCvGXISEhI477zz+OEPf8jUqVPJyspqdWzdiZL3HmjCkCSOHhjPo3PX4vHqhmUREZGOct9995GWlsaUKVM47bTTmDBhAlOmTDnofscddxyPPvoof/rTnzjqqKN45ZVXuOOOO4iKimrsc8opp/DGG28we/Zsxo0bx7hx4/jd7363102nv/rVr9i0aRNDhw49YCnNE088weWXX87tt9/O8OHDOfPMM5k7dy4ZGRmt/qxHHXUUc+fOZf369UybNo2jjz6aH//4xwedDSYjI4MXX3yRV199laOPPpo//vGPjTPrNP28AFdeeSW1tbVceeWVrY6ru9FsM23Q1WebaeqtL7dx/T8X8dAlozn9yH6BDkdERAQ48OwbPd0tt9zCe++9x5dffhnoUDrcAw88wM9//nN27dq11+j7v/71L6699lq2bt1KTExMACNsH4cy24xq3nuok0f1ZXBKLA/PWcNpR/Q94MINIiIi0vnuueceTjrpJOLi4njvvfd4+OGH+c1vfhPosDrEgw8+yNixY0lNTeXTTz/l//7v/7jssssa85PKykq2b9/Ob37zG66++upukbgfKpXN9FChIcbVU4bwxeYyPlm7M9DhiIiISDMLFizglFNO4YgjjuCBBx7gt7/9LTfffHOgw+oQq1ev5uyzz2bEiBH87Gc/47rrruOee+5p3H733XeTk5NDUlLSXvPa90Qqm2mD7lQ2A1Bd52Hy72czsn9vnr5i3MF3EBER6WAqm5GeRIs0SZtEhYdy+aRM5hYUsXzr7kCHIyIiIiIHEfTJu5ndYGbrzKzazBaa2QFv0Tazaf5+1Wa21syuO9xjdmffGZ9BbEQof5u75uCdRUREOoGqAqQnONSf86BO3s3sAuAB4DfAscDHwFtmlr6f/oOBN/39jgV+C/zZzM491GN2d/Ex4Vw8Pp3Xv9hGcXlNoMMREZEeLjw8nKqqqkCHIdLhqqqq9lnRtjWCOnkHfgA86Zx71Dm3wjn3PWAbcP1++l8HbHXOfc/f/1HgKeDWwzhmt3fqEf3weB0L1pcGOhQREenh0tLS2LJlC5WVlRqBl27JOUdlZSVbtmwhLS2tzfsH7VSRZhYBjAHubbbpHWDifnY7zr+9qf8Bs8wsHLBDOGa3d8SA3kSEhrBoYymnHtE30OGIiEgP1rt3bwC2bt1KXV1dgKMR6Rjh4eH06dOn8ee9LYI2eQdSgFBgR7P2HcCJ+9mnL/BeC/3D/Mezth7TzK4BrgH2WrGsO4kMC+XIgfEsWF8S6FBERETo3bv3ISU1Ij1BsJfNBJxz7hHnXK5zLvdAywp3dbkZiSzbspvqOk+gQxERERGR/Qjm5L0Y8AB9mrX3AbbvZ5/t++lf7z/eoRyzRxidkUitx8tXW8sCHYqIiIiI7EfQJu/OuVpgIXBSs00n4ZshpiWf7Kf/Audc3SEes0cYnZ4IoJtWRURERIJYMNe8A9wH/MPMPgM+wjebTH/gYQAzexrAOTfT3/9h4EYzux/4GzAJuAy4qLXH7KlSe0WSmRzDwg1K3kVERESCVVAn7865f5lZMvBToB+wDDjdObfB3yW9Wf91ZnY68Ed8Uz9uBW5yzr3YhmP2WKMzEplbUIRzDjMLdDgiIiIi0kxQJ+8AzrmHgIf2s216C21zgNGHesyeLDcjiZcWbWHDzkoyU2IDHY6IiIiINBO0Ne/S+cZk+OreVTojIiIiEpyUvEujrLQ4ekWFsXCjkncRERGRYKTkXRqFhBij0xNZqBlnRERERIJSm5N3MzvNzF43s+VmNsjfdpWZndD+4UlnG5ORSEHhHsqqtCS1iIiISLBpU/JuZpcA/wZWAYOBcP+mUOD29g1NAmFMRiLOwZJNuwIdioiIiIg009aR99uBq51zt+BbtbTBp8Ax7RWUBM4xgxIIMVi4viTQoYiIiIhIM21N3rPwrWLaXDnQ+/DDkUCLjQxjRL/eumlVREREJAi1NXnfCmS30D4VWHP44UgwGJORyJKNu6j3eAMdioiIiIg00dbk/RHgT2Y2yf9+kJnNAu4G/tqukUnAjMlIpKLWw8rtewIdioiIiIg00aYVVp1zd5tZPPAuEAXMBmqAe51zD3ZAfBIADYs1LdpYyhED4gMcjYiIiIg0aPNUkc65O4EUYBwwAUh1zv2svQOTwBmQEE2f3pEs0HzvIiIiIkGlTSPvDZxzlcCCdo5FgoSZkZuRxMINSt5FREREgkmbkncze/VA251z3zi8cCRYjM5I5I0vt7G9rJq+8VGBDkdEREREaHvZzM5mj934FmuaChS3b2gSSA117xp9FxEREQkebb1h9fKW2s3sD/gSeekmRvXvTVR4CAs3lHLGUf0CHY6IiIiIcAg3rO7H34DvttOxJAiEh4Zw1MAELdYkIiIiEkTaK3nPaafjSBDJzUjkqy1lVNV6Ah2KiIiIiND2G1b/1LwJ6AecBjzeXkFJcBiTkUi91/HF5l2MH5Ic6HBEREREery2ThV5ZLP3XqAIuAUl793O6HT/TasbS5W8i4iIiASBtt6wOqOjApHgkxgbwdDUWBZqsSYRERGRoNBeNe/STY3JSGThxlKcc4EORURERKTHO+jI+8EWZmpKizR1P2MyEvn3gs2sLa5gaGpcoMMRERER6dFaUzazs8OjkKA1JiMJgIXrS5W8i4iIiATYQZP3/S3M1NHMLBK4F7gIiAbeB25wzm0+wD4/Bs7BN3VlDfAp8GPn3LImfZ4EZjXbdb5zbkK7foBuYkhKLAkx4SzcUMr5YwcFOhwRERGRHi2Ya97vB87Fl7xPAXoDr5tZ6AH2mQ48BEwEjgfqgffMLKlZv/fwTXHZ8Di9PQPvTkJCjNHpiVqsSURERCQItHWqSMxsBr6EOh2IaLrNOXd8ewRlZvHAlcDlzrl3/W2XAhuAE4H/tbSfc+6UZse5FCgDJgGvNdlU45zb3h6x9gRjMhL5YGUhuyprSYiJOPgOIiIiItIh2jTybmaXAW8BvfCNchcBicBoYHk7xjUGCAfeaWhwzm0CVuAbVW+tXvg+Y/Nh48lmVmhmBWb2qJmlHW7A3dmYDN9874s0+i4iIiISUG0tm7kVuNE5dxFQh6+e/FjgGaC8HePqC3iA4mbtO/zbWusBYAnwSZO2t4GZwAnAD4FxwAf+Gvt9mNk1ZrbAzBYUFRW14dTdx9EDEwgLMRZuUPIuIiIiEkhtTd6H4KsXB98NoQ3Tj/wFuOxgO5vZr83MHeQxvY0x7e9c9wGTgXOdc56Gdufc8865V51zXzrnXgNOw3eD6xktHcc594hzLtc5l5uamtoeoXU50RGhjOrfmwVarElEREQkoNpa874TXykKwBbgCOALIBnfjDAHcz++UfoD2QhMAEKBFHylOQ36APMOdhIz+yNwITDDObf2QH2dc1vNbDOQdbDj9mSjMxJ57rON1Hm8hIcG833OIiIiIt1XW7OwecDJ/tf/Bv5kZk8AzwHvHmxn51yxc27lQR6VwEJ8ZTknNexrZgOBEcDHBzqHmT2A74ba451zKw8Wk5mlAAOAbQfr25ONyUikus7Lim27Ax2KiIiISI/V1uT9RnyJOsBvgXvwjbr/G7iqvYJyzpUBjwF3m9mJZnYs8A98o/wNZTuY2Uozu7HJ+weBy4GLgVIz6+t/xPm3x5nZvWZ2nJll+kt0XgMKgZfbK/7uqOGmVZXOiIiIiAROm8pmnHMlTV57gd+3e0RfuxnfPO3/4utFmmY2rV/HV6ue0uT9Df7n95sd65fAXfhugj0S3w2rCfhG22cD5zvn9rRr9N1Mv/hoBiREs3BjKVcwONDhiIiIiPRIbUrezWwJvhHw55xzWzskIj/nXA3wPf9jf33sQO9b6F8FnHKgPrJ/ozMSWbC+5OAdRURERKRDtLVs5k18pTMbzOw9M5vVUJIi3V9uRiLbyqrZsqsq0KGIiIiI9EhtSt6dcz9xzg0GZgAFwL3ADjN73sxanGpRuo+GunfN9y4iIiISGIc0559z7kPn3A1AP+ACfLXnr7ZnYBJ8hvftRUxEKIuUvIuIiIgERFvneW9kZoPwzepyCTAK+LC9gpLgFBYawjGDEliwQXXvIiIiIoHQppF3M0s0s2vMbA6wDt+sLc8Bg51z0zoiQAkuYzISWbFtDxU19YEORURERKTHaevI+3Z8K57+C7jZObe4/UOSYDYmIxGP17F08y4mDk05+A4iIiIi0m7amryfCbzvn+NdeqBj0/03ra4vVfIuIiIi0snaukjTux0ViHQN8dHhZPeJY+FG3bQqIiIi0tnaWvOeZGZ/NbMCM9tlZrubPjoqSAkuYzKSWLShFK/XBToUERERkR6lrWUzjwHHAo8AWwFlbz3QmIxEnvtsI6uLysnu0yvQ4YiIiIj0GG1N3k8ATnLOze+IYKRraFisacH6UiXvIiIiIp2orYs0FQLlHRGIdB2ZyTEMSIjm1aVbAh2KiIiISI/S1uT9TuBXZhbXEcFI12BmzDwug0/XlrBsS1mgwxERERHpMdqavP8UOBkoNLMVZvZF00cHxCdB6sJx6cREhPL4h+sCHYqIiIhIj9HWmvcXOiQK6XLio8M5P3cQz3y6gdtPHU7f+KhAhyQiIiLS7bV1nvdfdlQg0vVcMWkwT32ynqc/Wc/tpw4PdDgiIiIi3V5by2Ywsygz+7aZ3WFmCf62oWaW1O7RSVBLT47hlJF9+ef8jVTW1gc6HBEREZFur62LNA0DVgIPA/8PaEjYrwfubt/QpCu4aspgyqrqeHGRZp4RERER6WhtHXm/H3gH6ANUNWl/FZjRTjFJFzImI5GjB8bz+IfrtOKqiIiISAdra/I+EbjXOedp1r4R6N8+IUlXYmZcOWUI64or+GBlYaDDEREREenW2lzzDoS30JYOaMLvHuq0I/rSPz6Kv3+4NtChiIiIiHRrbU3e3wF+0OS9M7PewC+BN9otKulSwkNDuGxSphZtEhEREelgbU3efwBMNrN8IAr4F7Ae6Av8qH1Dk67kgrHpxGrRJhEREZEO1abk3Tm3FTgG+D3wN2ABcDsw2jlX1J6BmVmkmf3ZzIrNrMLMXjWzgQfZ5y4zc80e25v1MX+/rWZWZWZ5ZjaqPWPvieKjwzl/7CBeXbqV7WXVgQ5HREREpFtqc827c67KOfe4c+5G59wNzrm/O+eqDr5nm90PnAtcBEwBegOvm1noQfbLB/o1eRzZbPvtwA+B7wFjgULgXTPr1W6R91CXTxyMxzme/mR9oEMRERER6ZbatMKqmc3czyYHVAOrnXOLDzcoM4sHrgQud86962+7FNgAnAj87wC71zvntre0wcwMuBn4nXPuRX/bLHwJ/MX4fpsgh6jpok03Hj+MmIg2/XiJiIiIyEG0Nbt6EIjAN+OM198WAtT5X4eb2WLg1MMsoxnjP8c7DQ3OuU1mtgLfdJUHSt6HmNlWoAaYD/zEOdcwDcpgfPX5TY9bZWZz/cdV8n6YrpoymLe/2s6Li7Zw6YSMQIcjIiIi0q20tWzmfGAxMAnfDatR/tcLgbOBYwED7jvMuPoCHqC4WfsO/7b9mQ9cBpwKXO3v+7GZJTc5bsNxWnVcM7vGzBaY2YKionYt6++WxmQkcvSgBC3aJCIiItIB2pq83wd83zn3iXOu3v/4BN8sNH9wzi3FV0/e4mqrZvbrFm4obf6Yfqgfxjn3lnPu3865L5xz7wFn+j/jrMM45iPOuVznXG5qauqhHqbHMDOumjxYizaJiIiIdIC2ls1kApUttFf6twGsAxL3s//9wDMHOcdGYAIQCqQATYe7+wDzWhUp4JwrN7OvgCx/U0MtfB//eZoet8U6eWm7047oy4CEaP7+4VpOHNkn0OGIiIiIdBttHXn/DLjPzBpLTPyv78VXsgK+RHlzSzs754qdcysP8qjEV4ZTB5zU5DwDgRHAx60N1syigOHANn/TOnxJ+knN+kxpy3HlwMJCQ5g1MUOLNomIiIi0s7Ym71cB/YGNZrbezNbjG8Hu798GEAv8+nCCcs6VAY8Bd5vZiWZ2LPAP4AvgvYZ+ZrbSzG5s8v5eM5tmZoPNbDzwgj+ep/zHdfhG/+8ws3PM7AjgSaAcePZwYpa9adEmERERkfbXprIZ59wqf8J7MpDjb14JvOtPjHHOvdJOsd0M1ONbxTUaeB+Y6ZzzNOmTg6+0psFA4Dm+Lrf5FJjgnNvQpM/d/uM9iK+8Zz5wsnNuTzvFLXy9aNM/PtnA7acOp298VKBDEhEREenyzJ9zSyvk5ua6BQsWBDqMLmPjzkqm3zub66YN5fZThwc6HBEREZEuw8wWOudym7cfdOTdzH4APOScq/a/3i/n3OFOESndSHpyDKeM0qJNIiIiIu2lNdnU9/DVjFf7X++P4/Dnd5du5qopg3lrmRZtEhEREWkPB03enXODW3ot0hqj079etOmScemEhFigQxIRERHpslo124yZfWxmCU3e/9bMkpq8TzGzjS3uLD2aFm0SERERaT+tnSpyAhDR5P13gYQm70PxzfQiso+GRZvuf7+A5Vt3BzocERERkS6rrfO8N2ip9kHT1kiLwkJDuO2UHAq2l3P6n+bxjb98yDOfbmB3dV2gQxMRERHpUlo1VaSZeYG+zrlC//s9wNHOubX+932Arc650I4MNtA0VeThKa2o5ZUlW/jX55tYuX0PUeEhnH5EP84fO4jxg5MwUz28iIiICBzGVJF+jn1H1jXSLm2SGBvB5ZMGc9nETL7cUsa/Pt/Eq0u28tLiLQxOieW83IF8e/RA0nprQScRERGRlrRl5P1doMbfdBowB6j0v48ETtTIu7RVVa2HN7/cxr8WbOKzdSWEhhgzclK5YGw6M3JSCQs91MouERERka5rfyPvrU3en2jNSZxzlx9CbF2GkveOta64gn8v2MQLCzdTtKeGtF6RPDozl6MHJQQ6NBEREZFOdVjJu/goee8c9R4vs/OL+Nkry0jtFcl/vztJ88OLiIhIj7K/5F01CRJ0wkJDOGlkH3502nC+3FLGS4u3BDokERERkaCg5F2C1jeO7s/RgxK4538rqaytD3Q4IiIiIgGn5F2CVkiI8fMzR7Bjdw0Pz1kb6HBEREREAk7JuwS1MRlJnHlUPx6Zu4ZtZVWBDkdEREQkoJS8S9D70WnD8Tq4++38QIciIiIiElBK3iXoDUyM4arJg3l58RaWbNoV6HBEREREAkbJu3QJN8wYRkpcJL9+fTma3lRERER6KiXv0iXERYZx68nZLNhQyhtfbgt0OCIiIiIBoeRduozzcgcxol9vfvfWSqrrPIEOR0RERKTTKXmXLiM0xPjZGSPYXFrF4x+tC3Q4IiIiIp1Oybt0KROHpXDiiD48NHsNRXtqAh2OiIiISKcK2uTdzCLN7M9mVmxmFWb2qpkNPMg+683MtfB4o0mfu1rYvr3jP5G0l5+cPpzqOg/3vaupI0VERKRnCdrkHbgfOBe4CJgC9AZeN7PQA+wzFujX5DEacMC/m/XLb9bvyPYMXDrWkNQ4Zh6Xyb8+38SKbbsDHY6IiIhIpwkLdAAtMbN44Ergcufcu/62S4ENwInA/1razzlX1Ow4VwK72Td5r3fOabS9C/v+CVm8tHgzv35jOc9cOR4za/MxNpVU8sriLTggMizE9wgP9b/2P4eHEBHatD2EjORYQkPafj4RERGRwxWUyTswBggH3mlocM5tMrMVwET2k7w3Zb5s7krgGedcVbPNQ8xsK1ADzAd+4pxb217BS8eLjwnn5hOyuOu15XywspATRvRp9b47y2v48wer+ef8DdR52j5n/NjMRJ6+YjzREQf6JZCIiIhI+wvW5L0v4AGKm7Xv8G9rjZOAwcCjzdrnA5cBK4E04KfAx2Y2yjm3s/lBzOwa4BqA9PT0Vp5aOsMlEzL4x6cb+H9vrmBqdirhoQeuAiuvqefv89by6Ny1VNV5OD93EN8/MYvUuEhq6r3+h4eauiav673UNmnfUFLJ3W+v5Pp/LuSRS3OJCAvmyjMRERHpbjo1eTezXwN3HqTbjHY63dXA5865pU0bnXNvNYvpU2AtMAu4r/lBnHOPAI8A5ObmamnPIBIeGsKdZ4zgiicX8MynG7h80uAW+9XUe3hu/kb+/MFqdlbUcuqovtx6Sg7D0uIa+4SFhhAb2brzxkeH8+OXvuTW/yzl/guOIUQlNCIiItJJOnvk/X7gmYP02QhMAEKBFKBpHXsfYN7BTmJmacA3ge8erK9zrtzMvgKyDtZXgs+MnDQmD0vh/vdWcfaxA0iIiWjc5vU6/rt0C394p4DNpVUcNySZO04bzjGDEg7rnBeNS6e0spa7384nISacX35j1CHV3IuIiIi0Vacm7865YvYthdmHmS0E6vCVvjzrbxsIjAA+bsWpLsNXz/5cK84VBQwHZrfiuBJkzIyfnjmC0x+YxwPvr+IXZ43COUdefhG/f3slK7fvYWS/3jx1xZFMzUpptyT7+mlD2VVZxyNz15IYE8EtJ2W3y3FFREREDiQoa96dc2Vm9hhwt5kVAjvxlbR8AbzX0M/MVgJ/cc79pUmbAVcBzzvnypsf28zuBV7DN8KfBvwMiAWe6rhPJB1peN/eXDA2nX98soFjBiXwz/kb+WxdCelJMTxw4TGcdVT/di9tMTN+fNpwSitqeeD9VSTEhO+3bEdERESkvQRl8u53M1AP/AuIBt4HZjrnPE365OArrWlqOr4SmO/s57gD8Y3IN5TkfApMcM5taK/ApfP94KRsXlu6le8/v4SUuAh+9c1RXDg2vUNvKDUzfnvOkZRV1fHL15aTGBPBt44d0GHnExERETHndA9ma+Xm5roFCxYEOgzZj/eW72BtcTmXjM8gNrLzvpdW13m4/InP+Wx9CY/OHMPxw1s/baWIiIhIS8xsoXMut3m75rmTbuPEkX24ZurQTk3cAaLCQ3lk5hhG9uvN9c8s4rN1JZ16fhEREek5lLyLtINeUeE8eflYBiRGc+VTn7N86+5AhyQiIiLdkJJ3kXaSHBfJP64cT1xkGDMf/4z1xRWBDklERES6GSXvIu1oQEI0/7hyHB6vl+88Np8du6sDHZKIiIh0I0reRdrZsLRePHn5OEorapn52GfsqqwNdEgiIiLSTSh5F+kARw9K4JGZuawrrmDW45+xcrtq4EVEROTwKXkX6SCThqXwl4uPZW1xBac9MI/vPbeYtUX7rBsmIiIi0mrBvEiTSJd38qi+zBucxKPz1vLER+t544utnDt6IDedkMWgpJhAhyciIiJdjBZpagMt0iSHo7i8hr/mreEfn27AOceFY9O58fhh9OkddcjH3LKrio9WF1NV6+G0I/qSdhjHEhERkeCxv0WalLy3gZJ3aQ/by6r58wer+NfnmwgNMWYel8F104aSHBd50H1LK2r5ZO1OPlpdzEeri1m/s7JxW4jBlKxUzh0zkJNH9iEqPLQjP4aIiIh0ICXv7UDJu7SnTSWV3P/eKl5evJmo8FCumDSYq6cOIT46vLFPZW09n68v5ePVxXy4upjl23bjHMRGhDJ+SDKThqUwaVgyYSEhvLx4My8v2sLWsmp6RYZxxlH9OGf0QMZmJmJmAfyk0t2VVtTy27dWUFJRS4gZoSFGSIgR2vDajNAQmrz2PQ9MjOabxwwgtdfBv7iKiPQ0St7bgZJ36QirC8u5/70CXv9iG72jwrhy8hDM4KPVxSzaWEqdxxEeahybnsikoSlMzkrmqIEJhIfue7+51+v4dO1OXly0hbeWbaOy1sOgpGjOOXYg54weQEZybAA+oXRnO3ZXc+lj81lfXMmwtDi8zuHxOjzO4W18Zp82j9exp7qe0BBjRk4a5+UO5PjhaS3+XIuI9ERK3tuBknfpSMu37ua+dwt4b8UOzGBkv95MGpbCxKHJjBucRExE2+4vr6ip539fbefFRZv5eM1OnIOxmYmcM3ogZxzVj95R4Qc/iMgBbNxZySWPfcrO8lr+PjOXicNS2rT/6sJy/rNwEy8t2kLRnhpS4iL41jEDOH/sILL79OqgqEVEugYl7+1Aybt0hvXFFfSODicpNqLdjrl1VxUvL97Ci4s2s7aogojQEDJTYkhPiiE9KZb0pGjSk33vBybGqF5eDip/+x4ufWw+tR4vT14+jmMGJRzyseo9XuYUFPGfBZt5b8UO6r2OowclcN6YgZx1dP+9SskOpqyqjo07K9lQUsHGkkoGJcZw6hF9NaIvIl2Okvd2oORdujrnHEs3l/HWl9tYW1zBppJKNpZUUlnr2atfn96RpCfFMCipIcGPITMllqEpccTHtN+Ivdfr2Fxaxcrtu1ldVE5uRhLjBie12/GlYyzeWMplT3xOZFgI/7hyPDl922+UfGd5Da8s2cp/Fmxi5fY9RIaFcOoRfTk/dxDHDUkGYMeeajbsrGxM0jfsrGRTSSUbSirZVVm3zzH79o5i5sQMLhqbTmI7fikWEelISt7bgZJ36Y6cc+ysqGVjiT8B2ulL6Bveb99dTdN/JlLiIhiSGsfQ1FiGpsYxxP88MDGG0JD93xhbXF5D/vY9rNy+h4Lte1i5Yw+rduzZ54vDzOMyuOPU4cRGahmKYPTR6mKufnoBKXGRPHPleNKTO2a9Auccy7bs5t8LNvHfJVvYXV1PUmwEFTX11NR7G/uFhhgDEqLJ8P/myPccS0ZyDAMTo/l8fQmPf7ieD1cXExUewjmjB3L5xEyyVJYjIkFOyXs7UPIuPVF1nYfNpVVs2FnBmqJy1hRWsLa4nDVFFZRU1Db2ayjFGZoax9DUONJ6R7KuuIL87Xso2LGH4vKv+ybFRpDTpxc5fb9+pCfF8Ne8NTz+0ToGJkZzz7ePZoJ/pFWCw/++2s73nl3M4JRY/nHluE5bV6C6zsM7y3eQl19IcmwE6cmxZPgT9f4J0a0qiVm5fTdPfrSelxZvobbey9TsVK6YlMnUrFRCDvClU0QkUJS8twMl7yJ7K62o9SXyhRWs8T+vLSpnQ0klHq8jOjyU7D5x5PTtRXafXgzv25ucvr1IiYvY7/SVn60r4bYXlrJhZyWXTczk9lNz2nyzrrS/Fxdu5vYXv+DIAfE8eflYEmK6ZvnJzvIanvtsI09/soHCPTUMTY3lskmDOXf0AP2ciUhQUfLeDpS8i7RObb2Xkopa0npFHtKoZmVtPXe/nc+TH68nMzmGe847mrGZwVMLX+fxsnVXFZtKqthU+nWJ0abSKrbtqsIBYSG++cy/fg7xPYd+3R4WEkJYqNE/Pppxg331/gMTo4NuXv4nP1rHXa8tZ9KwZB65NLdblDTV1nt588ttPPbhOr7cUkbvqDAuGp/Od8ZntOs1KKuq46utZXy1ZTfLtpbx5ZYyPF7HxePSuXBceptuxhWRnkXJeztQ8i7SuT5Zs5PbX1zK5tIqrpw0mFtPyem0mXCcc6wuLGfZ1jJfkl5SyabSSjaVVLGtrApvk386w0KMAYnRpCfF0D8+mpAQw+P1Uu/1zWde73HUe72+103aPF5HndfL2qIKyqp8N1r2j49i/JDkxmR+SEpswJJ55xx//mA1971bwMkj+/Cni47tdjMROedYuKGUxz9ax9vLtuN1EBMRysDEaAYmxvifoxmUGNP4PiEmvMVrsrO8hq+27ubLLWV8tbWMZVt2s7Hk61WQByREM6p/b3ZX1/Hp2hJiI0I5f+wgrpg0mEFJHXPvQGtV13m63bUV6eqUvLcDJe8ina+ipp7fvbWSf3y6gSEpsdxz3tGMyUjskHNt3VXFR6uL+XjNTj5aXUzhnprGbWm9IhmUFMMgf5I+MCmGQYkxpCfH0Ld31AFv1j0Yr9dRULiHz9aVMH9tCfPXlVBc7jt3Slwk4/2J/PghSWSn9eqUGm3nHL9+YwWPfbiOc0cP5PfnHklYN59ucXNpJe8u38Gmkio2l/p+k7K5pJI9NfV79YuNCG1M5PslRLFjdw1fbSlja1l1Y5+M5BiO6B/PqAG9fc/9e5Mc9/VKsl9tLeOxeet4delWvM5x6hF9uXLykA772W5JTb2Ht5dt55/zN/LZuhL6xUdxzKCExseRA+PbrZSorKqO6joPfTrpPgnpGeo83m49DayS93ag5F0kcD5eXcxtL3zBtrIqrp4yhFtOyj7skcJdlbV8smYnH60p5uPVO1lbXAFAcmwExw1NZtKwFMZkJJKe1Llz3zvnWFdc4Uvm15Uwf+3OxsQwPjqcowbGExMRSmiIERoSQqjhew75+jksJIQQ85XphJgRERZCVHgIkWGhRIWHEBUWSlR4KJFhIUSF+9uavP/T+6v4z8LNXD4pk5+dMbJH39RZVlXH5tJKNpdW+R++15tKKtm6q4qUXpEcOSC+MVkf1S++1VOqbi+r5qlP1vPPTzewu7qeY9MTuHrKEE4e2afDvixtKqnkn/M38p8Fm9hZUUt6UgxnHNWPzaVVLNlUyqaSKsA3k092n14cMyjen9AnMiwtrsUvqs45SivrWL+zgg07K1hfXOl73ul7LvVP4Tl+cBKXTMjglFF9iAzTSH93U1PvYcH6UvLyC1m8cRcZybEcm57AsekJ5PTpdVg/08451hRVsGB9CZ+tL2HB+lI2llSSEhdJZrJvICUz2TfTVEZyLJnJMV323pwGSt7bgZJ3kcAqr6nnN2+u4Nn5GxmaGsvPzxpFWq9IwkKMkBAj1Hz15CH+mvIQ//tQM0JDDa9zLNm4qzFZX7a1DOcvkxg/OIlJw1KYNCyFnD6dM7rdFptLK5m/toTP1pXw1bYy6uodHucrvdnr0VKb11Hr8R78JM3cfGIW3z8hK+hq8Lujipp6Xly0mcc+XMeGnZUMTIzmikmDOX/sIOLa4R6Deo+XD1YW8s/5G5m7qggDThzRh+9MyGDysJS9ft6Ly2tYumkXS5o89lT7fvsQFxnGkQPiOSY9gVAzf7JeyfqdFY19AMx8ZUJfJ1Mx1Hkc//p8ExtLKkmKjeC83IFcPC6djOTYw/ps5TX1fLS6mNkrC9m+u5qstLjGG+SHpcURHaEvCR1pU0kleQVFzMkv5OM1O6ms9RARGsLI/r3ZVFLJTv+sZNHhoRw5MJ5jB/mS+WMGJdI3fv+/ianzePlq625fsr6uhAUbShtnOEuOjSA3M5GcPr3Ysbumcb2HbU1++wXQOyqMzJRY31olybGkJ8eQEhdBXGQ4sZGhxEWGERcZRmxkGJFhIUH3b12XS97N7BrgIuBYIB4Y7Jxb34r9zgX+DxgKrAHudM693GS7Ab8ArgESgfnAd51zXx3s2EreRYLD3IIifvTiF3uVKbRFeKhx7KBEf7KezNGDErr1r17BV5pT6/FSU+elut5DdZ2Hmnov1XUequu8zd576BcfzeSslECH3eN4vI73VuzgsXnr+Gx9Cb0iw7hw3CDGZCTSNz6a/vFRpMS1/kbwHburef6zTTz/+Ua2lVXTp3ckF45N58Jxg+gXH92qY3i9jnU7K1iy8etkfsW23ThgUGJ04yhnRnIsmSm+54GJ0S2OrHu9jg9XF/Ps/I28u2IHHq9jSlYKl4xP54QRfVr993BtUTmz84uYvbKQ+et2Uudx9IoMY0BiNGuLK6j1rwVgBpnJsb5Zr/r0Iqdvb3L6xpGZHNvhZWBlVXVsKqmkcE81ZkZ4SAjhoUZYaAgRob6b1cNDfW3h/ve+dn9bSEjQDSKA7/6Iz9aVkJdfxJyCQtYU+X5jOTAxmuk5qUzPTuO4ocnERobhnGNTSRWLN5Wy2P/zs3zr7sbBhH7xUf5E3pfM13m8fLauhM/Xl7B44y6q6nzrgGQkx/gX8UskN3P/9wJV13nYVFLZ+Bufhi+WG3ZWsmVXFR7v/nPesBAjLiqM2AhfQh8X5Uvq4yJD+ctFowNyLbpi8n4zEA1UAX+kFcm7mR0HzMOXnL8EnAP8EpjknJvv73MH8FPgMiAf+DkwGchxzu050PGVvIsEjz3VdcxfW0Kdx7vPaLPXOTxefO0eLx7nSxq8zpHTtxfjBidpWkAJeks27eKxD9fx5pfb9ko6wkKMPr2j6J8QRb94X919v95R9EuIpn98NH3jo8jfvodnPt3QLEHO4MQRae2StNbUewgxO6wvvTt2V/Ovzzfx/Gcb2VpWTWqvSC4cO4gLxg5iYOLeN/DW1HuYv7aE2fmFzF5ZyPqdvhuBs9LimDE8jRk5aeRmJhIeGoLH61i/s4KC7XvI37GHfP/z+uKKxhvNI0JDGJoWR3afONJ6RZIYG0FiTASJMeG+Z//7hJjw/X7GhlmnGha121hSyeaSr9833IR+OMJCvk7wI8JC/K+/Tvi/bjMiwnxlbxFhIUQ2PkIb30eEhhAZ7nuOCAslLMQw85VHhZjvdYiZ/wEhIU1em7GxpJI5BUV8smYnVXUeIsJCGD84iek5aUzPSW31zfU19R6Wb93dmMwvblKqBRBiMKJfb8ZmJjE2M4nczMR2uVeizuNlS2kVpZW1VNR4KK+pp7ymnopmzw2vK2o87Kmpp7bey1vfn3LY5z8UXS55b2BmucDntC55/xeQ5Jw7qUnbe0CRc+4i/6j7VuAvzrn/598eDRQCtzrn/nag4yt5FxGRztZQc79tVzXbyqrYWlbN9rJqtu6qYpv/dUtlUYkx4ZyfO4iLxqWTmXJ4pSkdyeN15OX7Snpm5xcCMCMnjfNzB1FSUcvs/EI+Wl1MZa2HyLAQJg5NbkzY2zJLT3Wdh9WF5RQ0SehX7ShnZ0UN1XX7LyvrFRX2dWIfG0FNnZeNJZX7zDoVHmoMTIxhUFIM6Um+G9vTk2IaE886j6Pe46XW46Xe46jzeKnzOurqvdR7vdT6t9d5vNQ1bPe/rq337v3e46Wufu/tNR4vNXWext+w1Xq8vvZ632/V2iPdy0iOYXp2KtNz0pgwJLndSpIaSrXCQkMYnZ5AryhNoQo9J3nfCPzZOXdPk7bbgBudcxlmNgRfKc0459znTfq8ARQ752Yd6PhHJSe7104/Y6+2XqedStLFF+OtqmLTNdfus0/82WeTcM7Z1JeWsuWm7++zPfGiC+l9+unUbdvG1tvv2Gd70uWX0+v4GdSsXcf2X/xin+0p119H7MSJVK9YwY7f/Haf7am33ELM6GOpXLSYoj/+cZ/tfX7yY6JGjKDi448p/uvD+2zv+8tfEjlkMHs+mE3JE0/ss73/3b8nvF8/dr/5JqXPPb/P9gF/eoCwxER2vfQyZS+/vM/2QY/8jZDoaEqefZY9b729z/aMfzwNwM7HHqc8L2+vbRYVRfqjjwBQ9NBDVH7y6V7bQxMSGPjnPwFQ+If7qFqyZK/tYX37MuCeuwHY/pvfULNi5V7bIzIz6fd/vwJg289+Tu369XttjxwxnL4/+QkAW267nfrt2/faHn3MMaT98AcAbP7eTXh27dpre8xxE0i94QYANl59Da567xKQuOnTSb7yCgA2XDqT5vSzp5890M+efvY+xeGraa+t91IVHceX1/yYpLhIct95lrovvthr/2D/2fOMn8jL2TN4/vNN3Pqm79pFhoWSEBNOQkw4/b55Fn1mXtLuP3se55u+te7cCyk9egLlq9eQ+PAfGqd5rfc46ryO/x17GuszRzG6ppAZ7/+z8QbwhhHutCD92XP+R8qfH6QuPJLdzz9P7Xvv4PxbG1LBsD89jNeB57l/4D750Le1YVtMNDlPPgbo373O+ncv85l/tJi8d7ffG/cFdjRr2+Fvp8lzS30GtHRAf+39NQDDe/VqnyhFRETaiUFjOUXv3lGMmTQYgML3Qjj8wo3OFR8dwQ9PzuGmE7JYsfgxwsNCiA4PpaEYIzysY+qOQ80IDTP6JcUyKiuFmtA9bG+hVGPKt474+ovjosDOzd8W5n/ERoYREh0B0WHsCdu3HCgjzZfn7OwVRXmz0kLTOgBBo1NH3s3s18CdB+k2wzmX12Sftoy81wJXOeeebtI2E3jUORdpZhOBj4AM59zGJn0eBwY450450PFVNiMiIiIinWF/ZTOdPfJ+P/DMQfpsPMj2A9kO9GnW1sffTpPnPs3O07SPiIiIiEhQ6tTk3TlXDBR34Ck+AU4C7mnSdhLwsf/1OnxJ+kn4RvMxsyhgCnBbB8YlIiIiInLYgrbm3cz64qtRz/Y3jTSzBGCjc67E3+d94DPn3I/9fR4A5prZj4BXgLOBGfimgsQ558zsfuAnZrYSKMA3bWQ58GwnfCwRERERkUMWzKuSXAcsBv7pf/+G//03mvQZCvRreOOc+xi4EN8c7l8AM4ELGuZ497sb37zxDwIL/PuffLA53kVEREREAi3op4oMJrphVUREREQ6w/5uWA3mkXcREREREWlCybuIiIiISBehspk2MLMiYEOATp9Cx87UI8FF17tn0fXuWXS9exZd756nva55hnMutXmjkvcuwswWtFT3JN2TrnfPouvds+h69yy63j1PR19zlc2IiIiIiHQRSt5FRERERLoIJe9dxyOBDkA6la53z6Lr3bPoevcsut49T4dec9W8i4iIiIh0ERp5FxERERHpIpS8i4iIiIh0EUreRURERES6CCXvIiIiIiJdhJJ3EREREZEuQsm7iIiIiEgXoeRdRERERKSLUPIuIiIiItJFKHkXEREREekilLyLiIiIiHQRSt5FRERERLoIJe8iIiIiIl2EkncRERERkS5CybuIiIiISBeh5F1EREREpItQ8i4iIiIi0kUoeRcRERER6SKUvIuIiIiIdBFK3kVEREREuggl7yIiIiIiXYSSdxERERGRLkLJu4iIiIhIF6HkXURERESki1DyLiIiIiLSRSh5FxERERHpIpS8i4iIiIh0EUreRURERES6CCXvIiIiIiJdhJJ3EREREZEuQsm7iIiIiEgXoeRdRERERKSLUPIuIiIiItJFKHkXEREREekiwgIdQFeSkpLiMjMzA3LuiooKYmNjA3Ju6Xy63j2LrnfPouvds+h69zztdc0XLlxY7JxLbd6u5L0NMjMzWbBgQUDOnZeXx/Tp0wNybul8ut49i653z6Lr3bPoevc87XXNzWxDS+0qmxERERER6SKUvIuIiIiIdBFK3kVEREREuggl7yIiIiIiXYSSdxERERGRLkLJu4iIiIhIF6GpIkVERERE/JxzrCosZ/bKQvJ37OG+848JdEh7UfIuIiIiIj1aZW09H6/eyez8QvLyi9iyqwqA4X17sae6jl5R4QGO8GsBTd7N7E+HsNtdzrmSdg9GRERERHqM9cUVzM4vZHZ+EZ+u3UltvZeYiFAmD0vhxuOHMT0nlX7x0YEOcx+BHnm/EfgEqG1l/8nA/YCSdxERERFpteo6D5+tK2kcXV9XXAHA0NRYZk7IYMbwNHIzE4kMCw1wpAcW6OQd4GznXGFrOprZno4ORkRERES6j12VtTz58Xqe/Hg9uyrriAwLYeLQZC6flMn07DTSk2MCHWKbBDp5vxwoa0P/a4EdHRSLiIiIiHQTRXtq+PuHa3nmkw1U1Ho4cUQfLh4/iOOGpBAdEdyj6wcS0OTdOfdUG/s/21GxiIiIiEjXt3VXFY/MXctzn22k1uPlzKP6c8P0oYzo1zvQobWLQI+8t8jMEmg2B71uUhURERGR/VlfXMHDc9bw4qLNOAdnHzuA66cPZUhqXKBDa1dBk7ybWQbwMDAdiGi6CXBA1/39hoiIiIh0iIIde3hw9mpeW7qVsNAQLhqXzjVThzAwsWvVsrdW0CTvwBNAAnAlsBVfwi4iIiIiso8vN5fxl9mr+N9XO4iJCOWqKUO4avJg0npHBTq0DhVMyfs4YIJzblmgAxERERGR4FJd52H+uhLm5BeRV1DI2qIKekeFcdMJWVw+MZPE2IiDH6QbCKbkfR0QGeggRERERCQ4rCuuYE5+IXkFvoWUquu8RISFMGFIMjMnZHDumIFBtfppZwim5P37wG/N7Abn3OpAByMiIiIinauytp5P1+4kL7+IOQVFbNhZCcDglFguHJvOtJxUJgxO7tJTPR6uYEre/4tv5D3fzGqA+qYbnXPdY34fEREREWlUUlHLa0u38t6KHcxfV0JtvZfo8FAmDk3mysmDmZadSkZybKDDDBrBlLzfGOgARERERKTj1dR7+GBFIS8u2kJefiH1XsewtDhmTshgek4auZmJRIX33NH1Awma5L2tCzaJiIiISNfhnGPRxl28tGgzr3+xjbKqOtJ6RXLl5MGcPXoAw/uqyKI1Apq8m1lSw+JLZpZ0oL5apElERESk69lUUslLi7bw8uLNrN9ZSXR4KKeM6sM5owcyaVgKoSEW6BC7lECPvBeZWT/nXCFQTMtzu2uRJhEREZEuZHd1HW9+sY2XFm3hs/UlmMFxQ5K58fgsTj2iL3GRgU5Bu65A/8kdDzSMqM8IZCAiIiIicmicc6wpqiAvv5A5BUXMX1tCrcfL0NRYbjslh28dO4ABCdGBDrNbCGjy7pyb09JrEREREQlulbX1fLx6J3kFheTlF7G5tArAd+PpcRmcdXR/jhoYj5nKYtpToEfe9+GvfU8DQpq2O+eWByYiEREREfGNrpeTl19EXn4Rn63zja7HRIQycWgK100byrTsVAYlxQQ61G4taJJ3MzsWeAI4sqEJX617q2vezezHwDlADlADfAr82Dm3rEkfA34BXAMkAvOB7zrnvmq3DyMiIiLSDZTX1PPJmp3MaTa6npUWx6yJX0/rGBmmWxM7S9Ak78DjwBZ8K63uoOWbVw9mOvAQ8Dm+pP9XwHtmNrLJbDW3Az8ELgPygZ8D75pZjnNuz+F8ABEREZGuzDnHyu17mFNQxJz8IhZsKKHO44iJCGXSsBSun+4bXR+YqNH1QAmm5D0LOM85t/pQD+CcO6XpezO7FCgDJgGv+UfdbwZ+55x70d9nFlAIXAz87VDPLSIiItIV7aqs5cPVxczJL2JOQRGFe2oAGN63F1f4VzjNzUgiIizkIEeSzhBMyfuHwAjgkJP3FvTCVztf6n8/GOgLvNPQwTlXZWZzgYkoeRcREZFuzuN1fLF5l290vaCIpZt24XUQHx3O5KwUpmWnMi07lT69owIdqrTAnDuU6pT2Z2YDgL8DbwPLgLqm251zcw/hmP/GN6Kf65zzmNlE4CMgwzm3sUm/x4EBzUfu/duuwVcfT58+fcY8//zzbQ2jXZSXlxMXFxeQc0vn0/XuWXS9exZd754lWK53WY1jWXE9XxR5WLbTQ0Wdr754cHwIR6aEcmRKKIPjQ7RgUjtor2s+Y8aMhc653ObtwTTyngUcC+yTQHMIizSZ2X3AZGCyc85zqEE55x4BHgHIzc1106dPP9RDHZa8vDwCdW7pfLrePYuud8+i692zBOp613u8LN60izn5ReQVFLJsy24AUuIiOOXIvkzPSWPysBSSYiM6PbburqOveTAl738D3gN+y6HfsAqAmf0RuBCY4Zxb22TTdv9zH2Bjk/Y+TbaJiIiIdDnby6qZW+BL1uetKmZPdT2hIcbo9ARuOyWHadmpjOzXmxCNrndpwZS8DwROd86tOZyDmNkDwAX4EveVzTavw5ekn4RvRhrMLAqYAtx2OOcVERER6UzOOZZuLuPtZdvJyy9k5XbfpHl9ekdy+hH9mJaTyqRhKcRHhwc4UmlPwZS8vwuMAQ45eTezB4FLgW8BpWbW17+p3DlX7pxzZnY/8BMzWwkUAD8FyoFnDyN2ERERkU6xurCcV5ds4b9Lt7JhZyXhoUZuRhI/Om0403NSyenTS6uadmPBlLy/DfzBzI4CvmTfG1ZfasUxbvA/v9+s/ZfAXf7XdwPRwIN8vUjTyZrjXURERILV9rJqXlu6lf8u3cKyLbsJMZg4NIXvzhjGKaP6anS9Bwmm5P0h//NPWtjWqhtWnXMH/ZrpfNPr3MXXybyIiIhI0CmrrOOtZdv475KtfLpuJ87B0QPj+dmZIznrqH6kaSrHHiloknfnnGb+FxERkR6tus7D+ysK+e+SLeTlF1Hr8TIkJZbvn5DFN48ZwOCU2ECHKAEWNMm7iIiISE9UuKea2SsLeW9FIR+uKqaqzkNar0hmHpfBN48ZwBEDequGXRoFNHk3s/OBV5xzta3sfzbwP+dcZcdGJiIiItIxnHN8tXU3H6ws5P0VO1i6uQyA/vFRfHvMQE49oi8ThiRrwSRpUaBH3p8D+gJFrez/FHAMsPYg/URERESCRnWdh4/XFPP+ikI+WFnItrJqzODogQncenI2J4zow/C+miVGDi7QybsBz5hZTSv7684MERER6RK2l1UzZ1Mdzzy1gI9W+8phYiJCmZKVwi0nZTMjJ43UXpGBDlO6mEAn70+1sf8/gd0dEYiIiIjI4aj3eFm0cRez8wvJyy9ixTZfyjIgYTfn5Q7k+OFpTBiSTFT4QSfQE9mvgCbvzrnLA3l+ERERkcNRuLuavIIi5uQXMXdVEXuq6wkNMXIzErnj1OHE7dnAd86coXIYaTeBHnkXERER6TI8XseSTaXMXlnE7PxCvtrqG11P6xXJaUf0ZUZOGpOyUugd5Vs0KS9vkxJ3aVdK3kVEREQOYMfuauYUFDG3oIh5q4opq6ojNMQYnZ7AbafkMD0nlZH9NJ2jdA4l7yIiIiJN1NR7+HxdKXNX+RL2ldv3AJDaK5ITR/RhxvBUpgxLJT4mPMCRSk+k5F1ERER6NOcc64orGkfXP11bQlWdh/BQY2xmEj86bThTs1IZ0U9TOUrgKXkXERGRHmdPdR0fr9nZmLBvLq0CIDM5hvNzBzI1O5UJQ5KJjVSqJMElqH4izew04LvAEOAU59wmM7sKWOecez+w0YmIiEhX5Zxj+bbdzPHPDLNwQyn1XkdsRCjHDU3h2mlDmZaVSnpyTKBDFTmgoEnezewS4GHg78AJQEMhWShwO6DkXURERFqttKKWeauLG6dxLNrjWxNyZL/eXD11CFOzUhmTkUhEWEiAIxVpvaBJ3vEl6Fc75573j7Y3+BT4VYBiEhERkS7C43Us3byLOflFzCkoYunmXTgHCTHhTMlKZVp2KlOzUkjrrQXbpesKpuQ9C/ikhfZyoHcnxyIiIiJdwNZdVcxb5ZvC8cPVxeyqrMMMjh6YwE3HZzE9J5WjBiYQGqIbTaV7CKbkfSuQDWxo1j4VWNP54YiIiEiwqaipZ/66ncwtKGbeqiLWFFUAvkWSThjeh2k5qUwZlkJibESAIxXpGMGUvD8C/KlJycwgM5sC3A3cFbCoREREJGC8XseyrWXMW+VL1hduKKXO44gKD2H84GQuGpfO1OxUstLiNI2j9AhBk7w75+42s3jgXSAKmA3UAPc65x4MaHAiIiLSaXbsrm68yfSj1cWUVtYBMKp/b66cPIQpWSmMyUgkKjw0wJGKdL6gSd4BnHN3mtn/A0YCIcBy51x5gMMSERGRDlTv8bJ40y7y8guZvbKI5dt2A9CndyTHD+/D1OwUJg1LISUuMsCRigReUCXvAM65SmBBoOMQERGRjlO0p4Y5BUXMzi9kXkERu6vrCQ0xcjMSuePU4UzPSWV4X61oKtJc0CTvZhYJ3ADMANLwjbw3cs6NC0RcIiIicvg8XscS/+h6Xn4RX24pA3w3mp56RF9m5KQxKSuF3lHhBzmSSM8WNMk78ChwJvBfYDngAhuOiIiIHI6iPTXMLSgir6CIeauK2FVZR4jB6PREbjslh+k5qYzs11uj6yJtEEzJ+zeAbzrn5gQ6EBEREWm7prXrcwqKWLbFV7ueEhfJ8cPTmJGTxpSsFBJiNI2jyKEKpuS9ECgOdBAiIiLSetvLqv2j64XMW1XMHn/t+uj0BG47JYdp2b7R9RAtkiTSLoIpef8J8Bszu8w5VxroYERERGRfdR4vC9aXkldQyJz8IlZu3wP4ZoY57Yi+TM9JY9KwFOKjVbsu0hGCKXl/B7gWKDSz7UBd043OuSEBiUpERKSH21ZWRV5+EXn5hXy0eiflNfWEhRi5mYn86LThTMvWzDAinSWYkven8c3vfj+wA92wKiIiEhC19V4WbChhTn4ReflF5O/wja73j4/irKP7My07lUnDkumlmWFEOl0wJe8nAcc75+YfzkHMbCpwKzAG6A9c7px7ssn2J4FZzXab75ybcDjnFRER6cq27mo6ul5MRa2H8FBjbGYSPxkznOk5aWSlxWl0XSTAgil53wjUtMNx4oBl+Ebyn95Pn/eAS5u8r22H84qIiHQZNfUeFqwvZU6BL2Ev2OFb0HxAQjTfPHYA07NTmTgshbjIYEoVRCSY/kbeAtxtZjc451Yf6kGcc28Cb0LjKHtLapxz2w/1HCIiIl3RppLKxmkcP16zk8paDxGhIYwdnMh5YwYxPSeVYRpdFwlqwZS8/weIBPLNrAaob7rROde7Hc812cwKgV3AHOBO51xhOx5fREQk4KrrPHy6didzCoqYk1/E2uIKAAYlRXPu6IFMz0llwpBkYjW6LtJlmHPBcV+omTWvQ9+Lc+6pQzhmOXBjs5r3C4FKYB2QCfwaCAXGOOf2Kdsxs2uAawD69Okz5vnnn29rGO2ivLycuLi4gJxbOp+ud8+i692zdOT1ds6xo9LxZZGHL4o9rCzxUOeF8BAYnhTKUSmhHJkaSp8Y0+h6J9Hf756nva75jBkzFjrncpu3B03y3hFaSt5b6NMf2ABc4Jx76UDHy83NdQsWLGjfIFspLy+P6dOnB+Tc0vl0vXsWXe+epb2vd1llHR+vKWbuqiLmFhSzZVcVAENSYpmWk8q0bN/oelR4aLudU1pPf797nva65mbWYvIe0N+TmVmSc66k4fWB+jb0a2/Oua1mthnI6ojji4iItKd6j5elm3cxp6CYeauKWLppF14HvSLDOG5oMtdPH8rUrFTSk2MCHaqIdIBAF7kVmVk/f715MS3P7W7+9g4ZMjCzFGAAsK0jji8iInK4NpVUMqegiHmrivh49U721NQTYnDUwARunDGMqdmpHD0ogfDQkECHKiIdLNDJ+/FASZPXh13DY2ZxwDD/2xAg3cyO8Z+nBLgLeBFfsp4J/BYoBF4+3HOLiIi0h4qaej5Zs9NfClPE+p2VgG+RpDOO6seULN8iSQkxEQGOVEQ6W0CTd+fcnCav89rpsLnA7Cbvf+l/PAVcDxwJzAQS8CXws4HznXN72un8IiIibeKcY8W2Pcxd5ZsVZsGGEuo8jujwUCYMSWLWxEymZKUyNDVWN5qK9HCBHnlvZGYeoF/zKRvNLBkodM61qmzG/yXgQP+ynXLIQYqIiLSTkopa5vlvMp27qoiiPb4Jz4b37cXlkwYzNSuV3MxE3WgqInsJmuSd/SfckWgFVBER6eI8XseSTaW8uKqWPy77kC+2lOEcxEeHMyUrhanZqUzNSqVvfFSgQxWRIBbw5N3MfuB/6YDr/NM7NggFpgArOz0wERGRw1RcXsOc/CLyCny162VVdRhwbLpx8wnZTM1O4aiBCYSGqBRGRFon4Mk78D3/swFXAZ4m22qB9cB1nRyTiIhIm3m8jqWbd5GXX0RefiFfbC4DICUukhNH9GHG8FRsRz5nnDQpwJGKSFcV8OTdOTcYwMxmA+c450oDHJKIiEirlVTUMrfAl6zPKSiitLKOEINjBiXww5OymZ6Txqj+vQnxj67n5RUEOGIR6coCnrw3cM7NCHQMIiIiB9MwM8wHK3fw/spClmzahXOQFBvBjJw0puX4atcTYzWNo4i0v6BJ3gHM7ALgBCAN3xztjZxz3whIUCIi0uNV13n4eE0x768o5IOVhWwrqwbgqIHxfP+ELGbkpHHkgPjG0XURkY4SNMm7md0D3Ixv3vWttMOCTSIiIodqe1k1H6ws5IOVO/hwdTHVdV5iIkKZkpXCLSdmM314Kmm9NDOMiHSuoEne8S2cdJFz7oVAByIiIj2P1+v4cksZ7/sT9mVbdgMwMDGaC3IHccKIPowfkkRkmOZdF5HACabkPQRYEuggRESk5yitqG1c1XTuqiKKy2sJMRiTkcgdpw7nhBFpZKXFaVVTEQkawZS8PwJ8B7grwHGIiEg31TC6npdfRF5BIUs37cLrIDEmnKnZqUzPSWV6dppuNhWRoBVMyXsCcLGZnQR8AdQ13eicuykQQYmISNfWMJXjHP9CSTsrajGDowcm8L3js5iek6qFkkSkywim5H0kX5fNDG+2TTeviohIq3i8ji8272JOQRF5+UUs3fz1VI7T/KPrU7JSSdLouoh0QUGTvGuedxEROVRFe2oaR9fnrfItlGT+hZJuPiGb6TmpmspRRLqFoEneRUREWqve42Xxpl2Nq5o2zAyTEhfBjOFpTM9JY8qwFNWui0i3EzTJu5m9eqDtWqRJRKRn215WzZyCQvLyi/hwdTF7qusJDTFGpydw2yk5TMtOZWS/3hpdF5FuLWiSd2Bns/fhwNHAIOClzg9HREQCbWd5DW9+uY3/LtnKgg2lAPTpHcnpR/RjWk4qk4alEB8dHuAoRUQ6T9Ak7865y1tqN7M/ALs7ORwREQmQipp63l2+g/8u2cK8VcXUex1ZaXHcenI2J47sQ06fXpp3XUR6rKBJ3g/gb8CHwC8DHYiIiHSMOo+XuQVF/HfJVt5dvoOqOg/946O4cspgvnn0AEb0U8IuIgJdI3nPCXQAIiLS/rxex8KNpbyyeAtvfrmN0so6EmLCOXv0AL51zAByMxJVvy4i0kzQJO9m9qfmTUA/4DTg8c6PSERE2lt1nYf560qYW1DE28u2s2VXFVHhIZw0si/fOqY/U7JSiQgLCXSYIiJBK2iSd+DIZu+9QBFwC0reRUS6JOccqwvLfaubripm/tqd1NR7iQgLYdLQZG47JYeTRvYhNjKY/jsSEQleQfOv5YEWaTKzWKC+E8MREZFDVFZVx8eri30Je0ERW8uqARiaGssl4zOYmp3C+MHJREeEBjhSEZGuJ2iS95aYWRRwI3A7kBbgcEREpAVer+PLLWXM8a9wumTTLjxeR6/IMCYNS+HG41OZmp3CwMSYQIcqItLlBTx5N7MI4BfAyUAdcLdz7hUzmwn8DnDAHwMYooiINFNeU8+Hq4r5YOUOPlhZRHF5DWZw1IB4bpg+lKnZqRwzKIHwUNWvi4i0p4An78BdwHeBd4FJwH/M7FHgBODHwLPOubrAhSciIgCbSip5f8UO3l9ZyPy1JdR6vPSKCmN6ThrHD09lWnYaSbERgQ5TRKRbC4bk/XzgMufcy2Z2NLAYSARGOedU5y4iEiAer2PRxlLeX1HIByt3ULCjHIAhqbHMmpjBCSP6MCYjUaPrIiKdKBiS90HA5wDOuaVmVgv8Xom7iEjn21lew4eri8nLL2J2fiG7KusICzHGDU7igrHpHD88jcEpsYEOU0SkxwqG5D0cqGnyvg4oC1AsIiI9Sp3Hy6INpcxdVcTcgmKWbS3DOUiMCef44WmcMLwPU7JT6B0VHuhQRUSE4EjeAX5rZpX+1xHAXWa2VwLvnLupNQcys6nArcAYoD9wuXPuySbbDd8NstfgK8+ZD3zXOffV4X4IEZGuYMPOCuYWFDGnoJhP1hRTUeshNMQYnZ7AD07MZmp2KkcMiCdUq5uKiASdYEje5wJDm7z/GEhv1se14XhxwDLgaf+juduBHwKXAfnAz4F3zSzHObenDecREekSymvq+WTNTuYWFDF3VREbdvrGSgYmRvPNYwcwNSuVicOSNbouItIFBDx5d85Nb+fjvQm8CWBmTzbd5h91vxn4nXPuRX/bLKAQuBj4W3vGIiISCF6vY/m23Y2LJC3aWEqdxxEdHspxQ5O5fGImU7NTGZwSi++fRRER6SoCnrx3ssFAX+CdhgbnXJWZzQUmouRdRLqooj01zFvlS9Y/XF1McXktACP69eaKyYOZlpXKmMxEIsO0qqmISFdmzrWlIqVrMbNy4MaGmnczmwh8BGQ45zY26fc4MMA5d0oLx7gGX308ffr0GfP88893Ruj7KC8vJy4uLiDnls6n692zHMr1rvc6VpV6+bLYw7JiDxv3eAHoFQ6jUkI5MiWUUSmhJERqGsdgo7/fPYuud8/TXtd8xowZC51zuc3be9rIe5s55x4BHgHIzc1106dPD0gceXl5BOrc0vl0vXuW1l7v9cUVzF1VxJz8Ij5Zu5PKWg9hIcbojEQumJjK1KxURvXvTYhuNA1q+vvds+h69zwdfc17WvK+3f/cB9jYpL1Pk20iIkGh6Y2mcwqK2Fjiu9E0PSmGc0b7bjQ9bmgyvXSjqYhIj9HTkvd1+JL0k/AvDGVmUcAU4LYAxiUignN732i6cIPvRtOYiFAmDk3mqimDmZqVSqYWSRIR6bGCJnk3Mw/QzzlX2Kw9GSh0zrXqLisziwOG+d+GAOlmdgxQ4pzbaGb3Az8xs5VAAfBToBx4tl0+iIhIG+wsr+GTrfW8+u8lzC0oprjct2bdiH69uXLyEKZmpzAmQzeaioiIT9Ak78D+ijQjgdo2HCcXmN3k/S/9j6fwze1+NxANPMjXizSdrDneRaQz1Hm8LN64q3HO9S+3+FY0TYotYvKwFKZlpzIlO4W0XlGBDlVERIJQwJN3M/uB/6UDrvPPENMgFF9Jy8rWHs85l8f+vwjgfNPr3OV/iIh0uE0llcz1T+P48eqd7Kmp32tF09jyjVx21vG60VRERA4q4Mk78D3/swFXAZ4m22qB9cB1nRyTiMghq6r18Onanb7a9VVFrC2qAGBAQjRnHt2fadkpTByW0riiaV7eFiXuIiLSKgFP3p1zgwHMbDZwjnOuNMAhiYi0iXOOVYXlzMn3zQrz2foSauu9RIaFMGFIMt8Zn8HU7FSGpmpFUxEROTwBT94bOOdmBDoGEZHWKquq4+PVxczxT+O4rawagKy0OGZO8CXr4wYnERWuG01FRKT9BDR5N7M/AT92zlX4X++Xc+6mTgpLRGQfXq9j2dayxtH1xZt24fE6ekWGMTkrhe+fkMrU7FT6J0QHOlQREenGAj3yfiQQ3uS1iEjQKC6vYZ5/RdO5q4opqfBNfHXkgHiunzaUaTmpHDMogfDQkABHKiIiPUVAk/empTIqmxGRQKvzeFm4obRxGsdlW3YDkBwbwbTsVKZlpzI5K4WUuMgARyoiIj1VoEfeG5nZOOfcZ/vZ9h3n3DOdHZOIdH+bSiob69Y/WbOTcv80jmPSE7n15GymZqdyRP94zQYjIiJBIWiSd+B1M5vqnNtrTnczuxR4GFDyLiKHrbK2nk/X7mRuQTFzC4pYW/z1NI7fOKY/U7NSmTgsuXEaRxERkWASTMn7H4B3zGyic24zgJnNBP4KXBDQyESky3LOsXL7nsZSmM/XlVLr8RIV7p/GcUIG03JSGZKiaRxFRCT4BU3y7pz7vZmlAu+Z2WTgDHyJ+3nOuTcCG52IdCU7y2v40D+N47xVxRTtqQEgu08csyb6pnEcm6lpHEVEpOsJmuQdwDl3q5klA/OBvsC3nXNvBjgsEQlydR4vizaUMndVEXMLilm2tQznICEmnMnDUpiancrUrFT6xkcFOlQREZHDEuh53s9poflN4ATgOSCqoY9z7qXOjE1EgtumkkryCoqY2+xG02MHJXDLib4bTY8cEE+objQVEZFuJNAj7y8cYNsV/geAA/T7bZEerKbew4L1pcxeWcjs/ELWFH19o+lZR/dnWnYKxw1NIT5aN5qKiEj3Feh53rWyiYjs15ZdVeTlF5KXX8RHq4uprPUQERrC+CFJXDw+g+m60VRERHqYQI+8H5CZhTvn6gIdh4h0jjqPlwXrSxsT9vwdewDf6Po5owcwPTuNicOSiYkI6n+6REREOkzQ/A9oZjcBW5xzL/rfPw7MNLM1wDecc/kBDVBEOsTm0srGOdc/Wl3Mnpp6wkONsZlJ3DlmBDOGpzI0NU6j6yIiIgRR8g7chL/G3cymAucBFwPn4psD/szAhSYi7aW6zsOna3cyx3+zaUPtev/4KM48uh/Tc9KYNCyFuMhg+udJREQkOATT/44DgHX+12cB/3HO/dvMvgTmBS4sETkczjlWFZYzt6CIOQVFzF9XQm29l8iwEMYPSeaicelMz9HouoiISGsEU/K+G0gDNgEnAff42+sATc4s0oWUVdXx0epi5uT7VjXdVlYNwLC0OL4z3rei6fjBWiRJRESkrYIpeX8HeNTMFgHDgLf87aP4ekReRIJQw+j67JWFfLCykAUbSvF4Hb0iw5g0LIWbTkhlanYqAxKiAx2qiIhIlxZMyft3gf8HpONbWbXE3z4a34JNIhJEqmo9fLK2mA9WFjJ7ZRFbdlUBMLxvL66ZOoQZOWkcm55AeKhmhBUREWkvQZO8O+d2A99rof0XAQhHRFqwqaSS2fm+0fVP1uykpt5LdHgok4al8N0Zw5iek0p/ja6LiIh0mIAm72aW1DDCbmZJB+rbZCReRDpJdZ2Hz9aVMG9VEbPzi1hdWA5AZnIMF49PZ0ZOGuOHJBEZptp1ERGRzhDokfciM+vnnCsEigHXQh/ztys7EOlgzjnyd+xhXkExc1d9PTNMRGgI4wYncdG4dI4fnsbglNhAhyoiItIjBTp5Px5oGFGfEchARHqq4vIaPlpdzNyCYuatKqJwTw0AWWlxXDohgylZKYwfnEx0hL4/i4iIBFpAk3fn3JyWXotIx/F4HZ+vL2FOQRHzVhWxbMtuABJiwpk8LIWp2alMyUqhX7xq10VERIJNoEfeG5lZf3yrqeYAtUA+8G/nXGlAAxPpJvK37+GlxZv57+KtbN9dTViIMTojkVtPzmZqdiqj+scTGqJFkkRERIJZUCTvZnYtcD8QiW+xJoDewH1mdqVz7nl/v2Occ0sCEqRIF1S0p4b/LtnCy4u38NXW3YSFGNOyU/npmSOYlp1Kr6jwQIcoIiIibRDw5N3MTgUeBP4M3OOc2+pv7w/cAfzDzDYCNwAFwJLDPN9dQPPpJ3c45/oeznFFgkV1nYd3lu/gpUWbmbeqGI/XcdTAeH5x1kjOOro/KXGRgQ5RREREDlHAk3fgNuBe59yPmjb6k/jvm1kV8B6wE7irnc6ZD0xv8t7TTscVCQiv1zF/XQkvL97Mm19up7ymnv7xUVw7dQjnjB7AsLRegQ5RRERE2kEwJO+5wI0H2P4UcDswwzm3up3OWe+c295OxxIJiNp6LwvWl/DBykLeWradLbuqiI0I5bQj+3HO6AFMGJxMiGrYRUREupVgSN7DgaoDbK8CqtsxcQcYYmZbgRpgPvAT59zadjy+SIco2lNDXn4hs/MLmVtQTHlNPRGhIUwclsztp+Zw8si+mtJRRESkGzPnWloXqRMDMFsM/NU598h+tl8LXOecO7adznca0AtYCaQBPwWGA6Occztb6H8NcA1Anz59xjz//PPtEUablZeXExcXF5BzS+druN5e59iw28vSIg9LizysK/MCkBBpHJUayjGpoYxMDiUqTCPsXZn+fvcsut49i653z9Ne13zGjBkLnXO5zduDIXn/HvBLYJZz7rVm274BPAn83Dn3lw46fxywFvidc+6+A/XNzc11CxYs6IgwDiovL4/p06cH5NzSucpr6nn45Tx2hKaSV1BE0Z4azOCYQQkcn5PGjOFpjOrfGzMl7N2F/n73LLrePYuud8/TXtfczFpM3oOhbOYvwCTgv2ZWAKzwt48AsvDN9d4hiTuAc67czL7yn0skIGrqPcxeWcR/l2zh/ZWF1NZ76R21nanZqRw/PI1p2akka5YYERGRHi/gybvzDf1faGYvARfjW6QJfGUtP3PO/acjz29mUfjKZmZ35HlEmvN6HZ+tL+G/S7bwxhfb2F1dT0pcBBePS6df/Xau/OYMwkJDAh2miIiIBJGAJ+8NnHP/Bv7d0ecxs3uB14CN+GrefwbE4pvVRqTDrdy+m1cWb+XVJVvYWlZNTEQop4zqyzeP6c/kYSmEhYaQl1ekxF1ERET2ETTJeycaCDwHpABFwKfABOfchoBGJd3a1l1VvLp0K68s3sLK7XsIDTGmZqVwx2nDOWlkH2IieuJfRREREWmrHpcxOOcuDHQM0v0551hXXEFefhHvLN/O/HUlOAfHpifwy2+M4oyj+mmlUxEREWmzHpe8i3SUqloPn6wtJi+/iLz8IjaWVAIwNDWWm0/I5lvH9icjOTbAUYqIiEhXpuRd5BA551jrH13Pyy9k/roSauu9RIWHMGloCldPGcy07DTSk2MCHaqIiIh0E0reRdqg6ej67PxCNpX4FgcekhrLd8ZnMD0nlXGDk4gK1yqnIiIi0v4Cmryb2Z9a29c5d1NHxiKyP+uKK5i9spDZTUbXo8NDmTg0mWumDGF6ThqDkjS6LiIiIh0v0CPvR7ayX2CXgZUepbrOwydrd5K3spC8giI27PTVrg9JjeXSCb7R9bGZGl0XERGRzhfQ5N05NyOQ5xdpsGGnb3Q9r6CIT9bspMZfuz5xaApXTh7MdNWui4iISBAI9Mi7SEBU13n4dO1O5hT4ZoZZV1wBwOCUWC4al86M4WmMV+26iIiIBJmgSt7NLBv4NpAORDTd5py7IiBBSbfgnGNNUQVzCoqYU1DE/LW+0fWIsBCOG5LMrOMymJ6TRmaKpnIUERGR4BU0ybuZnQG8CCwGxgCfA0OBSGBeAEOTLmpPdR0fr/GNrs/JL2LLrq9nhrl4fDrTslMZPziZ6AiNrouIiEjXEDTJO/Ar4JfOud+a2R7gUmAr8A/gk4BGJl2Cc44V2/aQV1DInPwiFm4opd7riI0IZdKwFK6fPpRp2amaGUZERES6rGBK3nOAf/lf1wExzrlqM/sV8AZwX8Aik6C1q7KWeauKG8thivbUADCyX2+unjqEadmpjE5PJCIsJMCRioiIiBy+YEre9wBR/tfbgGHAMnwxJgYqKAkuXq/jyy1l/htNC1myaRdeBwkx4UzJSmVqVgrTslNJ6x118IOJiIiIdDHBlLzPByYDy/GNtP/BzI4GzkZlMz3azvIa5q7y1a3PXVVMSUUtZnDUwARuPD6L6TmpHD0wgdAQC3SoIiIiIh0qmJL3HwBx/td3Ab2Ac4EC/zbpIeo9XpZu3sWc/CLyCor4cksZzkFybATTslOZnpPK5GEpJMdFBjpUERERkU4VNMm7c25tk9eVwPUBDEc62bayKub669Y/XFXM7up6QgyOTU/kBydmMy0nlSP6xxOi0XURERHpwYImeTeztcBY59zOZu0JwCLn3JCABCYdoqbew4L1pY3TOObv2ANAn96RnHpEX6ZlpzF5WArxMeEBjlREREQkeARN8g5kAi1NuB0JDOjcUKQjrC/2LZI0t6CIj9fspKrOQ3ioMW5wEueOGc607DSy+8RhptF1ERERkZYEPHk3s3OavD3DzMqavA8FTgDWd2pQ0i4qa/9/e3cfZFV933H8/VmeRCCI7LLiA/K4oFWLig4qDysNCY2Z1jQPxjREnRGT0FibxNSY0ZR0Jp02tUZTdYyZZKRpLGnTGEtTq9GAIBAVrFVUnoQAirDL47qALLt8+8c5i7dXiKi799x77uc1c+fe+/udPb8vfLnLd3/7O+fXzrLOTZLWNLNxxz4Ahp14PJ+ccCpTG+qYOHIw/fpk/s/QzMzMrCKUQ9X0s/Q5gB8W9R0kKdy/WsqA7L2JCNZsa+WJNU08saaZZzbsoq3jEH179eCiUYO55uLhNI4dwvDaflmHamZmZlaRMi/eI6IGQNIGkjXv2zMOyd6FPfsPsmTddp5Yncyub215E4Cx9QO4+pLhTBlTx4Thgziu15FWRJmZmZnZu5F58d4pIkZkHYO9s4jgxS0tLFiVzK7/z+bddBwKBhzXk8npBklTGuoYOrBv1qGamZmZ5U7ZFO8Aki4DbgLOJFlG8xLwdxHxX5kGVuX2tbWzZN0Ofr1qGwtWvTW7fs6pA5ndOIqpDXWMP+0EevaoyThSMzMzs3wrm+Jd0rXAPcBPgLlp82TgQUlfjIgfZRZcFdq8cx8LVjfx+MtNLFu/g7b2Q/Tvk8yuTxs3hMaxQ6gb4E2SzMzMzEqpbIp3khn3r0TEXQVtP5S0Avg64OK9G7V3HOLZTbt5fNU2FqxqYs22VgBG1PZj5sTTmTZuCBcMP5HePT27bmZmZpaVcirehwH/fYT2h4HbShxLVWhqefPwbRwXr93Onv0H6VmT3Hf9UxNOY9q4IYys6591mGZmZmaWKqfifRMwHVhX1P4hYGPpw8mftvZDrNi463DB/vLrLQDUDejDB8+o5w/OGMKkMbV84DjvampmZmZWjjIv3iX9CLiBZHb9HyWdByxNuy8BZgLXZxRexdu8c9/hYn3puu3sbeugZ42YMHwQN80Yx9SGOs4YOsC7mpqZmZlVgMyLd+Aq4OsR8X1JTSQbMnXuuvoy8KmIeCiz6CrM/rYOntrw1q6m65v3AnDqoL5cfu4pTG2o4+LRtfT3rqZmZmZmFaccKrjDU74R8SDwYEkGlWYDXwOGAi8CfxERi0sxdleKCFZve4NFa5pZtGY7T/92J23th+jTs4aLRg1m5sTTmdJQx8jafp5dNzMzM6tw5VC8Q3JP95KRdAVwJzAbeDJ9fljSmRGxqZSxvBc797axeG1SrC9e20zTGwcAaKjvz+fSYv3CESd6V1MzMzOznCmX4n3rO80KR0RXVqJfAe6PiB+k76+XNAP4InBzF47TJQ52HGL1zg6WP7KaRWubeeG1PUTACcf3YtLoWqY01DF5TK13NTUzMzPLuXIp3q8DdpdiIEm9gfN5++0nHwUuLkUM78ayV3Yw65+W03qgnR41r3DuaSfw5Q82MKWhjrNPGUiPGi+FMTMzM6sWiijpipW3ByAdAk6KiKYSjXcy8BowNSIWFbR/E/jTiBhbdPx1JD9cUF9ff/68efNKEeZhLQeCn69tY3T/g5x7Sj/69XKxXg1aW1vp39/32K8Wznd1cb6ri/Ndfboq55deeumKiJhQ3F4OM+/Z/vTwDiLiPuA+gAkTJkRjY2PJY/ijD8PChQvJYmzLhvNdXZzv6uJ8Vxfnu/p0d87LYa/7Uk8lbwc6gPqi9npga4ljMTMzMzM7ZpkX7xFRU6olM+l4bcAKkt1cC03nrc2hzMzMzMzKTjksm8nC7cCPJT0NLAG+AJwM3JtpVGZmZmZmv0NVFu8R8VNJg4FbSDZpWgl8JCI2ZhuZmZmZmdnRVWXxDhAR9wD3ZB2HmZmZmdmxynzNu5mZmZmZHRsX72ZmZmZmFSLzTZoqiaRmIKt18bUkt7m06uB8Vxfnu7o439XF+a4+XZXz0yOirrjRxXuFkLT8SLtsWT4539XF+a4uznd1cb6rT3fn3MtmzMzMzMwqhIt3MzMzM7MK4eK9ctyXdQBWUs53dXG+q4vzXV2c7+rTrTn3mnczMzMzswrhmXczMzMzswrh4t3MzMzMrEK4eC9zkmZL2iDpTUkrJE3OOiZ7/yRNkfQfkl6TFJKuLuqXpDmStkjaL2mhpN/LKFx7nyTdLOkZSS2SmiXNl3RW0THOeU5I+jNJz6f5bpG0TNJlBf3OdY6ln/eQdFdBm3OeI2kuo+ixtaC/W/Pt4r2MSboCuBP4G+BcYCnwsKRhmQZmXaE/sBK4Adh/hP6/BL4KXA9cADQBv5I0oGQRWldqBO4BLgamAe3AY5JOLDjGOc+PV4GbgPOACcCvgV9IOiftd65zStJE4Drg+aIu5zx/VgNDCx5nF/R1a759wWoZk/QU8HxEzCpoWwv8LCJuzi4y60qSWoEvRcT96XsBW4C7IuLbaVtfkg//jRHx/axita4hqT+wB7g8IuY75/knaSdwM8ldKJzrHJI0EHgWuBb4K2BlRHzJn+/8kTQH+EREnHWEvm7Pt2fey5Sk3sD5wKNFXY+SzN5Zfo0ATqIg9xGxH1iEc58XA0i+/+5K3zvnOSWph6RPk/y2bSnOdZ7dRzK5tqCo3TnPp5HpspgNkuZJGpm2d3u+XbyXr1qgB7CtqH0byT8Ky6/O/Dr3+XUn8BywLH3vnOeMpLPT36odAO4FPhYRL+Bc55KkWcBo4JYjdDvn+fMUcDUwA5hFkselkgZTgnz37IqTmJnZsZF0OzAJmBQRHVnHY91mNTAeGAh8ApgrqTHDeKybSBpLcm3apIg4mHU81v0i4uHC95J+A6wHrgJ+093je+a9fG0HOoD6ovZ6YOvbD7cc6cyvc58zkr4LXAlMi4j1BV3Oec5ERFtErIuIFek1Ss8BX8a5zqOLSH5b/qKkdkntwFRgdvp6R3qcc55TEdEKvAiMoQSfcRfvZSoi2oAVwPSirukk6yYtvzaQfMAP517SccBknPuKJelO3ircVxV1O+f5VwP0wbnOo1+Q3GlkfMFjOTAvfb0G5zzX0nyOA16nBJ9xL5spb7cDP5b0NLAE+AJwMsn6Satg6d1GRqdva4BhksYDOyNik6Q7gG9IWkXyjf8WoBV4IINw7X2SdDcwE7gc2CWpc91ja0S0RkQ45/kh6W+BXwKbSS5O/gzJ7UIvc67zJyJ2A7sL2yTtJfl+vjJ9fwfOeW5Iug2YD2wChgC3Av2AuaX4jLt4L2MR8dP04odbSO4huhL4SERszDYy6wITgMI7EnwrfcwluQjmO0Bf4G5gEMnFMR+KiDdKG6Z1kdnp8+NF7d8C5qSvnfP8OAn45/R5D8k9v/8wIh5J+53r6uOc58upwL+QLJdqJlnnPrGgPuvWfPs+72ZmZmZmFcJr3s3MzMzMKoSLdzMzMzOzCuHi3czMzMysQrh4NzMzMzOrEC7ezczMzMwqhIt3MzMzM7MK4eLdzMwyJ2mOpJVZx2FmVu58n3czsyoj6X6gNiI+Wvi6RGMPJ9k+/IKIWF7Q3h/oExE7ShGHmVml8g6rZmb2vknqCXTEe5wRiohWku3Dzczsd/CyGTOzKiVpDnAVcJmkSB+Nad8pkuZJ2pU+filpTOHXSlop6WpJrwAHgH6SZkhanH7NTkmPSDqjYNgN6fMz6XgLC89XcP4aSbdK2izpgKQXJP1xQf/w9Os/LulXkvZJeknS9O752zIzKw8u3s3MqtdtwL8CjwFD08dSSccDC4A3ganARcDrwGNpX6cRwGeATwK/nx7fD7gDuBBoBPYA8yX1Tr/mwvR5RjrenxwlthuArwE3AWcDDwI/lzS+6LhvA99Lx38GmJcuwTEzyyUvmzEzq1IR0SppP3AgIrZ2tkv6LCDgms5lMJI+DzQBHyUp+AF6AzMjYlvBaf+9cAxJ1wAtJEX7k0Bz2rWjcMwjuBG4LSIeSN9/U9KUtP2zBcd9NyLmp2N9A/gcMD4dy8wsdzzzbmZmxc4nmVV/Q1KrpFaSGfRBwKiC414tKtyRNErSA5JekdQCbCP5v2bYsQ4u6QPAycCSoq4ngTOL2p4veL0lfR5yrGOZmVUaz7ybmVmxGuA54NNH6NtZ8HrvEfr/E3gV+DzwGtAOvEQyS98Vii+IPXi4IyIkgSemzCzHXLybmVW3NqBHUduzwJXA9ojYfawnkjQYGAfMjogFadt5/P//a9rS5+IxD4uIFklbgEuAxwu6JpH8IGBmVrU8O2FmVt1+C5wlaaykWkm9gJ+QLHd5SNJUSSMkTZH0D4V3nDmCXcB2YJak0ZKmAveSzL53agL2Ax+WVC9p4FHO9ffAjZKulNQg6a+BySQX2ZqZVS0X72Zm1e0HwMvAcpKLSS+JiH3AFGA98G/AKmAuyZr3XUc7UUQcAq4AzgFWAncDt5LcRrLzmHbgz4FrSdaoP3SU032PpID/TnqujwEfj4j/fY9/TjOzXPAOq2ZmZmZmFcIz72ZmZmZmFcLFu5mZmZlZhXDxbmZmZmZWIVy8m5mZmZlVCBfvZmZmZmYVwsW7mZmZmVmFcPFuZmZmZlYhXLybmZmZmVUIF+9mZmZmZhXi/wAjOyLjAr/uxQAAAABJRU5ErkJggg==\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "import matplotlib.pyplot as plt \n", + "\n", + "plt.rcParams['font.size'] = 14\n", + "\n", + "fig, (ax1, ax2) = plt.subplots(2, 1, sharex=True, figsize=(12,8))\n", + "\n", + "# plot loss and reference value\n", + "ax1.plot(loss, label='Qiskit Runtime VQE')\n", + "ax1.axhline(y=target_energy, color='tab:red', ls='--', label='Target energy')\n", + "\n", + "# plot time taken\n", + "ax2.plot(np.cumsum(runtimes_in_min))\n", + "\n", + "# settings\n", + "ax1.set_title('Qiskit Runtime VQE')\n", + "ax1.set_ylabel('Eigenvalue')\n", + "ax1.legend(loc='best')\n", + "ax2.set_ylabel('Total Qiskit Runtime [min]')\n", + "ax2.set_xlabel('Iteration')\n", + "ax2.grid();\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Qiskit Runtime VQE: Direct call\n", + "\n", + "Instead of interacting with the Qiskit Runtime via the `VQEProgram` we can also directly call the the Qiskit Program in the cloud -- like running the Quantum Kernel Alignment (QKA) or Circuit Runner. Under the hood, `VQEProgram` is nothing but a proxy that takes care of creating the dictionaries interaction with the Qiskit Program running in the Qiskit Runtime on the cloud." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To call the VQE code on the Qiskit Runtime on the cloud, we interact with the `run` method of the provider\n", + "```\n", + "provider.run(progam_id, inputs, options, callback)\n", + "```\n", + "where \n", + "* `program_id` determines the Qiskit Runtime program run, i.e. `'vqe'` for the VQE\n", + "* `inputs` contains the input for the VQE algorithm\n", + "* `options` specifies the backend\n", + "* `callback` is the callback used inside the VQE" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's gather all the inputs to the VQE in a dictionary." + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [], + "source": [ + "vqe_inputs = {\n", + " 'ansatz': ansatz,\n", + " 'operator': hamiltonian,\n", + " 'optimizer': {'name': 'SPSA', 'maxiter': 5}, # let's only do a few iterations!\n", + " 'initial_point': initial_point,\n", + " 'measurement_error_mitigation': True,\n", + " 'shots': 1024\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The backend options only need to contain the name of the backend." + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": {}, + "outputs": [], + "source": [ + "backend_options = {\n", + " 'backend_name': backend.name()\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The callback acts a little different than before. Instead of only being passed the VQE callback arguments, it is passed a tuple with the job ID as first argument, followed by the VQE arguments." + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [], + "source": [ + "intermediate_info = {\n", + " 'nfev': [],\n", + " 'parameters': [],\n", + " 'energy': [],\n", + " 'stddev': []\n", + "}\n", + "\n", + "def raw_callback(*args):\n", + " job_id, (nfev, parameters, energy, stddev) = args\n", + " intermediate_info['nfev'].append(nfev)\n", + " intermediate_info['parameters'].append(parameters)\n", + " intermediate_info['energy'].append(energy)\n", + " intermediate_info['stddev'].append(stddev)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we can call the Qiskit Runtime VQE directly, without going through the convenience of the `VQEProgram`. \n", + "Note, that the result will not be of the same type as `VQE` or `VQEProgram`, but a plain dictionary." + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Job ID: c2d4ut8likuqc7s4nqog\n" + ] + } + ], + "source": [ + "job = provider.runtime.run(\n", + " program_id='vqe',\n", + " inputs=vqe_inputs,\n", + " options=backend_options,\n", + " callback=raw_callback\n", + ")\n", + "print('Job ID:', job.job_id())" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": {}, + "outputs": [], + "source": [ + "result = job.result()" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Reached -0.0805234521211203 after 16 evaluations.\n" + ] + } + ], + "source": [ + "print(f'Reached {result[\"optimal_value\"]} after {result[\"optimizer_evals\"]} evaluations.')" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Available keys: ['optimizer_evals', 'optimizer_time', 'optimal_value', 'optimal_point', 'optimal_parameters', 'cost_function_evals', 'eigenstate', 'eigenvalue', 'aux_operator_eigenvalues', 'optimizer_history']\n" + ] + } + ], + "source": [ + "print('Available keys:', list(result.keys()))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.10" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +}