Abstract Matrices ================= An AbstractMatrix is an array where the elements all come from a single Ring or Field. .. code:: ipython3 import finite_algebras as alg import numpy as np from abstract_matrix import AbstractMatrix import os aa_path = os.path.join(os.getenv("PYPROJ"), "abstract_algebra") alg_dir = os.path.join(aa_path, "Algebras") Algebras for Examples & Tests ----------------------------- Get the Built-In Examples ~~~~~~~~~~~~~~~~~~~~~~~~~ .. code:: ipython3 >>> ex = alg.Examples(alg_dir) .. parsed-literal:: ====================================================================== Example Algebras ---------------------------------------------------------------------- 17 example algebras are available. Use "Examples[INDEX]" to retrieve a specific example, where INDEX is the first number on each line below: ---------------------------------------------------------------------- 0: A4 -- Alternating group on 4 letters (AKA Tetrahedral group) 1: D3 -- https://en.wikipedia.org/wiki/Dihedral_group_of_order_6 2: D4 -- Dihedral group on four vertices 3: Pinter29 -- Non-abelian group, p.29, 'A Book of Abstract Algebra' by Charles C. Pinter 4: RPS -- Rock, Paper, Scissors Magma 5: S3 -- Symmetric group on 3 letters 6: S3X -- Another version of the symmetric group on 3 letters 7: V4 -- Klein-4 group 8: Z4 -- Cyclic group of order 4 9: F4 -- Field with 4 elements (from Wikipedia) 10: mag_id -- Magma with Identity 11: Example 1.4.1 -- See: Groupoids and Smarandache Groupoids by W. B. Vasantha Kandasamy 12: Ex6 -- Example 6: http://www-groups.mcs.st-andrews.ac.uk/~john/MT4517/Lectures/L3.html 13: Q8 -- Quaternion Group 14: SD16 -- Semidihedral group of order 16 15: A5 -- Alternating group on 5 letters 16: F2 -- Field with 2 elements from paper: 236w06fields.pdf ====================================================================== Two Very Small Fields from the Built-In Examples ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code:: ipython3 >>> f4 = ex[9] >>> f4.about() .. parsed-literal:: ** Field ** Name: F4 Instance ID: 4608036544 Description: Field with 4 elements (from Wikipedia) Order: 4 Identity: '0' Commutative? Yes Cyclic?: Yes Generators: ['1+a', 'a'] Elements: Index Name Inverse Order 0 '0' '0' 1 1 '1' '1' 2 2 'a' 'a' 2 3 '1+a' '1+a' 2 Cayley Table (showing indices): [[0, 1, 2, 3], [1, 0, 3, 2], [2, 3, 0, 1], [3, 2, 1, 0]] Mult. Identity: '1' Mult. Commutative? Yes Zero Divisors: None Multiplicative Cayley Table (showing indices): [[0, 0, 0, 0], [0, 1, 2, 3], [0, 2, 3, 1], [0, 3, 1, 2]] A Small Powerset Ring ~~~~~~~~~~~~~~~~~~~~~ .. code:: ipython3 >>> ps3 = alg.generate_powerset_ring(3) >>> ps3.about() .. parsed-literal:: ** Ring ** Name: PSRing3 Instance ID: 4607553808 Description: Autogenerated Ring on powerset of {0, 1, 2} w/ symm. diff. (add) & intersection (mult) Order: 8 Identity: '{}' Commutative? Yes Cyclic?: No Elements: Index Name Inverse Order 0 '{}' '{}' 1 1 '{0}' '{0}' 2 2 '{1}' '{1}' 2 3 '{2}' '{2}' 2 4 '{0, 1}' '{0, 1}' 2 5 '{0, 2}' '{0, 2}' 2 6 '{1, 2}' '{1, 2}' 2 7 '{0, 1, 2}' '{0, 1, 2}' 2 Cayley Table (showing indices): [[0, 1, 2, 3, 4, 5, 6, 7], [1, 0, 4, 5, 2, 3, 7, 6], [2, 4, 0, 6, 1, 7, 3, 5], [3, 5, 6, 0, 7, 1, 2, 4], [4, 2, 1, 7, 0, 6, 5, 3], [5, 3, 7, 1, 6, 0, 4, 2], [6, 7, 3, 2, 5, 4, 0, 1], [7, 6, 5, 4, 3, 2, 1, 0]] Mult. Identity: '{0, 1, 2}' Mult. Commutative? Yes Zero Divisors: ['{0}', '{1}', '{2}', '{0, 1}', '{0, 2}', '{1, 2}'] Multiplicative Cayley Table (showing indices): [[0, 0, 0, 0, 0, 0, 0, 0], [0, 1, 0, 0, 1, 1, 0, 1], [0, 0, 2, 0, 2, 0, 2, 2], [0, 0, 0, 3, 0, 3, 3, 3], [0, 1, 2, 0, 4, 1, 2, 4], [0, 1, 0, 3, 1, 5, 3, 5], [0, 0, 2, 3, 2, 3, 6, 6], [0, 1, 2, 3, 4, 5, 6, 7]] Some Test Matrices ------------------ Abstract Matrices over a Ring ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``__repr__`` does not produce a full representation here, since AbstractMatrix contains a NumPy ndarray, along with a Finite Algebra (Ring or Field), and it would be too cumbersome to include the entire Algebra. Consequently, ``__repr__`` only produces a copy-and-paste-able version of the ndarray. .. code:: ipython3 >>> arr0 = [['{1, 2}', '{0, 1, 2}'], >>> ['{0, 2}', '{}']] >>> mat0 = AbstractMatrix(arr0, ps3) >>> mat0 .. parsed-literal:: [['{1, 2}', '{0, 1, 2}'], ['{0, 2}', '{}']] ``__str__`` produces a “pretty print” string version of the matrix’s array. NOTE: The elements in the array are delimited by spaces, not commas, so it cannot be easily copied-and-pasted. .. code:: ipython3 >>> print(mat0) # Here, only spaces delimit the lists and the elements they contain. .. parsed-literal:: [['{1, 2}' '{0, 1, 2}'] ['{0, 2}' '{}']] .. code:: ipython3 >>> str(mat0) .. parsed-literal:: "[['{1, 2}' '{0, 1, 2}']\n ['{0, 2}' '{}']]" .. code:: ipython3 >>> print(str(mat0)) .. parsed-literal:: [['{1, 2}' '{0, 1, 2}'] ['{0, 2}' '{}']] .. code:: ipython3 >>> mat0.array .. parsed-literal:: array([['{1, 2}', '{0, 1, 2}'], ['{0, 2}', '{}']], dtype='>> arr1 = [['{1, 2}', '{0, 1, 2}', '{0, 2}'], >>> ['{0, 2}', '{}', '{1}'], >>> [ '{0}', '{1}', '{0, 1}']] >>> mat1 = AbstractMatrix(arr1, ps3) >>> mat1 .. parsed-literal:: [['{1, 2}', '{0, 1, 2}', '{0, 2}'], ['{0, 2}', '{}', '{1}'], ['{0}', '{1}', '{0, 1}']] .. code:: ipython3 >>> arr2 = [['{0, 1}', '{0, 2}', '{1, 2}'], >>> ['{0}' , '{1, 2}', '{2}'], >>> ['{0, 1}', '{}', '{}']] >>> mat2 = AbstractMatrix(arr2, ps3) >>> mat2 .. parsed-literal:: [['{0, 1}', '{0, 2}', '{1, 2}'], ['{0}', '{1, 2}', '{2}'], ['{0, 1}', '{}', '{}']] .. code:: ipython3 >>> arr3 = [[ '{}', '{0, 1, 2}', '{0, 1, 2}', '{}'], >>> ['{0, 1}', '{0, 2}', '{1, 2}', '{2}'], >>> ['{0, 2}', '{}', '{}', '{0, 1}'], >>> [ '{1}', '{0}', '{0, 2}', '{}']] >>> mat3 = AbstractMatrix(arr3, ps3) >>> mat3 .. parsed-literal:: [['{}', '{0, 1, 2}', '{0, 1, 2}', '{}'], ['{0, 1}', '{0, 2}', '{1, 2}', '{2}'], ['{0, 2}', '{}', '{}', '{0, 1}'], ['{1}', '{0}', '{0, 2}', '{}']] .. code:: ipython3 >>> arr4 = [['{1, 2}']] >>> mat4 = AbstractMatrix(arr4, ps3) >>> mat4 .. parsed-literal:: [['{1, 2}']] Abstract Matrix over a Field ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code:: ipython3 >>> arr5 = [[ '0', '1', 'a'], >>> [ '1', 'a', '1+a'], >>> ['1+a', '0', '1']] >>> mat5 = AbstractMatrix(arr5, f4) >>> mat5 .. parsed-literal:: [['0', '1', 'a'], ['1', 'a', '1+a'], ['1+a', '0', '1']] .. code:: ipython3 >>> arr6 = [['1+a', '0', 'a'], >>> ['1+a', '1+a', '0'], >>> [ '1', '1', '0']] >>> mat6 = AbstractMatrix(arr6, f4) >>> mat6 .. parsed-literal:: [['1+a', '0', 'a'], ['1+a', '1+a', '0'], ['1', '1', '0']] Matrix Addition & Subtraction ----------------------------- .. code:: ipython3 >>> mat1 + mat2 .. parsed-literal:: [['{0, 2}', '{1}', '{0, 1}'], ['{2}', '{1, 2}', '{1, 2}'], ['{1}', '{1}', '{0, 1}']] .. code:: ipython3 >>> try: >>> mat2p3 = mat2 + mat3 >>> print(mat2p3) >>> except Exception as exc: >>> print(exc) .. parsed-literal:: The array shapes are not equal: (3, 3) != (4, 4) .. code:: ipython3 >>> mat5 + mat6 .. parsed-literal:: [['1+a', '1', '0'], ['a', '1', '1+a'], ['a', '1', '1']] .. code:: ipython3 >>> mat2 - mat2 .. parsed-literal:: [['{}', '{}', '{}'], ['{}', '{}', '{}'], ['{}', '{}', '{}']] .. code:: ipython3 >>> try: >>> mat2m5 = mat2 - mat5 >>> print(mat2m5) >>> except Exception as exc: >>> print(exc) .. parsed-literal:: The array algebras must be equal Matrix Multiplication --------------------- .. code:: ipython3 >>> mat1 * mat2 .. parsed-literal:: [['{1}', '{1}', '{1}'], ['{0, 1}', '{0, 2}', '{2}'], ['{1}', '{0, 1}', '{}']] .. code:: ipython3 >>> mat2 * mat1 .. parsed-literal:: [['{0, 1, 2}', '{0}', '{0, 1}'], ['{2}', '{0}', '{0, 1}'], ['{1}', '{0, 1}', '{0}']] .. code:: ipython3 >>> try: >>> mat2x3 = mat2 * mat3 >>> print(mat2x3) >>> except Exception as exc: >>> print(exc) .. parsed-literal:: The array shapes are incompatible: 3 columns vs 4 rows .. code:: ipython3 >>> mat5 * mat6 .. parsed-literal:: [['1', '1', '0'], ['1', 'a', 'a'], ['1+a', '1', '1']] Abstract Matrix of “Zeros” -------------------------- .. code:: ipython3 >>> matz = AbstractMatrix.zeros((2, 3), ps3) >>> matz .. parsed-literal:: [['{}', '{}', '{}'], ['{}', '{}', '{}']] .. code:: ipython3 >>> matf = AbstractMatrix.zeros((3, 3), f4) >>> matf .. parsed-literal:: [['0', '0', '0'], ['0', '0', '0'], ['0', '0', '0']] Abstract Identity Matrix ------------------------ .. code:: ipython3 >>> AbstractMatrix.identity(4, ps3) .. parsed-literal:: [['{0, 1, 2}', '{}', '{}', '{}'], ['{}', '{0, 1, 2}', '{}', '{}'], ['{}', '{}', '{0, 1, 2}', '{}'], ['{}', '{}', '{}', '{0, 1, 2}']] .. code:: ipython3 >>> mat1 .. parsed-literal:: [['{1, 2}', '{0, 1, 2}', '{0, 2}'], ['{0, 2}', '{}', '{1}'], ['{0}', '{1}', '{0, 1}']] .. code:: ipython3 >>> id3 = AbstractMatrix.identity(3, ps3) >>> mat1 * id3 == mat1 .. parsed-literal:: True .. code:: ipython3 >>> id3 * mat1 == mat1 .. parsed-literal:: True .. code:: ipython3 >>> id3f = AbstractMatrix.identity(3, f4) >>> id3f .. parsed-literal:: [['1', '0', '0'], ['0', '1', '0'], ['0', '0', '1']] .. code:: ipython3 >>> mat5 * id3f - mat5 .. parsed-literal:: [['0', '0', '0'], ['0', '0', '0'], ['0', '0', '0']] Random Abstract Matrix ---------------------- .. code:: ipython3 >>> AbstractMatrix.random((3, 3), ps3) .. parsed-literal:: [['{1, 2}', '{1}', '{}'], ['{0, 2}', '{2}', '{0}'], ['{0, 1}', '{2}', '{}']] .. code:: ipython3 >>> AbstractMatrix.random((3, 3), f4) .. parsed-literal:: [['a', '1+a', '0'], ['0', '1+a', '1+a'], ['a', '1+a', 'a']] Get/Set Matrix Element Values ----------------------------- .. code:: ipython3 >>> mat2 .. parsed-literal:: [['{0, 1}', '{0, 2}', '{1, 2}'], ['{0}', '{1, 2}', '{2}'], ['{0, 1}', '{}', '{}']] .. code:: ipython3 >>> mat2[1, 2] .. parsed-literal:: np.str_('{2}') .. code:: ipython3 >>> mat2[1, 2] = '{0, 1, 2}' >>> mat2 .. parsed-literal:: [['{0, 1}', '{0, 2}', '{1, 2}'], ['{0}', '{1, 2}', '{0, 1, 2}'], ['{0, 1}', '{}', '{}']] .. code:: ipython3 >>> mat2[1, 2] .. parsed-literal:: np.str_('{0, 1, 2}') Put mat2 back the way it was… .. code:: ipython3 >>> mat2[1, 2] = '{2}' >>> mat2 .. parsed-literal:: [['{0, 1}', '{0, 2}', '{1, 2}'], ['{0}', '{1, 2}', '{2}'], ['{0, 1}', '{}', '{}']] Matrix Minor ------------ .. code:: ipython3 >>> mat2 .. parsed-literal:: [['{0, 1}', '{0, 2}', '{1, 2}'], ['{0}', '{1, 2}', '{2}'], ['{0, 1}', '{}', '{}']] .. code:: ipython3 >>> mnr2 = mat2.minor(0,0) >>> mnr2 .. parsed-literal:: [['{1, 2}', '{2}'], ['{}', '{}']] .. code:: ipython3 >>> mat5 .. parsed-literal:: [['0', '1', 'a'], ['1', 'a', '1+a'], ['1+a', '0', '1']] .. code:: ipython3 >>> mnr3 = mat5.minor(1, 1) >>> mnr3 .. parsed-literal:: [['0', 'a'], ['1+a', '1']] Abstract Cofactor Matrix ------------------------ .. code:: ipython3 >>> cof2 = mat2.cofactor_matrix() >>> cof2 .. parsed-literal:: [['{}', '{}', '{1}'], ['{}', '{1}', '{0}'], ['{1}', '{}', '{0, 1}']] .. code:: ipython3 >>> cof3 = mat5.cofactor_matrix() >>> cof3 .. parsed-literal:: [['a', '1+a', '1'], ['1', '1', '1+a'], ['0', 'a', '1']] Abstract Matrix Transpose ------------------------- .. code:: ipython3 >>> cof2_trans = cof2.transpose() >>> cof2_trans .. parsed-literal:: [['{}', '{}', '{1}'], ['{}', '{1}', '{}'], ['{1}', '{0}', '{0, 1}']] .. code:: ipython3 >>> cof3.transpose() .. parsed-literal:: [['a', '1', '0'], ['1+a', '1', 'a'], ['1', '1+a', '1']] Abstract Matrix Determinant --------------------------- .. code:: ipython3 >>> mat2 .. parsed-literal:: [['{0, 1}', '{0, 2}', '{1, 2}'], ['{0}', '{1, 2}', '{2}'], ['{0, 1}', '{}', '{}']] .. code:: ipython3 >>> mat2.determinant() .. parsed-literal:: '{1}' Here’s a breaksdown of the basic computations required to get the determinant. NOTE: Addition & multiplication for the ring, ps3, is commutative, so the order of addition and multiplication is irrelevant, below. .. code:: ipython3 >>> minor_det_0 = ps3.sub(ps3.mult('{1, 2}', '{}'), ps3.mult('{}', '{2}')) >>> minor_det_0 .. parsed-literal:: '{}' .. code:: ipython3 >>> minor_det_1 = ps3.sub(ps3.mult('{0}', '{}'), ps3.mult('{2}', '{0, 1}')) >>> minor_det_1 .. parsed-literal:: '{}' .. code:: ipython3 >>> minor_det_2 = ps3.sub(ps3.mult('{0}', '{}'), ps3.mult('{1, 2}', '{0, 1}')) >>> minor_det_2 .. parsed-literal:: '{1}' .. code:: ipython3 >>> det = ps3.sub(ps3.add(ps3.mult('{0, 1}', minor_det_0), >>> ps3.mult('{1, 2}', minor_det_2)), >>> ps3.mult('{0, 2}', minor_det_1)) >>> det .. parsed-literal:: '{1}' Determinant of an Abstract Matrix over a finite Field: .. code:: ipython3 >>> mat5.determinant() .. parsed-literal:: '1' Abstract Matrix Inverse ----------------------- An abstract matrix over a Ring cannot have a true inverse, but if we apply the `Laplace expansion algorithm `__ anyway, we obtain a matrix that, when multiplied by the original matrix, results in a diagonal matrix, just not an “identity matrix”, because Rings don’t necessarily have multiplicative identity elements. .. code:: ipython3 >>> mat2_inv = mat2.inverse() >>> mat2_inv .. parsed-literal:: [['{}', '{}', '{1}'], ['{}', '{1}', '{}'], ['{1}', '{}', '{1}']] The product of mat2_inv with mat2 yields a diagonal matrix: .. code:: ipython3 >>> mat2 * mat2_inv .. parsed-literal:: [['{1}', '{}', '{}'], ['{}', '{1}', '{}'], ['{}', '{}', '{1}']] .. code:: ipython3 >>> mat2_inv * mat2 .. parsed-literal:: [['{1}', '{}', '{}'], ['{}', '{1}', '{}'], ['{}', '{}', '{1}']] An Abstract Matrix over a Field can have an inverse, as long as its determinant is the Field’s multiplicative identity element: .. code:: ipython3 >>> mat5.determinant() # Based on the Field, f4, defined above .. parsed-literal:: '1' .. code:: ipython3 >>> mat5_inv = mat5.inverse() >>> mat5_inv .. parsed-literal:: [['a', '1', '0'], ['1+a', '1', 'a'], ['1', '1+a', '1']] .. code:: ipython3 >>> mat5 * mat5_inv .. parsed-literal:: [['1', '0', '0'], ['0', '1', '0'], ['0', '0', '1']] .. code:: ipython3 >>> mat5_inv * mat5 .. parsed-literal:: [['1', '0', '0'], ['0', '1', '0'], ['0', '0', '1']]