From 5d78477f8d864f47c0e6003961c082c067cdea77 Mon Sep 17 00:00:00 2001 From: Maximilian Seeber Date: Sat, 28 May 2022 20:23:29 +0200 Subject: [PATCH 01/10] implemented AP for MC --- demos/MC_AP.py | 6 ++ proxtoolbox/experiments/MC/MC_Experiment.py | 62 +++++++++++++++++++++ proxtoolbox/proxoperators/MC_P_A.py | 13 +++++ proxtoolbox/proxoperators/MC_P_M.py | 18 ++++++ proxtoolbox/proxoperators/__init__.py | 2 + 5 files changed, 101 insertions(+) create mode 100644 demos/MC_AP.py create mode 100644 proxtoolbox/experiments/MC/MC_Experiment.py create mode 100644 proxtoolbox/proxoperators/MC_P_A.py create mode 100644 proxtoolbox/proxoperators/MC_P_M.py diff --git a/demos/MC_AP.py b/demos/MC_AP.py new file mode 100644 index 0000000..a6fe657 --- /dev/null +++ b/demos/MC_AP.py @@ -0,0 +1,6 @@ +import SetProxPythonPath +from proxtoolbox.experiments.MC.MC_Experiment import MC_Experiment + +ART = MC_Experiment(algorithm='AP', fraction_known_entrees=0.3) +ART.run() +ART.show() \ No newline at end of file diff --git a/proxtoolbox/experiments/MC/MC_Experiment.py b/proxtoolbox/experiments/MC/MC_Experiment.py new file mode 100644 index 0000000..01366e4 --- /dev/null +++ b/proxtoolbox/experiments/MC/MC_Experiment.py @@ -0,0 +1,62 @@ +from proxtoolbox.experiments.experiment import Experiment +from proxtoolbox import proxoperators + +import numpy as np +import matplotlib.pyplot as plt + +class MC_Experiment(Experiment): + """ + MC experiment class + """ + + @staticmethod + def getDefaultParameters(): + defaultParams = { + 'experiment_name': 'MC', + 'MAXIT': 1000, + 'TOL': 1e-6, + 'n_1': 100, + 'n_2': 100, + 'rank': 5, + 'fraction_known_entrees': 0.5, + 'norm_data': 1, + 'diagnostic': False, + 'rnd_seed': None + } + return defaultParams + + def __init__(self, n_1, n_2, fraction_known_entrees, rank, norm_data, diagnostic, **kwargs): + super().__init__(**kwargs) + self.n_1 = n_1 + self.n_2 = n_2 + self.p = fraction_known_entrees + self.r = rank + self.norm_data = norm_data + self.diagnostic = False + + def loadData(self): + self.mask = np.random.rand(self.n_1, self.n_2) < self.p + self.u0 = (np.random.randn(self.n_1, self.r))@(np.random.randn(self.r, self.n_2)) + self.M = np.copy(self.u0) + self.u0 = self.u0*self.mask + + def setupProxOperators(self): + super().setupProxOperators() + self.proxOperators = ['MC_P_M', 'MC_P_A'] + + def show(self): + # print(self.output) + ch = self.output['stats']['changes'] + u = self.output['u'] + # for key in self.output['stats']: + # print(key) + + fig, ax = plt.subplots(1, 3, figsize=(16, 5)) + + ax[0].imshow(self.M) + ax[0].set(title = 'original matrix') + ax[1].imshow(u) + ax[1].set(title = 'reconstructed matrix') + ax[2].semilogy(ch) + ax[2].set(xlabel='$k$', ylabel='$|| X_{k-1} - X_k ||$', title='change for each iteration') + plt.show() \ No newline at end of file diff --git a/proxtoolbox/proxoperators/MC_P_A.py b/proxtoolbox/proxoperators/MC_P_A.py new file mode 100644 index 0000000..089acee --- /dev/null +++ b/proxtoolbox/proxoperators/MC_P_A.py @@ -0,0 +1,13 @@ +from proxtoolbox.proxoperators.proxoperator import ProxOperator + +#__all__ = ['MC_P_A'] + +class MC_P_A(ProxOperator): + def __init__(self, experiment): + super().__init__(experiment) + self.mask = experiment.mask + self.M = experiment.M + + def eval(self, u): + u[self.mask] = self.M[self.mask] + return u \ No newline at end of file diff --git a/proxtoolbox/proxoperators/MC_P_M.py b/proxtoolbox/proxoperators/MC_P_M.py new file mode 100644 index 0000000..f4c88c6 --- /dev/null +++ b/proxtoolbox/proxoperators/MC_P_M.py @@ -0,0 +1,18 @@ +from proxtoolbox.proxoperators.proxoperator import ProxOperator + +import numpy as np +from scipy.sparse.linalg import svds + + +__all__ = ['MC_P_M'] + +class MC_P_M(ProxOperator): + def __init__(self, experiment): + super().__init__(experiment) + self.r = experiment.r + + def eval(self, u): + X = np.copy(u) + U, sigma, V = svds(X, k=self.r) + X = U @ np.diag(sigma) @ V + return X \ No newline at end of file diff --git a/proxtoolbox/proxoperators/__init__.py b/proxtoolbox/proxoperators/__init__.py index 8f30018..cee57c8 100644 --- a/proxtoolbox/proxoperators/__init__.py +++ b/proxtoolbox/proxoperators/__init__.py @@ -35,3 +35,5 @@ from .sourceLocProx import * from .Pphase_phasepack import * from .P_orthonorm import * from .P_incoherent import * +from .MC_P_A import * +from .MC_P_M import * -- GitLab From 65a4f7f67a3401787f38de51b73629f310449aab Mon Sep 17 00:00:00 2001 From: Maximilian Seeber Date: Sun, 29 May 2022 18:07:05 +0200 Subject: [PATCH 02/10] implemented nnm for mc --- demos/MC_DR.py | 6 ++++ proxtoolbox/algorithms/DR.py | 34 +++++++++++++++++++++ proxtoolbox/algorithms/__init__.py | 3 +- proxtoolbox/experiments/MC/MC_Experiment.py | 26 +++++++++++++--- proxtoolbox/proxoperators/MC_P_A.py | 5 +-- proxtoolbox/proxoperators/MC_prox_nuc.py | 18 +++++++++++ proxtoolbox/proxoperators/__init__.py | 1 + 7 files changed, 85 insertions(+), 8 deletions(-) create mode 100644 demos/MC_DR.py create mode 100644 proxtoolbox/algorithms/DR.py create mode 100644 proxtoolbox/proxoperators/MC_prox_nuc.py diff --git a/demos/MC_DR.py b/demos/MC_DR.py new file mode 100644 index 0000000..0bea4da --- /dev/null +++ b/demos/MC_DR.py @@ -0,0 +1,6 @@ +import SetProxPythonPath +from proxtoolbox.experiments.MC.MC_Experiment import MC_Experiment + +ART = MC_Experiment(algorithm='DR', fraction_known_entrees=0.7) +ART.run() +ART.show() \ No newline at end of file diff --git a/proxtoolbox/algorithms/DR.py b/proxtoolbox/algorithms/DR.py new file mode 100644 index 0000000..cc277f7 --- /dev/null +++ b/proxtoolbox/algorithms/DR.py @@ -0,0 +1,34 @@ +from proxtoolbox.algorithms.algorithm import Algorithm + +import numpy as np + +class DR(Algorithm): + """ + Douglas-Rachford algorithm + """ + + def evaluate(self, u): + """ + Update for Douglas-Rachford algorithm. + + Parameters + ---------- + u : ndarray or a list of ndarray objects + The current iterate. + + Returns + ------- + u_new : ndarray or a list of ndarray objects + The new iterate (same type as input parameter `u`). + """ + + tmp1 = 2*self.proxOperators[0].eval(u) - u + tmp2 = self.proxOperators[1].eval(tmp1) - tmp1/2 + unew = u/2 + tmp2 + return unew + + def postprocess(self): + self.u = self.proxOperators[0].eval(self.u) + output = {'stats': {'iter': self.iter}, 'u': self.u} + self.iterateMonitor.postprocess(self, output) + return output diff --git a/proxtoolbox/algorithms/__init__.py b/proxtoolbox/algorithms/__init__.py index 46e1820..d0d152f 100644 --- a/proxtoolbox/algorithms/__init__.py +++ b/proxtoolbox/algorithms/__init__.py @@ -38,4 +38,5 @@ from .PHeBIE_ptychography_objective import * from .PHeBIE_phase_objective import * from .Wirtinger import * from .Wirt_IterateMonitor import * -from .NSLS_sourceloc_objective import * \ No newline at end of file +from .NSLS_sourceloc_objective import * +from .DR import * \ No newline at end of file diff --git a/proxtoolbox/experiments/MC/MC_Experiment.py b/proxtoolbox/experiments/MC/MC_Experiment.py index 01366e4..bf0b0b1 100644 --- a/proxtoolbox/experiments/MC/MC_Experiment.py +++ b/proxtoolbox/experiments/MC/MC_Experiment.py @@ -13,6 +13,7 @@ class MC_Experiment(Experiment): def getDefaultParameters(): defaultParams = { 'experiment_name': 'MC', + # 'algorithm': 'AP', 'MAXIT': 1000, 'TOL': 1e-6, 'n_1': 100, @@ -21,11 +22,20 @@ class MC_Experiment(Experiment): 'fraction_known_entrees': 0.5, 'norm_data': 1, 'diagnostic': False, - 'rnd_seed': None + 'rnd_seed': None, + 'gamma': 1 } return defaultParams - def __init__(self, n_1, n_2, fraction_known_entrees, rank, norm_data, diagnostic, **kwargs): + def __init__(self, + n_1, + n_2, + fraction_known_entrees, + rank, + norm_data, + diagnostic, + gamma, + **kwargs): super().__init__(**kwargs) self.n_1 = n_1 self.n_2 = n_2 @@ -33,6 +43,7 @@ class MC_Experiment(Experiment): self.r = rank self.norm_data = norm_data self.diagnostic = False + self.gamma = gamma def loadData(self): self.mask = np.random.rand(self.n_1, self.n_2) < self.p @@ -41,13 +52,18 @@ class MC_Experiment(Experiment): self.u0 = self.u0*self.mask def setupProxOperators(self): - super().setupProxOperators() - self.proxOperators = ['MC_P_M', 'MC_P_A'] + if self.algorithm_name == 'AP': + super().setupProxOperators() + self.proxOperators = ['MC_P_M', 'MC_P_A'] + # elif self.algorithm_name == 'RAAR': + else: + super().setupProxOperators() + self.proxOperators = ['MC_P_A', 'MC_prox_nuc'] def show(self): - # print(self.output) ch = self.output['stats']['changes'] u = self.output['u'] + print(f'reconstruction error: {np.linalg.norm(u-self.M)}') # for key in self.output['stats']: # print(key) diff --git a/proxtoolbox/proxoperators/MC_P_A.py b/proxtoolbox/proxoperators/MC_P_A.py index 089acee..933261f 100644 --- a/proxtoolbox/proxoperators/MC_P_A.py +++ b/proxtoolbox/proxoperators/MC_P_A.py @@ -9,5 +9,6 @@ class MC_P_A(ProxOperator): self.M = experiment.M def eval(self, u): - u[self.mask] = self.M[self.mask] - return u \ No newline at end of file + # u[self.mask] = self.M[self.mask] + # return u + return u + self.mask * (self.M - u) \ No newline at end of file diff --git a/proxtoolbox/proxoperators/MC_prox_nuc.py b/proxtoolbox/proxoperators/MC_prox_nuc.py new file mode 100644 index 0000000..379878d --- /dev/null +++ b/proxtoolbox/proxoperators/MC_prox_nuc.py @@ -0,0 +1,18 @@ +from proxtoolbox.proxoperators.proxoperator import ProxOperator + +import numpy as np + + +__all__ = ['MC_prox_nuc'] + +class MC_prox_nuc(ProxOperator): + def __init__(self, experiment): + super().__init__(experiment) + self.gamma = experiment.gamma + + def eval(self, u): + U, sigma, V = np.linalg.svd(u) + sigma = sigma - self.gamma + sigma[sigma < 0] = 0 + E = np.diag(sigma) + return U @ E @ V \ No newline at end of file diff --git a/proxtoolbox/proxoperators/__init__.py b/proxtoolbox/proxoperators/__init__.py index cee57c8..4e7af40 100644 --- a/proxtoolbox/proxoperators/__init__.py +++ b/proxtoolbox/proxoperators/__init__.py @@ -37,3 +37,4 @@ from .P_orthonorm import * from .P_incoherent import * from .MC_P_A import * from .MC_P_M import * +from .MC_prox_nuc import * -- GitLab From 014965c1df2e13f713b0873d595282f8fa6de476 Mon Sep 17 00:00:00 2001 From: Maximilian Seeber Date: Sun, 29 May 2022 20:38:43 +0200 Subject: [PATCH 03/10] implemented als for mc --- demos/MC_ALS.py | 6 ++ demos/MC_AP.py | 6 +- demos/MC_DR.py | 6 +- proxtoolbox/algorithms/ALS.py | 74 +++++++++++++++++++++ proxtoolbox/algorithms/__init__.py | 3 +- proxtoolbox/experiments/MC/MC_Experiment.py | 16 +++-- proxtoolbox/proxoperators/MC_ALS.py | 31 +++++++++ proxtoolbox/proxoperators/__init__.py | 1 + 8 files changed, 132 insertions(+), 11 deletions(-) create mode 100644 demos/MC_ALS.py create mode 100644 proxtoolbox/algorithms/ALS.py create mode 100644 proxtoolbox/proxoperators/MC_ALS.py diff --git a/demos/MC_ALS.py b/demos/MC_ALS.py new file mode 100644 index 0000000..083e2fd --- /dev/null +++ b/demos/MC_ALS.py @@ -0,0 +1,6 @@ +import SetProxPythonPath +from proxtoolbox.experiments.MC.MC_Experiment import MC_Experiment + +MC = MC_Experiment(algorithm='ALS', fraction_known_entrees=0.5) +MC.run() +MC.show() \ No newline at end of file diff --git a/demos/MC_AP.py b/demos/MC_AP.py index a6fe657..b36bb2a 100644 --- a/demos/MC_AP.py +++ b/demos/MC_AP.py @@ -1,6 +1,6 @@ import SetProxPythonPath from proxtoolbox.experiments.MC.MC_Experiment import MC_Experiment -ART = MC_Experiment(algorithm='AP', fraction_known_entrees=0.3) -ART.run() -ART.show() \ No newline at end of file +MC = MC_Experiment(algorithm='AP', fraction_known_entrees=0.3) +MC.run() +MC.show() \ No newline at end of file diff --git a/demos/MC_DR.py b/demos/MC_DR.py index 0bea4da..3b894b3 100644 --- a/demos/MC_DR.py +++ b/demos/MC_DR.py @@ -1,6 +1,6 @@ import SetProxPythonPath from proxtoolbox.experiments.MC.MC_Experiment import MC_Experiment -ART = MC_Experiment(algorithm='DR', fraction_known_entrees=0.7) -ART.run() -ART.show() \ No newline at end of file +MC = MC_Experiment(algorithm='DR', fraction_known_entrees=0.5) +MC.run() +MC.show() \ No newline at end of file diff --git a/proxtoolbox/algorithms/ALS.py b/proxtoolbox/algorithms/ALS.py new file mode 100644 index 0000000..f03961a --- /dev/null +++ b/proxtoolbox/algorithms/ALS.py @@ -0,0 +1,74 @@ +from proxtoolbox.algorithms.algorithm import Algorithm + +import numpy as np + +class ALS(Algorithm): + """ + Alternating least squares algorithm + """ + + def evaluate(self, u): + """ + Update for the Alternating least squares algorithm. + + Parameters + ---------- + u : ndarray or a list of ndarray objects + The current iterate. + + Returns + ------- + u_new : ndarray or a list of ndarray objects + The new iterate (same type as input parameter `u`). + """ + + unew = self.proxOperators[0].eval(u) + + return unew + + def run(self, u): + """ + Run the algorithm given an initial iterate u. The algorithm + runs until the stopping condition is satisfied. Statistics are + collected at each iteration by the iterate monitor. + + Parameters + ---------- + u: ndarray or a list of ndarray objects + Initial iterate. + + Returns + ------- + dictionary with the following entries: + u: ndarray or a list of ndarray objects + The last iterate. + iter: natural number + The last iteration count + + additional entries are given by the iterate monitor + + """ + self.U, self.V = u + self.u = self.U.T @ self.V + self.preprocess() + self.displayProgress() + + if self.progressbar == 'tqdm_notebook': + pbar = tqdm_notebook(total=self.maxIter) + elif self.progressbar == 'tqdm': + pbar = tqdm(total=self.maxIter) + else: + pbar = None + + while self.stoppingCondition(): + self.iter += 1 + if pbar is not None: + pbar.update() + self.U, self.V = self.evaluate((self.U, self.V)) + self.u_new = self.U.T @ self.V + self.updateStatistics() + self.displayProgress() + self.u = self.u_new + if pbar is not None: + pbar.close() + return self.postprocess() diff --git a/proxtoolbox/algorithms/__init__.py b/proxtoolbox/algorithms/__init__.py index d0d152f..8249cd5 100644 --- a/proxtoolbox/algorithms/__init__.py +++ b/proxtoolbox/algorithms/__init__.py @@ -39,4 +39,5 @@ from .PHeBIE_phase_objective import * from .Wirtinger import * from .Wirt_IterateMonitor import * from .NSLS_sourceloc_objective import * -from .DR import * \ No newline at end of file +from .DR import * +from .ALS import * \ No newline at end of file diff --git a/proxtoolbox/experiments/MC/MC_Experiment.py b/proxtoolbox/experiments/MC/MC_Experiment.py index bf0b0b1..bfc69b6 100644 --- a/proxtoolbox/experiments/MC/MC_Experiment.py +++ b/proxtoolbox/experiments/MC/MC_Experiment.py @@ -23,7 +23,8 @@ class MC_Experiment(Experiment): 'norm_data': 1, 'diagnostic': False, 'rnd_seed': None, - 'gamma': 1 + 'gamma': 1, + 'lambd': 1e-9 } return defaultParams @@ -35,6 +36,7 @@ class MC_Experiment(Experiment): norm_data, diagnostic, gamma, + lambd, **kwargs): super().__init__(**kwargs) self.n_1 = n_1 @@ -44,21 +46,27 @@ class MC_Experiment(Experiment): self.norm_data = norm_data self.diagnostic = False self.gamma = gamma + self.lambd = lambd def loadData(self): self.mask = np.random.rand(self.n_1, self.n_2) < self.p self.u0 = (np.random.randn(self.n_1, self.r))@(np.random.randn(self.r, self.n_2)) self.M = np.copy(self.u0) - self.u0 = self.u0*self.mask + if self.algorithm_name == 'ALS': + self.u0 = np.random.randn(self.r, self.n_1), np.random.randn(self.r, self.n_2) + else: + self.u0 = self.u0*self.mask def setupProxOperators(self): if self.algorithm_name == 'AP': super().setupProxOperators() self.proxOperators = ['MC_P_M', 'MC_P_A'] - # elif self.algorithm_name == 'RAAR': - else: + elif self.algorithm_name == 'DR': super().setupProxOperators() self.proxOperators = ['MC_P_A', 'MC_prox_nuc'] + elif self.algorithm_name == 'ALS': + super().setupProxOperators() + self.proxOperators = ['MC_ALS', 'MC_ALS'] def show(self): ch = self.output['stats']['changes'] diff --git a/proxtoolbox/proxoperators/MC_ALS.py b/proxtoolbox/proxoperators/MC_ALS.py new file mode 100644 index 0000000..6b9e0b2 --- /dev/null +++ b/proxtoolbox/proxoperators/MC_ALS.py @@ -0,0 +1,31 @@ +from proxtoolbox.proxoperators.proxoperator import ProxOperator + +import numpy as np + +#__all__ = ['MC_P_A'] + +class MC_ALS(ProxOperator): + def __init__(self, experiment): + super().__init__(experiment) + self.n_1 = experiment.n_1 + self.n_2 = experiment.n_2 + self.r = experiment.r + self.lambd = experiment.lambd + self.mask = experiment.mask + self.M = experiment.M + self.C_u = [np.diag(row) for row in self.mask] + self.C_v = [np.diag(col) for col in self.mask.T] + + def eval(self, u): + U, V = u + + for i in range(self.n_1): + U[:,i] = np.linalg.solve(V @ self.C_u[i] @ V.T + + self.lambd * np.eye(self.r), V @ self.C_u[i] @ self.M[i,:]) + + for j in range(self.n_2): + V[:,j] = np.linalg.solve(U @ self.C_v[j] @ U.T + + self.lambd * np.eye(self.r), U @ self.C_v[j] @ self.M[:,j]) + + + return U, V \ No newline at end of file diff --git a/proxtoolbox/proxoperators/__init__.py b/proxtoolbox/proxoperators/__init__.py index 4e7af40..8a41949 100644 --- a/proxtoolbox/proxoperators/__init__.py +++ b/proxtoolbox/proxoperators/__init__.py @@ -38,3 +38,4 @@ from .P_incoherent import * from .MC_P_A import * from .MC_P_M import * from .MC_prox_nuc import * +from .MC_ALS import * -- GitLab From 7a1fe73893c213448d53b48f95cbcfb36b9fc48e Mon Sep 17 00:00:00 2001 From: Maximilian Seeber Date: Fri, 17 Jun 2022 19:23:56 +0200 Subject: [PATCH 04/10] implemented animation --- demos/MC_ALS.py | 3 ++- demos/MC_AP.py | 3 ++- demos/MC_DR.py | 3 ++- proxtoolbox/experiments/MC/MC_Experiment.py | 22 +++++++++++++++++++-- 4 files changed, 26 insertions(+), 5 deletions(-) diff --git a/demos/MC_ALS.py b/demos/MC_ALS.py index 083e2fd..942733c 100644 --- a/demos/MC_ALS.py +++ b/demos/MC_ALS.py @@ -1,6 +1,7 @@ import SetProxPythonPath from proxtoolbox.experiments.MC.MC_Experiment import MC_Experiment -MC = MC_Experiment(algorithm='ALS', fraction_known_entrees=0.5) +MC = MC_Experiment(algorithm='ALS', fraction_known_entrees=0.5, rank=2, anim=True) MC.run() +MC.animate(MC.algorithm) MC.show() \ No newline at end of file diff --git a/demos/MC_AP.py b/demos/MC_AP.py index b36bb2a..edfc5c5 100644 --- a/demos/MC_AP.py +++ b/demos/MC_AP.py @@ -1,6 +1,7 @@ import SetProxPythonPath from proxtoolbox.experiments.MC.MC_Experiment import MC_Experiment -MC = MC_Experiment(algorithm='AP', fraction_known_entrees=0.3) +MC = MC_Experiment(algorithm='AP', fraction_known_entrees=0.4, rank=2, anim=True) MC.run() +MC.animate(MC.algorithm) MC.show() \ No newline at end of file diff --git a/demos/MC_DR.py b/demos/MC_DR.py index 3b894b3..e785082 100644 --- a/demos/MC_DR.py +++ b/demos/MC_DR.py @@ -1,6 +1,7 @@ import SetProxPythonPath from proxtoolbox.experiments.MC.MC_Experiment import MC_Experiment -MC = MC_Experiment(algorithm='DR', fraction_known_entrees=0.5) +MC = MC_Experiment(algorithm='DR', fraction_known_entrees=0.5, anim=True) MC.run() +MC.animate(MC.algorithm) MC.show() \ No newline at end of file diff --git a/proxtoolbox/experiments/MC/MC_Experiment.py b/proxtoolbox/experiments/MC/MC_Experiment.py index bfc69b6..2d5a3d4 100644 --- a/proxtoolbox/experiments/MC/MC_Experiment.py +++ b/proxtoolbox/experiments/MC/MC_Experiment.py @@ -3,6 +3,7 @@ from proxtoolbox import proxoperators import numpy as np import matplotlib.pyplot as plt +import time class MC_Experiment(Experiment): """ @@ -50,7 +51,12 @@ class MC_Experiment(Experiment): def loadData(self): self.mask = np.random.rand(self.n_1, self.n_2) < self.p - self.u0 = (np.random.randn(self.n_1, self.r))@(np.random.randn(self.r, self.n_2)) + # self.u0 = (np.random.randn(self.n_1, self.r))@(np.random.randn(self.r, self.n_2)) + + self.u0 = np.ones((self.n_1, self.n_2)) + self.u0[10:70, 10:20] = 0 + self.u0[10:70, 50:60] = 0 + self.M = np.copy(self.u0) if self.algorithm_name == 'ALS': self.u0 = np.random.randn(self.r, self.n_1), np.random.randn(self.r, self.n_2) @@ -83,4 +89,16 @@ class MC_Experiment(Experiment): ax[1].set(title = 'reconstructed matrix') ax[2].semilogy(ch) ax[2].set(xlabel='$k$', ylabel='$|| X_{k-1} - X_k ||$', title='change for each iteration') - plt.show() \ No newline at end of file + plt.show() + + def animate(self, alg): + # print(alg.u_new) + # print(alg.u_new) + if alg.u_new is not None: + if self.algorithm_name == 'DR': + self.animateFigure(alg.proxOperators[0].eval(alg.u_new), f'$X_{{{alg.iter}}}$') + else: + self.animateFigure(alg.u_new, f'$X_{{{alg.iter}}}$') + if alg.iter < 50: + time.sleep(0.3) + time.sleep(0.1) \ No newline at end of file -- GitLab From bf81d2d1dadc6bf7df0a5d917ced169d5d27da6d Mon Sep 17 00:00:00 2001 From: Maximilian Seeber Date: Sat, 18 Jun 2022 20:25:19 +0200 Subject: [PATCH 05/10] changed the visualization --- demos/MC_ALS.py | 2 +- demos/MC_AP.py | 2 +- demos/MC_DR.py | 2 +- proxtoolbox/experiments/MC/MC_Experiment.py | 50 ++++++++++++++++----- proxtoolbox/proxoperators/MC_prox_nuc.py | 5 ++- 5 files changed, 45 insertions(+), 16 deletions(-) diff --git a/demos/MC_ALS.py b/demos/MC_ALS.py index 942733c..8016b84 100644 --- a/demos/MC_ALS.py +++ b/demos/MC_ALS.py @@ -1,7 +1,7 @@ import SetProxPythonPath from proxtoolbox.experiments.MC.MC_Experiment import MC_Experiment -MC = MC_Experiment(algorithm='ALS', fraction_known_entrees=0.5, rank=2, anim=True) +MC = MC_Experiment(algorithm='ALS', fraction_known_entrees=0.3, rank=6, anim=True, n_2=200) MC.run() MC.animate(MC.algorithm) MC.show() \ No newline at end of file diff --git a/demos/MC_AP.py b/demos/MC_AP.py index edfc5c5..207efac 100644 --- a/demos/MC_AP.py +++ b/demos/MC_AP.py @@ -1,7 +1,7 @@ import SetProxPythonPath from proxtoolbox.experiments.MC.MC_Experiment import MC_Experiment -MC = MC_Experiment(algorithm='AP', fraction_known_entrees=0.4, rank=2, anim=True) +MC = MC_Experiment(algorithm='AP', fraction_known_entrees=0.4, rank=6, anim=True, n_2=200) MC.run() MC.animate(MC.algorithm) MC.show() \ No newline at end of file diff --git a/demos/MC_DR.py b/demos/MC_DR.py index e785082..65e35a0 100644 --- a/demos/MC_DR.py +++ b/demos/MC_DR.py @@ -1,7 +1,7 @@ import SetProxPythonPath from proxtoolbox.experiments.MC.MC_Experiment import MC_Experiment -MC = MC_Experiment(algorithm='DR', fraction_known_entrees=0.5, anim=True) +MC = MC_Experiment(algorithm='DR', fraction_known_entrees=0.3, anim=True, n_2=200) MC.run() MC.animate(MC.algorithm) MC.show() \ No newline at end of file diff --git a/proxtoolbox/experiments/MC/MC_Experiment.py b/proxtoolbox/experiments/MC/MC_Experiment.py index 2d5a3d4..f28b89e 100644 --- a/proxtoolbox/experiments/MC/MC_Experiment.py +++ b/proxtoolbox/experiments/MC/MC_Experiment.py @@ -48,14 +48,30 @@ class MC_Experiment(Experiment): self.diagnostic = False self.gamma = gamma self.lambd = lambd + self.frob_list = [] + self.rank_list = [] def loadData(self): self.mask = np.random.rand(self.n_1, self.n_2) < self.p # self.u0 = (np.random.randn(self.n_1, self.r))@(np.random.randn(self.r, self.n_2)) self.u0 = np.ones((self.n_1, self.n_2)) - self.u0[10:70, 10:20] = 0 - self.u0[10:70, 50:60] = 0 + self.u0[20:80, 10:20] = 0 + self.u0[30:40, 20:30] = 0 + self.u0[40:60, 30:40] = 0 + self.u0[60:70, 40:50] = 0 + self.u0[20:80, 50:60] = 0 + + self.u0[40:80, 80:90] = 0.5 + self.u0[20:40, 90:100] = 0.5 + self.u0[60:70, 90:100] = 0.5 + self.u0[40:80, 100:110] = 0.5 + + self.u0[20:80, 130:140] = 0 + self.u0[20:30, 140:150] = 0 + self.u0[20:60, 150:160] = 0 + self.u0[20:30, 160:170] = 0 + self.u0[20:80, 170:180] = 0 self.M = np.copy(self.u0) if self.algorithm_name == 'ALS': @@ -81,24 +97,34 @@ class MC_Experiment(Experiment): # for key in self.output['stats']: # print(key) - fig, ax = plt.subplots(1, 3, figsize=(16, 5)) + fig, ((ax11, ax12), (ax21, ax22)) = plt.subplots(2, 2, figsize=(16, 5)) - ax[0].imshow(self.M) - ax[0].set(title = 'original matrix') - ax[1].imshow(u) - ax[1].set(title = 'reconstructed matrix') - ax[2].semilogy(ch) - ax[2].set(xlabel='$k$', ylabel='$|| X_{k-1} - X_k ||$', title='change for each iteration') + ax11.imshow(self.M) + ax11.set(title = 'original matrix') + ax12.imshow(self.M * self.mask) + ax12.set(title = 'algorithm input') + ax21.semilogy(self.frob_list) + ax21.set(xlabel='$k$', ylabel='$|| M - X_k ||$', title='distance to M') + ax22.semilogy(ch) + ax22.set(xlabel='$k$', ylabel='$|| X_{k-1} - X_k ||$', title='change for each iteration') plt.show() def animate(self, alg): - # print(alg.u_new) - # print(alg.u_new) if alg.u_new is not None: if self.algorithm_name == 'DR': self.animateFigure(alg.proxOperators[0].eval(alg.u_new), f'$X_{{{alg.iter}}}$') + self.frob_list += [np.linalg.norm(alg.proxOperators[0].eval(alg.u_new) - self.M)] + self.rank_list += [np.linalg.matrix_rank(alg.u_new)] else: self.animateFigure(alg.u_new, f'$X_{{{alg.iter}}}$') + self.frob_list += [np.linalg.norm(alg.u_new - self.M)] + self.rank_list += [np.linalg.matrix_rank(alg.u_new)] if alg.iter < 50: time.sleep(0.3) - time.sleep(0.1) \ No newline at end of file + time.sleep(0.1) + else: + if self.algorithm_name == 'ALS': + self.animateFigure(self.u0[0].T@self.u0[1], '$X_0$') + else: + self.animateFigure(self.u0, '$X_0$') + time.sleep(0.7) \ No newline at end of file diff --git a/proxtoolbox/proxoperators/MC_prox_nuc.py b/proxtoolbox/proxoperators/MC_prox_nuc.py index 379878d..31b3a7d 100644 --- a/proxtoolbox/proxoperators/MC_prox_nuc.py +++ b/proxtoolbox/proxoperators/MC_prox_nuc.py @@ -11,8 +11,11 @@ class MC_prox_nuc(ProxOperator): self.gamma = experiment.gamma def eval(self, u): + n_1, n_2 = u.shape U, sigma, V = np.linalg.svd(u) sigma = sigma - self.gamma sigma[sigma < 0] = 0 - E = np.diag(sigma) + n = min(n_1, n_2) + E = np.zeros((n_1, n_2)) + E[:n, :n] = np.diag(sigma) return U @ E @ V \ No newline at end of file -- GitLab From baf13e4194fa83629960b8768002776f5e3822d3 Mon Sep 17 00:00:00 2001 From: Maximilian Seeber Date: Thu, 14 Jul 2022 14:41:53 +0200 Subject: [PATCH 06/10] improved the output with tight_layout() --- demos/MC_ALS.py | 7 ++++++- demos/MC_AP.py | 7 ++++++- demos/MC_DR.py | 6 +++++- proxtoolbox/experiments/MC/MC_Experiment.py | 1 + 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/demos/MC_ALS.py b/demos/MC_ALS.py index 8016b84..aef57da 100644 --- a/demos/MC_ALS.py +++ b/demos/MC_ALS.py @@ -1,7 +1,12 @@ import SetProxPythonPath from proxtoolbox.experiments.MC.MC_Experiment import MC_Experiment -MC = MC_Experiment(algorithm='ALS', fraction_known_entrees=0.3, rank=6, anim=True, n_2=200) +MC = MC_Experiment(algorithm='ALS', + fraction_known_entrees=0.3, + rank=6, + anim=True, + n_2=200, + MAXIT=300) MC.run() MC.animate(MC.algorithm) MC.show() \ No newline at end of file diff --git a/demos/MC_AP.py b/demos/MC_AP.py index 207efac..4aaee0d 100644 --- a/demos/MC_AP.py +++ b/demos/MC_AP.py @@ -1,7 +1,12 @@ import SetProxPythonPath from proxtoolbox.experiments.MC.MC_Experiment import MC_Experiment -MC = MC_Experiment(algorithm='AP', fraction_known_entrees=0.4, rank=6, anim=True, n_2=200) +MC = MC_Experiment(algorithm='AP', + fraction_known_entrees=0.4, + rank=4, + anim=True, + n_2=200, + MAXIT=300) MC.run() MC.animate(MC.algorithm) MC.show() \ No newline at end of file diff --git a/demos/MC_DR.py b/demos/MC_DR.py index 65e35a0..66d2ee8 100644 --- a/demos/MC_DR.py +++ b/demos/MC_DR.py @@ -1,7 +1,11 @@ import SetProxPythonPath from proxtoolbox.experiments.MC.MC_Experiment import MC_Experiment -MC = MC_Experiment(algorithm='DR', fraction_known_entrees=0.3, anim=True, n_2=200) +MC = MC_Experiment(algorithm='DR', + fraction_known_entrees=0.3, + anim=True, + n_2=200, + MAXIT=400) MC.run() MC.animate(MC.algorithm) MC.show() \ No newline at end of file diff --git a/proxtoolbox/experiments/MC/MC_Experiment.py b/proxtoolbox/experiments/MC/MC_Experiment.py index f28b89e..15d176a 100644 --- a/proxtoolbox/experiments/MC/MC_Experiment.py +++ b/proxtoolbox/experiments/MC/MC_Experiment.py @@ -107,6 +107,7 @@ class MC_Experiment(Experiment): ax21.set(xlabel='$k$', ylabel='$|| M - X_k ||$', title='distance to M') ax22.semilogy(ch) ax22.set(xlabel='$k$', ylabel='$|| X_{k-1} - X_k ||$', title='change for each iteration') + plt.tight_layout() plt.show() def animate(self, alg): -- GitLab From 6c01e7d4491ccc20c30d81637df50d936cc46631 Mon Sep 17 00:00:00 2001 From: Maximilian Seeber Date: Thu, 14 Jul 2022 15:35:09 +0200 Subject: [PATCH 07/10] added an option to toggle picture infilling --- demos/MC_ALS.py | 3 +- demos/MC_AP.py | 5 +-- demos/MC_DR.py | 3 +- proxtoolbox/experiments/MC/MC_Experiment.py | 40 ++++++++++++--------- 4 files changed, 30 insertions(+), 21 deletions(-) diff --git a/demos/MC_ALS.py b/demos/MC_ALS.py index aef57da..815275f 100644 --- a/demos/MC_ALS.py +++ b/demos/MC_ALS.py @@ -6,7 +6,8 @@ MC = MC_Experiment(algorithm='ALS', rank=6, anim=True, n_2=200, - MAXIT=300) + MAXIT=300, + picture_infilling=True) MC.run() MC.animate(MC.algorithm) MC.show() \ No newline at end of file diff --git a/demos/MC_AP.py b/demos/MC_AP.py index 4aaee0d..0eb9ed2 100644 --- a/demos/MC_AP.py +++ b/demos/MC_AP.py @@ -3,10 +3,11 @@ from proxtoolbox.experiments.MC.MC_Experiment import MC_Experiment MC = MC_Experiment(algorithm='AP', fraction_known_entrees=0.4, - rank=4, + rank=6, anim=True, n_2=200, - MAXIT=300) + MAXIT=300, + picture_infilling=True) MC.run() MC.animate(MC.algorithm) MC.show() \ No newline at end of file diff --git a/demos/MC_DR.py b/demos/MC_DR.py index 66d2ee8..d678512 100644 --- a/demos/MC_DR.py +++ b/demos/MC_DR.py @@ -5,7 +5,8 @@ MC = MC_Experiment(algorithm='DR', fraction_known_entrees=0.3, anim=True, n_2=200, - MAXIT=400) + MAXIT=300, + picture_infilling=True) MC.run() MC.animate(MC.algorithm) MC.show() \ No newline at end of file diff --git a/proxtoolbox/experiments/MC/MC_Experiment.py b/proxtoolbox/experiments/MC/MC_Experiment.py index 15d176a..8eebfc1 100644 --- a/proxtoolbox/experiments/MC/MC_Experiment.py +++ b/proxtoolbox/experiments/MC/MC_Experiment.py @@ -25,7 +25,8 @@ class MC_Experiment(Experiment): 'diagnostic': False, 'rnd_seed': None, 'gamma': 1, - 'lambd': 1e-9 + 'lambd': 1e-9, + 'picture_infilling': False } return defaultParams @@ -38,6 +39,7 @@ class MC_Experiment(Experiment): diagnostic, gamma, lambd, + picture_infilling, **kwargs): super().__init__(**kwargs) self.n_1 = n_1 @@ -48,30 +50,34 @@ class MC_Experiment(Experiment): self.diagnostic = False self.gamma = gamma self.lambd = lambd + self.picture_infilling = picture_infilling self.frob_list = [] self.rank_list = [] def loadData(self): self.mask = np.random.rand(self.n_1, self.n_2) < self.p - # self.u0 = (np.random.randn(self.n_1, self.r))@(np.random.randn(self.r, self.n_2)) - self.u0 = np.ones((self.n_1, self.n_2)) - self.u0[20:80, 10:20] = 0 - self.u0[30:40, 20:30] = 0 - self.u0[40:60, 30:40] = 0 - self.u0[60:70, 40:50] = 0 - self.u0[20:80, 50:60] = 0 + if self.picture_infilling: + self.u0 = np.ones((self.n_1, self.n_2)) + self.u0[20:80, 10:20] = 0 + self.u0[30:40, 20:30] = 0 + self.u0[40:60, 30:40] = 0 + self.u0[60:70, 40:50] = 0 + self.u0[20:80, 50:60] = 0 - self.u0[40:80, 80:90] = 0.5 - self.u0[20:40, 90:100] = 0.5 - self.u0[60:70, 90:100] = 0.5 - self.u0[40:80, 100:110] = 0.5 + self.u0[40:80, 80:90] = 0.5 + self.u0[20:40, 90:100] = 0.5 + self.u0[60:70, 90:100] = 0.5 + self.u0[40:80, 100:110] = 0.5 + + self.u0[20:80, 130:140] = 0 + self.u0[20:30, 140:150] = 0 + self.u0[20:60, 150:160] = 0 + self.u0[20:30, 160:170] = 0 + self.u0[20:80, 170:180] = 0 + else: + self.u0 = (np.random.randn(self.n_1, self.r))@(np.random.randn(self.r, self.n_2)) - self.u0[20:80, 130:140] = 0 - self.u0[20:30, 140:150] = 0 - self.u0[20:60, 150:160] = 0 - self.u0[20:30, 160:170] = 0 - self.u0[20:80, 170:180] = 0 self.M = np.copy(self.u0) if self.algorithm_name == 'ALS': -- GitLab From 6c7cbf0fc384309529adf50e762e4d08d47cac07 Mon Sep 17 00:00:00 2001 From: "maximilian.seeber" Date: Sun, 7 Aug 2022 20:51:52 +0200 Subject: [PATCH 08/10] code cleanup --- proxtoolbox/experiments/MC/MC_Experiment.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/proxtoolbox/experiments/MC/MC_Experiment.py b/proxtoolbox/experiments/MC/MC_Experiment.py index 8eebfc1..84a6cf1 100644 --- a/proxtoolbox/experiments/MC/MC_Experiment.py +++ b/proxtoolbox/experiments/MC/MC_Experiment.py @@ -14,7 +14,6 @@ class MC_Experiment(Experiment): def getDefaultParameters(): defaultParams = { 'experiment_name': 'MC', - # 'algorithm': 'AP', 'MAXIT': 1000, 'TOL': 1e-6, 'n_1': 100, @@ -100,8 +99,6 @@ class MC_Experiment(Experiment): ch = self.output['stats']['changes'] u = self.output['u'] print(f'reconstruction error: {np.linalg.norm(u-self.M)}') - # for key in self.output['stats']: - # print(key) fig, ((ax11, ax12), (ax21, ax22)) = plt.subplots(2, 2, figsize=(16, 5)) -- GitLab From 1a40a668a60c6862570125f31a9093d6272eb897 Mon Sep 17 00:00:00 2001 From: Maximilian Seeber Date: Thu, 11 Aug 2022 17:32:31 +0200 Subject: [PATCH 09/10] fixed a bug, when specifying matrix dimensions in picture_infilling mode --- demos/MC_ALS.py | 4 ++-- demos/MC_AP.py | 4 ++-- demos/MC_DR.py | 4 ++-- proxtoolbox/experiments/MC/MC_Experiment.py | 9 ++++++--- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/demos/MC_ALS.py b/demos/MC_ALS.py index 815275f..f0cc8c4 100644 --- a/demos/MC_ALS.py +++ b/demos/MC_ALS.py @@ -5,9 +5,9 @@ MC = MC_Experiment(algorithm='ALS', fraction_known_entrees=0.3, rank=6, anim=True, - n_2=200, MAXIT=300, - picture_infilling=True) + picture_infilling=True, + ) MC.run() MC.animate(MC.algorithm) MC.show() \ No newline at end of file diff --git a/demos/MC_AP.py b/demos/MC_AP.py index 0eb9ed2..7357f6f 100644 --- a/demos/MC_AP.py +++ b/demos/MC_AP.py @@ -5,9 +5,9 @@ MC = MC_Experiment(algorithm='AP', fraction_known_entrees=0.4, rank=6, anim=True, - n_2=200, MAXIT=300, - picture_infilling=True) + picture_infilling=True, + ) MC.run() MC.animate(MC.algorithm) MC.show() \ No newline at end of file diff --git a/demos/MC_DR.py b/demos/MC_DR.py index d678512..4551d6a 100644 --- a/demos/MC_DR.py +++ b/demos/MC_DR.py @@ -4,9 +4,9 @@ from proxtoolbox.experiments.MC.MC_Experiment import MC_Experiment MC = MC_Experiment(algorithm='DR', fraction_known_entrees=0.3, anim=True, - n_2=200, MAXIT=300, - picture_infilling=True) + picture_infilling=True, + ) MC.run() MC.animate(MC.algorithm) MC.show() \ No newline at end of file diff --git a/proxtoolbox/experiments/MC/MC_Experiment.py b/proxtoolbox/experiments/MC/MC_Experiment.py index 84a6cf1..0a029a0 100644 --- a/proxtoolbox/experiments/MC/MC_Experiment.py +++ b/proxtoolbox/experiments/MC/MC_Experiment.py @@ -53,10 +53,12 @@ class MC_Experiment(Experiment): self.frob_list = [] self.rank_list = [] - def loadData(self): - self.mask = np.random.rand(self.n_1, self.n_2) < self.p - + def loadData(self): if self.picture_infilling: + self.n_1 = 100 + self.n_2 = 200 + self.mask = np.random.rand(self.n_1, self.n_2) < self.p + self.u0 = np.ones((self.n_1, self.n_2)) self.u0[20:80, 10:20] = 0 self.u0[30:40, 20:30] = 0 @@ -75,6 +77,7 @@ class MC_Experiment(Experiment): self.u0[20:30, 160:170] = 0 self.u0[20:80, 170:180] = 0 else: + self.mask = np.random.rand(self.n_1, self.n_2) < self.p self.u0 = (np.random.randn(self.n_1, self.r))@(np.random.randn(self.r, self.n_2)) -- GitLab From 6763352cbf28ccf1d8582d42e1a8a52c7b2c74e2 Mon Sep 17 00:00:00 2001 From: Maximilian Seeber Date: Sat, 13 Aug 2022 13:00:53 +0200 Subject: [PATCH 10/10] changed the visualization of the first iterate --- demos/MC_ALS.py | 1 - demos/MC_AP.py | 3 +-- demos/MC_DR.py | 1 - proxtoolbox/experiments/MC/MC_Experiment.py | 20 +++++++++++++------- proxtoolbox/proxoperators/MC_ALS.py | 2 -- 5 files changed, 14 insertions(+), 13 deletions(-) diff --git a/demos/MC_ALS.py b/demos/MC_ALS.py index f0cc8c4..ade4e3e 100644 --- a/demos/MC_ALS.py +++ b/demos/MC_ALS.py @@ -4,7 +4,6 @@ from proxtoolbox.experiments.MC.MC_Experiment import MC_Experiment MC = MC_Experiment(algorithm='ALS', fraction_known_entrees=0.3, rank=6, - anim=True, MAXIT=300, picture_infilling=True, ) diff --git a/demos/MC_AP.py b/demos/MC_AP.py index 7357f6f..c582292 100644 --- a/demos/MC_AP.py +++ b/demos/MC_AP.py @@ -2,9 +2,8 @@ import SetProxPythonPath from proxtoolbox.experiments.MC.MC_Experiment import MC_Experiment MC = MC_Experiment(algorithm='AP', - fraction_known_entrees=0.4, + fraction_known_entrees=0.3, rank=6, - anim=True, MAXIT=300, picture_infilling=True, ) diff --git a/demos/MC_DR.py b/demos/MC_DR.py index 4551d6a..f41148f 100644 --- a/demos/MC_DR.py +++ b/demos/MC_DR.py @@ -3,7 +3,6 @@ from proxtoolbox.experiments.MC.MC_Experiment import MC_Experiment MC = MC_Experiment(algorithm='DR', fraction_known_entrees=0.3, - anim=True, MAXIT=300, picture_infilling=True, ) diff --git a/proxtoolbox/experiments/MC/MC_Experiment.py b/proxtoolbox/experiments/MC/MC_Experiment.py index 0a029a0..3fd5578 100644 --- a/proxtoolbox/experiments/MC/MC_Experiment.py +++ b/proxtoolbox/experiments/MC/MC_Experiment.py @@ -25,7 +25,9 @@ class MC_Experiment(Experiment): 'rnd_seed': None, 'gamma': 1, 'lambd': 1e-9, - 'picture_infilling': False + 'picture_infilling': False, + 'anim': True, + 'anim_colorbar': True } return defaultParams @@ -103,14 +105,18 @@ class MC_Experiment(Experiment): u = self.output['u'] print(f'reconstruction error: {np.linalg.norm(u-self.M)}') - fig, ((ax11, ax12), (ax21, ax22)) = plt.subplots(2, 2, figsize=(16, 5)) + fig, (ax11, ax12) = plt.subplots(1, 2, figsize=(20, 10)) + im = ax11.imshow(self.M) + ax11.set(title = 'original matrix $M$') + # plt.colorbar(im, cax=ax115) + ax12.imshow(self.M) + masked = np.ma.masked_where(self.mask == True, self.mask) + ax12.imshow(masked, alpha=1, cmap='Reds') + ax12.set(title = 'algorithm input $X_0$') - ax11.imshow(self.M) - ax11.set(title = 'original matrix') - ax12.imshow(self.M * self.mask) - ax12.set(title = 'algorithm input') + fig, (ax21, ax22) = plt.subplots(1, 2, figsize=(16, 5)) ax21.semilogy(self.frob_list) - ax21.set(xlabel='$k$', ylabel='$|| M - X_k ||$', title='distance to M') + ax21.set(xlabel='$k$', ylabel='$|| M - X_k ||$', title='distance to $M$') ax22.semilogy(ch) ax22.set(xlabel='$k$', ylabel='$|| X_{k-1} - X_k ||$', title='change for each iteration') plt.tight_layout() diff --git a/proxtoolbox/proxoperators/MC_ALS.py b/proxtoolbox/proxoperators/MC_ALS.py index 6b9e0b2..18afa15 100644 --- a/proxtoolbox/proxoperators/MC_ALS.py +++ b/proxtoolbox/proxoperators/MC_ALS.py @@ -2,8 +2,6 @@ from proxtoolbox.proxoperators.proxoperator import ProxOperator import numpy as np -#__all__ = ['MC_P_A'] - class MC_ALS(ProxOperator): def __init__(self, experiment): super().__init__(experiment) -- GitLab