From 9c307befdb10446994bda5ef72eededda82d6716 Mon Sep 17 00:00:00 2001 From: Alexandre Gramfort Date: Fri, 25 Dec 2020 15:19:34 +0100 Subject: [PATCH 1/4] WIP : add GPU solver using CUPY --- solvers/python_pgd.py | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/solvers/python_pgd.py b/solvers/python_pgd.py index 9fadec05..39ad09a7 100644 --- a/solvers/python_pgd.py +++ b/solvers/python_pgd.py @@ -1,32 +1,48 @@ +from math import sqrt import numpy as np from scipy import sparse - from benchopt import BaseSolver +from benchopt import safe_import_context + +with safe_import_context() as import_ctx: + import cupy as cp class Solver(BaseSolver): name = 'Python-PGD' # proximal gradient, optionally accelerated + requirements = ['cupy'] + # any parameter defined here is accessible as a class attribute - parameters = {'use_acceleration': [False, True]} + parameters = {'use_acceleration': [False, True], + 'use_gpu': [False, True]} + + def skip(self, X, y, lmbd): + if sparse.issparse(X) and self.use_gpu: + return True, "sparse is not supported with GPU" + return False, None def set_objective(self, X, y, lmbd): + if self.use_gpu: + X, y = cp.array(X), cp.array(y) self.X, self.y, self.lmbd = X, y, lmbd def run(self, n_iter): L = self.compute_lipschitz_cste() + xp = cp if self.use_gpu else np + n_features = self.X.shape[1] - w = np.zeros(n_features) + w = xp.zeros(n_features) if self.use_acceleration: - z = np.zeros(n_features) + z = xp.zeros(n_features) t_new = 1 for _ in range(n_iter): if self.use_acceleration: t_old = t_new - t_new = (1 + np.sqrt(1 + 4 * t_old ** 2)) / 2 + t_new = (1 + sqrt(1 + 4 * t_old ** 2)) / 2 w_old = w.copy() z -= self.X.T @ (self.X @ z - self.y) / L w = self.st(z, self.lmbd / L) @@ -35,10 +51,14 @@ def run(self, n_iter): w -= self.X.T @ (self.X @ w - self.y) / L w = self.st(w, self.lmbd / L) + if self.use_gpu: + w = cp.asnumpy(w) + self.w = w def st(self, w, mu): - w -= np.clip(w, -mu, mu) + xp = cp if self.use_gpu else np + w -= xp.clip(w, -mu, mu) return w def get_result(self): @@ -46,7 +66,8 @@ def get_result(self): def compute_lipschitz_cste(self, max_iter=100): if not sparse.issparse(self.X): - return np.linalg.norm(self.X, ord=2) ** 2 + xp = cp if self.use_gpu else np + return xp.linalg.norm(self.X, ord=2) ** 2 n, m = self.X.shape if n < m: From 165470a62424185540eb35058c99fdc9acaebcc2 Mon Sep 17 00:00:00 2001 From: Badr-MOUFAD Date: Tue, 14 Mar 2023 14:23:28 +0100 Subject: [PATCH 2/4] add requirements --- solvers/python_pgd.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/solvers/python_pgd.py b/solvers/python_pgd.py index 39ad09a7..0766171b 100644 --- a/solvers/python_pgd.py +++ b/solvers/python_pgd.py @@ -9,14 +9,20 @@ with safe_import_context() as import_ctx: import cupy as cp + class Solver(BaseSolver): name = 'Python-PGD' # proximal gradient, optionally accelerated - requirements = ['cupy'] + requirements = [ + 'conda-forge:cupy', + 'conda-forge:cudatoolkit=11.5' + ] # any parameter defined here is accessible as a class attribute - parameters = {'use_acceleration': [False, True], - 'use_gpu': [False, True]} + parameters = { + 'use_acceleration': [False, True], + 'use_gpu': [False, True] + } def skip(self, X, y, lmbd): if sparse.issparse(X) and self.use_gpu: From 20f14d2f316c926a6549896e510418bfd36e83d0 Mon Sep 17 00:00:00 2001 From: mathurinm Date: Tue, 14 Mar 2023 14:46:24 +0100 Subject: [PATCH 3/4] ci trigger From df44a09ebed552977ab364b8f44d1e462ea7cb1d Mon Sep 17 00:00:00 2001 From: Badr-MOUFAD Date: Tue, 14 Mar 2023 16:00:06 +0100 Subject: [PATCH 4/4] callback --> iteration --- solvers/python_pgd.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/solvers/python_pgd.py b/solvers/python_pgd.py index 79d8e19c..14f2e7f1 100644 --- a/solvers/python_pgd.py +++ b/solvers/python_pgd.py @@ -12,7 +12,7 @@ class Solver(BaseSolver): name = 'Python-PGD' # proximal gradient, optionally accelerated - stopping_strategy = "callback" + stopping_strategy = "iteration" requirements = [ 'conda-forge:cupy', @@ -53,7 +53,7 @@ def set_objective(self, X, y, lmbd, fit_intercept): self.X, self.y, self.lmbd = X, y, lmbd self.fit_intercept = fit_intercept - def run(self, callback): + def run(self, n_iter): L = self.compute_lipschitz_constant() xp = cp if self.use_gpu else np @@ -64,7 +64,7 @@ def run(self, callback): z = xp.zeros(n_features) t_new = 1 - while callback(w): + for _ in range(n_iter): if self.use_acceleration: t_old = t_new t_new = (1 + sqrt(1 + 4 * t_old ** 2)) / 2