# Source code for cvxpy.atoms.lambda_max

"""

This file is part of CVXPY.

CVXPY is free software: you can redistribute it and/or modify
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

CVXPY is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with CVXPY.  If not, see <http://www.gnu.org/licenses/>.
"""

from cvxpy.atoms.atom import Atom
from scipy import linalg as LA
import numpy as np
import scipy.sparse as sp

[docs]class lambda_max(Atom):
""" Maximum eigenvalue; :math:\lambda_{\max}(A).
"""

def __init__(self, A):
super(lambda_max, self).__init__(A)

def numeric(self, values):
"""Returns the largest eigenvalue of A.

Requires that A be symmetric.
"""
lo = hi = self.args[0].shape[0]-1
return LA.eigvalsh(values[0], eigvals=(lo, hi))[0]

def _domain(self):
"""Returns constraints describing the domain of the node.
"""
return [self.args[0].H == self.args[0]]

"""Gives the (sub/super)gradient of the atom w.r.t. each argument.

Matrix expressions are vectorized, so the gradient is a matrix.

Args:
values: A list of numeric values for the arguments.

Returns:
A list of SciPy CSC sparse matrices or None.
"""
w, v = LA.eigh(values[0])
d = np.zeros(w.shape)
d[-1] = 1
d = np.diag(d)
D = v.dot(d).dot(v.T)
return [sp.csc_matrix(D.ravel(order='F')).T]

def validate_arguments(self):
"""Verify that the argument A is square.
"""
if not self.args[0].ndim == 2 or self.args[0].shape[0] != self.args[0].shape[1]:
raise ValueError("The argument '%s' to lambda_max must resolve to a square matrix."
% self.args[0].name())

def shape_from_args(self):
"""Returns the (row, col) shape of the expression.
"""
return tuple()

def sign_from_args(self):
"""Returns sign (is positive, is negative) of the expression.
"""
return (False, False)

def is_atom_convex(self):
"""Is the atom convex?
"""
return True

def is_atom_concave(self):
"""Is the atom concave?
"""
return False

def is_incr(self, idx):
"""Is the composition non-decreasing in argument idx?
"""
return False

def is_decr(self, idx):
"""Is the composition non-increasing in argument idx?
"""
return False