Source code for SimpleGUICS2Pygame.numeric

# -*- coding: latin-1 -*-

"""
numeric module.

Replace the numeric module of CodeSkulptor.

Piece of SimpleGUICS2Pygame.
https://bitbucket.org/OPiMedia/simpleguics2pygame

:license: GPLv3 --- Copyright (C) 2013-2014, 2020-2021 Olivier Pirson
:author: Olivier Pirson --- http://www.opimedia.be/
:version: May 5, 2021
"""

from __future__ import division
from __future__ import print_function

# print('IMPORT', __name__)


from sys import float_info as _SYS_FLOAT_INFO

try:
    from typing import Sequence, Union
except ImportError:
    pass


# Global constant
#################
_EPSILON = _SYS_FLOAT_INFO.epsilon
"""
The default epsilon value.
"""


# Class
########
[docs]class Matrix: """ Matrix (m x n). See http://en.wikipedia.org/wiki/Matrix_%28mathematics%29 . """
[docs] def __init__(self, data, _copy=True): # type: (Sequence[Sequence[Union[int, float]]], bool) -> None """ Create a matrix with the 2-dimensional `data`. If not `_copy` then `data` is directly used without copy. In this case, `data` must be a correct list of list of float. **(Option not available in SimpleGUI of CodeSkulptor.)** :param data: (not empty tuple or list) of (same size tuple or list) of (int or float) :param _copy: bool """ # noqa assert isinstance(_copy, bool), type(_copy) self._data = [] if _copy: assert isinstance(data, (tuple, list)), type(data) assert len(data) >= 1 assert isinstance(data[0], (tuple, list)), type(data[0]) if __debug__: n = len(data[0]) assert n >= 1 for row in data: assert isinstance(row, (tuple, list)), type(row) assert n == len(row), (n, len(row)) for x in row: assert isinstance(x, (int, float)), type(x) self._data = [[float(x) for x in row] for row in data] else: assert isinstance(data, list), type(data) assert len(data) >= 1 assert isinstance(data[0], list), type(data[0]) if __debug__: n = len(data[0]) assert n >= 1 for row in data: assert isinstance(row, list), type(row) assert n == len(row), (n, len(row)) for x in row: assert isinstance(x, float), type(x) self._data = data
[docs] def __add__(self, other): # type: ('Matrix') -> 'Matrix' """ To a matrix (m x n) return the matrix plus other. :param other: Matrix (m x n) :return: Matrix (m x n) """ assert self._nb_lines() == other._nb_lines(), (self._nb_lines(), other._nb_lines()) # pylint: disable=protected-access # noqa assert self._nb_columns() == other._nb_columns(), (self._nb_columns(), other._nb_columns()) # pylint: disable=protected-access # noqa return Matrix([[self[i, j] + other[i, j] for j in range(self._nb_columns())] for i in range(self._nb_lines())], _copy=False)
[docs] def __getitem__(self, i_j): # type: (Sequence[int]) -> float """ Return the value of the (m x n) matrix at row i and column j. :param i_j: (0 <= int < m, 0 <= int < n) or [0 <= int < m, 0 <= int < n] :return: float """ # noqa assert isinstance(i_j, (tuple, list)), type(i_j) assert len(i_j) == 2, len(i_j) assert isinstance(i_j[0], int), type(i_j[0]) assert 0 <= i_j[0] < self._nb_lines(), (i_j[0], self._nb_lines()) assert isinstance(i_j[1], int), type(i_j[1]) assert 0 <= i_j[1] < self._nb_columns(), (i_j[1], self._nb_columns()) return self._data[i_j[0]][i_j[1]]
[docs] def __mul__(self, other): # type: ('Matrix') -> 'Matrix' """ To a matrix (m x k) return the matrix multiply by other. :param other: Matrix (k x n) :return: Matrix (m x n) """ assert self._nb_columns() == other._nb_lines(), (self._nb_columns(), other._nb_lines()) # pylint: disable=protected-access # noqa return Matrix([[sum([self[i, k] * other[k, j] for k in range(self._nb_columns())]) for j in range(other._nb_columns())] # pylint: disable=protected-access # noqa for i in range(self._nb_lines())], _copy=False)
[docs] def __setitem__(self, i_j, value): # type: (Sequence[int], Union[int, float]) -> None """ Change the value of the element at row i and column j, to the (m x n) matrix. :param i_j: (0 <= int < m, 0 <= int < n) or [0 <= int < m, 0 <= int < n] :param value: int or float """ # noqa assert isinstance(i_j, (tuple, list)), type(i_j) assert len(i_j) == 2, len(i_j) assert isinstance(i_j[0], int), type(i_j[0]) assert 0 <= i_j[0] < self._nb_lines(), (i_j[0], self._nb_lines()) assert isinstance(i_j[1], int), type(i_j[1]) assert 0 <= i_j[1] < self._nb_columns(), (i_j[1], self._nb_columns()) assert isinstance(value, (int, float)), type(value) self._data[i_j[0]][i_j[1]] = float(value)
[docs] def __str__(self): # type: () -> str """ Return the string representation of the matrix. :return: string """ return '[{}]'.format('\n '.join([str(row) for row in self._data]))
[docs] def __sub__(self, other): # type: ('Matrix') -> 'Matrix' """ To a matrix (m x n) return the matrix minus other. :param other: Matrix (m x n) :return: Matrix (m x n) """ assert self._nb_lines() == other._nb_lines(), (self._nb_lines(), other._nb_lines()) # pylint: disable=protected-access # noqa assert self._nb_columns() == other._nb_columns(), (self._nb_columns(), other._nb_columns()) # pylint: disable=protected-access # noqa return Matrix([[self[i, j] - other[i, j] for j in range(self._nb_columns())] for i in range(self._nb_lines())], _copy=False)
[docs] def _is_identity(self, epsilon=_EPSILON): # type: (Union[int, float]) -> bool """ If the matrix is an identity matrix then return True, else return False. **(Not available in SimpleGUI of CodeSkulptor.)** :param epsilon: 0 <= (float or int) < 1 :return: bool """ assert isinstance(epsilon, (float, int)), type(epsilon) assert 0 <= epsilon < 1, epsilon n = self._nb_lines() if n != self._nb_columns(): return False for i in range(n): for j in range(n): if abs(self[i, j] - 1 if i == j else self[i, j]) > epsilon: return False return True
[docs] def _is_zero(self, epsilon=_EPSILON): # type: (Union[int, float]) -> bool """ If the matrix is a zeros matrix then return True, else return False. **(Not available in SimpleGUI of CodeSkulptor.)** :param epsilon: 0 <= (float or int) < 1 :return: bool """ assert isinstance(epsilon, (float, int)), type(epsilon) assert 0 <= epsilon < 1, epsilon for i in range(self._nb_lines()): for j in range(self._nb_columns()): if abs(self[i, j]) > epsilon: return False return True
[docs] def _nb_columns(self): # type: () -> int """ Return n for a (m x n) matrix. **(Not available in SimpleGUI of CodeSkulptor.)** :return: int >= 1 """ return len(self._data[0])
[docs] def _nb_lines(self): # type: () -> int """ Return m to a (m x n) matrix. **(Not available in SimpleGUI of CodeSkulptor.)** :return: int >= 1 """ return len(self._data)
[docs] def abs(self): # type: () -> 'Matrix' """ To a matrix (m x n) return the matrix with each element is the absolute value. :return: Matrix (m x n) """ return Matrix([[abs(self[i, j]) for j in range(self._nb_columns())] for i in range(self._nb_lines())], _copy=False)
[docs] def copy(self): # type: () -> 'Matrix' """ Return a copy of the matrix (m x n). :return: Matrix (m x n) """ return Matrix(self._data)
[docs] def getcol(self, j): # type: (int) -> 'Matrix' """ Return the (1 x m) matrix that is a copy of column j of the (m x n) matrix. :param j: 0 <= int < n :return: Matrix (1 x m) """ assert isinstance(j, int), type(j) assert 0 <= j < self._nb_columns(), (j, self._nb_columns()) return Matrix([[row[j] for row in self._data]], _copy=False)
[docs] def getrow(self, i): # type: (int) -> 'Matrix' """ Return the (1 x n) matrix that is a copy of row i of the (m x n) matrix. :param i: 0 <= int < m :return: Matrix (1 x n) """ assert isinstance(i, int), type(i) assert 0 <= i < self._nb_lines(), (i, self._nb_lines()) return Matrix([list(self._data[i])], _copy=False)
[docs] def inverse(self, _epsilon=_EPSILON): # type: (Union[int, float]) -> 'Matrix' """ If the square matrix (n x n) is inversible then return the inverse, else raise an ValueError exception. Algorithm used: Gaussian elimination. See http://en.wikipedia.org/wiki/Gaussian_elimination . :param _epsilon: 0 <= (float or int) < 1 **(Option not available in SimpleGUI of CodeSkulptor.)** :return: Matrix (n x n) :raise: ValueError if the matrix is not inversible """ # noqa assert self._nb_lines() == self._nb_columns(), (self._nb_lines(), self._nb_columns()) assert isinstance(_epsilon, (float, int)), type(_epsilon) assert 0 <= _epsilon < 1, _epsilon n = self._nb_columns() mat = self.copy() inv = identity(n) # Diagonalize for i_with_pivot in range(n): for i in range(n): if i != i_with_pivot: diagonal = mat[i_with_pivot, i_with_pivot] if abs(diagonal) <= _epsilon: raise ValueError('matrix has no inverse') factor = mat[i, i_with_pivot] / diagonal for j in range(n): mat[i, j] -= mat[i_with_pivot, j] * factor mat[i, i_with_pivot] = 0 for j in range(n): inv[i, j] -= inv[i_with_pivot, j] * factor # Scale rows for i in range(n): for j in range(n): diagonal = mat[i, i] if abs(diagonal) <= _epsilon: raise ValueError('matrix has no inverse') inv[i, j] /= diagonal if all([abs(x) <= _epsilon for x in reversed(mat._data[-1])]): # pylint: disable=protected-access # noqa raise ValueError('matrix has no inverse') return inv
[docs] def scale(self, factor): # type: (Union[int, float]) -> 'Matrix' """ To a matrix (m x n) return the matrix with each element multiply by factor. **(Method available in CodeSkulptor2 but not in CodeSkulptor3.)** :param factor: int or float :return: Matrix (m x n) """ assert isinstance(factor, (int, float)), type(factor) return Matrix([[self[i, j] * factor for j in range(self._nb_columns())] for i in range(self._nb_lines())], _copy=False)
[docs] def shape(self): # type: () -> tuple """ Return (m, n) to a matrix (m x n). :return: (int >= 1, int >= 1) """ return (self._nb_lines(), self._nb_columns())
[docs] def summation(self): # type: () -> float """ Return the sum of all the elements of the matrix. :return: float """ return sum([sum(row) for row in self._data])
[docs] def transpose(self): # type: () -> 'Matrix' """ Return the transposition of the matrix (m x n). :return: Matrix (n x m) """ return Matrix([[self[i, j] for i in range(self._nb_lines())] for j in range(self._nb_columns())], _copy=False)
# Private function ##################
[docs]def _zero(m, n): # type: (int, int) -> Matrix """ Return a (`m` x `n`) zeros matrix. :param m: int >= 1 :param n: int >= 1 :return: Matrix (`m` x `n`) """ assert isinstance(m, int), type(m) assert m >= 1, m assert isinstance(n, int), type(n) assert n >= 1, n return Matrix([[0.0 for j in range(n)] for i in range(m)], _copy=False)
# Function ##########
[docs]def identity(size): # type: (int) -> Matrix """ Return a (`size` x `size`) identity matrix. :param size: int >= 1 :return: Matrix (`size` x `size`) """ assert isinstance(size, int), type(size) assert size >= 1, size return Matrix([[(1.0 if i == j else 0.0) for j in range(size)] for i in range(size)], _copy=False)
# Clean types use by static checking if 'Sequence' in dir(): del Sequence del Union