3636
3737 proj_positive
3838 proj_b2
39+ proj_lineq
40+ proj_spsd
3941
4042**Miscellaneous**
4143
@@ -803,24 +805,13 @@ class proj(func):
803805 See generic attributes descriptions of the
804806 :class:`pyunlocbox.functions.func` base class.
805807
806- Parameters
807- ----------
808- epsilon : float, optional
809- The radius of the ball. Default is 1.
810- method : {'FISTA', 'ISTA'}, optional
811- The method used to solve the problem. It can be 'FISTA' or 'ISTA'.
812- Default is 'FISTA'.
813-
814808 Notes
815809 -----
816810 * All indicator functions (projections) evaluate to zero by definition.
817811
818812 """
819-
820- def __init__ (self , epsilon = 1 , method = 'FISTA' , ** kwargs ):
813+ def __init__ (self , ** kwargs ):
821814 super (proj , self ).__init__ (** kwargs )
822- self .epsilon = epsilon
823- self .method = method
824815
825816 def _eval (self , x ):
826817 # Matlab version returns a small delta to avoid division by 0 when
@@ -835,9 +826,9 @@ class proj_positive(proj):
835826 r"""
836827 Projection on the positive octant (eval, prox).
837828
838- This function is the indicator function :math:`i_S(z)` of the set S which
839- is zero if `z` is in the set and infinite otherwise. The set S is defined
840- by :math:`\left\{z \ in \mathbb{R}^N \mid z \leq 0 \right\}` .
829+ This function is the indicator function :math:`i_S(z)` of the set
830+ :math:`S = \left\{z \ in \mathbb{R}^N \mid z \leq 0 \right\}`
831+ that is zero if :math:`z` is in the set and infinite otherwise .
841832
842833 See generic attributes descriptions of the
843834 :class:`pyunlocbox.functions.proj` base class. Note that the constructor
@@ -867,19 +858,78 @@ def _prox(self, x, T):
867858 return np .clip (x , 0 , np .inf )
868859
869860
861+ class proj_spsd (proj ):
862+ r"""
863+ Projection on symmetric positive semi-definite matrices (eval, prox).
864+
865+ This function is the indicator function :math:`i_S(M)` of the set
866+ :math:`S = \left\{M \in \mathbb{R}^{N \times N}
867+ \mid M \succeq 0, M=M^T \right\}`
868+ that is zero if :math:`M` is in the set and infinite otherwise.
869+
870+ See generic attributes descriptions of the
871+ :class:`pyunlocbox.functions.proj` base class. Note that the constructor
872+ takes keyword-only parameters.
873+
874+ Notes
875+ -----
876+ * The evaluation of this function is zero.
877+
878+ Examples
879+ --------
880+ >>> from pyunlocbox import functions
881+ >>> f = functions.proj_spsd()
882+ >>> A = np.array([[0, -1] , [-1, 1]])
883+ >>> A = (A + A.T) / 2 # Symmetrize the matrix.
884+ >>> np.linalg.eig(A)[0]
885+ array([-0.61803399, 1.61803399])
886+ >>> f.eval(A)
887+ 0
888+ >>> Aproj = f.prox(A, 0)
889+ >>> np.linalg.eig(Aproj)[0]
890+ array([0. , 1.61803399])
891+
892+ """
893+ def __init__ (self , ** kwargs ):
894+ # Constructor takes keyword-only parameters to prevent user errors.
895+ super (proj_spsd , self ).__init__ (** kwargs )
896+
897+ def _prox (self , x , T ):
898+ isreal = np .isreal (x ).all ()
899+
900+ # 1. make it symmetric.
901+ sol = (x + np .conj (x .T )) / 2
902+
903+ # 2. make it semi-positive.
904+ D , V = np .linalg .eig (sol )
905+ D = np .real (D )
906+ if isreal :
907+ V = np .real (V )
908+ D = np .clip (D , 0 , np .inf )
909+ sol = V @ np .diag (D ) @ np .conj (V .T )
910+ return sol
911+
912+
870913class proj_b2 (proj ):
871914 r"""
872915 Projection on the L2-ball (eval, prox).
873916
874- This function is the indicator function :math:`i_S(z)` of the set S which
875- is zero if `z` is in the set and infinite otherwise. The set S is defined
876- by :math:`\left\{z \in \mathbb{R}^N \mid \|A(z)-y\|_2 \leq \epsilon
877- \right\}`.
917+ This function is the indicator function :math:`i_S(z)` of the set
918+ :math:`S= \left\{z \in \mathbb{R}^N \mid \|Az-y\|_2 \leq \epsilon \right\}`
919+ that is zero if :math:`z` is in the set and infinite otherwise.
878920
879921 See generic attributes descriptions of the
880922 :class:`pyunlocbox.functions.proj` base class. Note that the constructor
881923 takes keyword-only parameters.
882924
925+ Parameters
926+ ----------
927+ epsilon : float, optional
928+ The radius of the ball. Default is 1.
929+ method : {'FISTA', 'ISTA'}, optional
930+ The method used to solve the problem. It can be 'FISTA' or 'ISTA'.
931+ Default is 'FISTA'.
932+
883933 Notes
884934 -----
885935 * The `tol` parameter is defined as the tolerance for the projection on the
@@ -893,6 +943,10 @@ class proj_b2(proj):
893943 :math:`\|A(z)-y\|_2 \leq \epsilon`. It is thus a projection of the vector
894944 `x` onto an L2-ball of diameter `epsilon`.
895945
946+ See Also
947+ --------
948+ proj_lineq : use instead of ``epsilon=0``
949+
896950 Examples
897951 --------
898952 >>> from pyunlocbox import functions
@@ -904,10 +958,11 @@ class proj_b2(proj):
904958 array([1.70710678, 1.70710678])
905959
906960 """
907-
908- def __init__ (self , ** kwargs ):
961+ def __init__ (self , epsilon = 1 , method = 'FISTA' , ** kwargs ):
909962 # Constructor takes keyword-only parameters to prevent user errors.
910963 super (proj_b2 , self ).__init__ (** kwargs )
964+ self .epsilon = epsilon
965+ self .method = method
911966
912967 def _prox (self , x , T ):
913968
@@ -993,6 +1048,78 @@ def _prox(self, x, T):
9931048 return sol
9941049
9951050
1051+ class proj_lineq (proj ):
1052+ r"""
1053+ Projection on the plane satisfying the linear equality Az = y (eval, prox).
1054+
1055+ This function is the indicator function :math:`i_S(z)` of the set
1056+ :math:`S = \left\{z \in \mathbb{R}^N \mid Az = y \right\}`
1057+ that is zero if :math:`z` is in the set and infinite otherwise.
1058+
1059+ The proximal operator is
1060+ :math:`\operatorname{arg\,min}_z \| z - x \|_2 \text{ s.t. } Az = y`.
1061+
1062+ See generic attributes descriptions of the
1063+ :class:`pyunlocbox.functions.proj` base class. Note that the constructor
1064+ takes keyword-only parameters.
1065+
1066+ Notes
1067+ -----
1068+ * A parameter `pinvA`, the pseudo-inverse of `A`, must be provided if the
1069+ parameter `A` is provided as an operator/callable (not a matrix).
1070+ * The evaluation of this function is zero.
1071+
1072+ See Also
1073+ --------
1074+ proj_b2 : quadratic case
1075+
1076+ Examples
1077+ --------
1078+ >>> from pyunlocbox import functions
1079+ >>> import numpy as np
1080+ >>> x = np.array([0, 0])
1081+ >>> A = np.array([[1, 1]])
1082+ >>> pinvA = np.linalg.pinv(A)
1083+ >>> y = np.array([1])
1084+ >>> f = functions.proj_lineq(A=A, pinvA=pinvA, y=y)
1085+ >>> sol = f.prox(x, 0)
1086+ >>> sol
1087+ array([0.5, 0.5])
1088+ >>> np.abs(A.dot(sol) - y) < 1e-15
1089+ array([ True])
1090+
1091+ """
1092+ def __init__ (self , A = None , pinvA = None , ** kwargs ):
1093+ # Constructor takes keyword-only parameters to prevent user errors.
1094+ super (proj_lineq , self ).__init__ (A = A , ** kwargs )
1095+
1096+ if pinvA is None :
1097+ if A is None :
1098+ print ("Are you sure about the parameters?" +
1099+ "The projection will return y." )
1100+ self .pinvA = lambda x : x
1101+ else :
1102+ if callable (A ):
1103+ raise ValueError (
1104+ "Provide A as a numpy array or provide pinvA." )
1105+ else :
1106+ # Transform matrix form to operator form.
1107+ self ._pinvA = np .linalg .pinv (A )
1108+ self .pinvA = lambda x : self ._pinvA .dot (x )
1109+ else :
1110+ if callable (pinvA ):
1111+ self .pinvA = pinvA
1112+ else :
1113+ self .pinvA = lambda x : pinvA .dot (x )
1114+
1115+ def _prox (self , x , T ):
1116+ # Applying the projection formula.
1117+ # (for now, only the non scalable version)
1118+ residue = self .A (x ) - self .y ()
1119+ sol = x - self .pinvA (residue )
1120+ return sol
1121+
1122+
9961123class structured_sparsity (func ):
9971124 r"""
9981125 Structured sparsity (eval, prox).
0 commit comments