Coverage for local_installation_linux/mumott/optimization/regularizers/l2_norm.py: 86%

32 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2024-08-11 23:08 +0000

1import numpy as np 

2from numpy.typing import NDArray 

3 

4from mumott.optimization.regularizers.base_regularizer import Regularizer 

5import logging 

6logger = logging.getLogger(__name__) 

7 

8 

9class L2Norm(Regularizer): 

10 

11 r"""Regularizes using the :math:`L_2` norm of the coefficient vector, also known as the Euclidean norm. 

12 Suitable for most representations, including non-local ones. Tends to reduce large values, 

13 and often leads to fast convergence. 

14 

15 The :math:`L_2` norm of a vector :math:`x` is given by :math:`\sum{\vert x \vert^2}`. 

16 

17 See also `the Wikipedia article on the Euclidean norm 

18 <https://en.wikipedia.org/wiki/Euclidean_space#Euclidean_norm>`_ 

19 """ 

20 

21 def __init__(self): 

22 super().__init__() 

23 

24 def get_regularization_norm(self, 

25 coefficients: NDArray[float], 

26 get_gradient: bool = False, 

27 gradient_part: str = None) -> dict[str, NDArray[float]]: 

28 """Retrieves the :math:`L_2` norm, of the coefficient vector. Appropriate for 

29 use with scalar coefficients or local basis sets. 

30 

31 Parameters 

32 ---------- 

33 coefficients 

34 An ``np.ndarray`` of values, with shape ``(X, Y, Z, W)``, where 

35 the last channel contains, e.g., tensor components. 

36 get_gradient 

37 If ``True``, returns a ``'gradient'`` of the same shape as :attr:`coefficients`. 

38 Otherwise, the entry ``'gradient'`` will be ``None``. Defaults to ``False``. 

39 gradient_part 

40 Used for the zonal harmonics (ZH) reconstructions to determine what part of the gradient is 

41 being calculated. Default is ``None``. 

42 If a flag is passed in (``'full'``, ``'angles'``, ``'coefficients'``), 

43 we assume that the ZH workflow is used and that the last two coefficients are Euler angles, 

44 which should not be regularized by this regularizer. 

45 

46 Returns 

47 ------- 

48 A dictionary with two entries, ``regularization_norm`` and ``gradient``. 

49 """ 

50 

51 result = dict(regularization_norm=None, gradient=None) 

52 if get_gradient: 

53 

54 if gradient_part is None: 

55 result['gradient'] = coefficients 

56 elif gradient_part in ('full', 'coefficients'): 

57 result['gradient'] = np.copy(coefficients) 

58 result['gradient'][..., -2:] = 0 

59 elif gradient_part in ('angles'): 59 ↛ 62line 59 didn't jump to line 62, because the condition on line 59 was never false

60 result['gradient'] = np.zeros(coefficients.shape) 

61 else: 

62 logger.warning('Unexpected argument given for gradient part.') 

63 raise ValueError 

64 

65 if gradient_part is None: 

66 result['regularization_norm'] = np.sum(coefficients ** 2) 

67 elif gradient_part in ('full', 'coefficients', 'angles'): 67 ↛ 70line 67 didn't jump to line 70, because the condition on line 67 was never false

68 result['regularization_norm'] = np.sum(coefficients[..., :-2] ** 2) 

69 else: 

70 raise ValueError('Unexpected argument given for gradient part.') 

71 

72 return result 

73 

74 @property 

75 def _function_as_str(self) -> str: 

76 return 'R(x) = lambda * abs(x) ** 2' 

77 

78 @property 

79 def _function_as_tex(self) -> str: 

80 return r'$R(\vec{x}) = \lambda \Vert \vec{x} \Vert_2^2$'