From 7f6ea590f12b912603c109e8ca5e2e76a293d1be Mon Sep 17 00:00:00 2001 From: MatteoRobbiati Date: Tue, 5 Mar 2024 12:01:58 -0600 Subject: [PATCH 01/12] feat: add optimizers --- src/qiboml/optimizers/abstract.py | 16 ++ src/qiboml/optimizers/gradient_based.py | 112 +++++++++++ src/qiboml/optimizers/heuristics.py | 194 +++++++++++++++++++ src/qiboml/optimizers/minimizers.py | 236 ++++++++++++++++++++++++ 4 files changed, 558 insertions(+) create mode 100644 src/qiboml/optimizers/abstract.py create mode 100644 src/qiboml/optimizers/gradient_based.py create mode 100644 src/qiboml/optimizers/heuristics.py create mode 100644 src/qiboml/optimizers/minimizers.py diff --git a/src/qiboml/optimizers/abstract.py b/src/qiboml/optimizers/abstract.py new file mode 100644 index 0000000..cf28a3c --- /dev/null +++ b/src/qiboml/optimizers/abstract.py @@ -0,0 +1,16 @@ +from abc import ABC, abstractmethod +from dataclasses import dataclass, field + +from qibo.config import raise_error + + +@dataclass +class Optimizer(ABC): + + verbosity: bool = field(default=True) + """Verbosity of the optimization process. If True, logging messages will be displayed.""" + + @abstractmethod + def fit(self): + """Compute the optimization strategy.""" + raise_error(NotImplementedError) diff --git a/src/qiboml/optimizers/gradient_based.py b/src/qiboml/optimizers/gradient_based.py new file mode 100644 index 0000000..fd49f97 --- /dev/null +++ b/src/qiboml/optimizers/gradient_based.py @@ -0,0 +1,112 @@ +"""Gradient descent strategies to optimize quantum models.""" + +from typing import List, Optional, Tuple, Union + +from numpy import ndarray +from qibo.backends import construct_backend +from qibo.config import log +from qibo.optimizers.abstract import Optimizer + + +class TensorflowSGD(Optimizer): + """ + Stochastic Gradient Descent (SGD) optimizer using Tensorflow backpropagation. + See `tf.keras.Optimizers https://www.tensorflow.org/api_docs/python/tf/keras/optimizers. + for a list of the available optimizers. + + Args: + optimizer_name (str): `tensorflow.keras.optimizer`, see + https://www.tensorflow.org/api_docs/python/tf/keras/optimizers + for the list of available optimizers. + compile (bool): if ``True`` the Tensorflow optimization graph is compiled. + **optimizer_options (dict): a dictionary containing the keywords arguments + to customize the selected keras optimizer. In order to properly + customize your optimizer please refer to https://www.tensorflow.org/api_docs/python/tf/keras/optimizers. + """ + + def __init__( + self, optimizer_name: str = "Adagrad", compile: bool = True, **optimizer_options + ): + + self.optimizer_name = optimizer_name + self.compile = compile + + if optimizer_options is None: + options = {} + else: + options = optimizer_options + + self.backend = construct_backend("tensorflow") + self.optimizer = getattr( + self.backend.tf.optimizers.legacy, self.optimizer_name + )(**options) + + def __str__(self): + return f"tensorflow_{self.optimizer_name}" + + def fit( + self, + initial_parameters: Union[List, ndarray], + loss: callable, + args: Union[Tuple] = None, + epochs: int = 10000, + nmessage: int = 100, + loss_threshold: Optional[float] = None, + ): + """ + Compute the SGD optimization according to the chosen optimizer. + + Args: + initial_parameters (np.ndarray or list): array with initial values + for gate parameters. + loss (callable): loss function to train on. + args (tuple): tuple containing loss function arguments. + epochs (int): number of optimization iterations [default 10000]. + nmessage (int): Every how many epochs to print + a message of the loss function [default 100]. + loss_threshold (float): if this loss function value is reached, training + stops [default None]. + + Returns: + (float): best loss value + (np.ndarray): best parameter values + (list): loss function history + """ + + vparams = self.backend.tf.Variable( + initial_parameters, dtype=self.backend.tf.float64 + ) + print(vparams) + loss_history = [] + + def sgd_step(): + """Compute one SGD optimization step according to the chosen optimizer.""" + with self.backend.tf.GradientTape() as tape: + tape.watch(vparams) + loss_value = loss(vparams, *args) + + grads = tape.gradient(loss_value, [vparams]) + self.optimizer.apply_gradients(zip(grads, [vparams])) + return loss_value + + if self.compile: + self.backend.compile(loss) + self.backend.compile(sgd_step) + + # SGD procedure: loop over epochs + for epoch in range(epochs): # pragma: no cover + # early stopping if loss_threshold has been set + if ( + loss_threshold is not None + and (epoch != 0) + and (loss_history[-1] <= loss_threshold) + ): + break + + loss_value = sgd_step().numpy() + loss_history.append(loss_value) + + if epoch % nmessage == 0: + log.info("ite %d : loss %f", epoch, loss_value) + + return loss(vparams, *args).numpy(), vparams.numpy(), loss_history diff --git a/src/qiboml/optimizers/heuristics.py b/src/qiboml/optimizers/heuristics.py new file mode 100644 index 0000000..ce0db2e --- /dev/null +++ b/src/qiboml/optimizers/heuristics.py @@ -0,0 +1,194 @@ +"""Meta-heuristic optimization algorithms.""" + +from dataclasses import dataclass, field +from typing import List, Optional, Tuple, Union + +import cma +from numpy import ndarray +from qibo.config import log +from qibo.optimizers.abstract import Optimizer +from scipy.optimize import basinhopping + + +@dataclass +class CMAES(Optimizer): + """ + Covariance Matrix Adaptation Evolution Strategy based on + `pycma `_. + + Args: + verbosity (Optional[bool]): verbosity level of the optimization. If `True`, logging messages are displayed. + sigma0 (Optional[float]): scalar, initial standard deviation in each coordinate. + sigma0 should be about 1/4th of the search domain width (where the + optimum is to be expected). + restarts (Optional[int]): number of restarts with increasing population size. + restart_from_best (Optional[bool]): which point to restart from. + iconpopsize (Optional[int]): multiplier for increasing the population size popsize before each restart. + callback (Optional[callable]): a callable called after each optimization iteration. + """ + + sigma0: Optional[float] = field(default=0.5) + restarts: Optional[int] = field(default=0) + restarts_from_best: Optional[bool] = field(default=False) + iconpopsize: Optional[int] = field(default=2) + callback: Optional[callable] = field(default=None) + + def __str__(self): + return "cmaes" + + def show_fit_options(self, keyword: str = None): + """ + Return all the available fit options for the optimizer. + + Args: + keyword (str): keyword to help the research of fit options into the + options dictionary. + """ + return cma.CMAOptions(keyword) + + def fit( + self, + initial_parameters: Union[List, ndarray], + loss: callable, + args: Optional[Tuple] = None, + fit_options: Optional[dict] = None, + ): + """Perform the optimizations via CMA-ES. + + Args: + initial_parameters (np.ndarray or list): array with initial values + for gate parameters. + loss (callable): loss function to train on. + args (tuple): tuple containing loss function arguments. + fit_options (dict): fit extra options. To have a look to all + possible options please use `CMAES.show_fit_options()`. + + Returns: + tuple: best loss value (float), best parameter values (np.ndarray), full cma result object. + """ + + if fit_options is None: + options = {} + else: + options = fit_options + + log.info(f"Optimization is performed using the optimizer: {self.__str__()}") + + r = cma.fmin2( + objective_function=loss, + x0=initial_parameters, + args=args, + sigma0=self.sigma0, + restarts=self.restarts, + restart_from_best=self.restarts_from_best, + incpopsize=self.iconpopsize, + callback=self.callback, + options=options, + ) + + return r[1].result.fbest, r[1].result.xbest, r + + +@dataclass +class BasinHopping(Optimizer): + """ + Global optimizer based on: https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.basinhopping.html. + Note that the Basin-Hopping optimizer combines a global stepping algorithm + together with a local minimization (which is implemented using an extra scipy minimizer). + It is designed to mimic the natural process of energy minimization of clusters + of atoms and it works well for similar problems with “funnel-like, but rugged” energy landscapes. + + Args: + verbosity (Optional[bool]): verbosity level of the optimization. If `True`, logging messages are displayed. + niter (Optional[int]): The number of basin-hopping iterations. There will + be a total of `niter+1` runs of the local minimizer. + T (Optional[float]): the “temperature” parameter for the acceptance or + rejection criterion. Higher “temperatures” mean that larger jumps in + function value will be accepted. For best results T should be comparable + to the separation (in function value) between local minima. + stepsize (Optional[float]): maximum step size for use in the random displacement. + take_step (Optional[callable]): replace the default step-taking routine with this routine. + accept_test (Optional[callable]): accept test function. It must be of shape + `accept_test(f_new=f_new, x_new=x_new, f_old=f_old, x_old=x_old)` and + return a boolean variable. If `True`, the new point is accepted, if + `False`, the step is rejected. It can also return `force accept`, which + will override any other tests in order to accept the step. + callback (Optional[callable]): a callable called after each optimization iteration. + target_accept_rate (Optional[float]): the target acceptance rate that is + used to adjust the stepsize. If the current acceptance rate is greater + than the target, then the stepsize is increased. Otherwise, it is decreased. + niter_success (Optional[int]): stop the run if the global minimum + candidate remains the same for this number of iterations. + minimizer_kwargs (Optional[dict]): extra keyword arguments to be passed + to the local minimizer. To visualize all the possible options available + for a fixed scipy `minimizer` please use the method + `BasinHopping.show_fit_options(method="method")`, where `"method"` is + selected among the ones provided by `scipy.optimize.minimize`. + """ + + niter: Optional[int] = field(default=10) + T: Optional[float] = field(default=1.0) + stepsize: Optional[float] = field(default=0.5) + take_step: Optional[callable] = field(default=None) + accept_test: Optional[callable] = field(default=None) + callback: Optional[callable] = field(default=None) + target_accept_rate: Optional[float] = field(default=0.5) + niter_success: Optional[int] = field(default=None) + minimizer_kwargs: Optional[dict] = field(default=None) + disp: bool = field(init=False, default=False) + + def __post_init__(self): + if self.verbosity: + self.disp = True + + def __str__(self): + return "basinhopping" + + def show_fit_options_list(self): + log.info(f"No `fit_options` are required for the Basin-Hopping optimizer.") + + def fit( + self, + initial_parameters: Union[List, ndarray], + loss: callable, + args: Optional[Tuple] = None, + ): + """Perform the optimizations via Basin-Hopping strategy. + + Args: + initial_parameters (np.ndarray or list): array with initial values + for gate parameters. + loss (callable): loss function to train on. + args (tuple): tuple containing loss function arguments. + + Returns: + tuple: best loss value (float), best parameter values (np.ndarray), full scipy OptimizeResult object. + """ + + log.info( + f"Optimization is performed using the optimizer: {type(self).__name__}" + ) + + if self.minimizer_kwargs is None: + options = {} + else: + options = self.minimizer_kwargs + + options.update({"args": args}) + + r = basinhopping( + func=loss, + x0=initial_parameters, + niter=self.niter, + T=self.T, + stepsize=self.stepsize, + take_step=self.take_step, + accept_test=self.accept_test, + callback=self.callback, + niter_success=self.niter_success, + target_accept_rate=self.target_accept_rate, + minimizer_kwargs=options, + disp=self.disp, + ) + + return r.fun, r.x, r diff --git a/src/qiboml/optimizers/minimizers.py b/src/qiboml/optimizers/minimizers.py new file mode 100644 index 0000000..18bee50 --- /dev/null +++ b/src/qiboml/optimizers/minimizers.py @@ -0,0 +1,236 @@ +"""Optimization algorithms inherited from Scipy's minimization module.""" + +from dataclasses import dataclass, field +from typing import List, Optional, Tuple, Union + +import numpy as np +from numpy import ndarray +from qibo.config import log +from qibo.optimizers.abstract import Optimizer +from scipy.optimize import Bounds, minimize, show_options + + +@dataclass +class ScipyMinimizer(Optimizer): + """ + Optimization approaches based on `scipy.optimize.minimize`. + + Attributes: + verbosity (bool): verbosity level of the optimization. If `True`, logging messages are displayed. + method (Optional[str]): optimization method among the minimizers provided by scipy, defaults to "Powell". + jac (Optional[dict]): method for computing the gradient vector. + hess (Optional[dict]): method for computing the hessian matrix. + hessp (Optional[callable]): hessian of objective function times an arbitrary vector. + bounds (Union[None, List[Tuple], Bounds]): bounds on variables. + constraints (Optional[dict]): constraints definition. + tol (Optional[float]): tolerance for termination. + callback (Optional[callable]): a callable called after each optimization iteration. + """ + + method: str = "Powell" + jac: Optional[dict] = None + hess: Optional[dict] = None + hessp: Optional[callable] = None + bounds: Union[None, List[Tuple], Bounds] = None + constraints: Optional[dict] = None + tol: Optional[float] = None + callback: Optional[callable] = None + + def __str__(self): + return f"scipy_minimizer_{self.method}" + + def show_fit_options(self): + """Return available extra options for chosen minimizer.""" + return show_options(solver="minimize", method=self.method) + + def fit( + self, + initial_parameters: Union[List, ndarray], + loss: callable, + args: Union[Tuple] = None, + fit_options: Optional[dict] = None, + ): + """Perform the optimizations via ScipyMinimizer. + + Args: + initial_parameters (np.ndarray or list): array with initial values + for gate parameters. + loss (callable): loss function to train on. + args (tuple): tuple containing loss function arguments. + fit_options (dict): dictionary containing extra options which depend + on the chosen `"method"`. This argument is called "options" in the + Scipy's documentation and we recommend to fill it according to the + official documentation. + + Returns: + tuple: best loss value (float), best parameter values (np.ndarray), full scipy OptimizeResult object. + """ + + if fit_options is None: + options = {} + else: + options = fit_options + + if self.verbosity: + log.info(f"Optimization is performed using the optimizer: {self.__str__()}") + + r = minimize(loss, initial_parameters, args=args, **options) + + return r.fun, r.x, r + + +@dataclass +class ParallelBFGS(ScipyMinimizer): + """ + Computes the L-BFGS-B with parallel evaluation using multiprocessing. + This implementation here is based on https://doi.org/10.32614/RJ-2019-030. + + Attributes: + verbosity (bool): verbosity level of the optimization. If `True`, logging messages are displayed. + jac (Optional[dict]): Method for computing the gradient vector. + hess (Optional[dict]): Method for computing the hessian matrix. + hessp (Optional[callable]): Hessian of objective function times an arbitrary vector. + bounds (Union[None, List[Tuple], Bounds]): Bounds on variables. + constraints (Optional[dict]): Constraints definition. + tol (Optional[float]): Tolerance for termination. + callback (Optional[callable]): a callable called after each optimization iteration. + processes (int): number of processes to be computed in parallel. + """ + + processes: int = field(default=1) + xval: float = field(init=False, default=None) + function_value: float = field(init=False, default=None) + jacobian_value: float = field(init=False, default=None) + precision: float = field(init=False, default=np.finfo("float64").eps) + + def __post_init__(self): + self.xval = None + self.function_value = None + self.jacobian_value = None + self.precision = np.finfo("float64").eps + + def __str__(self): + return f"scipy_minimizer_ParallelBFGS" + + def show_fit_options(self): + """Return available extra options for chosen minimizer.""" + return show_options(solver="minimize", method="L-BFGS-B") + + def fit( + self, + initial_parameters: Union[List, ndarray], + loss: callable, + args: Union[Tuple] = None, + fit_options: Optional[dict] = None, + ): + """Performs the optimizations via ParallelBFGS. + + Args: + initial_parameters (np.ndarray or list): array with initial values + for gate parameters. + loss (callable): loss function to train on. + args (tuple): tuple containing loss function arguments. + fit_options (dict): specific options accepted by the L-BFGS-B minimizer. + Use the method `ParallelBFGS.show_fit_options()` to visualize all + the available options. + + Returns: + tuple: best loss value (float), best parameter values (np.ndarray), full scipy OptimizeResult object. + """ + + if self.verbosity: + log.info(f"Optimization is performed using the optimizer: {self.__str__()}") + + self.loss = loss + self.args = args + self.params = initial_parameters + + if fit_options is None: + self.fit_options = {} + else: + self.fit_options = fit_options + + out = minimize( + fun=self.fun, + x0=initial_parameters, + jac=self.jac, + method="L-BFGS-B", + **self.fit_options, + ) + + out.hess_inv = out.hess_inv * np.identity(len(self.params)) + return out.fun, out.x, out + + @staticmethod + def _eval_approx(eps_at, fun, x, eps): + """Approximate evaluation + + Args: + eps_at (int): parameter index where approximation occurs + fun (function): loss function + x (np.ndarray): circuit parameters + eps (float): approximation delta + Returns: + (float): approximated loss value + """ + if eps_at == 0: + x_ = x + else: + x_ = x.copy() + if eps_at <= len(x): + x_[eps_at - 1] += eps + else: + x_[eps_at - 1 - len(x)] -= eps + return fun(x_) + + def evaluate(self, x, eps=1e-8): + """Handles function evaluation + + Args: + x (np.ndarray): circuit parameters + eps (float): approximation delta + Returns + (float): loss value + """ + if not ( + self.xval is not None and all(abs(self.xval - x) <= self.precision * 2) + ): + eps_at = range(len(x) + 1) + self.xval = x.copy() + + def operation(epsi): + return self._eval_approx( + epsi, lambda y: self.loss(y, *self.args), x, eps + ) + + from joblib import Parallel, delayed + + ret = Parallel(self.processes, prefer="threads")( + delayed(operation)(epsi) for epsi in eps_at + ) + self.function_value = ret[0] + self.jacobian_value = (ret[1 : (len(x) + 1)] - self.function_value) / eps + + def fun(self, x): + """Saves and returns loss function value + + Args: + x (np.ndarray): circuit parameters + Returns + (float): loss value + """ + + self.evaluate(x) + return self.function_value + + def jac(self, x): + """Evaluates the Jacobian + + Args: + x (np.ndarray): circuit parameters + Returns + (float): jacobian value + """ + + self.evaluate(x) + return self.jacobian_value From d9e057fd6dff9d8524decff1344045b80e142e71 Mon Sep 17 00:00:00 2001 From: MatteoRobbiati Date: Wed, 6 Mar 2024 17:48:19 -0600 Subject: [PATCH 02/12] update test_reuploading --- src/qiboml/test_reuploading.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/qiboml/test_reuploading.py b/src/qiboml/test_reuploading.py index 977d5b0..267b5a6 100644 --- a/src/qiboml/test_reuploading.py +++ b/src/qiboml/test_reuploading.py @@ -54,8 +54,10 @@ def mse(labels, predictions): for run in range(nruns): print(f"Running training {run+1}/{nruns}") - model = ReuploadingU3(nqubits=nqubits, nlayers=nlayers, data_dimensionality=(1,)) - # model = FourierReuploading(nqubits=nqubits, nlayers=nlayers, data_dimensionality=(1,)) + # model = ReuploadingU3(nqubits=nqubits, nlayers=nlayers, data_dimensionality=(1,)) + model = FourierReuploading( + nqubits=nqubits, nlayers=nlayers, data_dimensionality=(1,) + ) print(model.parameters) From 8451620a7e1289ed364e6d9ffc3821c2950bf60d Mon Sep 17 00:00:00 2001 From: MatteoRobbiati Date: Wed, 6 Mar 2024 17:48:54 -0600 Subject: [PATCH 03/12] feat: pqc model --- src/qiboml/models/__init__.py | 1 + src/qiboml/models/pqc.py | 100 ++++++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+) create mode 100644 src/qiboml/models/__init__.py create mode 100644 src/qiboml/models/pqc.py diff --git a/src/qiboml/models/__init__.py b/src/qiboml/models/__init__.py new file mode 100644 index 0000000..4720500 --- /dev/null +++ b/src/qiboml/models/__init__.py @@ -0,0 +1 @@ +from qiboml.models.pqc import PQC diff --git a/src/qiboml/models/pqc.py b/src/qiboml/models/pqc.py new file mode 100644 index 0000000..957c4c6 --- /dev/null +++ b/src/qiboml/models/pqc.py @@ -0,0 +1,100 @@ +"""Parametric Quantum Circuit""" + +from typing import Dict, List, Optional, Union + +from numpy import ndarray +from qibo import Circuit +from qibo.config import raise_error +from qibo.hamiltonians import Hamiltonian + +from qiboml.optimizers import Optimizer + + +class PQC(Circuit): + """Parametric Quantum Circuit built on top of ``qibo.Circuit``.""" + + def __init__( + self, + nqubits: int, + accelerators: Optional[Dict] = None, + density_matrix: bool = False, + wire_names: Optional[Union[list, dict]] = None, + ): + super().__init__(nqubits, accelerators, density_matrix, wire_names) + + self.parameters = [] + self.nparams = 0 + + def add(self, gate): + super().add(gate) + self.parameters.extend(gate.parameters) + self.nparams += gate.nparams + + def compile( + self, + optimizer: Optimizer, + loss: callable, + observable: Union[Hamiltonian, List[Hamiltonian]], + ): + """ + Compile the PQC to perform a training. + + Args: + optimizer (qiboml.optimizers.Optimizer): optimizer to be used. + loss (callable): loss function to be minimizer. + observable (qibo.hamiltonians.Hamiltonian): observable, or list of + observables, whose expectation value is used to compute predictions. + """ + self.optimizer = optimizer + self.loss = loss + self.observable = observable + self.encoding_config = encoding + self.compiled = True + + def fit( + self, + x_data: ndarray, + y_data: ndarray, + nshots: Optional[int] = None, + options: Optional[Dict] = None, + ): + """Perform the PQC training.""" + + if not self.compiled: + raise_error( + ValueError, + "Please compile the model through the `PQC.compile` method to train it.", + ) + + if options is None: + fit_options = {} + else: + fit_options = options + + def _loss(parameters, x_data, y_data): + self.set_parameters(parameters) + + predictions = [] + for x in x_data: + predictions.append(self.predict(x=x, nshots=nshots)) + loss_value = self.loss(predictions, y_data) + return loss_value + + results = self.optimizer.fit( + parameters=self.parameters, loss=_loss, args=(x_data, y_data) ** fit_options + ) + + return results + + def predict(self, x: ndarray, nshots: int = None): + """Perform prediction associated to a single input data ``x``.""" + + if not self.compiled: + raise_error( + ValueError, + "Please compile the model through the `PQC.compile` method to perform predictions.", + ) + + self.inject_data(x) + + return self.observable.expectation(self.execute(nshots=nshots).state()) From 912abf0a3a34fffbea00b3dd78c8b87599b8616a Mon Sep 17 00:00:00 2001 From: MatteoRobbiati Date: Wed, 6 Mar 2024 17:49:18 -0600 Subject: [PATCH 04/12] init file in optimizers --- src/qiboml/optimizers/__init__.py | 1 + 1 file changed, 1 insertion(+) create mode 100644 src/qiboml/optimizers/__init__.py diff --git a/src/qiboml/optimizers/__init__.py b/src/qiboml/optimizers/__init__.py new file mode 100644 index 0000000..e5abc11 --- /dev/null +++ b/src/qiboml/optimizers/__init__.py @@ -0,0 +1 @@ +from qiboml.optimizers.abstract import Optimizer From fe954dd388ee8986921a4017c9788e9547717f61 Mon Sep 17 00:00:00 2001 From: MatteoRobbiati Date: Thu, 14 Mar 2024 18:31:48 -0400 Subject: [PATCH 05/12] working on pqc --- src/qiboml/models/pqc.py | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/qiboml/models/pqc.py b/src/qiboml/models/pqc.py index 957c4c6..fb37ea8 100644 --- a/src/qiboml/models/pqc.py +++ b/src/qiboml/models/pqc.py @@ -27,14 +27,16 @@ def __init__( def add(self, gate): super().add(gate) - self.parameters.extend(gate.parameters) - self.nparams += gate.nparams + if len(gate.parameters) != 0: + self.parameters.extend(gate.parameters) + self.nparams += gate.nparams def compile( self, optimizer: Optimizer, loss: callable, observable: Union[Hamiltonian, List[Hamiltonian]], + encoding_config: Circuit, ): """ Compile the PQC to perform a training. @@ -48,7 +50,7 @@ def compile( self.optimizer = optimizer self.loss = loss self.observable = observable - self.encoding_config = encoding + self.encoding_circuit = encoding_config self.compiled = True def fit( @@ -81,7 +83,10 @@ def _loss(parameters, x_data, y_data): return loss_value results = self.optimizer.fit( - parameters=self.parameters, loss=_loss, args=(x_data, y_data) ** fit_options + initial_parameters=self.parameters, + loss=_loss, + args=(x_data, y_data), + **fit_options ) return results @@ -95,6 +100,9 @@ def predict(self, x: ndarray, nshots: int = None): "Please compile the model through the `PQC.compile` method to perform predictions.", ) - self.inject_data(x) + print(self.compiled) - return self.observable.expectation(self.execute(nshots=nshots).state()) + encoding_state = self.encoding_circuit.inject_data(x)().state() + return self.observable.expectation( + self(initial_state=encoding_state, nshots=nshots).state() + ) From 630378faaada3cb0a81f1d45bbab20d982bbb3f1 Mon Sep 17 00:00:00 2001 From: MatteoRobbiati Date: Thu, 14 Mar 2024 18:32:26 -0400 Subject: [PATCH 06/12] fix optimizers imports --- src/qiboml/optimizers/gradient_based.py | 3 ++- src/qiboml/optimizers/heuristics.py | 3 ++- src/qiboml/optimizers/minimizers.py | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/qiboml/optimizers/gradient_based.py b/src/qiboml/optimizers/gradient_based.py index fd49f97..32d29a2 100644 --- a/src/qiboml/optimizers/gradient_based.py +++ b/src/qiboml/optimizers/gradient_based.py @@ -5,7 +5,8 @@ from numpy import ndarray from qibo.backends import construct_backend from qibo.config import log -from qibo.optimizers.abstract import Optimizer + +from qiboml.optimizers.abstract import Optimizer class TensorflowSGD(Optimizer): diff --git a/src/qiboml/optimizers/heuristics.py b/src/qiboml/optimizers/heuristics.py index ce0db2e..3d65070 100644 --- a/src/qiboml/optimizers/heuristics.py +++ b/src/qiboml/optimizers/heuristics.py @@ -6,9 +6,10 @@ import cma from numpy import ndarray from qibo.config import log -from qibo.optimizers.abstract import Optimizer from scipy.optimize import basinhopping +from qiboml.optimizers.abstract import Optimizer + @dataclass class CMAES(Optimizer): diff --git a/src/qiboml/optimizers/minimizers.py b/src/qiboml/optimizers/minimizers.py index 18bee50..3344be2 100644 --- a/src/qiboml/optimizers/minimizers.py +++ b/src/qiboml/optimizers/minimizers.py @@ -6,9 +6,10 @@ import numpy as np from numpy import ndarray from qibo.config import log -from qibo.optimizers.abstract import Optimizer from scipy.optimize import Bounds, minimize, show_options +from qiboml.optimizers.abstract import Optimizer + @dataclass class ScipyMinimizer(Optimizer): From f67b08b4f45e69a4a3d8e1798d1aae7e74323712 Mon Sep 17 00:00:00 2001 From: MatteoRobbiati Date: Thu, 14 Mar 2024 18:32:49 -0400 Subject: [PATCH 07/12] add encoding model --- src/qiboml/models/encodings.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 src/qiboml/models/encodings.py diff --git a/src/qiboml/models/encodings.py b/src/qiboml/models/encodings.py new file mode 100644 index 0000000..f5a73fa --- /dev/null +++ b/src/qiboml/models/encodings.py @@ -0,0 +1,22 @@ +"""Strategies for data encoding into a Parametrized Quantum Circuit.""" + +from qibo import Circuit + + +class EncodingCircuit: + """ + An encoding circuit is a quantum circuit with a data encoding strategy. + + Args: + circuit (Circuit): a Qibo circuit. + encoding_strategy (callable): a callable function which defines the encoding + strategy of the data inside the circuit. + """ + + def __init__(self, circuit: Circuit, encoding_strategy: callable): + self.circuit = circuit + self.encoding_strategy = encoding_strategy + + def inject_data(self, data): + """Encode the data into ``circuit`` according to the chosen encoding strategy.""" + return self.encoding_strategy(self.circuit, data) From 5489e75440d05114f18525298a6ab50a5ff7d419 Mon Sep 17 00:00:00 2001 From: MatteoRobbiati Date: Thu, 14 Mar 2024 18:33:18 -0400 Subject: [PATCH 08/12] starting tutorial --- tutorials/Untitled.ipynb | 303 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 303 insertions(+) create mode 100644 tutorials/Untitled.ipynb diff --git a/tutorials/Untitled.ipynb b/tutorials/Untitled.ipynb new file mode 100644 index 0000000..bdeeb45 --- /dev/null +++ b/tutorials/Untitled.ipynb @@ -0,0 +1,303 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "983788cc-d67d-474c-b61e-4c6df384b91b", + "metadata": {}, + "source": [ + "## Define and train a custom Parametric Quantum Circuit" + ] + }, + { + "cell_type": "code", + "execution_count": 251, + "id": "47a5556c-a167-4983-b372-ac6b733aa1fd", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "[Qibo 0.2.6|INFO|2024-03-14 18:25:24]: Using numpy backend on /CPU:0\n" + ] + } + ], + "source": [ + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "\n", + "import qibo\n", + "from qibo import Circuit, gates, hamiltonians\n", + "\n", + "from qiboml.models import pqc, encodings\n", + "from qiboml.optimizers.minimizers import ScipyMinimizer\n", + "\n", + "from importlib import reload\n", + "reload(pqc)\n", + "reload(encodings)\n", + "\n", + "qibo.set_backend(\"numpy\")" + ] + }, + { + "cell_type": "code", + "execution_count": 252, + "id": "3b12bdc6-5b07-4079-84e7-835d0c76185b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[0.06122449 0.06122449]\n" + ] + } + ], + "source": [ + "ndim = 2 # Number of dimensions\n", + "ndata = 50 # Number of data points\n", + "\n", + "# Create the linspace\n", + "linspace_column = np.linspace(0, 1, ndata)\n", + "\n", + "# Repeat the linspace for ndim columns and reshape\n", + "data = np.repeat(linspace_column[:, np.newaxis], ndim, axis=1)\n", + "\n", + "# example data\n", + "print(data[3])" + ] + }, + { + "cell_type": "code", + "execution_count": 253, + "id": "273699ec-4a2c-42cc-884e-bfe7cc5a213c", + "metadata": {}, + "outputs": [], + "source": [ + "def target_function(data):\n", + " return np.sum(np.sin(data)**2 - np.cos(4*data)**2, axis=1)" + ] + }, + { + "cell_type": "code", + "execution_count": 254, + "id": "60bf02f4-779d-47ab-b9da-029208674337", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "labels = target_function(data)\n", + "\n", + "plt.plot(data[:,1], output)\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 255, + "id": "7884e8c7-e84b-4e05-b832-fed90af36696", + "metadata": {}, + "outputs": [], + "source": [ + "nqubits = 2\n", + "nlayers = 3\n", + "\n", + "model = pqc.PQC(nqubits=nqubits)\n", + "\n", + "for l in range(nlayers):\n", + " for q in range(nqubits):\n", + " model.add(gates.RY(q=q, theta=0))\n", + " model.add(gates.RY(q=q, theta=0))\n", + " for q in range(0, nqubits-1, 1):\n", + " model.add(gates.CNOT(q0=q, q1=q+1))\n", + " model.add(gates.CNOT(q0=nqubits-1, q1=0))\n", + "model.add(gates.M(*range(nqubits)))" + ] + }, + { + "cell_type": "code", + "execution_count": 256, + "id": "bac02fa4-6422-4a51-a0fb-9efee19134dd", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "q0: ─RY─RY─o─X─RY─RY─o─X─RY─RY─o─X─M─\n", + "q1: ─RY─RY─X─o─RY─RY─X─o─RY─RY─X─o─M─\n" + ] + } + ], + "source": [ + "print(model.draw())" + ] + }, + { + "cell_type": "code", + "execution_count": 257, + "id": "f66df344-994d-4aeb-997a-343b937b507b", + "metadata": {}, + "outputs": [], + "source": [ + "# define the optimizer\n", + "opt = ScipyMinimizer()\n", + "\n", + "# define the loss function\n", + "def loss_function(predictions, labels):\n", + " return np.sum( (predictions - labels)**2 ) / len(predictions)\n", + "\n", + "# define the observable\n", + "obs = hamiltonians.Z(nqubits=nqubits)\n", + "\n", + "# define the encoding strategy\n", + "# define the encoding circuit\n", + "\n", + "def build_encoding_circuit(nqubits):\n", + " \"\"\"Simple example: one RX per gate.\"\"\"\n", + " encoder = Circuit(nqubits)\n", + " for q in range(nqubits):\n", + " encoder.add(gates.RX(q, 0))\n", + " return encoder\n", + "\n", + "def define_encoding_strategy(circuit, data):\n", + " \"\"\"Simple example: one data per rotation angle.\"\"\"\n", + " circuit.set_parameters(data)\n", + " return circuit\n", + "\n", + "encoding_circuit = encodings.EncodingCircuit(\n", + " build_encoding_circuit(nqubits),\n", + " define_encoding_strategy\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 258, + "id": "cbebdd4b-95d9-4900-8002-930b51c5df97", + "metadata": {}, + "outputs": [], + "source": [ + "model.compile(\n", + " optimizer=opt,\n", + " loss=loss_function,\n", + " observable=obs,\n", + " encoding_config=encoding_circuit\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 259, + "id": "a044b120-ac16-462b-b0d6-f1801cbdd852", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "[Qibo 0.2.6|INFO|2024-03-14 18:25:26]: Optimization is performed using the optimizer: scipy_minimizer_Powell\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "True\n" + ] + }, + { + "ename": "AttributeError", + "evalue": "'bool' object has no attribute 'executor'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m/tmp/ipykernel_56062/3614230015.py\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mmodel\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfit\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx_data\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m10\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0my_data\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mlabels\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m10\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m~/Documents/PhD/qibogang/qiboml/src/qiboml/models/pqc.py\u001b[0m in \u001b[0;36mfit\u001b[0;34m(self, x_data, y_data, nshots, options)\u001b[0m\n\u001b[1;32m 84\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mloss_value\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 85\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 86\u001b[0;31m results = self.optimizer.fit(\n\u001b[0m\u001b[1;32m 87\u001b[0m \u001b[0minitial_parameters\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mparameters\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mloss\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0m_loss\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0margs\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx_data\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0my_data\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mfit_options\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 88\u001b[0m )\n", + "\u001b[0;32m~/Documents/PhD/qibogang/qiboml/src/qiboml/optimizers/minimizers.py\u001b[0m in \u001b[0;36mfit\u001b[0;34m(self, initial_parameters, loss, args, fit_options)\u001b[0m\n\u001b[1;32m 75\u001b[0m \u001b[0mlog\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0minfo\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34mf\"Optimization is performed using the optimizer: {self.__str__()}\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 76\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 77\u001b[0;31m \u001b[0mr\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mminimize\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mloss\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0minitial_parameters\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0margs\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0moptions\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 78\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 79\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mr\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfun\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mr\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mr\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/Documents/PhD/envs/qibo/lib/python3.10/site-packages/scipy/optimize/_minimize.py\u001b[0m in \u001b[0;36mminimize\u001b[0;34m(fun, x0, args, method, jac, hess, hessp, bounds, constraints, tol, callback, options)\u001b[0m\n\u001b[1;32m 706\u001b[0m \u001b[0mres\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0m_minimize_cg\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfun\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mx0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mjac\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcallback\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0moptions\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 707\u001b[0m \u001b[0;32melif\u001b[0m \u001b[0mmeth\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;34m'bfgs'\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 708\u001b[0;31m \u001b[0mres\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0m_minimize_bfgs\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfun\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mx0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mjac\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcallback\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0moptions\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 709\u001b[0m \u001b[0;32melif\u001b[0m \u001b[0mmeth\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;34m'newton-cg'\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 710\u001b[0m res = _minimize_newtoncg(fun, x0, args, jac, hess, hessp, callback,\n", + "\u001b[0;32m~/Documents/PhD/envs/qibo/lib/python3.10/site-packages/scipy/optimize/_optimize.py\u001b[0m in \u001b[0;36m_minimize_bfgs\u001b[0;34m(fun, x0, args, jac, callback, gtol, norm, eps, maxiter, disp, return_all, finite_diff_rel_step, xrtol, c1, c2, hess_inv0, **unknown_options)\u001b[0m\n\u001b[1;32m 1475\u001b[0m \u001b[0mmaxiter\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx0\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0;36m200\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1476\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1477\u001b[0;31m sf = _prepare_scalar_function(fun, x0, jac, args=args, epsilon=eps,\n\u001b[0m\u001b[1;32m 1478\u001b[0m finite_diff_rel_step=finite_diff_rel_step)\n\u001b[1;32m 1479\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/Documents/PhD/envs/qibo/lib/python3.10/site-packages/scipy/optimize/_optimize.py\u001b[0m in \u001b[0;36m_prepare_scalar_function\u001b[0;34m(fun, x0, jac, args, bounds, epsilon, finite_diff_rel_step, hess)\u001b[0m\n\u001b[1;32m 400\u001b[0m \u001b[0;31m# ScalarFunction caches. Reuse of fun(x) during grad\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 401\u001b[0m \u001b[0;31m# calculation reduces overall function evaluations.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 402\u001b[0;31m sf = ScalarFunction(fun, x0, args, grad, hess,\n\u001b[0m\u001b[1;32m 403\u001b[0m finite_diff_rel_step, bounds, epsilon=epsilon)\n\u001b[1;32m 404\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/Documents/PhD/envs/qibo/lib/python3.10/site-packages/scipy/optimize/_differentiable_functions.py\u001b[0m in \u001b[0;36m__init__\u001b[0;34m(self, fun, x0, args, grad, hess, finite_diff_rel_step, finite_diff_bounds, epsilon)\u001b[0m\n\u001b[1;32m 164\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 165\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_update_fun_impl\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mupdate_fun\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 166\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_update_fun\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 167\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 168\u001b[0m \u001b[0;31m# Gradient evaluation\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/Documents/PhD/envs/qibo/lib/python3.10/site-packages/scipy/optimize/_differentiable_functions.py\u001b[0m in \u001b[0;36m_update_fun\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 260\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m_update_fun\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 261\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mf_updated\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 262\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_update_fun_impl\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 263\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mf_updated\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mTrue\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 264\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/Documents/PhD/envs/qibo/lib/python3.10/site-packages/scipy/optimize/_differentiable_functions.py\u001b[0m in \u001b[0;36mupdate_fun\u001b[0;34m()\u001b[0m\n\u001b[1;32m 161\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 162\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mupdate_fun\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 163\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mf\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mfun_wrapped\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 164\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 165\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_update_fun_impl\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mupdate_fun\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/Documents/PhD/envs/qibo/lib/python3.10/site-packages/scipy/optimize/_differentiable_functions.py\u001b[0m in \u001b[0;36mfun_wrapped\u001b[0;34m(x)\u001b[0m\n\u001b[1;32m 143\u001b[0m \u001b[0;31m# Overwriting results in undefined behaviour because\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 144\u001b[0m \u001b[0;31m# fun(self.x) will change self.x, with the two no longer linked.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 145\u001b[0;31m \u001b[0mfx\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mfun\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcopy\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 146\u001b[0m \u001b[0;31m# Make sure the function returns a true scalar\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 147\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0misscalar\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfx\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/Documents/PhD/qibogang/qiboml/src/qiboml/models/pqc.py\u001b[0m in \u001b[0;36m_loss\u001b[0;34m(parameters, x_data, y_data)\u001b[0m\n\u001b[1;32m 80\u001b[0m \u001b[0mpredictions\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 81\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mx\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mx_data\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 82\u001b[0;31m \u001b[0mpredictions\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mappend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpredict\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnshots\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mnshots\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 83\u001b[0m \u001b[0mloss_value\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mloss\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mpredictions\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0my_data\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 84\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mloss_value\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/Documents/PhD/qibogang/qiboml/src/qiboml/models/pqc.py\u001b[0m in \u001b[0;36mpredict\u001b[0;34m(self, x, nshots)\u001b[0m\n\u001b[1;32m 102\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 103\u001b[0m \u001b[0mencoding_state\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mencoding_circuit\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0minject_data\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstate\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 104\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mobservable\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mexpectation\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0minitial_state\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mencoding_state\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnshots\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mnshots\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstate\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 105\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/Documents/PhD/qibo/src/qibo/models/circuit.py\u001b[0m in \u001b[0;36m__call__\u001b[0;34m(self, initial_state, nshots)\u001b[0m\n\u001b[1;32m 1115\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m__call__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0minitial_state\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnshots\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m1000\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1116\u001b[0m \u001b[0;34m\"\"\"Equivalent to ``circuit.execute``.\"\"\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1117\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mexecute\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0minitial_state\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0minitial_state\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnshots\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mnshots\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1118\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1119\u001b[0m \u001b[0;34m@\u001b[0m\u001b[0mproperty\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/Documents/PhD/qibo/src/qibo/models/circuit.py\u001b[0m in \u001b[0;36mexecute\u001b[0;34m(self, initial_state, nshots)\u001b[0m\n\u001b[1;32m 1100\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcompiled\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1101\u001b[0m \u001b[0;31m# pylint: disable=E1101\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1102\u001b[0;31m \u001b[0mstate\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcompiled\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mexecutor\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0minitial_state\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnshots\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1103\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_final_state\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcompiled\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mresult\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mstate\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnshots\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1104\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_final_state\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mAttributeError\u001b[0m: 'bool' object has no attribute 'executor'" + ] + } + ], + "source": [ + "model.fit(x_data=data[10:], y_data=labels[10:])" + ] + }, + { + "cell_type": "code", + "execution_count": 250, + "id": "7f934531-b6de-4f40-bb45-99716eefd450", + "metadata": {}, + "outputs": [ + { + "ename": "AttributeError", + "evalue": "'bool' object has no attribute 'executor'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m/tmp/ipykernel_56062/1075888772.py\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mmodel\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpredict\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0.3\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m0.4\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m~/Documents/PhD/qibogang/qiboml/src/qiboml/models/pqc.py\u001b[0m in \u001b[0;36mpredict\u001b[0;34m(self, x, nshots)\u001b[0m\n\u001b[1;32m 100\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 101\u001b[0m \u001b[0mencoding_state\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mencoding_circuit\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0minject_data\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstate\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 102\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mobservable\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mexpectation\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0minitial_state\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mencoding_state\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnshots\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mnshots\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstate\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 103\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/Documents/PhD/qibo/src/qibo/models/circuit.py\u001b[0m in \u001b[0;36m__call__\u001b[0;34m(self, initial_state, nshots)\u001b[0m\n\u001b[1;32m 1115\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m__call__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0minitial_state\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnshots\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m1000\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1116\u001b[0m \u001b[0;34m\"\"\"Equivalent to ``circuit.execute``.\"\"\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1117\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mexecute\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0minitial_state\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0minitial_state\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnshots\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mnshots\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1118\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1119\u001b[0m \u001b[0;34m@\u001b[0m\u001b[0mproperty\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/Documents/PhD/qibo/src/qibo/models/circuit.py\u001b[0m in \u001b[0;36mexecute\u001b[0;34m(self, initial_state, nshots)\u001b[0m\n\u001b[1;32m 1100\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcompiled\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1101\u001b[0m \u001b[0;31m# pylint: disable=E1101\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1102\u001b[0;31m \u001b[0mstate\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcompiled\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mexecutor\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0minitial_state\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnshots\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1103\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_final_state\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcompiled\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mresult\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mstate\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnshots\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1104\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_final_state\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mAttributeError\u001b[0m: 'bool' object has no attribute 'executor'" + ] + } + ], + "source": [ + "model.predict([0.3, 0.4])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5cb71387-ce34-4b4e-8957-e20eb3e401ec", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.10.0" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From 9077b560bcd979973d083d145282455e4198e37e Mon Sep 17 00:00:00 2001 From: Matteo Robbiati <62071516+MatteoRobbiati@users.noreply.github.com> Date: Fri, 15 Mar 2024 09:01:17 -0400 Subject: [PATCH 09/12] Apply suggestions from code review Thanks! Co-authored-by: Alessandro Candido --- src/qiboml/optimizers/heuristics.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/qiboml/optimizers/heuristics.py b/src/qiboml/optimizers/heuristics.py index 3d65070..b4399a3 100644 --- a/src/qiboml/optimizers/heuristics.py +++ b/src/qiboml/optimizers/heuristics.py @@ -93,8 +93,8 @@ def fit( @dataclass class BasinHopping(Optimizer): """ - Global optimizer based on: https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.basinhopping.html. - Note that the Basin-Hopping optimizer combines a global stepping algorithm + Global optimizer based on: :func:`scipy.optimize.basinhopping`. + Note that the basin-hopping optimizer combines a global stepping algorithm together with a local minimization (which is implemented using an extra scipy minimizer). It is designed to mimic the natural process of energy minimization of clusters of atoms and it works well for similar problems with “funnel-like, but rugged” energy landscapes. From b4288bb444ef54dc42df59fccb2429a45188a7cd Mon Sep 17 00:00:00 2001 From: MatteoRobbiati Date: Fri, 15 Mar 2024 09:50:23 -0400 Subject: [PATCH 10/12] fix: change name to compile function avoiding overriding Circuit functions --- src/qiboml/models/pqc.py | 94 +++++++++++++++++++++++++++++++--------- 1 file changed, 73 insertions(+), 21 deletions(-) diff --git a/src/qiboml/models/pqc.py b/src/qiboml/models/pqc.py index fb37ea8..f371bb5 100644 --- a/src/qiboml/models/pqc.py +++ b/src/qiboml/models/pqc.py @@ -2,11 +2,13 @@ from typing import Dict, List, Optional, Union -from numpy import ndarray +from numpy import array, ndarray from qibo import Circuit from qibo.config import raise_error +from qibo.gates import Gate from qibo.hamiltonians import Hamiltonian +from qiboml.models.encodings import EncodingCircuit from qiboml.optimizers import Optimizer @@ -25,18 +27,35 @@ def __init__( self.parameters = [] self.nparams = 0 - def add(self, gate): + def add(self, gate: Gate): + """ + Add a gate to the PQC. + + Args: + gate (qibo.Gate): Qibo gate to be added to the PQC. + """ super().add(gate) if len(gate.parameters) != 0: self.parameters.extend(gate.parameters) self.nparams += gate.nparams - def compile( + def set_parameters(self, parameters: Union[List, ndarray]): + """ + Set model parameters. + + Args: + parameters (Union[List, ndarray]): new set of parameters to be set + into the PQC. + """ + self.parameters = parameters + super().set_parameters(parameters) + + def setup( self, optimizer: Optimizer, loss: callable, observable: Union[Hamiltonian, List[Hamiltonian]], - encoding_config: Circuit, + encoding_config: EncodingCircuit, ): """ Compile the PQC to perform a training. @@ -46,26 +65,38 @@ def compile( loss (callable): loss function to be minimizer. observable (qibo.hamiltonians.Hamiltonian): observable, or list of observables, whose expectation value is used to compute predictions. + encoding_config (qiboml.models.EncodingCircuit): encoding circuit, which + is a Qibo circuit defined together with an encoding strategy. + """ self.optimizer = optimizer self.loss = loss self.observable = observable self.encoding_circuit = encoding_config - self.compiled = True + self._compiled = True def fit( self, - x_data: ndarray, - y_data: ndarray, + input_data: ndarray, + output_data: ndarray, nshots: Optional[int] = None, options: Optional[Dict] = None, ): - """Perform the PQC training.""" + """ + Perform the PQC training according to the chosen trainig setup. + + Args: + input_data (np.ndarray): input data to train on. + output_data (np.ndarray): output data used as labels in the training process. + nshots (Optional[int]): number of shots for circuit evaluations. + options (Optional[Dict]): extra fit options eventually needed by the + chosen optimizer. + """ - if not self.compiled: + if not self._compiled: raise_error( ValueError, - "Please compile the model through the `PQC.compile` method to train it.", + "Please compile the model through the `PQC.setup` method to train it.", ) if options is None: @@ -73,36 +104,57 @@ def fit( else: fit_options = options - def _loss(parameters, x_data, y_data): + def _loss(parameters, input_data, output_data): self.set_parameters(parameters) predictions = [] - for x in x_data: - predictions.append(self.predict(x=x, nshots=nshots)) - loss_value = self.loss(predictions, y_data) + for x in input_data: + predictions.append(self.predict(input_datum=x, nshots=nshots)) + loss_value = self.loss(predictions, output_data) return loss_value results = self.optimizer.fit( initial_parameters=self.parameters, loss=_loss, - args=(x_data, y_data), + args=(input_data, output_data), **fit_options ) return results - def predict(self, x: ndarray, nshots: int = None): - """Perform prediction associated to a single input data ``x``.""" + def predict(self, input_datum: Union[array, List, tuple], nshots: int = None): + """ + Perform prediction associated to a single ``input_datum``. - if not self.compiled: + Args: + input_datum (Union[array, List, tuple]): one single element of the + input dataset. + nshots (int): number of circuit execution to compute the prediction. + """ + + if not self._compiled: raise_error( ValueError, "Please compile the model through the `PQC.compile` method to perform predictions.", ) - print(self.compiled) - - encoding_state = self.encoding_circuit.inject_data(x)().state() + encoding_state = self.encoding_circuit.inject_data(input_datum)().state() return self.observable.expectation( self(initial_state=encoding_state, nshots=nshots).state() ) + + def predict_sample(self, input_data: ndarray, nshots: int = None): + """ + Compute predictions for a set of data ``input_data``. + + Args: + input_data (np.ndarray): input data. + nshots (int): number of times the circuit is executed to compute the + predictions. + """ + + predictions = [] + for x in input_data: + predictions.append(self.predict(input_datum=x, nshots=nshots)) + + return predictions From 03daa63cdbe7e534b25831ea00bc3fde3f657383 Mon Sep 17 00:00:00 2001 From: MatteoRobbiati Date: Fri, 15 Mar 2024 09:52:03 -0400 Subject: [PATCH 11/12] feat: more details in pqc tutorial --- tutorials/Untitled.ipynb | 303 --------------------------------------- tutorials/pqc.ipynb | 286 ++++++++++++++++++++++++++++++++++++ 2 files changed, 286 insertions(+), 303 deletions(-) delete mode 100644 tutorials/Untitled.ipynb create mode 100644 tutorials/pqc.ipynb diff --git a/tutorials/Untitled.ipynb b/tutorials/Untitled.ipynb deleted file mode 100644 index bdeeb45..0000000 --- a/tutorials/Untitled.ipynb +++ /dev/null @@ -1,303 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "983788cc-d67d-474c-b61e-4c6df384b91b", - "metadata": {}, - "source": [ - "## Define and train a custom Parametric Quantum Circuit" - ] - }, - { - "cell_type": "code", - "execution_count": 251, - "id": "47a5556c-a167-4983-b372-ac6b733aa1fd", - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "[Qibo 0.2.6|INFO|2024-03-14 18:25:24]: Using numpy backend on /CPU:0\n" - ] - } - ], - "source": [ - "import numpy as np\n", - "import matplotlib.pyplot as plt\n", - "\n", - "import qibo\n", - "from qibo import Circuit, gates, hamiltonians\n", - "\n", - "from qiboml.models import pqc, encodings\n", - "from qiboml.optimizers.minimizers import ScipyMinimizer\n", - "\n", - "from importlib import reload\n", - "reload(pqc)\n", - "reload(encodings)\n", - "\n", - "qibo.set_backend(\"numpy\")" - ] - }, - { - "cell_type": "code", - "execution_count": 252, - "id": "3b12bdc6-5b07-4079-84e7-835d0c76185b", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[0.06122449 0.06122449]\n" - ] - } - ], - "source": [ - "ndim = 2 # Number of dimensions\n", - "ndata = 50 # Number of data points\n", - "\n", - "# Create the linspace\n", - "linspace_column = np.linspace(0, 1, ndata)\n", - "\n", - "# Repeat the linspace for ndim columns and reshape\n", - "data = np.repeat(linspace_column[:, np.newaxis], ndim, axis=1)\n", - "\n", - "# example data\n", - "print(data[3])" - ] - }, - { - "cell_type": "code", - "execution_count": 253, - "id": "273699ec-4a2c-42cc-884e-bfe7cc5a213c", - "metadata": {}, - "outputs": [], - "source": [ - "def target_function(data):\n", - " return np.sum(np.sin(data)**2 - np.cos(4*data)**2, axis=1)" - ] - }, - { - "cell_type": "code", - "execution_count": 254, - "id": "60bf02f4-779d-47ab-b9da-029208674337", - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAi8AAAGdCAYAAADaPpOnAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8g+/7EAAAACXBIWXMAAA9hAAAPYQGoP6dpAABNVUlEQVR4nO3deXhTZcIF8JOkTbqndF+hlAJl3ymtoCAoiwooIiAKKIILOqMw+sGoIKLirqOj4y6oILiAICCCCKhQKEvL1gUKlC50L026pm1yvz/SVtACbWny5ibn9zx5ZkgTe7hic3jvuygkSZJAREREJBNK0QGIiIiIWoLlhYiIiGSF5YWIiIhkheWFiIiIZIXlhYiIiGSF5YWIiIhkheWFiIiIZIXlhYiIiGTFSXSAtmYymXD+/Hl4enpCoVCIjkNERETNIEkSysrKEBISAqXyymMrdldezp8/j/DwcNExiIiIqBWysrIQFhZ2xdfYXXnx9PQEYP7Ne3l5CU5DREREzaHX6xEeHt74OX4ldldeGm4VeXl5sbwQERHJTHOmfHDCLhEREckKywsRERHJCssLERERyQrLCxEREckKywsRERHJCssLERERyQrLCxEREckKywsRERHJCssLERERyQrLCxEREckKywsRERHJCssLERERyQrLCxERETWLoc6IB1YexDcHslBrNAnLwfJCREREzbIrrRC/pOTjze0noWrG6c+WwvJCREREzbIx6TwA4LY+wVAqWV6IiIjIhpUb6vBLSj4AYELfUKFZWF6IiIjoqradyIOhzoRIf3f0CPESmsUq5eW9995DREQEXFxcEBMTg4SEhMu+dsWKFVAoFJc8XFxcrBGTiIiILmND/S2j8X1CoBA43wWwQnlZu3Yt5s+fjyVLluDw4cPo06cPRo8ejYKCgsu+x8vLC7m5uY2Pc+fOWTomERERXUZxuQF/pBcBMJcX0SxeXt58803MmTMH9913H7p3744PPvgAbm5u+Oyzzy77HoVCgaCgoMZHYGCgpWMSERHRZWw5lgujSULvMC0i/T1Ex7FseampqcGhQ4cwatSoP7+hUolRo0YhPj7+su8rLy9Hhw4dEB4ejgkTJuDEiROWjElERERXcPEtI1tg0fJSVFQEo9H4t5GTwMBA5OXlNfmerl274rPPPsOGDRvw1VdfwWQyIS4uDtnZ2U2+3mAwQK/XX/IgIiKitpF9oRIHz12AQgHc5gjlpTViY2MxY8YM9O3bFzfccAPWrVsHf39/fPjhh02+fvny5dBqtY2P8PBwKycmIiKyXz8eyQUADOnoi0Av21hAY9Hy4ufnB5VKhfz8/Euez8/PR1BQULP+Gc7OzujXrx/S09Ob/PqiRYug0+kaH1lZWdecm4iIiMw2JOUAACb0tY1RF8DC5UWtVmPAgAHYsWNH43Mmkwk7duxAbGxss/4ZRqMRx44dQ3BwcJNf12g08PLyuuRBRERE1+5kfhlS88rgrFJgbM+mP4dFcLL0N5g/fz5mzpyJgQMHYvDgwXj77bdRUVGB++67DwAwY8YMhIaGYvny5QCA559/HkOGDEFUVBRKS0vx2muv4dy5c3jggQcsHZWIiIgu0nAcwA1dAqB1cxac5k8WLy9TpkxBYWEhFi9ejLy8PPTt2xdbt25tnMSbmZkJpfLPAaALFy5gzpw5yMvLQ7t27TBgwADs3bsX3bt3t3RUIiIiqidJEjYcsb1bRgCgkCRJEh2iLen1emi1Wuh0Ot5CIiIiaqXDmRdwx/t74aZW4dAzN8FVrbLo92vJ57fNrTYiIiIi8RpuGd3cPdDixaWlWF6IiIjoEnVGEzYdNS+RFn2CdFNYXoiIiOgS8WeKUVRuQDs3Zwzt7Cc6zt+wvBAREdElGo4DGNcrGM4q26sKtpeIiIiIhKmuNeLn4+YjfGzxlhHA8kJEREQX2ZVWgDJDHUK0LhjYoZ3oOE1ieSEiIqJGDbeMbusbAqVSIThN01heiIiICACgr67FjtQCAMB4GzlBuiksL0RERAQA2HYiHzV1JkQFeKB7sO1u9MryQkRERAAuOkG6TwgUCtu8ZQSwvBARERGAwjID9qQXAQBus+FbRgDLCxEREQHYciwXJgnoE+6NCD930XGuiOWFiIiIGm8Z2fJE3QYsL0RERA4uq6QShzNLoVQAt/UOFh3nqlheiIiIHNzGI+a9XWI7+SLAy0VwmqtjeSEiInJwG+s3ppvQxzaPA/grlhciIiIHlpqnR1p+GdQqJUb3DBIdp1mcRAcgItuQVVKJC5U1cHVWwVWtgpvaCW5qFTROSpve74GIrk3DqMvwrv7QujoLTtM8LC9EDkqSJKTklmHriTxsO5GH1LyyJl+nUACuziq4qc2lJtTbFbf3C8WtvUPgruGPECI5kySp8SwjWz1Buin8yUPkQIwmCYczL+Dn43n4OTkPWSVVjV9TKRUI8NSgqtaIyhojaupMAABJAiprzM8BQFZJFfadKcHzPybjtj4hmDIoHH3DvTk6QyRDhzMvIKe0Cu5qFUZ2CxAdp9lYXojsnCRJ2Hu6GJuO5mJ7cj6Kyg2NX9M4KXFDF3+M7hGEkd0C4O2mbvxandGE6joTKmvqUFVfXiprjDiQUYK1B7JwtqgCaw5kYc2BLHQN9MSUQeG4vV8o2rmrm4pBRDao4ZbR6B5BcHFWCU7TfApJkiTRIdqSXq+HVquFTqeDl5ftHipFZA1F5QY8vf4Yfj6R3/icp4sTRkYHYEzPIFzfxR9u6pb/HUaSJCScNZeYzcdyYagfpWmY8DdtUDhiO/lyNIbIhtUZTYh5aQeKK2rw+X2DMKKr2JGXlnx+s7wQ2amtx3Px7/XHUVJRA2eVAncOCMPYnsEYEukLtVPbLTTUVdViY1IOvk7IQnKuvvH5W3oH46Xbe8lmAiCRo9l9shAzP0uAj7sa+/89Es4qsQuQW/L5zdtGRHZGV1mLJRuP44f64eDoIE+8cVcf9AjRWuT7aV2dcW9sBO6NjcDxHB1WJ2SaR2SO5iIpsxRvTemLwR19LPK9iaj1Gm4Z3dIrWHhxaSl5pSWiK9qZVoCb396NH5LOQ6kA5o3ohA2PXmex4vJXPUO1eOn2XvjuoVi093FDTmkVpn4Ujze3paHOaLJKBiK6uupaI34+kQcAGN/X9s8y+iuWFyI7UG6ow8Lvj+K+zw8gX29ApJ87vn84Dk+OjobGyfqT8Pq1b4ct/xyGSf3DYJKAd35Nx+QP45FZXGn1LET0d7+mFqDcUIdQb1cMaN9OdJwWY3khkrn408UY8/ZvWHMgCwBw/3Udsfkfw9BP8A8kD40T3rirD96Z1g+eGickZpZi3Du/Y31ittBcRPTnLaPb+oRAqZTfxHrOeSGSsc/+OIvnNyUDAMLaueK1O/sgtpOv4FSXGt8nBP3CvfHE2iQcPHcBT6w9gt1phXh+Yk94uXAyL5G16atr8WtaAQDzf59yxJEXIpn6ct+5xuIyZWA4tj5+vc0VlwbhPm5YM3cIHh/VGUoF8EPSeYz7z+84cV4nOhqRw/n5eB5q6kzoHOCBbsGeouO0CssLkQx9cyALz/5wHADw8PBOeHlSL3jY+Fb9TiolHh/VBd8+FIuwdq7IvlCFuz/ezwJDZGUbjzQcBxAi272YWF6IZGZDUg7+b91RAMB910XgqdFdZfUDaEAHn/o5Od7QVdXink/2I+Wi/WGIyHIKyqqxJ70IgHm+i1yxvBDJyE/HcjH/myOQJGB6THssvrW7rIpLA62rM1bePxh9wr1xobIW0z/Zj7TLHAxJRG1ny9FcmCSgb7g3Ovi6i47TaiwvRDLxS3I+Hvs6EUaThDsHhGHZhJ6yLC4NvFyc8cX9g9E7TIuSihrc/fE+nMxngSGypA31t4zkOlG3AcsLkQz8drIQj6w6jDqThPF9QvDKpN6yXN74V1pXZ3x5fwx6hnqhuL7ApBewwBBZQmZxJRIzS6FUALf2DhYd55qwvBDZuPjTxZjzxUHUGE0Y2zMIb97VByo7KC4NtG7O+Gp2DLoHe6GovAbTPt6P04XlomMR2Z0fj5pHXeI6+SHAy0VwmmvD8kJkww6dK8HslQdgqDNhZHQA/jO1H5xkdgZJc3i7qbHqgRhEB3misMyAaR/twxkWGKI2I0kSfkjMASD/W0YAywuRzTqaXYpZnx1AZY0Rwzr74b3p/dv0NGhb087dXGC6BnqioMyAaR/vQ0ZRhehYRHYhNa8MpwrKoVYpMbpnkOg418x+fxISydiFiho8+OUhlBnqENPRBx/dOxAuztY/o8jafD00WDUnBp0DPJCvNxcYnodEdO0a9nYZEe0Prav8d7ZmeSGyMZIk4anvjyJXV42Ofu74dNYguKrtv7g08PPQYPWcIYgK8ECurhozP0+AvrpWdCwi2TKZJGxovGUUKjhN22B5IbIxX+47h+3J+VCrlHh3Wj+b3znXEvw9NVg9Jwah3q44W1SB+WuPwGSSRMcikqUDGSU4r6uGp8YJI7sFiI7TJlheiGxI8nk9XticAgBYODYaPUO1ghOJE+Dpgv/dY57n80tKPt7flS46EpEs/ZBkHnUZ2yvIbm4/s7wQ2YjKmjo89vVh1NSvLLrvugjRkYTrHeaNFyb0BAC8sf0kdtWfhEtEzWOoM2Lz0VwAwMS+9nHLCGB5IbIZSzcm43RhBQI8NXhtch9Z757blu4aFI5pg9tDkoB/rklCVgkn8BI1187UQuir6xDk5YKYSNs8db41WF6IbMCPR85j7cEsKBTA21P7wsddLTqSTXlufHf0CTcf5Pjgl4dQVWMUHYlIFjbU3zIa3zfErja3ZHkhEiyrpBL/XncMADBveBTiOvkJTmR7NE4qfHBPf/i6q5Gcq8fT649BkjiBl+hKdFW12JFivtVqT7eMAJYXIqFqjSY89nUiygx1GNChHR4f1Vl0JJsVrHXFf+/uD5VSgXWJOfhy3znRkYhs2k/HclFjNKFLoAe6BXuKjtOmWF6IBHpj20kkZZXCy8UJ/5na1y63/m9LsZ18sWhsNADg+R+TcTCjRHAiItvVsMpoYr9Qu5tDx5+URIL8fqoQH+w+DQB4ZVJvhLVzE5xIHmYP7YhbewejziTh4VWHUaCvFh2JyOacL63CvjPmcj/Bzm4ZASwvREIUlRvwxNojAIC7Y9pjbC95H09vTQqFAq9M6o0ugR4oLDPgkVXm5eVE9KeG4wAGd/RBqLer4DRtj+WFyMokScJT3x1FUbkBXQI9sPjW7qIjyY67xgkf3jsQnhonHDx3AS9tSREdicimNJwgfXs/+xt1AVheiKzu5xN5+DW1oH77//52s+OltXX0c8ebU/oCAFbszeAGdkT1UnL1SM0rg1qlxLie9jmqy/JCZEVVNUYs22QeJXjwhkh0DbKvFQDWdlP3QMyKiwAALPz+GHRVPMCRqGGi7ohof2jd5H+CdFNYXois6P1d6cgprUKotyseGR4lOo5d+L8x0ejo5448fTWW/nhCdBwioUwmCRuTzPNd7PWWEcDyQmQ1GUUV+HD3GQDAs7d2g6uat4vagqtahdcn94ZSAaw7nIPtyfmiIxEJs/9sCXJ11fB0ccLwrvZxgnRTWF6IrOT5TcmoMZowrLMfRvcIEh3Hrgzo4IM5wyIBAIvWHcOFihrBiYjEaJioe0uvYLueT2eV8vLee+8hIiICLi4uiImJQUJCwhVf/+233yI6OhouLi7o1asXtmzZYo2YRBbzS3I+fk0tgLNKgefG97C7DaNswRM3dUHnAA8UlRuweCNvH5Hjqa41Ystx8wnS9ri3y8UsXl7Wrl2L+fPnY8mSJTh8+DD69OmD0aNHo6Cg6ZUBe/fuxbRp0zB79mwkJiZi4sSJmDhxIo4fP27pqEQWUV1rxNJN5g/T2UMj0cnfQ3Ai++TirMIbd/WBSqnAj0fOY8uxXNGRiKxqZ2oByqrrEKx1QUxHH9FxLMri5eXNN9/EnDlzcN9996F79+744IMP4Obmhs8++6zJ1//nP//BmDFj8OSTT6Jbt25YtmwZ+vfvj//+97+WjkpkER/uPoOskioEebngsRs5SdeSeod545HhnQAAz/xwHEXlBsGJiKznh4tOkFba0QnSTbFoeampqcGhQ4cwatSoP7+hUolRo0YhPj6+yffEx8df8noAGD169GVfbzAYoNfrL3kQ2Yqskkq8vysdAPD0Ld3grnESnMj+PXZjZ0QHeaKkooanT5PD0FXWYmdqIQD7XmXUwKLlpaioCEajEYGBgZc8HxgYiLy8vCbfk5eX16LXL1++HFqttvERHh7eNuGJ2sDzm5JhqDMhrpMvbu1tn5tF2Rq1kxJv3NUHTkoFfj6Rjw31y0aJ7NmW4+YTpKODPBEd5CU6jsXJfrXRokWLoNPpGh9ZWVmiIxEBAHamFWB7cj6clAos5SRdq+oRosU/RnYGACzecBz5PLyR7Nz6xD9PkHYEFi0vfn5+UKlUyM+/dN+F/Px8BAU1vVQ0KCioRa/XaDTw8vK65EEkmqHOiKX1K15mxUWgcyB30rW2h4d3Qq9QLfTVdVj4/VHePiK7lX2hEglnS6BQAOP7hIiOYxUWLS9qtRoDBgzAjh07Gp8zmUzYsWMHYmNjm3xPbGzsJa8HgO3bt1/29US26JPfzyKjuBL+nhr8c1Rn0XEckrPKfPtIrVJiZ1ohvj2ULToSkUU0nCAd09EHIXZ4gnRTLH7baP78+fj444+xcuVKpKSk4OGHH0ZFRQXuu+8+AMCMGTOwaNGixtf/85//xNatW/HGG28gNTUVzz33HA4ePIhHH33U0lGJ2kROaRX++2v9JN1x3eDpYp9ni8hBl0BPzL+5CwBg2Y/JyNPx9hHZF0mSsP6wfZ8g3RSLl5cpU6bg9ddfx+LFi9G3b18kJSVh69atjZNyMzMzkZv7534McXFxWL16NT766CP06dMH3333HX744Qf07NnT0lGJ2sSLm5NRVWvE4AgfTOjrGEO4tmzOsEj0a++NMkMdlm1OFh2HqE0dy9HhVEE5NE5KjLHTE6SbopDs7EawXq+HVquFTqfj/BeyukPnSjDpf/FQKRXY9NhQdAvmn0FbkHxej1vf/R0mCfji/sG4vou/6EhEbWLJhuNYGX8O4/uE4J1p/UTHuSYt+fyW/WojIlvyxraTAIDJA8JYXGxI9xAvzIrrCMC8+qi61ig4EdG1M9QZsaF+vsukAWGC01gXywtRG4k/XYy9p4vhrFLgUe6ka3OeuKkzAjw1yCiubDzdm0jOfk0pQGllLQK9NBga5Sc6jlWxvBC1AUmS8Ob2NADAtMHtEdbOTXAi+itPF2c8e2t3AMB7u9JxrrhCcCKia/Nd/Qq6O/qHQWXnxwH8FcsLURv47VQRDmRcgMZJiXkjOOpiq27tHYyhUX6oqTNh8YYT3PuFZKuwzIBdJ83HAUzq71i3jACWF6JrJkkS3txmHnW5Z0gHBHq5CE5El6NQKPD8hB5Qq5TYfbIQP59o+tgRIlu3ISkHRpOEvuHeiApwvJPqWV6IrtGOlAIcydbB1VmFh+tPNCbbFenvgQdviAQALP0xGRWGOsGJiFpGkqTGW0Z3OthE3QYsL0TXwGSS8MZ28wqjWddFwM9DIzgRNce8EVEI93FFrq4a7+w4JToOUYucOK9Hal4Z1E5K3NbbMfeSYnkhugZbT+QhJVcPD40T5g6LFB2HmsnFWYXnbusBAPj0j7NIyysTnIio+RpGXW7qHgitm2Pu4M3yQtRKRpOEt+pHXWYP7Yh27mrBiaglRnYLxM3dA1FnkvDsD8c5eZdkoabOhA1J5uMAHPWWEcDyQtRqPx45j1MF5dC6OmP2sI6i41ArLBnfA67OKiRklGBd/fkwRLZsZ1oBLlTWwt9Tg2EOtrfLxVheiFqhzmjCf+rnSsy9PhJePHxRlkK9XRtP/X5pSwpKK2sEJyK6ssa9XfqFwknluB/hjvs7J7oG6xJzcLaoAj7uasyKixAdh67B/dd1ROcADxRX1OC1n9NExyG6rOJyA3amFgBwvOMA/orlhaiFaupM+M8v5lGXh2/oBHeNk+BEdC3UTkosm2g+tX51QiaOZJWKDUR0GRuSzqPOJKF3mBZdAj1FxxGK5YWohb45mIWc0ir4e2pwz5AOouNQGxgS6Yvb+4VCkoClP3LnXbJNjr63y8VYXohaoLrWiP/+mg4AmDe8E1zVKsGJqK0sHBsNN7UKhzNLsbH+pF4iW5F8Xo/kXD3UKsfd2+ViLC9ELbB6fyby9NUI0bpgWkx70XGoDQV6ueCR+h2SX/4pFZU13HmXbMf3h82jLiO7BXBbBrC8EDVbVY0R7+86DQB49MbO0Dhx1MXePDAsEqHe5p13P/rtjOg4RACAWqMJPyRyb5eLsbwQNdPXCZkoKjcg3McVkwfyB4g9cnFWYdG4aADAB7tP43xpleBERMCutEIUV9TAz0OD67v4i45jE1heiJqh1mjCp3+cBQA8dEMnODvw/gr27pZewRgc4YPqWhNe2ZoqOg4RvjuUBQCY2DeEP3vq8SoQNcOWY7nIKa2Cr7sak/pz1MWeKRQKLL6tOxQK89LUQ+cuiI5EDqykoga/cm+Xv2F5IboKSZLw4W7z/IdZcRFwceZcF3vXM1SLyfUfFM//eAImE5dOkxgbk3JQa5TQM9QL3YK9RMexGSwvRFfxR3oRknP1cHVWcV8XB/Kv0V3hoXHCkWwd1ify3CMS47v6VUYc8b0UywvRVTSsOpkyKJxLFB1IgKcL5o2IAgC8sjUVFQYunSbrOnFeh+M5ejirFJjQN1R0HJvC8kJ0BcdzdPj9VBFUSgVmD+XJ0Y7m/qERaO/jhoIyAz7YfVp0HHIwXydkAgBu7hEEH/7F6RIsL0RX8PHv5lGXW3oFI9zHTXAasjaNkwr/HtcNgHkELvtCpeBE5CgqDHX4IdG80/Pdg7kh5l+xvBBdRvaFSmw6mgsAmHt9pOA0JMroHoEYEukDQ50Jy3/i0mmyjk1Hz6PcUIcIXzfERvqKjmNzWF6ILuPTP87CaJIwNMoPPUO1ouOQIAqFAotv7QGlAth8NBcJZ0tERyIHsHq/+ZbR1MHtoVQqBKexPSwvRE0orazBmgTzxlAcdaHuIV6YMsg8dP/8Ji6dJss6nqPDkWwdnFUKHgdwGSwvRE34at85VNUa0S3YC8M6+4mOQzZgwc1d4KlxwvEcPb47lC06Dtmxiyfq+nloBKexTSwvRH9RXWvEir0ZAIAHr4+EQsEhWwL8PDT4x8jOAIDXtqVx6TRZRIWhDhuSzBN1p3Oi7mWxvBD9xbrDOSgqr0Gotytu6R0sOg7ZkBlxHdDexw2FZQZ8yFOnyQJ+PHLRRN1OnKh7OSwvRBcxmiR8Ur88+v6hHXkIGl1C46TCwrHmU6c/+u008nTVghORvWm4ZTRtcHuO+l4BfzITXWR7cj7OFFVA6+qMqYPCRcchGzS2ZxAGdmiH6loTXt+WJjoO2RFO1G0+lheiepIk4cPfzLuo3jOkPdw1ToITkS1SKBR4+hbzxnXfH87G8Ryd4ERkLxpGXUb3CIIvJ+peEcsLUb2D5y4gMbMUaiclZsZFiI5DNqxf+3YY3ycEkgS8uDkFksSl03RtLp6oe3cMJ+peDcsLUb0Pd5vnukzqH4oATxfBacjWPTWmK9ROSsSfKcaOlALRcUjmGibqdvRz5466zcDyQgQgvaAMv6TkQ6EAHhjGTeno6sLauTUe1vnSlhTUGk2CE5Gc/TlRN5wTdZuB5YUIwKd/ZAAAbuoWiE7+HmLDkGw8MrwTfN3VOFNU0bidO1FLNUzUVauUmNSfE3Wbg+WFHJ6ushY/JOYAQOPfpImaw9PFGY/f1AUA8PYvJ6GrqhWciOSocaJuT07UbS6WF3J43x7KQlWtEdFBnhjc0Ud0HJKZaYPCERXggQuVtXhvZ7roOCQzF0/UnTaY2zM0F8sLOTSjScIX8ecAADPjInivmVrMSaXE0+PMS6dX7MlAVkml4EQkJ5yo2zosL+TQdqUVILOkEl4uTpjYN1R0HJKp4V39MTTKDzVGE17emio6DsnIak7UbRWWF3JoK+tHXaYMCoerWiU4DcmVQqHAv8d1g0IBbD6ai0PnLoiORDJwPEeHo/UTde8cwFtGLcHyQg7rdGE5fjtZCIUCuHdIhOg4JHPdQ7wwuX5L9xc2J3PjOrqq1RdN1PVxVwtOIy8sL+SwvqwfdRkZHYD2vm6C05A9WHBzV7ipVUjMLMWmo7mi45ANqzDUYUP9Kse7B3NH3ZZieSGHVG6ow3eHsgEAM2IjxIYhuxHo5YIHr+8EAHj5p1RU1xoFJyJb9f3hbFTUGBHp544hkVzl2FIsL+SQ1h3ORrmhDpH+7hga5Sc6DtmRuddHIsjLBTmlVfhsz1nRccgGmUwSPt+TAYCrHFuL5YUcjiRJWLk3AwAwMzYCSiV/cFDbcVWr8NSYrgCA93eeRmGZQXAisjW/phbgbFEFvFyccOcA7qjbGiwv5HD2pBfjdGEF3NUq3NGfy6Op7U3sG4reYVqUG+rw5vaTouOQjWkYkZs2uD3cNU6C08gTyws5nBX1oy53DgiDp4uz2DBkl5RKBZ65pTsAYO2BTKTm6QUnIluRfF6PvaeLoVIqMDMuQnQc2WJ5IYeSVVKJHan5AIB7OVGXLGhwRx+M6xUEkwS8sCmFS6cJwJ+jLmN7BiHE21VwGvlieSGH8tW+c5AkYFhnP0QF8PRosqyFY7pBrVLij/Qi7EwrEB2HBCsoq8bG+nOMeAjstWF5IYdRVWPEmgNZAMwTdYksrb2vG+67LgIA8MLmFNQaTWIDkVBf7ctEjdGE/u290a99O9FxZI3lhRzGhqQc6KpqEdbOFSOiA0THIQcx78Yo+LqrcaawAqv3Z4qOQ4JU1xqxap95Y8z7OepyzVheyCFIktR4jtGM2A5QcXk0WYmXizOeuKkLAOCtX05CV1krOBGJsCEpB8UVNQj1dsWYHkGi48ieRctLSUkJpk+fDi8vL3h7e2P27NkoLy+/4nuGDx8OhUJxyeOhhx6yZExyAAcyLiAlVw8XZyXuGsgD0Mi6pg4KR5dAD5RW1uKdX0+JjkNWJkkSPv3DPFF3ZlwHOKk4bnCtLHoFp0+fjhMnTmD79u3YtGkTfvvtN8ydO/eq75szZw5yc3MbH6+++qolY5IDaNiUbmLfUHi78QA0si4nlRJP1y+d/iI+A2eLKgQnImv6I70IJ/PL4aZWYcognmPUFixWXlJSUrB161Z88skniImJwdChQ/Huu+9izZo1OH/+/BXf6+bmhqCgoMaHl5eXpWKSA8jTVWPriTwA4L4KJMwNXfwxvKs/ao0Slm9JER2HrKhh1OWugeHQunJvqbZgsfISHx8Pb29vDBw4sPG5UaNGQalUYv/+/Vd876pVq+Dn54eePXti0aJFqKysvOxrDQYD9Hr9JQ+ii63afw5Gk4TBHX3QLZhFmMR55pZuUCkV2Jacj72ni0THIStILyjDrrRCKBRoXHlG185i5SUvLw8BAZeu6HBycoKPjw/y8vIu+767774bX331FXbu3IlFixbhyy+/xD333HPZ1y9fvhxarbbxER7O+Qz0p1qjqXF59IzYDoLTkKOLCvDE9BjzbYMXNqXAaOLGdfbus/oDGEd1C0QHX3exYexIi8vLwoUL/zah9q+P1NTUVgeaO3cuRo8ejV69emH69On44osvsH79epw+fbrJ1y9atAg6na7xkZWV1ervTfbnl+R8FJYZ4OehwWjO8Ccb8PioLvB0cUJyrh7fH84WHYcs6EJFDdbV/zvmpnRtq8UnQi1YsACzZs264msiIyMRFBSEgoJLd5Ssq6tDSUkJgoKa/yESExMDAEhPT0enTp3+9nWNRgONRtPsfx45llX1+2pMGRQGZ87wJxvg467GP27sjBe3pOC1n9MwrlcwPHg4n11anZCJ6loTeoR4Iaajj+g4dqXF/8X4+/vD39//qq+LjY1FaWkpDh06hAEDBgAAfv31V5hMpsZC0hxJSUkAgODg4JZGJQeXUVSBP9KLoFAAUznDn2zIzLgIrNp/DhnFlXh3xyksGtdNdCRqYzV1psZVjvdf1xEKBfeWaksW+6tot27dMGbMGMyZMwcJCQnYs2cPHn30UUydOhUhISEAgJycHERHRyMhIQEAcPr0aSxbtgyHDh1CRkYGNm7ciBkzZuD6669H7969LRWV7NTXB8yjLtd39ke4j5vgNER/UjspseS2HgDMK1HSC668/xXJz+Zj51FQZoC/pwa39QkRHcfuWHQcfdWqVYiOjsbIkSMxbtw4DB06FB999FHj12tra5GWlta4mkitVuOXX37BzTffjOjoaCxYsACTJk3Cjz/+aMmYZIcMdUZ8d9B8r/nuGI66kO0ZER2AkdEBqDNJWPrjCZ46bUcu3pRuxpAOUDvxlnVbs+iNVh8fH6xevfqyX4+IiLjkP9jw8HDs3r3bkpHIQfx8Ih/FFTUI9NJgJM8xIhu1+Lbu+P1UEX4/VYRtyfmcVG4nEs6W4HiOHhonJaYP4SpHS2AdJLu0er/5HKMpg9pzK26yWR183TH3+kgAwLJNyaiuNQpORG3hvzvTAQB39A+Djzt39LYE/lQnu3O6sBz7zpRAqTCfKUNkyx4Z0QkhWhdkX6jCB7ub3hKC5ONgRgl+P1UEJ6UCjwz/+wpZahssL2R3vq5fHj2iawBCvF0FpyG6Mje1U+O5R//bdRpZJZffUZxs39u/mA/evHNAGBcKWBDLC9mV6lojvjvMibokL+N6BSE20heGOhNe2JwsOg610oGMEvyRbh51mTciSnQcu8byQnblp+O5KK2sRYjWBcO7cqIuyYNCocDSCT2gUirw84l8/HayUHQkaoW3fzkJAJg8kKMulsbyQnZldf0to6mD20Ol5KZQJB9dAj0xMzYCAPDcjydQU2cSG4haJOFsCfakF9fPdeGoi6WxvJDdOJlfhgMZF6BSKjCFE3VJhh6/qTP8PNQ4U1iBFXvPio5DLfDnqEs4R12sgOWF7EbDqMvI6AAEerkITkPUcl4uzvi/MdEAgP/8cgr5+mrBiag59p8pxt7TxXBWKTBvBFcYWQPLC9mFqhpj4wm93BSK5GxS/zD0a++NihojXv4pVXQcaoaGFUaTB4YjrB1HXayB5YXswqaj51FWXYdwH1cMi/ITHYeo1ZRKBZaO7wGFAlifmIMDGSWiI9EV7DtTjPgzDaMunOtiLSwvZBdWJ9RP1B3UHkpO1CWZ6x3m3bjB4uINJ1Bn5ORdW9Uw1+WugeEI5b5SVsPyQrKXfF6PxMxSOCkVmDwwTHQcojbx5OhoeLk4ISVXjxV7M0THoSbEny7GvjMlHHURgOWFZG91gvkco9E9ghDgyYm6ZB983NVYNK4bAOD1bWnILObOu7amYdRlyqBw7uZtZSwvJGsVhjr8kHgeAHfUJfszdVA4hkT6oLrWhH+vPwZJkkRHonp7Txdh/9kSqFVKjroIwPJCsvbjkfMoN9QhwtcNsZG+ouMQtSmFQoGX7+gNjZMSf6QX4btD2aIjEQBJkhpXGE0dHI5gLUddrI3lhWStYaLutMGcqEv2KcLPHU/c1AUA8MLmFBSWGQQnovjTxUioH3V5mCdHC8HyQrJ1PEeHo9k6OKsUuHMAJ+qS/XpgaEf0CPGCrqoWz/14QnQch3bxqMs0jroIw/JCsrX2QBYA4OYeQfD10AhOQ2Q5TiolXpnUGyqlApuP5mJ7cr7oSA5r7+liJGSUQO2kxMM8w0gYlheSpaoaI35IygEATBvEibpk/3qGajFnWCQA4JkfjkFfXSs4keMxmiQs/ykFAHD34PYI0nJ1oygsLyRLm4/lNu6oG9eJE3XJMTw+qjMifN2Qrzfw6AABvk7IxPEcPTxdnLjCSDCWF5KltQfME3WnDAznRF1yGC7OKiy/ozcA80Gk+88UC07kOC5U1OD1bWkAgPk3dYG/J29Vi8TyQrKTXlCGAxkXoFIqMHlguOg4RFYV28kX0wab/9wvWncM1bVGwYkcw6s/p6G0shbRQZ64l4e/CsfyQrKzJsE8UXdE1wAEevGeMzmehWO7IcBTgzNFFXhnxynRceze0exSrKkf7X1+Qk84qfjRKRr/DZCsGOqMWJdonqjbcHAdkaPRujpj2cSeAIAPfzuDE+d1ghPZL5NJwrMbTkCSgNv7hWJwRx/RkQgsLyQz25PzUVJRg0AvDYZ39Rcdh0iY0T2CMLZnEIwmCQu/P8aTpy3k20NZOJJVCg+NExaNjRYdh+qxvJCsNNwyumtgOIduyeEtndADXi5OOJajw/u7TouOY3dKK2vwylbzJN3HR3VGAG9T2wz+9CfZyCqpxB/pRVAozOWFyNEFeLrgufE9AJhPOE44WyI4kX15Y9tJlFTUoEugB2bGRYiOQxdheSHZaNhRd2iUH8J93ASnIbINd/QPwx39QmGSgH+uSURpZY3oSHbheI4Oq/afAwA8N74HnDnSa1P4b4Nkoc5owreHzOVlKnfUJbrE8xN7oqOfO3J11Xjyu6OQJEl0JFkzmSQs3nAcJgm4tXcw4jr5iY5Ef8HyQrKwM60Q+XoDfN3VuKl7oOg4RDbFQ+OEd6f1g1qlxPbkfHwRf050JFlbl5iDw5mlcFOr8PQt3UTHoSawvJAsNOyoO2lAGNRO/GNL9Fc9Q7VYNM68GubFzSlcPt1KuqpavFx/ftE/RnbmqdE2ip8CZPPydNX4NbUAACfqEl3JrLgIjOoWgBqjCY+tTkSFoU50JNl5a/tJFJXXINLfHfdf11F0HLoMlheyed8ezIJJAgZH+CAqwEN0HCKbpVAo8NqdfRDk5YIzRRVYsvGE6EiykpKrxxfxGQCApeN7cJTXhvHfDNk0k0nC2oP1E3UHc9SF6Grauavxn6l9oVQA3x3KxvrEbNGRZKHOaMLT64/BJAFjewZhWGdugmnLWF7Ipu05XYTsC1XwdHHC2J7BouMQyUJMpC/+ObILAOCZ9cdxtqhCcCLb98b2kzicad5J95lbu4uOQ1fB8kI2rWFH3dv7hcJVrRKchkg+Hr0xCjEdfVBRY8RjXx+GoY6nT1/OztQC/K9+h+JXJvVGqDcn6do6lheyWcXlBmxLzgPAvV2IWkqlVOA/U/uhnZszjufo8cpPaaIj2aTzpVWY/00SAGBGbAfc0psjvHLA8kI2a93hHNQaJfQJ06J7iJfoOESyE6R1wRt39QEAfLbnLH4+kSc4kW2pNZrw2NeJuFBZi56hXtzTRUZYXsgmSZKEr+v3dpnCUReiVrsxOhCzh5qX/P5zTSISMy8ITmQ73th2EofOXYCnxgnv3d0fGifempYLlheySQfPXcCZwgq4qVUY3zdEdBwiWVs4Nho3dPFHda0Js1cexJnCctGRhPs1NR8f7K6f53Jnb3TwdReciFqC5YVs0tcJ5lGX23qHwEPjJDgNkbw5q5R4f3p/9A7ToqSiBjM/T0BBWbXoWMKY57kcAQDMjO2Acb04z0VuWF7I5uiqarHlWC4AYAr3diFqE+4aJ3w2axA6+Lohq6QK9684gHIH3IG3YZ5LaWUteoVq8W/Oc5EllheyORuTclBda0LXQE/0C/cWHYfIbvh5aLDyvsHwdVfjeI4eD391CDV1JtGxrOr1n9M4z8UOsLyQTZEkCV8n/LmjrkKhEJyIyL5E+Lnjs1mD4Oqswu+nirDw+6OQJEl0LKvYkZKPD387AwB49c7eaO/rJjgRtRbLC9mU4zl6JOfqoXZS4vZ+oaLjENmlPuHeeP+e/lApFViXmINXttr/HjA5pVVY8K15nsusuAiM5TwXWWN5IZvSsDx6bM8geLupBachsl8jugbg5Tt6AQA+2H0aK/acFZzIcioMdZi36jBKK2vRO0yLReOiRUeia8TyQjajwlCHjUnnAQBTBnGiLpGlTR4Yjn/dbD4Daemm5MaJ8vZEX12LGZ8lICmrFJ4uTvjvNM5zsQcsL2QzNh/LRbmhDhG+boiN9BUdh8ghzBsRhXuGtIckAY+vTcK+M8WiI7WZ0soa3PPJfhw6dwFeLk74cnYM57nYCZYXshlrEv7cUZcTdYmsQ6FQYOn4nri5eyBq6kyY8VkCNiTliI51zYrKDZj60T4czdbBx12Nr+cOQV+uXrQbLC9kE07ml+FwZimclApMGsCJukTWpFIq8M60fhgZHYCaOhP+uSYJb25Lg8kkz1VI+fpqTP1oH1LzyuDvqcGauUPQI0QrOha1IZYXsglr6pdHj+wWgABPF8FpiByPi7MKH80YiLnXRwIA3vk1HfNWH0Zljbw2sssprcKUD+ORXlCOYK0L1s4dgi6BnqJjURtjeSHhqmuNWJeYDQCYykMYiYRRKRX497hueO3O3nBWKfDT8Tzc9WE8cnVVoqM1S2ZxJe76IB4ZxZUIa+eKbx6MRaS/h+hYZAEsLyTctuR8lFbWIkTrguu7+IuOQ+TwJg8Mx+o5Q+BTvxPvhP/uQVJWqehYV3S6sByTP9yLnNIqdPRzxzcPxiLch5Nz7RXLCwnXMFF38sBwqJScqEtkCwZF+GDDvOvQNdATBWUGTPkwHhuPnBcdq0lpeWWY8uE+5OsN6BzggbVzhyDE21V0LLIgi5WXF198EXFxcXBzc4O3t3ez3iNJEhYvXozg4GC4urpi1KhROHXqlKUikg04V1yBvaeLoVAAkweGiY5DRBcJ93HDdw/H4sboABjqTPjH14l4c/tJm5nIazJJ+O5QNqZ8FI+icgO6B3thzdwhCPDivDl7Z7HyUlNTg8mTJ+Phhx9u9nteffVVvPPOO/jggw+wf/9+uLu7Y/To0aiudtyj2+3d2gPmibrXd/ZHWDsO8RLZGk8XZ3x88UTeHacw54uDSC8oF5rraHYpJn2wF//69ghKK2vRJ9wbq+fEwNdDIzQXWYdCsvCJXCtWrMDjjz+O0tLSK75OkiSEhIRgwYIF+Ne//gUA0Ol0CAwMxIoVKzB16tRmfT+9Xg+tVgudTgcvL69rjU8WVGs0Ie7lX1FYZsD/pvfnWSNENu6bg1l4ev0x1BolKBXAxL6h+MfIzojwc7dahqJyA17bmoZvDmVBkgB3tQqPjeyM+6/rCLUTZ0LIWUs+v52slOmqzp49i7y8PIwaNarxOa1Wi5iYGMTHx1+2vBgMBhgMhsZf6/V6i2eltrEztQCFZQb4eagxslug6DhEdBV3DQxH7zAt3th2EtuT87EuMQcbjpzHpP6heOzGzhadIFtrNOHL+HN465eTKKs2L9++vV8oFo6NRiBvEzkcmykveXl5AIDAwEs/xAIDAxu/1pTly5dj6dKlFs1GlrGm/pbRpAFh/BsTkUxEB3nh4xkDcSxbhze3p2FnWiG+OZiN9Yk5uGtgOB69MQrB2radLLsnvQhLfzyBk/nmW1U9QrywdHwPDIzwadPvQ/LRok+MhQsXQqFQXPGRmppqqaxNWrRoEXQ6XeMjKyvLqt+fWidXV4VdaQUAgCkDeQgjkdz0CtPi8/sG4/uH4zA0yg+1Rgmr9mfihld34bmNJ5Cvb/1cRUmScK64AhuScvDglwcx/ZP9OJlfjnZuznjp9l7Y+OhQFhcH16KRlwULFmDWrFlXfE1kZGSrggQFBQEA8vPzERz859yH/Px89O3b97Lv02g00Gg4QUtuvj2YDZMExHT04SZSRDI2oEM7fPVADPafKcYb208i4WwJVuzNwIq9GQjw1CAqwOPPh7/5f/09NZecX1ZYZsCRrFIczS5FUrYOR7NLUVpZ2/h1pQKYERuBJ0Z1gdbNWcRvk2xMi8qLv78//P0ts4lYx44dERQUhB07djSWFb1ej/3797doxRLZPpNJalxlNHUwR12I7EFMpC/Wzh2CvaeL8db2kzh47gIKygwoKDNg7+lLT6r2dHFCVIAHfN01SMnVI6f07zv4qlVKdAvxQt8wLabFtEd0EBdg0J8sNuclMzMTJSUlyMzMhNFoRFJSEgAgKioKHh7mv2lHR0dj+fLluP3226FQKPD444/jhRdeQOfOndGxY0c8++yzCAkJwcSJEy0VkwT4Pb0IOaVV8HJxwtieXGFEZC8UCgWui/LDdVF+0FfX4nRBOdILypFeWN74/zNLKlFWXYfEzNKL3gdE+XugT7i3+RGmRXSQF+fC0WVZrLwsXrwYK1eubPx1v379AAA7d+7E8OHDAQBpaWnQ6XSNr3nqqadQUVGBuXPnorS0FEOHDsXWrVvh4sKZ5PZk9f5zAIA7+ofBxVklOA0RWYKXizP6tW+Hfu3bXfJ8da0RGcUVSC8oR1GZAV2DvNArTAsPjc2sHyEZsPg+L9bGfV5sW76+GnEv/wqjScK2J67naa9ERASgZZ/fHJMjq/rmQBaMJgkDO7RjcSEiolZheSGrMZqkxr1dpg9pLzgNERHJFcsLWc1vJwuRU1oFbzdnTtQlIqJWY3khq1m1PxMAMIkTdYmI6BqwvJBV5Oqq8GtqPgBg2mDeMiIiotZjeSGrWJOQ1bijblQAd9QlIqLWY3khi6szmhp31L07hqMuRER0bVheyOJ2phUiT18NH3c1xvQMEh2HiIhkjuWFLK5hR907B4RB48SJukREdG1YXsiisi9UYtfJQgCcqEtERG2D5YUsau2BLEgSENfJFx393EXHISIiO8DyQhZTy4m6RERkASwvZDE7UgpQUGaAn4caN3fnRF0iImobLC9kMasTzDvqTh4YDrUT/6gREVHb4CcKWURWSSV+P1U/UXcQbxkREVHbYXkhi/g6IROSBAzr7If2vm6i4xARkR1heaE2V1NnwjcHzRN1p3OiLhERtTGWF2pz25PzUVReA39PDUZ2CxQdh4iI7AzLC7W51QnmHXWnDAyHs4p/xIiIqG3xk4XaVEZRBfakF0OhAKYODhcdh4iI7BDLC7WphuXRN3TxR1g7TtQlIqK2x/JCbaaqxti4o+49MR0EpyEiInvF8kJtZuORHOiqahHu44oR0QGi4xARkZ1ieaE2IUkSVuw1T9SdMSQCKqVCcCIiIrJXLC/UJg6eu4CUXD1cnJWYPDBMdBwiIrJjLC/UJlbszQAATOwbCm83tdgwRERk11he6Jrl6arx8/E8AMCM2AixYYiIyO6xvNA1W52QiTqThMERPuge4iU6DhER2TmWF7omNXUmrN5v3ttlRhyXRxMRkeWxvNA1+el4LorKDQj00mB0jyDRcYiIyAGwvNA1WVk/UXd6TAeeY0RERFbBTxtqtWPZOhzOLIWzSoFpg9uLjkNERA6C5YVabWV8BgDgll7B8PfUiA1DREQOg+WFWqW43ICNR84DAGbERYgNQ0REDoXlhVpl7cEs1NSZ0DtMi37h3qLjEBGRA2F5oRarM5qwal/98ujYCCgUPMeIiIish+WFWuyXlALklFbBx12NW3sHi45DREQOhuWFWuyL+om6UweFw8VZJTYMERE5HJYXapGT+WXYe7oYSgUwfQh31CUiIutjeaEWaRh1ubl7EEK9XcWGISIih8TyQs2mr67FusM5AHiOERERicPyQs323cFsVNYY0SXQA7GRvqLjEBGRg2J5oWYxmiSsqD/H6F4ujyYiIoFYXqhZth7PQ2ZJJdq5OWNS/1DRcYiIyIGxvNBVSZKEj347DcA86uKmdhKciIiIHBnLC13VvjMlOJKtg8ZJiZmxnKhLRERisbzQVTWMukweGAZfD54eTUREYrG80BWl5ZVhZ1ohFArggaGRouMQERGxvNCVffTbGQDAmB5BiPBzF5yGiIiI5YWuIE9XjY1HzJvSzb2eoy5ERGQbWF7osj7fcxa1RgmDO/qgX/t2ouMQEREBYHmhy9BX12LV/kwAwIMcdSEiIhvC8kJN+np/JsoNdegc4IERXQNExyEiImrE8kJ/U1Nnwmd7zgIA5lwfCaWSRwEQEZHtYHmhv9mQlIN8vQEBnhpM6BsiOg4REdElLFZeXnzxRcTFxcHNzQ3e3t7Nes+sWbOgUCgueYwZM8ZSEakJkiTh49/Ny6PvH9oRGieV4ERERESXstghNTU1NZg8eTJiY2Px6aefNvt9Y8aMweeff974a42GO7pa0660QpzML4eHxgl3x7QXHYeIiOhvLFZeli5dCgBYsWJFi96n0WgQFBRkgUTUHB/sNh8FMG1wOLxcnAWnISIi+jubm/Oya9cuBAQEoGvXrnj44YdRXFx8xdcbDAbo9fpLHtQ6R7JKsf9sCZyUCtw/tKPoOERERE2yqfIyZswYfPHFF9ixYwdeeeUV7N69G2PHjoXRaLzse5YvXw6tVtv4CA8Pt2Ji+9JwFMD4viEI1roKTkNERNS0FpWXhQsX/m1C7V8fqamprQ4zdepUjB8/Hr169cLEiROxadMmHDhwALt27brsexYtWgSdTtf4yMrKavX3d2Tniivw0/FcADwKgIiIbFuL5rwsWLAAs2bNuuJrIiPb7oMvMjISfn5+SE9Px8iRI5t8jUaj4aTeNvDJ72dhkoDhXf0RHeQlOg4REdFltai8+Pv7w9/f31JZ/iY7OxvFxcUIDg622vd0RPn6anxz0DxixVEXIiKydRab85KZmYmkpCRkZmbCaDQiKSkJSUlJKC8vb3xNdHQ01q9fDwAoLy/Hk08+iX379iEjIwM7duzAhAkTEBUVhdGjR1sqJgF4b2c6DHUmDOzQDrGRvqLjEBERXZHFlkovXrwYK1eubPx1v379AAA7d+7E8OHDAQBpaWnQ6XQAAJVKhaNHj2LlypUoLS1FSEgIbr75Zixbtoy3hSwo+0Ilvk4wH8A4/+YuUCh4FAAREdk2hSRJkugQbUmv10Or1UKn08HLi3M3rmbh90ex5kAW4jr5YvWcIaLjEBGRg2rJ57dNLZUm68ooqsC3h7IBAAtu7iI4DRERUfOwvDiwd3acgtEkYXhXfwzo4CM6DhERUbOwvDio9IIyrE/KAQDMv4mjLkREJB8sLw7qrV9OQZKAm7sHoneYt+g4REREzcby4oCSz+ux+ah5N90nOOpCREQyw/LigN765SQA4JbewegWzBVZREQkLywvDuZIVim2J+dDqQCeGNVZdBwiIqIWY3lxMG9uN4+6TOwbiqgAT8FpiIiIWo7lxYEczCjB7pOFUCkV+CdHXYiISKZYXhzIG9vMoy6TB4Shg6+74DREREStw/LiIPamFyH+TDHUKiUeG8lRFyIiki+WFwcgSRLeqJ/rMnVwOEK9XQUnIiIiaj2WFwew+2QhDp27AI2TEvNGRImOQ0REdE1YXuycJEmNK4zuHdIBgV4ughMRERFdG5YXO/dDUg6OZuvgplbhoeGdRMchIiK6ZiwvdkxfXYsXN6cCAOaNiIKfh0ZwIiIiomvH8mLH3tp+EkXlBnT0c8cDwzqKjkNERNQmWF7sVEquHl/EnwMAPDe+BzROKsGJiIiI2gbLix2SJAmLNxyH0SRhbM8g3NDFX3QkIiKiNsPyYofWJ+bgQMYFuDqr8Myt3UXHISIialMsL3ZGV1WLl7akAAAeGxnFDemIiMjusLzYGfMk3RpE+rvjgaGRouMQERG1OZYXO5J8Xo8v4jMAAEvH94Daif96iYjI/vDTzU40TNI1ScC4XkEY1pmTdImIyD6xvNiJdYdzcPBc/STdWzhJl4iI7BfLix3QVdVi+U/mSbr/GNkZIZykS0REdozlxQ5cPEl39lDupEtERPaN5UXmLp6k+/z4npykS0REdo+fdDJmMv05SfeWXsEY2tlPdCQiIiKLY3mRsS/3ncPBcxfgplbhmVu7iY5DRERkFSwvMnU8R4cXN5sn6T45uiuCtZykS0REjoHlRYbKqmsxb/Vh1BhNuKl7IGbFRYiOREREZDUsLzIjSRIWrjuGc8WVCPV2xWt39oZCoRAdi4iIyGpYXmRm1f5MbD6aCyelAu/e3Q/ebmrRkYiIiKyK5UVGTpzX4flNyQCAp8Z0Rf/27QQnIiIisj6WF5koN9Th0dWJqKkz4cboAJ4YTUREDovlRQYkScLT64/hbFEFgrUueGNyHyiVnOdCRESOieVFBtYeyMKGpPNQKRV4d1o/tHPnPBciInJcLC82LjVPjyUbTwAAFtzcBQMjfAQnIiIiEovlxYZVGOowb9VhGOpMuKGLPx66vpPoSERERMKxvNgoSZLw7A/HcbqwAoFeGrx5F+e5EBERASwvNmvl3gysS8yBUgG8M7UffD00oiMRERHZBCfRAejvPt9zFkt/NO/nsuDmroiJ9BWciIiIyHawvNiYj387gxe3mA9cfOiGTnhkOOe5EBERXYzlxYb8b9dpvLI1FQDw2I1RmH9TF55bRERE9BcsLzbi3R2n8Mb2kwCAx0d1xuOjughOREREZJtYXgSTJAlv/3IK/9lxCgDwr5u74NEbOwtORUREZLtYXgSSJAlvbDuJ/+5MBwAsHBuNh27gHBciIqIrYXkRRJIkvLw1FR/uPgMAeOaWbnhgGA9bJCIiuhqWFwEkScKLm1PwyR9nAQBLbuuO+67rKDgVERGRPLC8WFlhmQHLNiVj45HzAIBlE3vi3iEdBKciIiKSD5YXK6kzmvDlvnN4c9tJlBnqoFAAL07shbtj2ouORkREJCssL1aQcLYEizccR2peGQCgd5gWz0/oib7h3mKDERERyRDLiwUVlFVj+ZZUrE/MAQB4uznjqdHRmDIoHCoeskhERNQqFjuYMSMjA7Nnz0bHjh3h6uqKTp06YcmSJaipqbni+6qrqzFv3jz4+vrCw8MDkyZNQn5+vqViWkSd0YRP/ziLka/vxvrEHCgUwLTB7bFzwXDcHdOexYWIiOgaWGzkJTU1FSaTCR9++CGioqJw/PhxzJkzBxUVFXj99dcv+74nnngCmzdvxrfffgutVotHH30Ud9xxB/bs2WOpqG1CkiTklFYhMbMU7+1Mv+QW0bIJPdGHt4iIiIjahEKSJMla3+y1117D//73P5w5c6bJr+t0Ovj7+2P16tW48847AZhLULdu3RAfH48hQ4Zc9Xvo9XpotVrodDp4eXm1af4GkiQhV1eNo9k6HM/R4WiO+X9LKv4cVeItIiIiouZryee3Vee86HQ6+Pj4XPbrhw4dQm1tLUaNGtX4XHR0NNq3b3/Z8mIwGGAwGBp/rdfr2zZ0vZzSKqxNyGwsKkXlf7/95aRUoGuQJ+I6+eKR4VFo5662SBYiIiJHZrXykp6ejnffffeKt4zy8vKgVqvh7e19yfOBgYHIy8tr8j3Lly/H0qVL2zJqkyoMdXjn1/TGX6uUCnQJ9ETvUC16hmnRO1SLrkGecHFWWTwLERGRI2txeVm4cCFeeeWVK74mJSUF0dHRjb/OycnBmDFjMHnyZMyZM6flKa9g0aJFmD9/fuOv9Xo9wsPD2/R7AEAnfw9MHRSOHiFe6BmqRbdgLxYVIiIiAVpcXhYsWIBZs2Zd8TWRkX+e0XP+/HmMGDECcXFx+Oijj674vqCgINTU1KC0tPSS0Zf8/HwEBQU1+R6NRgONRtPs/K2lUirw8qTeFv8+REREdGUtLi/+/v7w9/dv1mtzcnIwYsQIDBgwAJ9//jmUyiuvzB4wYACcnZ2xY8cOTJo0CQCQlpaGzMxMxMbGtjQqERER2SGL7fOSk5OD4cOHo3379nj99ddRWFiIvLy8S+au5OTkIDo6GgkJCQAArVaL2bNnY/78+di5cycOHTqE++67D7Gxsc1aaURERET2z2ITdrdv34709HSkp6cjLCzskq81rM6ura1FWloaKisrG7/21ltvQalUYtKkSTAYDBg9ejTef/99S8UkIiIimbHqPi/WYI19XoiIiKhtteTz22K3jYiIiIgsgeWFiIiIZIXlhYiIiGSF5YWIiIhkheWFiIiIZIXlhYiIiGSF5YWIiIhkheWFiIiIZIXlhYiIiGTFYscDiNKwYbBerxechIiIiJqr4XO7ORv/2115KSsrAwCEh4cLTkJEREQtVVZWBq1We8XX2N3ZRiaTCefPn4enpycUCkWb/rP1ej3Cw8ORlZXFc5MsiNfZOnidrYPX2Xp4ra3DUtdZkiSUlZUhJCQESuWVZ7XY3ciLUqn82ynWbc3Ly4v/YVgBr7N18DpbB6+z9fBaW4clrvPVRlwacMIuERERyQrLCxEREckKy0sLaDQaLFmyBBqNRnQUu8brbB28ztbB62w9vNbWYQvX2e4m7BIREZF948gLERERyQrLCxEREckKywsRERHJCssLERERyQrLy1+89957iIiIgIuLC2JiYpCQkHDF13/77beIjo6Gi4sLevXqhS1btlgpqby15Dp//PHHGDZsGNq1a4d27dph1KhRV/33QmYt/fPcYM2aNVAoFJg4caJlA9qJll7n0tJSzJs3D8HBwdBoNOjSpQt/djRDS6/z22+/ja5du8LV1RXh4eF44oknUF1dbaW08vTbb7/htttuQ0hICBQKBX744YervmfXrl3o378/NBoNoqKisGLFCovnhESN1qxZI6nVaumzzz6TTpw4Ic2ZM0fy9vaW8vPzm3z9nj17JJVKJb366qtScnKy9Mwzz0jOzs7SsWPHrJxcXlp6ne+++27pvffekxITE6WUlBRp1qxZklarlbKzs62cXF5aep0bnD17VgoNDZWGDRsmTZgwwTphZayl19lgMEgDBw6Uxo0bJ/3xxx/S2bNnpV27dklJSUlWTi4vLb3Oq1atkjQajbRq1Srp7Nmz0s8//ywFBwdLTzzxhJWTy8uWLVukp59+Wlq3bp0EQFq/fv0VX3/mzBnJzc1Nmj9/vpScnCy9++67kkqlkrZu3WrRnCwvFxk8eLA0b968xl8bjUYpJCREWr58eZOvv+uuu6RbbrnlkudiYmKkBx980KI55a6l1/mv6urqJE9PT2nlypWWimgXWnOd6+rqpLi4OOmTTz6RZs6cyfLSDC29zv/73/+kyMhIqaamxloR7UJLr/O8efOkG2+88ZLn5s+fL1133XUWzWlPmlNennrqKalHjx6XPDdlyhRp9OjRFkwmSbxtVK+mpgaHDh3CqFGjGp9TKpUYNWoU4uPjm3xPfHz8Ja8HgNGjR1/29dS66/xXlZWVqK2thY+Pj6Viyl5rr/Pzzz+PgIAAzJ492xoxZa8113njxo2IjY3FvHnzEBgYiJ49e+Kll16C0Wi0VmzZac11jouLw6FDhxpvLZ05cwZbtmzBuHHjrJLZUYj6HLS7gxlbq6ioCEajEYGBgZc8HxgYiNTU1Cbfk5eX1+Tr8/LyLJZT7lpznf/q//7v/xASEvK3/2DoT625zn/88Qc+/fRTJCUlWSGhfWjNdT5z5gx+/fVXTJ8+HVu2bEF6ejoeeeQR1NbWYsmSJdaILTutuc533303ioqKMHToUEiShLq6Ojz00EP497//bY3IDuNyn4N6vR5VVVVwdXW1yPflyAvJyssvv4w1a9Zg/fr1cHFxER3HbpSVleHee+/Fxx9/DD8/P9Fx7JrJZEJAQAA++ugjDBgwAFOmTMHTTz+NDz74QHQ0u7Jr1y689NJLeP/993H48GGsW7cOmzdvxrJly0RHozbAkZd6fn5+UKlUyM/Pv+T5/Px8BAUFNfmeoKCgFr2eWnedG7z++ut4+eWX8csvv6B3796WjCl7Lb3Op0+fRkZGBm677bbG50wmEwDAyckJaWlp6NSpk2VDy1Br/jwHBwfD2dkZKpWq8blu3bohLy8PNTU1UKvVFs0sR625zs8++yzuvfdePPDAAwCAXr16oaKiAnPnzsXTTz8NpZJ/d28Ll/sc9PLystioC8CRl0ZqtRoDBgzAjh07Gp8zmUzYsWMHYmNjm3xPbGzsJa8HgO3bt1/29dS66wwAr776KpYtW4atW7di4MCB1ogqay29ztHR0Th27BiSkpIaH+PHj8eIESOQlJSE8PBwa8aXjdb8eb7uuuuQnp7eWA4B4OTJkwgODmZxuYzWXOfKysq/FZSGwijxSL82I+xz0KLTgWVmzZo1kkajkVasWCElJydLc+fOlby9vaW8vDxJkiTp3nvvlRYuXNj4+j179khOTk7S66+/LqWkpEhLlizhUulmaOl1fvnllyW1Wi199913Um5ubuOjrKxM1G9BFlp6nf+Kq42ap6XXOTMzU/L09JQeffRRKS0tTdq0aZMUEBAgvfDCC6J+C7LQ0uu8ZMkSydPTU/r666+lM2fOSNu2bZM6deok3XXXXaJ+C7JQVlYmJSYmSomJiRIA6c0335QSExOlc+fOSZIkSQsXLpTuvffextc3LJV+8sknpZSUFOm9997jUmkR3n33Xal9+/aSWq2WBg8eLO3bt6/xazfccIM0c+bMS17/zTffSF26dJHUarXUo0cPafPmzVZOLE8tuc4dOnSQAPztsWTJEusHl5mW/nm+GMtL87X0Ou/du1eKiYmRNBqNFBkZKb344otSXV2dlVPLT0uuc21trfTcc89JnTp1klxcXKTw8HDpkUcekS5cuGD94DKyc+fOJn/eNlzbmTNnSjfccMPf3tO3b19JrVZLkZGR0ueff27xnApJ4vgZERERyQfnvBAREZGssLwQERGRrLC8EBERkaywvBAREZGssLwQERGRrLC8EBERkaywvBAREZGssLwQERGRrLC8EBERkaywvBAREZGssLwQERGRrLC8EBERkaz8P1VqNwFuLfBmAAAAAElFTkSuQmCC\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "labels = target_function(data)\n", - "\n", - "plt.plot(data[:,1], output)\n", - "plt.show()" - ] - }, - { - "cell_type": "code", - "execution_count": 255, - "id": "7884e8c7-e84b-4e05-b832-fed90af36696", - "metadata": {}, - "outputs": [], - "source": [ - "nqubits = 2\n", - "nlayers = 3\n", - "\n", - "model = pqc.PQC(nqubits=nqubits)\n", - "\n", - "for l in range(nlayers):\n", - " for q in range(nqubits):\n", - " model.add(gates.RY(q=q, theta=0))\n", - " model.add(gates.RY(q=q, theta=0))\n", - " for q in range(0, nqubits-1, 1):\n", - " model.add(gates.CNOT(q0=q, q1=q+1))\n", - " model.add(gates.CNOT(q0=nqubits-1, q1=0))\n", - "model.add(gates.M(*range(nqubits)))" - ] - }, - { - "cell_type": "code", - "execution_count": 256, - "id": "bac02fa4-6422-4a51-a0fb-9efee19134dd", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "q0: ─RY─RY─o─X─RY─RY─o─X─RY─RY─o─X─M─\n", - "q1: ─RY─RY─X─o─RY─RY─X─o─RY─RY─X─o─M─\n" - ] - } - ], - "source": [ - "print(model.draw())" - ] - }, - { - "cell_type": "code", - "execution_count": 257, - "id": "f66df344-994d-4aeb-997a-343b937b507b", - "metadata": {}, - "outputs": [], - "source": [ - "# define the optimizer\n", - "opt = ScipyMinimizer()\n", - "\n", - "# define the loss function\n", - "def loss_function(predictions, labels):\n", - " return np.sum( (predictions - labels)**2 ) / len(predictions)\n", - "\n", - "# define the observable\n", - "obs = hamiltonians.Z(nqubits=nqubits)\n", - "\n", - "# define the encoding strategy\n", - "# define the encoding circuit\n", - "\n", - "def build_encoding_circuit(nqubits):\n", - " \"\"\"Simple example: one RX per gate.\"\"\"\n", - " encoder = Circuit(nqubits)\n", - " for q in range(nqubits):\n", - " encoder.add(gates.RX(q, 0))\n", - " return encoder\n", - "\n", - "def define_encoding_strategy(circuit, data):\n", - " \"\"\"Simple example: one data per rotation angle.\"\"\"\n", - " circuit.set_parameters(data)\n", - " return circuit\n", - "\n", - "encoding_circuit = encodings.EncodingCircuit(\n", - " build_encoding_circuit(nqubits),\n", - " define_encoding_strategy\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 258, - "id": "cbebdd4b-95d9-4900-8002-930b51c5df97", - "metadata": {}, - "outputs": [], - "source": [ - "model.compile(\n", - " optimizer=opt,\n", - " loss=loss_function,\n", - " observable=obs,\n", - " encoding_config=encoding_circuit\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 259, - "id": "a044b120-ac16-462b-b0d6-f1801cbdd852", - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "[Qibo 0.2.6|INFO|2024-03-14 18:25:26]: Optimization is performed using the optimizer: scipy_minimizer_Powell\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "True\n" - ] - }, - { - "ename": "AttributeError", - "evalue": "'bool' object has no attribute 'executor'", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m/tmp/ipykernel_56062/3614230015.py\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mmodel\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfit\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx_data\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m10\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0my_data\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mlabels\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m10\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;32m~/Documents/PhD/qibogang/qiboml/src/qiboml/models/pqc.py\u001b[0m in \u001b[0;36mfit\u001b[0;34m(self, x_data, y_data, nshots, options)\u001b[0m\n\u001b[1;32m 84\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mloss_value\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 85\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 86\u001b[0;31m results = self.optimizer.fit(\n\u001b[0m\u001b[1;32m 87\u001b[0m \u001b[0minitial_parameters\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mparameters\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mloss\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0m_loss\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0margs\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx_data\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0my_data\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mfit_options\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 88\u001b[0m )\n", - "\u001b[0;32m~/Documents/PhD/qibogang/qiboml/src/qiboml/optimizers/minimizers.py\u001b[0m in \u001b[0;36mfit\u001b[0;34m(self, initial_parameters, loss, args, fit_options)\u001b[0m\n\u001b[1;32m 75\u001b[0m \u001b[0mlog\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0minfo\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34mf\"Optimization is performed using the optimizer: {self.__str__()}\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 76\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 77\u001b[0;31m \u001b[0mr\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mminimize\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mloss\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0minitial_parameters\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0margs\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0moptions\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 78\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 79\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mr\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfun\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mr\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mr\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/Documents/PhD/envs/qibo/lib/python3.10/site-packages/scipy/optimize/_minimize.py\u001b[0m in \u001b[0;36mminimize\u001b[0;34m(fun, x0, args, method, jac, hess, hessp, bounds, constraints, tol, callback, options)\u001b[0m\n\u001b[1;32m 706\u001b[0m \u001b[0mres\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0m_minimize_cg\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfun\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mx0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mjac\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcallback\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0moptions\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 707\u001b[0m \u001b[0;32melif\u001b[0m \u001b[0mmeth\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;34m'bfgs'\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 708\u001b[0;31m \u001b[0mres\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0m_minimize_bfgs\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfun\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mx0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mjac\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcallback\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0moptions\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 709\u001b[0m \u001b[0;32melif\u001b[0m \u001b[0mmeth\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;34m'newton-cg'\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 710\u001b[0m res = _minimize_newtoncg(fun, x0, args, jac, hess, hessp, callback,\n", - "\u001b[0;32m~/Documents/PhD/envs/qibo/lib/python3.10/site-packages/scipy/optimize/_optimize.py\u001b[0m in \u001b[0;36m_minimize_bfgs\u001b[0;34m(fun, x0, args, jac, callback, gtol, norm, eps, maxiter, disp, return_all, finite_diff_rel_step, xrtol, c1, c2, hess_inv0, **unknown_options)\u001b[0m\n\u001b[1;32m 1475\u001b[0m \u001b[0mmaxiter\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx0\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0;36m200\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1476\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1477\u001b[0;31m sf = _prepare_scalar_function(fun, x0, jac, args=args, epsilon=eps,\n\u001b[0m\u001b[1;32m 1478\u001b[0m finite_diff_rel_step=finite_diff_rel_step)\n\u001b[1;32m 1479\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/Documents/PhD/envs/qibo/lib/python3.10/site-packages/scipy/optimize/_optimize.py\u001b[0m in \u001b[0;36m_prepare_scalar_function\u001b[0;34m(fun, x0, jac, args, bounds, epsilon, finite_diff_rel_step, hess)\u001b[0m\n\u001b[1;32m 400\u001b[0m \u001b[0;31m# ScalarFunction caches. Reuse of fun(x) during grad\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 401\u001b[0m \u001b[0;31m# calculation reduces overall function evaluations.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 402\u001b[0;31m sf = ScalarFunction(fun, x0, args, grad, hess,\n\u001b[0m\u001b[1;32m 403\u001b[0m finite_diff_rel_step, bounds, epsilon=epsilon)\n\u001b[1;32m 404\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/Documents/PhD/envs/qibo/lib/python3.10/site-packages/scipy/optimize/_differentiable_functions.py\u001b[0m in \u001b[0;36m__init__\u001b[0;34m(self, fun, x0, args, grad, hess, finite_diff_rel_step, finite_diff_bounds, epsilon)\u001b[0m\n\u001b[1;32m 164\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 165\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_update_fun_impl\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mupdate_fun\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 166\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_update_fun\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 167\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 168\u001b[0m \u001b[0;31m# Gradient evaluation\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/Documents/PhD/envs/qibo/lib/python3.10/site-packages/scipy/optimize/_differentiable_functions.py\u001b[0m in \u001b[0;36m_update_fun\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 260\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m_update_fun\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 261\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mf_updated\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 262\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_update_fun_impl\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 263\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mf_updated\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mTrue\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 264\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/Documents/PhD/envs/qibo/lib/python3.10/site-packages/scipy/optimize/_differentiable_functions.py\u001b[0m in \u001b[0;36mupdate_fun\u001b[0;34m()\u001b[0m\n\u001b[1;32m 161\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 162\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mupdate_fun\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 163\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mf\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mfun_wrapped\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 164\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 165\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_update_fun_impl\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mupdate_fun\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/Documents/PhD/envs/qibo/lib/python3.10/site-packages/scipy/optimize/_differentiable_functions.py\u001b[0m in \u001b[0;36mfun_wrapped\u001b[0;34m(x)\u001b[0m\n\u001b[1;32m 143\u001b[0m \u001b[0;31m# Overwriting results in undefined behaviour because\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 144\u001b[0m \u001b[0;31m# fun(self.x) will change self.x, with the two no longer linked.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 145\u001b[0;31m \u001b[0mfx\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mfun\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcopy\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 146\u001b[0m \u001b[0;31m# Make sure the function returns a true scalar\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 147\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0misscalar\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfx\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/Documents/PhD/qibogang/qiboml/src/qiboml/models/pqc.py\u001b[0m in \u001b[0;36m_loss\u001b[0;34m(parameters, x_data, y_data)\u001b[0m\n\u001b[1;32m 80\u001b[0m \u001b[0mpredictions\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 81\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mx\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mx_data\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 82\u001b[0;31m \u001b[0mpredictions\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mappend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpredict\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnshots\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mnshots\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 83\u001b[0m \u001b[0mloss_value\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mloss\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mpredictions\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0my_data\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 84\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mloss_value\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/Documents/PhD/qibogang/qiboml/src/qiboml/models/pqc.py\u001b[0m in \u001b[0;36mpredict\u001b[0;34m(self, x, nshots)\u001b[0m\n\u001b[1;32m 102\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 103\u001b[0m \u001b[0mencoding_state\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mencoding_circuit\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0minject_data\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstate\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 104\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mobservable\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mexpectation\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0minitial_state\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mencoding_state\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnshots\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mnshots\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstate\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 105\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/Documents/PhD/qibo/src/qibo/models/circuit.py\u001b[0m in \u001b[0;36m__call__\u001b[0;34m(self, initial_state, nshots)\u001b[0m\n\u001b[1;32m 1115\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m__call__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0minitial_state\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnshots\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m1000\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1116\u001b[0m \u001b[0;34m\"\"\"Equivalent to ``circuit.execute``.\"\"\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1117\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mexecute\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0minitial_state\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0minitial_state\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnshots\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mnshots\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1118\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1119\u001b[0m \u001b[0;34m@\u001b[0m\u001b[0mproperty\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/Documents/PhD/qibo/src/qibo/models/circuit.py\u001b[0m in \u001b[0;36mexecute\u001b[0;34m(self, initial_state, nshots)\u001b[0m\n\u001b[1;32m 1100\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcompiled\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1101\u001b[0m \u001b[0;31m# pylint: disable=E1101\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1102\u001b[0;31m \u001b[0mstate\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcompiled\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mexecutor\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0minitial_state\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnshots\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1103\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_final_state\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcompiled\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mresult\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mstate\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnshots\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1104\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_final_state\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mAttributeError\u001b[0m: 'bool' object has no attribute 'executor'" - ] - } - ], - "source": [ - "model.fit(x_data=data[10:], y_data=labels[10:])" - ] - }, - { - "cell_type": "code", - "execution_count": 250, - "id": "7f934531-b6de-4f40-bb45-99716eefd450", - "metadata": {}, - "outputs": [ - { - "ename": "AttributeError", - "evalue": "'bool' object has no attribute 'executor'", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m/tmp/ipykernel_56062/1075888772.py\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mmodel\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpredict\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0.3\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m0.4\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;32m~/Documents/PhD/qibogang/qiboml/src/qiboml/models/pqc.py\u001b[0m in \u001b[0;36mpredict\u001b[0;34m(self, x, nshots)\u001b[0m\n\u001b[1;32m 100\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 101\u001b[0m \u001b[0mencoding_state\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mencoding_circuit\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0minject_data\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstate\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 102\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mobservable\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mexpectation\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0minitial_state\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mencoding_state\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnshots\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mnshots\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstate\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 103\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/Documents/PhD/qibo/src/qibo/models/circuit.py\u001b[0m in \u001b[0;36m__call__\u001b[0;34m(self, initial_state, nshots)\u001b[0m\n\u001b[1;32m 1115\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m__call__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0minitial_state\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnshots\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m1000\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1116\u001b[0m \u001b[0;34m\"\"\"Equivalent to ``circuit.execute``.\"\"\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1117\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mexecute\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0minitial_state\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0minitial_state\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnshots\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mnshots\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1118\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1119\u001b[0m \u001b[0;34m@\u001b[0m\u001b[0mproperty\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/Documents/PhD/qibo/src/qibo/models/circuit.py\u001b[0m in \u001b[0;36mexecute\u001b[0;34m(self, initial_state, nshots)\u001b[0m\n\u001b[1;32m 1100\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcompiled\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1101\u001b[0m \u001b[0;31m# pylint: disable=E1101\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1102\u001b[0;31m \u001b[0mstate\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcompiled\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mexecutor\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0minitial_state\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnshots\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1103\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_final_state\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcompiled\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mresult\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mstate\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnshots\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1104\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_final_state\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mAttributeError\u001b[0m: 'bool' object has no attribute 'executor'" - ] - } - ], - "source": [ - "model.predict([0.3, 0.4])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5cb71387-ce34-4b4e-8957-e20eb3e401ec", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "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.10.0" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/tutorials/pqc.ipynb b/tutorials/pqc.ipynb new file mode 100644 index 0000000..f57f542 --- /dev/null +++ b/tutorials/pqc.ipynb @@ -0,0 +1,286 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "983788cc-d67d-474c-b61e-4c6df384b91b", + "metadata": {}, + "source": [ + "## Define and train a custom Parametric Quantum Circuit" + ] + }, + { + "cell_type": "code", + "execution_count": 54, + "id": "47a5556c-a167-4983-b372-ac6b733aa1fd", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "[Qibo 0.2.6|INFO|2024-03-15 09:36:11]: Using numpy backend on /CPU:0\n" + ] + } + ], + "source": [ + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "\n", + "import qibo\n", + "from qibo import Circuit, gates, hamiltonians\n", + "\n", + "from qiboml.models import pqc, encodings\n", + "from qiboml.optimizers.minimizers import ScipyMinimizer\n", + "\n", + "from importlib import reload\n", + "reload(pqc)\n", + "reload(encodings)\n", + "\n", + "qibo.set_backend(\"numpy\")" + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "id": "3b12bdc6-5b07-4079-84e7-835d0c76185b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[0.06122449 0.06122449]\n" + ] + } + ], + "source": [ + "ndim = 2 # Number of dimensions\n", + "ndata = 50 # Number of data points\n", + "\n", + "# Create the linspace\n", + "linspace_column = np.linspace(0, 1, ndata)\n", + "\n", + "# Repeat the linspace for ndim columns and reshape\n", + "data = np.repeat(linspace_column[:, np.newaxis], ndim, axis=1)\n", + "\n", + "# example data\n", + "print(data[3])" + ] + }, + { + "cell_type": "code", + "execution_count": 69, + "id": "273699ec-4a2c-42cc-884e-bfe7cc5a213c", + "metadata": {}, + "outputs": [], + "source": [ + "def target_function(data):\n", + " labels = np.sum(np.sin(data)**2 - np.cos(4*data)**2, axis=1)\n", + " labels = (labels - max(labels)) / (max(labels) - min(labels)) * 2 + 1\n", + " return labels" + ] + }, + { + "cell_type": "code", + "execution_count": 70, + "id": "60bf02f4-779d-47ab-b9da-029208674337", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "labels = target_function(data)\n", + "\n", + "plt.plot(data[:,1], labels)\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 81, + "id": "7884e8c7-e84b-4e05-b832-fed90af36696", + "metadata": {}, + "outputs": [], + "source": [ + "nqubits = 2\n", + "nlayers = 6\n", + "\n", + "model = pqc.PQC(nqubits=nqubits)\n", + "\n", + "for l in range(nlayers):\n", + " for q in range(nqubits):\n", + " model.add(gates.RY(q=q, theta=0))\n", + " model.add(gates.RZ(q=q, theta=0))\n", + " for q in range(0, nqubits-1, 1):\n", + " model.add(gates.CNOT(q0=q, q1=q+1))\n", + " model.add(gates.CNOT(q0=nqubits-1, q1=0))\n", + "model.add(gates.M(*range(nqubits)))" + ] + }, + { + "cell_type": "code", + "execution_count": 82, + "id": "bac02fa4-6422-4a51-a0fb-9efee19134dd", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "q0: ─RY─RZ─o─X─RY─RZ─o─X─RY─RZ─o─X─RY─RZ─o─X─RY─RZ─o─X─RY─RZ─o─X─M─\n", + "q1: ─RY─RZ─X─o─RY─RZ─X─o─RY─RZ─X─o─RY─RZ─X─o─RY─RZ─X─o─RY─RZ─X─o─M─\n" + ] + } + ], + "source": [ + "print(model.draw())" + ] + }, + { + "cell_type": "code", + "execution_count": 84, + "id": "f66df344-994d-4aeb-997a-343b937b507b", + "metadata": {}, + "outputs": [], + "source": [ + "# define the optimizer\n", + "opt = ScipyMinimizer(method=\"BFGS\")\n", + "\n", + "# define the loss function\n", + "def loss_function(predictions, labels):\n", + " loss = np.sum( (predictions - labels)**2 ) / len(predictions)\n", + " print(loss)\n", + " return loss\n", + "\n", + "# define the observable\n", + "obs = hamiltonians.Z(nqubits=nqubits)\n", + "\n", + "# define the encoding strategy\n", + "# define the encoding circuit\n", + "\n", + "def build_encoding_circuit(nqubits):\n", + " \"\"\"Simple example: one RX per gate.\"\"\"\n", + " encoder = Circuit(nqubits)\n", + " for q in range(nqubits):\n", + " encoder.add(gates.RX(q, 0))\n", + " return encoder\n", + "\n", + "def define_encoding_strategy(circuit, data):\n", + " \"\"\"Simple example: one data per rotation angle.\"\"\"\n", + " circuit.set_parameters(data)\n", + " return circuit\n", + "\n", + "encoding_circuit = encodings.EncodingCircuit(\n", + " build_encoding_circuit(nqubits),\n", + " define_encoding_strategy\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 85, + "id": "cbebdd4b-95d9-4900-8002-930b51c5df97", + "metadata": {}, + "outputs": [], + "source": [ + "model.setup(\n", + " optimizer=opt,\n", + " loss=loss_function,\n", + " observable=obs,\n", + " encoding_config=encoding_circuit\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4441381e-7eb5-45d2-aa45-8ed3ef10bf6a", + "metadata": {}, + "outputs": [], + "source": [ + "model.set_parameters(np.random.randn(model.nparams))\n", + "print(model.parameters)\n", + "model.fit(input_data=data, output_data=labels )" + ] + }, + { + "cell_type": "code", + "execution_count": 91, + "id": "7f934531-b6de-4f40-bb45-99716eefd450", + "metadata": {}, + "outputs": [], + "source": [ + "predictions = model.predict_sample(data[10:])" + ] + }, + { + "cell_type": "code", + "execution_count": 93, + "id": "e8ca9446-7150-497a-b0d8-791b61b13f6b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[]" + ] + }, + "execution_count": 93, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plt.plot(data[10:,0], predictions)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "495c5e10-7b26-44de-bbd9-b43b90fca450", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.10.0" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From 161e88074557a1c0f695bc6b376db7dedc3e6ca8 Mon Sep 17 00:00:00 2001 From: MatteoRobbiati Date: Fri, 15 Mar 2024 10:47:44 -0400 Subject: [PATCH 12/12] fix: enable multi dimensional encoding in u3 model --- src/qiboml/models/reuploading/u3.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/qiboml/models/reuploading/u3.py b/src/qiboml/models/reuploading/u3.py index 80b152f..94f19ba 100644 --- a/src/qiboml/models/reuploading/u3.py +++ b/src/qiboml/models/reuploading/u3.py @@ -14,7 +14,7 @@ def __init__( nlayers: int, data_dimensionality: Tuple, actf1: Callable = lambda x: x, - actf2: Callable = lambda x: log(x), + actf2: Callable = lambda x: log(np.abs(x) + 1e-5), actf3: Callable = lambda x: exp(x), ): """Reuplading U3 ansatz.""" @@ -46,16 +46,17 @@ def build_circuit(self): def inject_data(self, x): new_parameters = [] k = 0 + for _ in range(self.nlayers): - for _ in range(self.nqubits): + for q in range(self.nqubits): new_parameters.append( - self.parameters[k] * self.actf1(x) + self.parameters[k + 1] + self.parameters[k] * self.actf1(x[q]) + self.parameters[k + 1] ) new_parameters.append( - self.parameters[k + 2] * self.actf2(x) + self.parameters[k + 3] + self.parameters[k + 2] * self.actf2(x[q]) + self.parameters[k + 3] ) new_parameters.append( - self.parameters[k + 4] * self.actf3(x) + self.parameters[k + 5] + self.parameters[k + 4] * self.actf3(x[q]) + self.parameters[k + 5] ) k += 6 self.circuit.set_parameters(new_parameters)