Advertisement
makut

Untitled

Nov 20th, 2021
843
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 7.85 KB | None | 0 0
  1. import sys
  2. import numpy as np
  3.  
  4.  
  5. class PermutationMatrix:
  6.     def __init__(self, n):
  7.         if isinstance(n, int):
  8.             self._permutation = list(range(n))
  9.         else:
  10.             permutation = n
  11.             assert sorted(permutation) == list(range(len(permutation)))
  12.             self._permutation = permutation
  13.  
  14.     def inverse(self):
  15.         result = [None] * len(self._permutation)
  16.         for i, elem in enumerate(self._permutation):
  17.             result[elem] = i
  18.  
  19.         assert all(elem is not None for elem in result)
  20.  
  21.         return PermutationMatrix(result)
  22.  
  23.     def add_right_down(self, other):
  24.         adding = len(self._permutation)
  25.         for elem in other._permutation:
  26.             self._permutation.append(elem + adding)
  27.         assert sorted(self._permutation) == list(range(len(self._permutation)))
  28.         return self
  29.  
  30.     def asarray(self):
  31.         n = len(self._permutation)
  32.         result = np.zeros((n, n), dtype=int)
  33.         result[self._permutation, np.arange(n)] = 1
  34.         return result
  35.  
  36.     def apply_to_columns(self, matrix):
  37.         return matrix[:, self._permutation]
  38.  
  39.     def __matmul__(self, other):
  40.         assert len(self._permutation) == len(other._permutation)
  41.         result = []
  42.         for i in range(len(self._permutation)):
  43.             result.append(self._permutation[other._permutation[i]])
  44.         return PermutationMatrix(result)
  45.  
  46.  
  47. def calculate_power(number, power, modulo):
  48.     if power == 0:
  49.         return 1
  50.     if power == 1:
  51.         return number
  52.  
  53.     if power % 2 == 0:
  54.         halfed_result = calculate_power(number, power // 2)
  55.         return (halfed_result ** 2) % modulo
  56.     else:
  57.         decremented_result = calculate_power(number, power - 1)
  58.         return (decremented_result * number) % modulo
  59.  
  60.  
  61. def calculate_inverse(number, modulo):
  62.     assert 0 < number < modulo
  63.     return calculate_power(number, modulo - 2, modulo)
  64.  
  65.  
  66. def decompose_matrix(matrix):
  67.     size = matrix.shape[0]
  68.     matrix_lu = matrix[:size // 2, :size // 2]
  69.     matrix_ru = matrix[:size // 2, size // 2:]
  70.     matrix_ld = matrix[size // 2:, :size // 2]
  71.     matrix_rd = matrix[size // 2:, size // 2:]
  72.     return (matrix_lu, matrix_ru, matrix_ld, matrix_rd)
  73.  
  74.  
  75. def multiply_matrices(first_matrix, second_matrix, modulo):
  76.     size = first_matrix.shape[0]
  77.     assert first_matrix.shape == second_matrix.shape == (size, size)
  78.  
  79.     if size == 1:
  80.         return (first_matrix * second_matrix) % modulo
  81.  
  82.     assert size % 2 == 0
  83.     (
  84.         first_matrix_lu, first_matrix_ru,
  85.         first_matrix_ld, first_matrix_rd
  86.     ) = decompose_matrix(first_matrix)
  87.  
  88.     (
  89.         second_matrix_lu, second_matrix_ru,
  90.         second_matrix_ld, second_matrix_rd
  91.     ) = decompose_matrix(second_matrix)
  92.  
  93.     aux1 = multiply_matrices(first_matrix_lu + first_matrix_rd,
  94.                              second_matrix_lu + second_matrix_rd,
  95.                              modulo)
  96.     aux2 = multiply_matrices(first_matrix_ld + first_matrix_rd,
  97.                              second_matrix_lu,
  98.                              modulo)
  99.     aux3 = multiply_matrices(first_matrix_lu,
  100.                              second_matrix_ru - second_matrix_rd,
  101.                              modulo)
  102.     aux4 = multiply_matrices(first_matrix_rd,
  103.                              second_matrix_ld - second_matrix_lu,
  104.                              modulo)
  105.     aux5 = multiply_matrices(first_matrix_lu + first_matrix_ru,
  106.                              second_matrix_rd,
  107.                              modulo)
  108.     aux6 = multiply_matrices(first_matrix_ld - first_matrix_lu,
  109.                              second_matrix_lu + second_matrix_ru,
  110.                              modulo)
  111.     aux7 = multiply_matrices(first_matrix_ru - first_matrix_rd,
  112.                              second_matrix_ld + second_matrix_rd,
  113.                              modulo)
  114.  
  115.     result_lu = (aux1 + aux4 - aux5 + aux7) % modulo
  116.     result_ru = (aux3 + aux5) % modulo
  117.     result_ld = (aux2 + aux4) % modulo
  118.     result_rd = (aux1 - aux2 + aux3 + aux6) % modulo
  119.  
  120.     result_upper = np.concatenate((result_lu, result_ru), axis=1)
  121.     result_down = np.concatenate((result_ld, result_rd), axis=1)
  122.  
  123.     result = np.concatenate((result_upper, result_down), axis=0)
  124.     assert result.shape == (size, size)
  125.     return result
  126.  
  127.  
  128. def inverse_lower_triagle_matrix(matrix, modulo):
  129.     n = matrix.shape[0]
  130.     assert matrix.shape == (n, n)
  131.     if n == 1:
  132.         return np.array([[calculate_inverse(matrix[0, 0], modulo)]], dtype=int)
  133.  
  134.     assert np.all(matrix[np.triu_indices_from(matrix, 1)] == 0)
  135.     assert n % 2 == 0
  136.  
  137.     B = matrix[:n // 2, :n // 2]
  138.     C = matrix[n // 2:, :n // 2]
  139.     D = matrix[n // 2:, n // 2:]
  140.  
  141.     B_inv = inverse_lower_triagle_matrix(B, modulo)
  142.     D_inv = inverse_lower_triagle_matrix(D, modulo)
  143.  
  144.     result = -multiply_matrices(
  145.         multiply_matrices(D_inv, C, modulo),
  146.         B_inv, modulo
  147.     ) % modulo
  148.     return np.block([
  149.         [B_inv, np.zeros((n // 2, n // 2), dtype=int)],
  150.         [result, D_inv],
  151.     ])
  152.  
  153.  
  154. def multiply_by_blocks(square_matrix, rectangular_matrix, modulo):
  155.     assert square_matrix.shape[0] == square_matrix.shape[1] == rectangular_matrix.shape[0]
  156.     assert rectangular_matrix.shape[1] % rectangular_matrix.shape[0] == 0
  157.  
  158.     matrices = []
  159.     for i in range(0, rectangular_matrix.shape[1], rectangular_matrix.shape[0]):
  160.         submatrix = rectangular_matrix[:, i: i + rectangular_matrix.shape[0]]
  161.         matrices.append(multiply_matrices(square_matrix, submatrix, modulo))
  162.  
  163.     result = np.concatenate(matrices, axis=1)
  164.     assert result.shape == rectangular_matrix.shape
  165.  
  166.     return result
  167.  
  168.  
  169. def lup_decompose_recursive(matrix, modulo=2):
  170.     m, n = matrix.shape
  171.     if m == 1:
  172.         L = np.array([[1]], dtype=int)
  173.  
  174.         non_zero_column = 0
  175.         while matrix[0, non_zero_column] == 0:
  176.             non_zero_column += 1
  177.  
  178.         permutation = list(range(n))
  179.         permutation[0], permutation[non_zero_column] = non_zero_column, 0
  180.         P = PermutationMatrix(permutation)
  181.  
  182.         U = P.apply_to_columns(matrix)
  183.         return L, U, P
  184.  
  185.     B, C = matrix[:m // 2], matrix[m // 2:]
  186.     L1, U1, P1 = lup_decompose_recursive(B, modulo=modulo)
  187.  
  188.     D = P1.inverse().apply_to_columns(C)
  189.     E = U1[:, :m // 2]
  190.     F = D[:, :m // 2]
  191.     E_inv = inverse_lower_triagle_matrix(E.T, modulo=modulo).T
  192.     FE_inv = multiply_matrices(F, E_inv, modulo=modulo)
  193.     FE_invU1 = multiply_by_blocks(FE_inv, U1, modulo=modulo)
  194.  
  195.     G = (D - FE_invU1) % modulo
  196.     assert np.all(G[:, :m // 2] == 0)
  197.     G = G[:, m // 2:]
  198.     L2, U2, P2 = lup_decompose_recursive(G, modulo=modulo)
  199.  
  200.     P3 = PermutationMatrix(m // 2).add_right_down(P2)
  201.     H = P3.inverse().apply_to_columns(U1)
  202.  
  203.     L = np.block([
  204.         [L1, np.zeros((m // 2, m // 2), dtype=int)],
  205.         [FE_inv, L2],
  206.     ])
  207.  
  208.     U = np.concatenate([
  209.         H, np.concatenate([
  210.             np.zeros((m // 2, m // 2), dtype=int), U2,
  211.         ], axis=1)
  212.     ], axis=0)
  213.  
  214.     P = P3 @ P1
  215.  
  216.     return L, U, P
  217.  
  218.  
  219. def lup_decompose(matrix):
  220.     m, n = matrix.shape
  221.  
  222.     required_m = 1
  223.     while required_m < m:
  224.         required_m *= 2
  225.  
  226.     adding = required_m - m
  227.     if adding > 0:
  228.         matrix = np.block([
  229.             [matrix, np.zeros((m, adding), dtype=int)],
  230.             [np.zeros((adding, n), dtype=int), np.eye(adding, dtype=int)],
  231.         ])
  232.  
  233.     L, U, P = lup_decompose_recursive(matrix)
  234.     P = P.asarray()
  235.     if adding > 0:
  236.         L = L[:-adding, :-adding]
  237.         U = U[:-adding, :-adding]
  238.         P = P[:-adding, :-adding]
  239.  
  240.     return L, U, P
  241.  
  242.  
  243. def print_matrix(matrix):
  244.     for line in matrix:
  245.         print(' '.join(map(str, line)))
  246.  
  247.  
  248. if __name__ == '__main__':
  249.     matrix = np.array([list(map(int, line.split())) for line in sys.stdin])
  250.  
  251.     L, U, P = lup_decompose(matrix)
  252.     print_matrix(L)
  253.     print_matrix(U)
  254.     print_matrix(P)
  255.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement