Source code for tensorcircuit.interfaces.scipy

"""
Interface wraps quantum function as a scipy function for optimization
"""

from typing import Any, Callable, Tuple, Optional

import numpy as np

from ..cons import backend, dtypestr
from .tensortrans import general_args_to_numpy, numpy_args_to_backend

Tensor = Any


[docs]def scipy_optimize_interface( fun: Callable[..., Any], shape: Optional[Tuple[int, ...]] = None, jit: bool = True, gradient: bool = True, ) -> Callable[..., Any]: """ Convert ``fun`` into a scipy optimize interface compatible version :Example: .. code-block:: python n = 3 def f(param): c = tc.Circuit(n) for i in range(n): c.rx(i, theta=param[0, i]) c.rz(i, theta=param[1, i]) loss = c.expectation( [ tc.gates.y(), [ 0, ], ] ) return tc.backend.real(loss) # A gradient-based optimization interface f_scipy = tc.interfaces.scipy_optimize_interface(f, shape=[2, n]) r = optimize.minimize(f_scipy, np.zeros([2 * n]), method="L-BFGS-B", jac=True) # A gradient-free optimization interface f_scipy = tc.interfaces.scipy_optimize_interface(f, shape=[2, n], gradient=False) r = optimize.minimize(f_scipy, np.zeros([2 * n]), method="COBYLA") :param fun: The quantum function with scalar out that to be optimized :type fun: Callable[..., Any] :param shape: the shape of parameters that ``fun`` accepts, defaults to None :type shape: Optional[Tuple[int, ...]], optional :param jit: whether to jit ``fun``, defaults to True :type jit: bool, optional :param gradient: whether using gradient-based or gradient free scipy optimize interface, defaults to True :type gradient: bool, optional :return: The scipy interface compatible version of ``fun`` :rtype: Callable[..., Any] """ if gradient: vg = backend.value_and_grad(fun, argnums=0) if jit: vg = backend.jit(vg) def scipy_vg(*args: Any, **kws: Any) -> Tuple[Tensor, Tensor]: scipy_args = numpy_args_to_backend(args, dtype=dtypestr) if shape is not None: scipy_args = list(scipy_args) scipy_args[0] = backend.reshape(scipy_args[0], shape) scipy_args = tuple(scipy_args) vs, gs = vg(*scipy_args, **kws) scipy_vs = general_args_to_numpy(vs) gs = backend.reshape(gs, [-1]) scipy_gs = general_args_to_numpy(gs) scipy_vs = scipy_vs.real.astype(np.float64) scipy_gs = scipy_gs.real.astype(np.float64) return scipy_vs, scipy_gs return scipy_vg # no gradient if jit: fun = backend.jit(fun) def scipy_v(*args: Any, **kws: Any) -> Tensor: scipy_args = numpy_args_to_backend(args, dtype=dtypestr) if shape is not None: scipy_args = list(scipy_args) scipy_args[0] = backend.reshape(scipy_args[0], shape) scipy_args = tuple(scipy_args) vs = fun(*scipy_args, **kws) scipy_vs = general_args_to_numpy(vs) scipy_vs = scipy_vs.real.astype(np.float64) return scipy_vs return scipy_v
scipy_interface = scipy_optimize_interface