"""
This module provides exceptions that could be raised in this library.
"""
# *** base exceptions *** #
[docs]class BaseError(Exception):
""" This is the base exception for all exceptions in this package. """
def __init__(self, message):
self.message = message
super().__init__(message)
def __str__(self):
return self.message
# *** matrix exceptions *** #
[docs]class MatrixError(BaseError):
""" An exception related to a matrix. """
def __init__(self, matrix, message=None):
self.matrix = matrix
if message is None:
message = 'Error with matrix with shape {}'.format(self.matrix.shape)
super().__init__(message)
[docs]class MatrixNotSquareError(MatrixError):
""" A matrix is not a square matrix although a square matrix is required. """
def __init__(self, matrix):
message = 'Matrix with shape {} is not a square matrix.'.format(matrix.shape)
super().__init__(matrix, message=message)
[docs]class MatrixNotFiniteError(MatrixError):
""" A matrix has non-finite entries although a finite matrix is required. """
def __init__(self, matrix):
message = 'Matrix with shape {} has not finite entries.'.format(matrix.shape)
super().__init__(matrix, message=message)
[docs]class MatrixSingularError(MatrixError):
""" A matrix is singular although an invertible matrix is required. """
def __init__(self, matrix):
message = 'Matrix with shape {} is singular.'.format(matrix.shape)
super().__init__(matrix, message=message)
[docs]class MatrixNotHermitianError(MatrixError):
""" A matrix is not Hermitian although a Hermitian matrix is required. """
def __init__(self, matrix, i=None, j=None):
message = 'Matrix with shape {} is not Hermitian.'.format(matrix.shape)
if i is not None and j is not None:
if i != j:
message += (' Its value at index ({i}, {j}) is {A_i_j}'
' and its value at index ({j}, {i}) is {A_j_i}.'
).format(i=i, j=j, A_i_j=matrix[i, j], A_j_i=matrix[j, i])
else:
message += (' Its {i}-th diagonal value {A_i_i} is complex.'
).format(i=i, A_i_i=matrix[i, i])
super().__init__(matrix, message=message)
[docs]class MatrixComplexDiagonalValueError(MatrixNotHermitianError):
""" A matrix has complex diagonal values although real diagonal values are required. """
def __init__(self, matrix, i=None):
super().__init__(matrix, matrix, i=i, j=i)
# *** general decomposition exceptions *** #
[docs]class DecompositionError(BaseError):
""" An exception related to a decomposition. """
def __init__(self, decomposition, message=None):
self.decomposition = decomposition
if message is None:
message = 'Error with decomposition {}'.format(self.decomposition)
super().__init__(message)
[docs]class DecompositionNotFiniteError(DecompositionError):
""" A decomposition of a matrix has non-finite entries although a finite matrix is required. """
def __init__(self, decomposition):
message = 'Decomposition {} has non-finite entries.'.format(decomposition)
super().__init__(decomposition, message)
[docs]class DecompositionSingularError(DecompositionError):
""" A decomposition represents a singular matrix although a non-singular matrix is required. """
def __init__(self, decomposition):
message = 'Decomposition {} represents a singular matrix.'.format(decomposition)
super().__init__(decomposition, message)
[docs]class DecompositionInvalidFile(DecompositionError, OSError):
""" An attempt was made to load a decomposition from an invalid file. """
def __init__(self, filename):
self.filename = filename
message = 'File {} is not a valid decomposition file.'.format(filename)
super().__init__(message)
[docs]class DecompositionInvalidDecompositionTypeFile(DecompositionInvalidFile):
""" An attempt was made to load a decomposition from an file in which another decomposition type is stored. """
def __init__(self, filename, type_file, type_needed):
self.filename = filename
message = 'File {} contains a decomposition of type {} but type {} is needed.'.format(filename, type_file, type_needed)
super().__init__(message)
# *** decomposition not calculable exceptions *** #
[docs]class NoDecompositionPossibleError(BaseError):
""" It is not possible to calculate a desired matrix decomposition. """
def __init__(self, base, desired_type):
self.desired_type = desired_type
self.base = base
message = 'Base {} can not be converted to type {}.'.format(base, desired_type)
super().__init__(message)
[docs]class NoDecompositionPossibleWithProblematicSubdecompositionError(NoDecompositionPossibleError):
""" It is not possible to calculate a desired matrix decomposition. Only a subdecompostion could be calculated """
def __init__(self, base, desired_type, problematic_leading_principal_submatrix_index, subdecomposition=None):
super().__init__(base, desired_type)
self.problematic_leading_principal_submatrix_index = problematic_leading_principal_submatrix_index
if subdecomposition is not None:
self.subdecomposition = subdecomposition
[docs]class NoDecompositionPossibleTooManyEntriesError(NoDecompositionPossibleError):
""" The decomposition is not possible for this matrix because it would have too many entries. """
def __init__(self, matrix, desired_type):
super().__init__(matrix, desired_type)
[docs]class NoDecompositionConversionImplementedError(NoDecompositionPossibleError):
""" A decomposition conversion is not implemented for this type. """
def __init__(self, decomposition, desired_type):
super().__init__(decomposition, desired_type)
# *** other exceptions *** #
[docs]class TooManyIterationsError(BaseError):
""" Too many iterations are needed for the calculation. """
def __init__(self, message=None, iteration=None, result=None):
if message is None:
if iteration is None:
message = 'No solution found in maximal number of iterations.'
else:
message = f'No solution found in {iteration} iterations.'
self.message = message
super(message)
if iteration is not None:
self.iteration = iteration
if result is not None:
self.result = result