From c17248ad03fb90f9ba75b94fea7b24d3e39221f2 Mon Sep 17 00:00:00 2001 From: chaithyagr Date: Fri, 9 Apr 2021 15:22:34 +0200 Subject: [PATCH 01/12] Added support for tensorflow --- modopt/base/backend.py | 67 ++++++++++++++++++++++++++-------------- modopt/opt/algorithms.py | 32 ++++--------------- 2 files changed, 50 insertions(+), 49 deletions(-) diff --git a/modopt/base/backend.py b/modopt/base/backend.py index 1f5a7a3a..ecec63b9 100644 --- a/modopt/base/backend.py +++ b/modopt/base/backend.py @@ -21,21 +21,39 @@ import_torch = True # Handle the compatibility with variable -gpu_compatibility = { - 'cupy': False, - 'cupy-cudnn': False, +LIBRARIES = { + 'cupy': None, + 'tensorflow': None, + 'numpy': np, } if util.find_spec('cupy') is not None: try: import cupy as cp - gpu_compatibility['cupy'] = True + LIBRARIES['cupy'] = cp + except ImportError: + pass - if util.find_spec('cupy.cuda.cudnn') is not None: - gpu_compatibility['cupy-cudnn'] = True +if util.find_spec(tensorflow) is not None: + try: + from tensorflow.experimental.numpy as tnp + LIBRARIES['tensorflow'] = tnp except ImportError: pass +from modopt.interface.errors import warn + +def get_backend(backend): + if backend not in LIBRARIES.keys() or LIBRARIES[backend] is None: + warn( + compute_type + + ' backend not possible, please ensure that ' + 'the optional libraries are installed. \n' + 'Reverting to numpy' + ) + backend = 'numpy' + return LIBRARIES['backend'], backend + def get_array_module(input_data): """Get Array Module. @@ -54,13 +72,17 @@ def get_array_module(input_data): The numpy or cupy module """ - if gpu_compatibility['cupy']: - return cp.get_array_module(input_data) - - return np + if LIBRARIES['tensorflow'] is not None: + if isinstance(input_data, LIBRARIES['tensorflow'].ndarray): + return LIBRARIES['tensorflow'] + elif LIBRARIES['cupy'] is not None: + if isinstance(input_data, LIBRARIES['cupy'].ndarray): + return LIBRARIES['cupy'] + else: + return np -def move_to_device(input_data): +def change_backend(input_data, backend='cupy'): """Move data to device. This method moves data from CPU to GPU if we have the @@ -79,16 +101,11 @@ def move_to_device(input_data): """ xp = get_array_module(input_data) - - if xp == cp: + txp, target_backend = get_backend(backend) + if xp == txp: return input_data - - if gpu_compatibility['cupy']: - return cp.array(input_data) - - warnings.warn('Cupy is not installed, cannot move data to GPU') - - return input_data + else: + return txp.array(input_data) def move_to_cpu(input_data): @@ -110,10 +127,14 @@ def move_to_cpu(input_data): """ xp = get_array_module(input_data) - if xp == np: + if xp == LIBRARIES['numpy']: return input_data - - return input_data.get() + elif xp == LIBRARIES['cupy']: + return input_data.get() + elif xp == LIBRARIES['tensorflow']: + return input_data.data.numpy() + else: + raise ValueError('Cant identify the kind of array!') def convert_to_tensor(input_data): diff --git a/modopt/opt/algorithms.py b/modopt/opt/algorithms.py index ccd8fe21..66a2258e 100644 --- a/modopt/opt/algorithms.py +++ b/modopt/opt/algorithms.py @@ -54,11 +54,6 @@ from modopt.opt.cost import costObj from modopt.opt.linear import Identity -try: - import cupy as cp -except ImportError: # pragma: no cover - pass - class SetUp(Observable): r"""Algorithm Set-Up. @@ -92,7 +87,7 @@ def __init__( verbose=False, progress=True, step_size=None, - use_gpu=False, + compute_backend='numpy', **dummy_kwargs, ): @@ -123,20 +118,7 @@ def __init__( ) self.add_observer('cv_metrics', observer) - # Check for GPU - if use_gpu: - if backend.gpu_compatibility['cupy']: - self.xp = cp - else: - warn( - 'CuPy is not installed, cannot run on GPU!' - + 'Running optimization on CPU.', - ) - self.xp = np - use_gpu = False - else: - self.xp = np - self.use_gpu = use_gpu + self.xp, self.compute_backend = get_backend(compute_backend) @property def metrics(self): @@ -184,10 +166,7 @@ def copy_data(self, input_data): Copy of input data """ - if self.use_gpu: - return backend.move_to_device(input_data) - - return self.xp.copy(input_data) + return self.xp.copy(backend.change_backend(input_data, self.compute_backend)) def _check_input_data(self, input_data): """Check input data type. @@ -205,8 +184,9 @@ def _check_input_data(self, input_data): For invalid input type """ - if not isinstance(input_data, self.xp.ndarray): - raise TypeError('Input data must be a numpy array.') + if not isinstance(input_data, self.xp.ndarray) or \ + not isinstance(input_data, np.ndarray): + raise TypeError('Input data must be a numpy array or backend array') def _check_param(self, param_val): """Check algorithm parameters. From 71ce91c29be58f6649efddabbf3c979edbece45c Mon Sep 17 00:00:00 2001 From: chaithyagr Date: Fri, 9 Apr 2021 16:48:03 +0200 Subject: [PATCH 02/12] Updates to get tests passing --- modopt/base/backend.py | 7 +++---- modopt/opt/algorithms.py | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/modopt/base/backend.py b/modopt/base/backend.py index ecec63b9..7c52e1cc 100644 --- a/modopt/base/backend.py +++ b/modopt/base/backend.py @@ -34,9 +34,9 @@ except ImportError: pass -if util.find_spec(tensorflow) is not None: +if util.find_spec('tensorflow') is not None: try: - from tensorflow.experimental.numpy as tnp + import tensorflow.experimental.numpy as tnp LIBRARIES['tensorflow'] = tnp except ImportError: pass @@ -78,8 +78,7 @@ def get_array_module(input_data): elif LIBRARIES['cupy'] is not None: if isinstance(input_data, LIBRARIES['cupy'].ndarray): return LIBRARIES['cupy'] - else: - return np + return np def change_backend(input_data, backend='cupy'): diff --git a/modopt/opt/algorithms.py b/modopt/opt/algorithms.py index 66a2258e..c4b7af6f 100644 --- a/modopt/opt/algorithms.py +++ b/modopt/opt/algorithms.py @@ -118,7 +118,7 @@ def __init__( ) self.add_observer('cv_metrics', observer) - self.xp, self.compute_backend = get_backend(compute_backend) + self.xp, self.compute_backend = backend.get_backend(compute_backend) @property def metrics(self): From 990c8219e6849040e60b7a6aca6f688dd22c0b60 Mon Sep 17 00:00:00 2001 From: chaithyagr Date: Mon, 12 Apr 2021 09:36:57 +0200 Subject: [PATCH 03/12] Or --> And --- modopt/base/backend.py | 2 +- modopt/opt/algorithms.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modopt/base/backend.py b/modopt/base/backend.py index 7c52e1cc..79222e83 100644 --- a/modopt/base/backend.py +++ b/modopt/base/backend.py @@ -52,7 +52,7 @@ def get_backend(backend): 'Reverting to numpy' ) backend = 'numpy' - return LIBRARIES['backend'], backend + return LIBRARIES[backend], backend def get_array_module(input_data): diff --git a/modopt/opt/algorithms.py b/modopt/opt/algorithms.py index c4b7af6f..74fcc2a0 100644 --- a/modopt/opt/algorithms.py +++ b/modopt/opt/algorithms.py @@ -184,7 +184,7 @@ def _check_input_data(self, input_data): For invalid input type """ - if not isinstance(input_data, self.xp.ndarray) or \ + if not isinstance(input_data, self.xp.ndarray) and \ not isinstance(input_data, np.ndarray): raise TypeError('Input data must be a numpy array or backend array') From 110f453a6b876caa421f065ef4a3ea5982d3346b Mon Sep 17 00:00:00 2001 From: chaithyagr Date: Tue, 13 Apr 2021 15:30:20 +0200 Subject: [PATCH 04/12] Moving modopt to allow working with tensorflow --- modopt/base/backend.py | 5 +++-- modopt/math/matrix.py | 14 +++----------- modopt/opt/algorithms.py | 8 +++++--- 3 files changed, 11 insertions(+), 16 deletions(-) diff --git a/modopt/base/backend.py b/modopt/base/backend.py index 79222e83..c3ab844c 100644 --- a/modopt/base/backend.py +++ b/modopt/base/backend.py @@ -15,6 +15,7 @@ try: import torch + from torch.utils.dlpack import from_dlpack, to_dlpack except ImportError: # pragma: no cover import_torch = False else: @@ -170,7 +171,7 @@ def convert_to_tensor(input_data): if xp == np: return torch.Tensor(input_data) - return torch.utils.dlpack.from_dlpack(input_data.toDlpack()).float() + return from_dlpack(input_data.toDlpack()).float() def convert_to_cupy_array(input_data): @@ -202,6 +203,6 @@ def convert_to_cupy_array(input_data): ) if input_data.is_cuda: - return cp.fromDlpack(torch.utils.dlpack.to_dlpack(input_data)) + return cp.fromDlpack(to_dlpack(input_data)) return input_data.detach().numpy() diff --git a/modopt/math/matrix.py b/modopt/math/matrix.py index cb54cebc..15af171e 100644 --- a/modopt/math/matrix.py +++ b/modopt/math/matrix.py @@ -12,12 +12,7 @@ import numpy as np -from modopt.base.backend import get_array_module - -try: - import cupy as cp -except ImportError: # pragma: no cover - pass +from modopt.base.backend import get_array_module, get_backend def gram_schmidt(matrix, return_opt='orthonormal'): @@ -303,7 +298,7 @@ def __init__( data_shape, data_type=float, auto_run=True, - use_gpu=False, + compute_backend='numpy', verbose=False, ): @@ -311,10 +306,7 @@ def __init__( self._data_shape = data_shape self._data_type = data_type self._verbose = verbose - if use_gpu: - self.xp = cp - else: - self.xp = np + self.xp, self.compute_backend = get_backend(compute_backend) if auto_run: self.get_spec_rad() diff --git a/modopt/opt/algorithms.py b/modopt/opt/algorithms.py index 74fcc2a0..55285dd5 100644 --- a/modopt/opt/algorithms.py +++ b/modopt/opt/algorithms.py @@ -134,6 +134,8 @@ def metrics(self, metrics): raise TypeError( 'Metrics must be a dictionary, not {0}.'.format(type(metrics)), ) + else: + self._metrics = metrics def any_convergence_flag(self): """Check convergence flag. @@ -250,7 +252,7 @@ def _check_operator(self, operator): if not any(parent in tree for parent in self._op_parents): message = '{0} does not inherit an operator parent.' - warn(message.format(str(operator.__class__))) + #warn(message.format(str(operator.__class__))) def _compute_metrics(self): """Compute metrics during iteration. @@ -759,8 +761,8 @@ def _update(self): self._z_new = self._x_new # Update old values for next iteration. - self.xp.copyto(self._x_old, self._x_new) - self.xp.copyto(self._z_old, self._z_new) + self._x_old = self.xp.copy(self._x_new) + self._z_old = self.xp.copy(self._z_new) # Update parameter values for next iteration. self._update_param() From bba9db95083930bff46760af770371a8b4ee4d39 Mon Sep 17 00:00:00 2001 From: chaithyagr Date: Tue, 13 Apr 2021 17:37:03 +0200 Subject: [PATCH 05/12] Fix issues with wos --- modopt/math/matrix.py | 4 +++- modopt/opt/algorithms.py | 14 ++++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/modopt/math/matrix.py b/modopt/math/matrix.py index 15af171e..be737f52 100644 --- a/modopt/math/matrix.py +++ b/modopt/math/matrix.py @@ -306,7 +306,9 @@ def __init__( self._data_shape = data_shape self._data_type = data_type self._verbose = verbose - self.xp, self.compute_backend = get_backend(compute_backend) + xp, compute_backend = get_backend(compute_backend) + self.xp = xp + self.compute_backend = compute_backend if auto_run: self.get_spec_rad() diff --git a/modopt/opt/algorithms.py b/modopt/opt/algorithms.py index 55285dd5..4bb96999 100644 --- a/modopt/opt/algorithms.py +++ b/modopt/opt/algorithms.py @@ -118,7 +118,9 @@ def __init__( ) self.add_observer('cv_metrics', observer) - self.xp, self.compute_backend = backend.get_backend(compute_backend) + xp, compute_backend = backend.get_backend(compute_backend) + self.xp = xp + self.compute_backend = compute_backend @property def metrics(self): @@ -168,7 +170,10 @@ def copy_data(self, input_data): Copy of input data """ - return self.xp.copy(backend.change_backend(input_data, self.compute_backend)) + return self.xp.copy(backend.change_backend( + input_data, + self.compute_backend, + )) def _check_input_data(self, input_data): """Check input data type. @@ -188,7 +193,8 @@ def _check_input_data(self, input_data): """ if not isinstance(input_data, self.xp.ndarray) and \ not isinstance(input_data, np.ndarray): - raise TypeError('Input data must be a numpy array or backend array') + raise TypeError('Input data must be a numpy array or ' + 'backend array') def _check_param(self, param_val): """Check algorithm parameters. @@ -252,7 +258,7 @@ def _check_operator(self, operator): if not any(parent in tree for parent in self._op_parents): message = '{0} does not inherit an operator parent.' - #warn(message.format(str(operator.__class__))) + warn(message.format(str(operator.__class__))) def _compute_metrics(self): """Compute metrics during iteration. From b88c73e37f439e90ec586c9706d78d68f4d2538e Mon Sep 17 00:00:00 2001 From: chaithyagr Date: Tue, 13 Apr 2021 18:27:43 +0200 Subject: [PATCH 06/12] Fix all flakes finally! --- modopt/base/backend.py | 69 ++++++++++++++++++++++++++-------------- modopt/opt/algorithms.py | 15 +++++---- 2 files changed, 53 insertions(+), 31 deletions(-) diff --git a/modopt/base/backend.py b/modopt/base/backend.py index c3ab844c..aa257be5 100644 --- a/modopt/base/backend.py +++ b/modopt/base/backend.py @@ -8,25 +8,29 @@ """ -import warnings +import types from importlib import util import numpy as np +from modopt.interface.errors import warn + try: import torch - from torch.utils.dlpack import from_dlpack, to_dlpack + from torch.utils.dlpack import from_dlpack as torch_from_dlpack + from torch.utils.dlpack import to_dlpack as torch_to_dlpack + except ImportError: # pragma: no cover import_torch = False else: import_torch = True # Handle the compatibility with variable -LIBRARIES = { +LIBRARIES = types.MappingProxyType({ 'cupy': None, 'tensorflow': None, 'numpy': np, -} +}) if util.find_spec('cupy') is not None: try: @@ -37,21 +41,34 @@ if util.find_spec('tensorflow') is not None: try: - import tensorflow.experimental.numpy as tnp + from tensorflow.experimental import numpy as tnp LIBRARIES['tensorflow'] = tnp except ImportError: pass -from modopt.interface.errors import warn def get_backend(backend): + """Get backend. + + Returns the backend module for input specified by string + + Parameters + ---------- + backend: str + String holding the backend name. One of `tensorflow`, + `numpy` or `cupy`. + + Returns + ------- + The module for carrying out calculations + """ if backend not in LIBRARIES.keys() or LIBRARIES[backend] is None: - warn( - compute_type + - ' backend not possible, please ensure that ' - 'the optional libraries are installed. \n' - 'Reverting to numpy' + msg = ( + '{0} backend not possible, please ensure that ' + + 'the optional libraries are installed.\n' + + 'Reverting to numpy' ) + warn(msg.format(backend)) backend = 'numpy' return LIBRARIES[backend], backend @@ -85,34 +102,35 @@ def get_array_module(input_data): def change_backend(input_data, backend='cupy'): """Move data to device. - This method moves data from CPU to GPU if we have the - compatibility to do so. It returns the same data if - it is already on GPU. + This method changes the backend of an array + This can be used to copy data to GPU or to CPU Parameters ---------- input_data : numpy.ndarray or cupy.ndarray Input data array to be moved + backend: str, default `cupy` + The backend to use, one among `tensorflow`, `cupy` and + `numpy` Returns ------- - cupy.ndarray - The CuPy array residing on GPU + backend.ndarray + An ndarray of specified backend """ xp = get_array_module(input_data) txp, target_backend = get_backend(backend) if xp == txp: return input_data - else: - return txp.array(input_data) + return txp.array(input_data) def move_to_cpu(input_data): """Move data to CPU. - This method moves data from GPU to CPU.It returns the same data if it is - already on CPU. + This method moves data from GPU to CPU. + It returns the same data if it is already on CPU. Parameters ---------- @@ -124,6 +142,10 @@ def move_to_cpu(input_data): numpy.ndarray The NumPy array residing on CPU + Raises + ------ + ValueError + if the input does not correspond to any array """ xp = get_array_module(input_data) @@ -133,8 +155,7 @@ def move_to_cpu(input_data): return input_data.get() elif xp == LIBRARIES['tensorflow']: return input_data.data.numpy() - else: - raise ValueError('Cant identify the kind of array!') + raise ValueError('Cant identify the kind of array!') def convert_to_tensor(input_data): @@ -171,7 +192,7 @@ def convert_to_tensor(input_data): if xp == np: return torch.Tensor(input_data) - return from_dlpack(input_data.toDlpack()).float() + return torch_from_dlpack(input_data.toDlpack()).float() def convert_to_cupy_array(input_data): @@ -203,6 +224,6 @@ def convert_to_cupy_array(input_data): ) if input_data.is_cuda: - return cp.fromDlpack(to_dlpack(input_data)) + return cp.fromDlpack(torch_to_dlpack(input_data)) return input_data.detach().numpy() diff --git a/modopt/opt/algorithms.py b/modopt/opt/algorithms.py index 4bb96999..9d80579f 100644 --- a/modopt/opt/algorithms.py +++ b/modopt/opt/algorithms.py @@ -132,12 +132,13 @@ def metrics(self, metrics): if isinstance(metrics, type(None)): self._metrics = {} - elif not isinstance(metrics, dict): + return + elif isinstance(metrics, dict): + self._metrics = metrics + else: raise TypeError( 'Metrics must be a dictionary, not {0}.'.format(type(metrics)), ) - else: - self._metrics = metrics def any_convergence_flag(self): """Check convergence flag. @@ -191,10 +192,10 @@ def _check_input_data(self, input_data): For invalid input type """ - if not isinstance(input_data, self.xp.ndarray) and \ - not isinstance(input_data, np.ndarray): - raise TypeError('Input data must be a numpy array or ' - 'backend array') + if not (isinstance(input_data, (self.xp.ndarray, np.ndarray))): + raise TypeError( + 'Input data must be a numpy array or backend array', + ) def _check_param(self, param_val): """Check algorithm parameters. From d9c5004447d913ede8556cdf957061460c25b1f2 Mon Sep 17 00:00:00 2001 From: Chaithya G R Date: Wed, 14 Apr 2021 15:50:46 +0200 Subject: [PATCH 07/12] Update modopt/base/backend.py Co-authored-by: Samuel Farrens --- modopt/base/backend.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modopt/base/backend.py b/modopt/base/backend.py index aa257be5..77de45cd 100644 --- a/modopt/base/backend.py +++ b/modopt/base/backend.py @@ -109,9 +109,9 @@ def change_backend(input_data, backend='cupy'): ---------- input_data : numpy.ndarray or cupy.ndarray Input data array to be moved - backend: str, default `cupy` + backend: str, optional The backend to use, one among `tensorflow`, `cupy` and - `numpy` + `numpy`. Default is `cupy`. Returns ------- From 04feea9d87a8f5a85fc8b6c5c2d27c161ef840c0 Mon Sep 17 00:00:00 2001 From: Chaithya G R Date: Wed, 14 Apr 2021 15:50:52 +0200 Subject: [PATCH 08/12] Update modopt/base/backend.py Co-authored-by: Samuel Farrens --- modopt/base/backend.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modopt/base/backend.py b/modopt/base/backend.py index 77de45cd..66a46b15 100644 --- a/modopt/base/backend.py +++ b/modopt/base/backend.py @@ -155,7 +155,7 @@ def move_to_cpu(input_data): return input_data.get() elif xp == LIBRARIES['tensorflow']: return input_data.data.numpy() - raise ValueError('Cant identify the kind of array!') + raise ValueError('Cannot identify the array type.') def convert_to_tensor(input_data): From c334747c4b09022172c15ecd93d50a5853212a8d Mon Sep 17 00:00:00 2001 From: chaithyagr Date: Wed, 14 Apr 2021 15:52:53 +0200 Subject: [PATCH 09/12] Minute updates to codes --- modopt/base/backend.py | 3 ++- modopt/opt/algorithms.py | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modopt/base/backend.py b/modopt/base/backend.py index 66a46b15..0bc1fe25 100644 --- a/modopt/base/backend.py +++ b/modopt/base/backend.py @@ -60,7 +60,8 @@ def get_backend(backend): Returns ------- - The module for carrying out calculations + module + The module for carrying out calculations """ if backend not in LIBRARIES.keys() or LIBRARIES[backend] is None: msg = ( diff --git a/modopt/opt/algorithms.py b/modopt/opt/algorithms.py index 9d80579f..fea737cb 100644 --- a/modopt/opt/algorithms.py +++ b/modopt/opt/algorithms.py @@ -132,7 +132,6 @@ def metrics(self, metrics): if isinstance(metrics, type(None)): self._metrics = {} - return elif isinstance(metrics, dict): self._metrics = metrics else: From 4e48300abc2665bd6d902fc7fde5016edc24534c Mon Sep 17 00:00:00 2001 From: chaithyagr Date: Wed, 14 Apr 2021 15:55:40 +0200 Subject: [PATCH 10/12] Add dynamic module --- modopt/base/backend.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modopt/base/backend.py b/modopt/base/backend.py index 0bc1fe25..d1d09f59 100644 --- a/modopt/base/backend.py +++ b/modopt/base/backend.py @@ -26,11 +26,11 @@ import_torch = True # Handle the compatibility with variable -LIBRARIES = types.MappingProxyType({ +LIBRARIES = { 'cupy': None, 'tensorflow': None, 'numpy': np, -}) +} if util.find_spec('cupy') is not None: try: From cbe8b277ffbdb1ca73bacba0ad95b0c8e8aa0851 Mon Sep 17 00:00:00 2001 From: chaithyagr Date: Wed, 14 Apr 2021 16:03:47 +0200 Subject: [PATCH 11/12] Fix docu --- modopt/base/backend.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/modopt/base/backend.py b/modopt/base/backend.py index d1d09f59..80bf5893 100644 --- a/modopt/base/backend.py +++ b/modopt/base/backend.py @@ -60,8 +60,10 @@ def get_backend(backend): Returns ------- - module - The module for carrying out calculations + tuple + Returns the module for carrying out calculations and the actual backend + that was reverted towards. If the right libraries are not installed, the + function warns and reverts to `numpy` backend """ if backend not in LIBRARIES.keys() or LIBRARIES[backend] is None: msg = ( From 954ee287b517007937f6b78a9bef82410fb5ffb8 Mon Sep 17 00:00:00 2001 From: chaithyagr Date: Wed, 14 Apr 2021 16:16:32 +0200 Subject: [PATCH 12/12] Fix PEP --- modopt/base/backend.py | 5 ++--- setup.cfg | 3 ++- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/modopt/base/backend.py b/modopt/base/backend.py index 80bf5893..def77151 100644 --- a/modopt/base/backend.py +++ b/modopt/base/backend.py @@ -8,7 +8,6 @@ """ -import types from importlib import util import numpy as np @@ -62,8 +61,8 @@ def get_backend(backend): ------- tuple Returns the module for carrying out calculations and the actual backend - that was reverted towards. If the right libraries are not installed, the - function warns and reverts to `numpy` backend + that was reverted towards. If the right libraries are not installed, + the function warns and reverts to `numpy` backend """ if backend not in LIBRARIES.keys() or LIBRARIES[backend] is None: msg = ( diff --git a/setup.cfg b/setup.cfg index 56a40d12..74fb3f79 100644 --- a/setup.cfg +++ b/setup.cfg @@ -26,7 +26,8 @@ per-file-ignores = #Justification: Needed for keeping package version and current API *__init__.py*: F401,F403,WPS347,WPS410,WPS412 #Todo: Rethink conditional imports - modopt/base/backend.py: WPS229, WPS420 + #Todo: How can we bypass mutable constants? + modopt/base/backend.py: WPS229, WPS420, WPS407 #Todo: Rethink conditional imports modopt/base/observable.py: WPS420,WPS604 #Todo: Check string for log formatting