Skip to content

Commit

Permalink
Merge pull request Qiskit#2 from Zoufalc/dal_varqte
Browse files Browse the repository at this point in the history
Updates VarQRTE --> Fix Right Side Operator for SLE & Streamline Imaginary Part Integration
  • Loading branch information
dlasecki authored Nov 15, 2021
2 parents 4969945 + 3add141 commit 5bd2599
Show file tree
Hide file tree
Showing 15 changed files with 250 additions and 137 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@
from typing import Union, Dict, Iterable, Optional

import numpy as np
from qiskit.circuit import Parameter
from qiskit.opflow import CircuitSampler
from qiskit.circuit import Parameter, QuantumCircuit
from qiskit.opflow import CircuitSampler, StateFn, OperatorBase
from qiskit.utils import QuantumInstance
from qiskit.providers import BaseBackend


# TODO change name, refactor expected val calculation, will be used for error bounds
Expand Down Expand Up @@ -59,3 +61,27 @@ def _inner_prod(x: Iterable, y: Iterable) -> Union[np.ndarray, np.complex, np.fl
Returns: Inner product of x,y
"""
return np.matmul(np.conj(np.transpose(x)), y)


def energy(hamiltonian: OperatorBase,
ansatz: QuantumCircuit,
param_dict: Dict,
backend: Optional[Union[BaseBackend, QuantumInstance]] = None,
) -> float:
"""
Compute energy for a given Hamiltonian, Ansatz and parameter dictionary.
Args:
hamiltonian: System hamiltonian.
ansatz: Parameterized ansatz.
param_dict: Parameter dictionary.
backend: Backend used for energy calculation.
Returns: Energy
"""
energy = ~StateFn(hamiltonian) @ StateFn(ansatz)
if backend is not None:
energy = CircuitSampler(backend).convert(energy)
energy_val = energy.assign_parameters(param_dict).eval()
return energy_val

Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ def eval_evolution_grad(
grad_circ_sampler: CircuitSampler,
backend: Optional[Union[BaseBackend, QuantumInstance]] = None,
):
# TODO use hashing for more efficient execution
# TODO Where is the backend used? Something is wrong here
if backend is not None:
grad_res = np.array(grad_circ_sampler.convert(evolution_grad, params=param_dict).eval())
else:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ def calculate(
qfi_method=qfi_method,
regularization=regularization,
).convert(operator * 0.5, parameters)
#TODO we should include the Circuit Sampler here not below to allow for hashing

return nat_grad

Expand All @@ -56,3 +57,41 @@ def eval_nat_grad_result(
raise Warning("The imaginary part of the gradient are non-negligible.")

return nat_grad_result

def eval_grad_result(
grad: Union[OperatorBase, callable],
param_dict: Dict[Parameter, Union[float, complex]],
grad_circ_sampler: CircuitSampler,
backend: Optional[Union[BaseBackend, QuantumInstance]] = None,
):

if isinstance(grad, OperatorBase):
grad_result = grad
else:
grad_result = grad(param_dict, backend)

print('Gradient object')
print(grad_result)
print('Parameter dict ', param_dict)
if backend is not None:
grad_result = grad_circ_sampler.convert(grad_result, param_dict)
else:
grad_result = grad_result.assign_parameters(param_dict)
grad_result = grad_result.eval()
if any(np.abs(np.imag(grad_item)) > 1e-8 for grad_item in grad_result):
raise Warning("The imaginary part of the gradient are non-negligible.")

return grad_result

def eval_metric_result(
metric,
param_dict: Dict[Parameter, Union[float, complex]],
metric_circ_sampler: CircuitSampler,
backend: Optional[Union[BaseBackend, QuantumInstance]] = None,
):
if backend is not None:
metric_result = metric_circ_sampler.convert(metric, params=param_dict).eval()
else:
metric_result = metric.assign_parameters(param_dict).eval()

return metric_result
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
# 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.
from typing import Union, Dict
from typing import Union, Dict, List

from qiskit.algorithms.quantum_time_evolution.variational.calculators import (
metric_tensor_calculator,
Expand Down Expand Up @@ -43,10 +43,11 @@ def __init__(
def _get_raw_metric_tensor(
self,
ansatz,
param_dict: Dict[Parameter, Union[float, complex]],
params: List[Parameter],
# param_dict: Dict[Parameter, Union[float, complex]],
):
raw_metric_tensor_real = metric_tensor_calculator.calculate(
ansatz, list(param_dict.keys()), self._qfi_method
ansatz, params, self._qfi_method
)

return raw_metric_tensor_real
Expand All @@ -55,13 +56,14 @@ def _get_raw_evolution_grad(
self,
hamiltonian,
ansatz,
param_dict: Dict[Parameter, Union[float, complex]],
params: List[Parameter],
# param_dict: Dict[Parameter, Union[float, complex]],
):
raw_evolution_grad_real = evolution_grad_calculator.calculate(
hamiltonian, ansatz, list(param_dict.keys()), self._grad_method
hamiltonian, ansatz, params, self._grad_method
)

return raw_evolution_grad_real
return (-1)*raw_evolution_grad_real

@staticmethod
def _calc_metric_tensor(
Expand All @@ -73,4 +75,4 @@ def _calc_metric_tensor(
def _calc_evolution_grad(
raw_evolution_grad: OperatorBase, param_dict: Dict[Parameter, Union[float, complex]]
) -> OperatorBase:
return -raw_evolution_grad.bind_parameters(param_dict)
return raw_evolution_grad.bind_parameters(param_dict)
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
# 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.
from typing import Union, Dict, Optional
from typing import Union, Dict, Optional, List

from qiskit.algorithms.quantum_time_evolution.variational.calculators import (
evolution_grad_calculator,
Expand All @@ -19,7 +19,10 @@
RealVariationalPrinciple,
)
from qiskit.circuit import Parameter
from qiskit.opflow import CircuitQFI, OperatorBase, Y
from qiskit.providers import BaseBackend
from qiskit.utils import QuantumInstance
from qiskit.opflow import CircuitQFI, OperatorBase, StateFn, SummedOp, Y, I, \
PauliExpectation, CircuitSampler


class RealMcLachlanVariationalPrinciple(RealVariationalPrinciple):
Expand All @@ -39,25 +42,40 @@ def __init__(
def _get_raw_metric_tensor(
self,
ansatz,
param_dict: Dict[Parameter, Union[float, complex]],
parameters: List[Parameter],
# param_dict: Dict[Parameter, Union[float, complex]],
):
raw_metric_tensor_real = metric_tensor_calculator.calculate(
ansatz, list(param_dict.keys()), self._qfi_method
ansatz, parameters, self._qfi_method
)

return raw_metric_tensor_real
return raw_metric_tensor_real * 0.25 # QFI/4

def _get_raw_evolution_grad(
self,
hamiltonian,
ansatz,
param_dict: Dict[Parameter, Union[float, complex]],
parameters: List[Parameter],
):

raw_evolution_grad_imag = evolution_grad_calculator.calculate(
hamiltonian, ansatz, list(param_dict.keys()), self._grad_method, basis=-1j * Y
)
def raw_evolution_grad_imag(param_dict: Dict,
backend: Optional[Union[BaseBackend, QuantumInstance]] = None):
energy = ~StateFn(hamiltonian) @ StateFn(ansatz)
energy = PauliExpectation().convert(energy)
#TODO rewrite to be more efficient
if backend is not None:
energy = CircuitSampler(backend).convert(energy, param_dict).eval()
else:
energy = energy.assign_parameters(param_dict).eval()

energy_term = I ^ hamiltonian.num_qubits
energy_term *= -1
energy_term *= energy
hamiltonian_ = SummedOp([hamiltonian, energy_term])
basis_operator = Y
basis_operator *= -1j
return evolution_grad_calculator.calculate(hamiltonian_, ansatz, parameters,
self._grad_method, basis=basis_operator)
return raw_evolution_grad_imag

@staticmethod
Expand All @@ -70,7 +88,7 @@ def _calc_metric_tensor(
def _calc_evolution_grad(
raw_evolution_grad_imag: OperatorBase, param_dict: Dict[Parameter, Union[float, complex]]
) -> OperatorBase:
return raw_evolution_grad_imag.bind_parameters(param_dict)
return raw_evolution_grad_imag.bind_parameters(param_dict) / 2.0

def _calc_nat_grad(
self,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ def _calc_evolution_grad(

def _calc_nat_grad(
self,
raw_operator: OperatorBase,
raw_operator: OperatorBase, # <ansatz|H|ansatz>
param_dict: Dict[Parameter, Union[float, complex]],
regularization: Optional[str] = None,
) -> OperatorBase:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.
from abc import ABC, abstractmethod
from typing import Union, Dict, Optional
from typing import Union, Dict, Optional, List

from qiskit.circuit import Parameter
from qiskit.opflow import (
Expand Down Expand Up @@ -43,28 +43,29 @@ def _lazy_init(
self,
hamiltonian,
ansatz,
param_dict: Dict[Parameter, Union[float, complex]],
regularization: Optional[str] = None,
parameters: List[Parameter]
):

self._hamiltonian = hamiltonian
self._ansatz = ansatz
self._param_dict = param_dict
self._operator = ~StateFn(hamiltonian) @ StateFn(ansatz)
self._operator = self._operator / self._operator.coeff # Remove the time from the operator
raw_metric_tensor = self._get_raw_metric_tensor(ansatz, param_dict)
self._params = parameters


raw_evolution_grad = self._get_raw_evolution_grad(hamiltonian, ansatz, param_dict)

self._metric_tensor = self._calc_metric_tensor(raw_metric_tensor, param_dict)
self._evolution_grad = self._calc_evolution_grad(raw_evolution_grad, param_dict)
self._raw_evolution_grad = self._get_raw_evolution_grad(hamiltonian, ansatz, parameters)
self._raw_metric_tensor = self._get_raw_metric_tensor(ansatz, parameters)

self._nat_grad = self._calc_nat_grad(self._operator, param_dict, regularization)
# self._metric_tensor = self._calc_metric_tensor(raw_metric_tensor, param_dict)
# self._evolution_grad = self._calc_evolution_grad(raw_evolution_grad, param_dict)

# self._nat_grad = self._calc_nat_grad(self._operator, param_dict, regularization)

@abstractmethod
def _get_raw_metric_tensor(
self,
ansatz,
param_dict: Dict[Parameter, Union[float, complex]],
parameters: List[Parameter],
):
pass

Expand All @@ -73,7 +74,7 @@ def _get_raw_evolution_grad(
self,
hamiltonian,
ansatz,
param_dict: Dict[Parameter, Union[float, complex]],
parameters: List[Parameter],
):
pass

Expand All @@ -100,7 +101,11 @@ def _calc_nat_grad(

@abstractmethod
def _calc_error_bound(
self, error: float, et: float, h_squared_expectation, h_trip: float, trained_energy: float
self, error: float,
et: float,
h_squared_expectation: float,
h_trip: float,
trained_energy: float
) -> float:
pass

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ def __init__(
def var_qte_ode_function(self, t: float, parameters_values: Iterable) -> Iterable:
current_param_dict = dict(zip(self._param_dict.keys(), parameters_values))
nat_grad_res = self._linear_solver._solve_sle(
self._variational_principle, current_param_dict, self._t_param, t
self._variational_principle, current_param_dict, self._t_param, t, self._regularization
)

return nat_grad_res
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ def _run(self, evolution_time: float):
t0=0,
y0=self._init_params, # appending 0 was here in case of bounds, not yet supported.
vectorized=False,
atol=1e-6,
)
param_values = None

Expand All @@ -62,5 +63,8 @@ def _run(self, evolution_time: float):
break
elif ode_solver.status == "failed":
raise QiskitError("ODESolver failed.")
#TODO It seems weird to me that the ODE time here is not yet the evolution time.
# Where does it change?
print('ODE time', ode_solver.t)

return param_values
Loading

0 comments on commit 5bd2599

Please sign in to comment.