Hermite and Smith Normal Forms

desr uses the diophantine package, which in turn uses the methods found in [Havas1998], to calculate the Hermite normal form of matrices.

matrix_normal_forms should be used for all normal form calculations - we never call the diophantine package directly from other parts of desr.

This is also where the Smith normal form functions live, which use Hermite normal forms at their core.

Hermite Normal Forms

desr.matrix_normal_forms.is_hnf_row(matrix_)[source]

Decide whether a matrix is in Hermite normal form, when acting on rows. This is according to the Havas Majewski Matthews definition.

Parameters:matrix (sympy.Matrix) – The matrix in question.
Returns:bool
>>> is_hnf_row(sympy.eye(4))
True
>>> is_hnf_row(sympy.ones(2))
False
>>> is_hnf_row(sympy.Matrix([[1, 2, 0], [0, 1, 0]]))
False
>>> is_hnf_row(sympy.Matrix([[1, 0, 0], [0, 2, 1]]))
True
>>> is_hnf_row(sympy.Matrix([[1, 0, 0], [0, -2, 1]]))
False
desr.matrix_normal_forms.is_hnf_col(matrix_)[source]

Decide whether a matrix is in row Hermite normal form, when acting on rows.

Parameters:matrix (sympy.Matrix) – The matrix in question.
Returns:bool
desr.matrix_normal_forms.hnf_row(matrix_)

Default function for calculating row Hermite normal forms.

Parameters:matrix (sympy.Matrix) – Input matrix.
Returns:hermite_normal_form (sympy.Matrix): The column Hermite normal form of matrix_.

normal_multiplier (sympy.Matrix): The normal Hermite multiplier.

Return type:tuple
Return type:(sympy.Matrix, sympy.Matrix)
desr.matrix_normal_forms.hnf_col(matrix_)

Default function for calculating column Hermite normal forms.

Parameters:matrix (sympy.Matrix) – Input matrix.
Returns:
Tuple containing:
hermite_normal_form (sympy.Matrix): The column Hermite normal form of matrix_.

normal_multiplier (sympy.Matrix): The normal Hermite multiplier.

Return type:tuple
Return type:(sympy.Matrix, sympy.Matrix)
desr.matrix_normal_forms.normal_hnf_row(matrix_)[source]

Return the row HNF and the unique normal Hermite multiplier.

Parameters:matrix (sympy.Matrix) – Input matrix.
Returns:Tuple containing:
hermite_normal_form (sympy.Matrix): The row Hermite normal form of matrix_. normal_multiplier (sympy.Matrix): The normal Hermite multiplier.
Return type:tuple
desr.matrix_normal_forms.normal_hnf_col(matrix_)[source]

Return the column HNF and the unique normal Hermite multiplier.

Parameters:matrix (sympy.Matrix) – Input matrix.
Returns:
Tuple containing:
hermite_normal_form (sympy.Matrix): The column Hermite normal form of matrix_. normal_multiplier (sympy.Matrix): The normal Hermite multiplier.
Return type:tuple
>>> A = sympy.Matrix([[8, 2, 15, 9, 11],
...                   [6, 0, 6, 2, 3]])
>>> h, v = normal_hnf_col(A)
>>> h
Matrix([
[1, 0, 0, 0, 0],
[0, 1, 0, 0, 0]])
>>> v
Matrix([
[  0,  0,   1,  0,   0],
[  0,  0,   0,  1,   0],
[  2,  1,   3,  1,   5],
[  9,  2,  21,  3,  21],
[-10, -3, -22, -4, -24]])
>>> (A * v) == h
True

Smith Normal Form

desr.matrix_normal_forms.is_smf(matrix_)[source]

Given a rectangular \(n \times m\) integer matrix, determine whether it is in Smith normal form or not.

Parameters:matrix (sympy.Matrix) – The rectangular matrix to be decomposed
Returns:True if in Smith normal form, False otherwise.
Return type:bool
>>> matrix_ = sympy.diag(1, 1, 2)
>>> is_smf(matrix_)
True
>>> matrix_ = sympy.diag(-1, 1, 2)
>>> is_smf(matrix_)
False
>>> matrix_ = sympy.diag(2, 1, 1)
>>> is_smf(matrix_)
False
>>> matrix_ = sympy.diag(1, 2, 0)
>>> is_smf(matrix_)
True
>>> matrix_ = sympy.diag(2, 6, 0)
>>> is_smf(matrix_)
True
>>> matrix_ = sympy.diag(2, 5, 0)
>>> is_smf(matrix_)
False
>>> matrix_ = sympy.diag(0, 1, 1)
>>> is_smf(matrix_)
False
>>> matrix_ = sympy.diag(0)
>>> is_smf(sympy.diag(0)), is_smf(sympy.diag(1)), is_smf(sympy.Matrix()),
(True, True, True)

Check a real example:

>>> matrix_ = sympy.Matrix([[2, 4, 4],
...                         [-6, 6, 12],
...                         [10, -4, -16]])
>>> is_smf(matrix_)
False
>>> matrix_ = sympy.diag(2, 6, 12)
>>> is_smf(matrix_)
True

Check it works for non-square matrices:

>>> matrix_ = sympy.Matrix(4, 5, range(20))
>>> is_smf(matrix_)
False
>>> matrix_ = sympy.Matrix([[1, 0], [1, 2]])
>>> is_smf(matrix_)
False
desr.matrix_normal_forms.smf(matrix_)[source]

Given a rectangular \(n \times m\) integer matrix, calculate the Smith Normal Form \(S\) and multipliers \(U\), \(V\) such that \(U \textrm{matrix_} V = S\).

Parameters:matrix (sympy.Matrix) – The rectangular matrix to be decomposed.
Returns:
  • S (sympy.Matrix) – The Smith normal form of matrix_.
  • U (sympy.Matrix) – \(U\) (the matrix representing the row operations of the decomposition).
  • V (sympy.Matrix) – \(V\) (the matrix representing the column operations of the decomposition).
Return type:(sympy.Matrix, sympy.Matrix, sympy.Matrix)
>>> matrix_ = sympy.Matrix([[2, 4, 4],
...                         [-6, 6, 12],
...                         [10, -4, -16]])
>>> smf(matrix_)[0]
Matrix([
[2, 0,  0],
[0, 6,  0],
[0, 0, 12]])
>>> matrix_ = sympy.diag(2, 1, 0)
>>> smf(matrix_)[0]
Matrix([
[1, 0, 0],
[0, 2, 0],
[0, 0, 0]])
>>> matrix_ = sympy.diag(5, 2, 0)
>>> smf(matrix_)[0]
Matrix([
[1,  0, 0],
[0, 10, 0],
[0,  0, 0]])