Source code for scikit_quri.backend.oqtopus_gradient_estimator
from dataclasses import dataclass
from typing import Sequence
import numpy as np
from numpy.typing import NDArray
# from quri_parts.circuit.parameter_shift import ShiftedParameters
from quri_parts.core import quantum_state
from quri_parts.core.estimator import Estimatable, Estimate
from scikit_quri.circuit import LearningCircuit
from .base_gradient_estimator import BaseGradientEstimator
from .oqtopus_estimator import OqtopusEstimator
[docs]@dataclass
class LearningCircuitParameter:
"""学習回路のパラメータを保持するデータクラス。
Attributes:
input_param: 入力データに対応するパラメータ。
learning_param: 最適化対象の学習パラメータ。
"""
input_param: NDArray[np.float64]
learning_param: NDArray[np.float64]
def _parametric_estimate(
op_state: tuple[Estimatable, LearningCircuit], params: Sequence[LearningCircuitParameter]
) -> Sequence[Estimate[complex]]:
"""複数のパラメータセットに対して期待値を一括計算する。
Args:
op_state: (演算子, 学習回路)のタプル。
params: パラメータセットのシーケンス。
Returns:
各パラメータセットに対する期待値のEstimateオブジェクトのシーケンス。
"""
operator, circuit = op_state
estimator = OqtopusEstimator("qulacs")
n_qubits = circuit.n_qubits
estimates = []
states = []
for param in params:
bind_circuit = circuit.bind_input_and_parameters(param.input_param, param.learning_param)
state = quantum_state(n_qubits=n_qubits, circuit=bind_circuit)
states.append(state)
estimates = estimator.estimate([operator], states)
return list(estimates)
[docs]def numerical_gradient_estimates(
op: Estimatable,
circuit: LearningCircuit,
params: LearningCircuitParameter,
delta: float,
) -> Sequence[complex]:
"""数値微分により勾配を計算する。
中心差分法を用いて各学習パラメータに対する勾配を計算する。
grad[i] = (f(θ_i + δ/2) - f(θ_i - δ/2)) / δ
Args:
op: 期待値を計算する演算子。
circuit: 学習回路。
params: 入力パラメータと学習パラメータ。
delta: 数値微分の刻み幅。
Returns:
各学習パラメータに対する勾配のシーケンス。
"""
v = []
input_param = params.input_param
for i in range(len(params.learning_param)):
current_learning_param = params.learning_param.copy()
current_learning_param[i] += delta * 0.5
v.append(LearningCircuitParameter(input_param, current_learning_param))
current_learning_param = params.learning_param.copy()
current_learning_param[i] -= delta * 0.5
v.append(LearningCircuitParameter(input_param, current_learning_param))
estimates = _parametric_estimate((op, circuit), v)
grad = []
for i in range(len(params.learning_param)):
d = estimates[2 * i].value - estimates[2 * i + 1].value
grad.append(d / delta)
return grad
# def parameter_shift_gradient_estimates(
# op: Estimatable,
# circuit: LearningCircuit,
# params: LearningCircuitParameter,
# ) -> Sequence[complex]:
# """パラメータシフト法により勾配を計算する。
# Note:
# 現在実装中。
# Args:
# op: 期待値を計算する演算子。
# circuit: 学習回路。
# params: 入力パラメータと学習パラメータ。
# Returns:
# 各学習パラメータに対する勾配のシーケンス。
# """
# gen_params = circuit.generate_bound_params(
# np.array(params.input_param), np.array(params.learning_param)
# )
# param_mapping = circuit.circuit.param_mapping
# parameter_shift = ShiftedParameters(param_mapping)
# derivatives = parameter_shift.get_derivatives()
# shifted_params_and_coefs = [d.get_shifted_parameters_and_coef(gen_params) for d in derivatives]
# gate_params = set()
# for params_and_coefs in shifted_params_and_coefs:
# for p, _ in params_and_coefs:
# gate_params.add(p)
# gate_params_list = list(gate_params)
# pass
[docs]class OqtopusGradientEstimator(BaseGradientEstimator):
"""Oqtopusを用いて勾配を計算するGradient Estimator Class。
OqtopusEstimatorを内部で使用し、数値微分により勾配を計算する。
"""
[docs] def estimate_gradient(self, operators, state, params):
"""全パラメータに対する勾配を計算する。
Note:
現在未実装。
Raises:
NotImplementedError: このメソッドは未実装。
"""
raise NotImplementedError("OqtopusGradientEstimator is not implemented yet.")
[docs] def estimate_learning_param_gradient(self, operators, circuit, params) -> Sequence[complex]:
"""学習パラメータに対する勾配を計算する。
入力パラメータは固定し、学習パラメータのみに対する勾配を計算する。
Args:
operators: 期待値を計算する演算子。
circuit: 学習回路。
params: 全パラメータ(入力+学習)の値。
Returns:
各学習パラメータに対する勾配のシーケンス。
"""
learning_params = LearningCircuitParameter(
input_param=np.array([params[i] for i in circuit.get_input_params_indexes()]),
learning_param=np.array([params[i] for i in circuit.get_learning_params_indexes()]),
)
estimate_gradients = numerical_gradient_estimates(
operators, circuit, learning_params, delta=1e-5
)
return estimate_gradients