packages feed

egison-5.1.0: lib/math/algebra/matrix.egi

--
-- Matrices
--

inductive pattern Matrix a:=
  | quadCons (Matrix a) (Matrix a) (Matrix a) (Matrix a)
  | matCons Integer Integer a (Matrix a) (Matrix a) (Matrix a) (Matrix a)

def matrix : Matcher (Matrix MathValue) :=
  matcher
    | quadCons $ $ $ $ as (mathValue, matrix, matrix, matrix) with
      | $tgt ->
        match tensorShape tgt as list integer with
          | $m :: $n :: _ ->
            [(tgt_1_1, tgt_1_(2, n), tgt_(2, m)_1, tgt_(2, m)_(2, n))]
          | _ -> []
    | matCons #$i #$j $ $ $ $ $ as (mathValue, matrix, matrix, matrix, matrix) with
      | $tgt ->
        let ns := tensorShape tgt
            m := nth 1 ns
            n := nth 2 ns
         in [ ( tgt_i_j
            , tgt_(1, i - 1)_(1, j - 1)
            , tgt_(1, i - 1)_(j + 1, n)
            , tgt_(i + 1, m)_(1, j - 1)
            , tgt_(i + 1, m)_(j + 1, n) ) ]
    | #$val as () with
      | $tgt -> if val = tgt then [()] else []
    | $ as (something) with
      | $tgt -> [tgt]

def M.tensorIndexValue (index: TensorIndex) : MathValue :=
  match index as tensorIndex with
    | (subIndex $x | supIndex $x | diagIndex $x | userIndex $x) -> x

def M.attachOrdinaryIndex {a}
      (value: Matrix a) (index: TensorIndex) : Matrix a :=
  let variance := tensorIndexVariance index
      x := M.tensorIndexValue index
   in match assert "ordinary matrix indices must be up or down"
                   (variance = "down" || variance = "up")
            as bool with
        | #True ->
            if variance = "down"
              then subrefs value [x]
              else suprefs value [x]

-- Preserve an ordinary matrix's explicit index contract after local
-- `withSymbols` indices have become anonymous result axes.
def M.copyOrdinaryIndices {a}
      (source: Matrix a) (value: Matrix a) : Matrix a :=
  let indices := tensorIndices source
   in match assert
              "matrix result must match its source shape and ordinary indices"
              (length (tensorShape source) = 2
               && tensorShape source = tensorShape value
               && (indices = [] || length indices = 2)
               && all
                    (\variance -> variance = "down" || variance = "up")
                    (tensorVariances source))
        as bool with
        | #True -> foldl M.attachOrdinaryIndex value indices

def trace {Ring a} (t: Matrix a) : a :=
  withSymbols [i]
    contractWith (+) t~i_i

def sym {Field a} (a: Matrix a) : Matrix a :=
  M.copyOrdinaryIndices a
    (withSymbols [i, j]
      ((a_i_j + a_j_i) / 2))

def antisym {Field a} (a: Matrix a) : Matrix a :=
  M.copyOrdinaryIndices a
    (withSymbols [i, j]
      ((a_i_j - a_j_i) / 2))

def M.inverse (m: Matrix MathValue) : Matrix MathValue :=
  let d := M.det m
   in generateTensor
        (\[i, j] ->
          match m as matrix with
          | matCons #j #i _ $A $B $C $D ->
            if isEven (i + j)
              then M.det (M.join A B C D) / d
              else - (M.det (M.join A B C D) / d))
        (tensorShape m)

def M.* (s: Matrix MathValue) (t: Matrix MathValue) : Matrix MathValue := 
  withSymbols [i, j, k] (s~i~j . t_j_k)

def M.*' (s: Matrix MathValue) (t: Matrix MathValue) : Matrix MathValue :=
  withSymbols [i, j, k] (s~i~j .' t_j_k)

-- Matrix-vector multiplication.  M.* indexes its right operand twice
-- (t_j_k), which requires rank 2; a vector operand contracts on the
-- single shared index instead.
def MV.* (s: Matrix MathValue) (t: Vector MathValue) : Vector MathValue :=
  withSymbols [i, j] (s~i~j . t_j)

def M.power (t: Matrix MathValue) (k: Integer) : Matrix MathValue := 
  foldl M.* t (take (k - 1) (repeat1 t))

def M.comm (m1: Matrix MathValue) (m2: Matrix MathValue) : Matrix MathValue := 
  withSymbols [i, j, k] m1~i~j . m2_j_k - m2~i~j . m1_j_k

def M.join (A: Matrix MathValue) (B: Matrix MathValue) (C: Matrix MathValue) (D: Matrix MathValue)
  : Matrix MathValue :=
  let ashape := tensorShape A
      bshape := tensorShape B
      cshape := tensorShape C
      dshape := tensorShape D
  in let a1 := nth 1 ashape
         a2 := nth 2 ashape
         b1 := nth 1 bshape
         b2 := nth 2 bshape
         c1 := nth 1 cshape
         c2 := nth 2 cshape
         d1 := nth 1 dshape
         d2 := nth 2 dshape
     in let m1 := max a1 b1
            m2 := max a2 c2
            n1 := max c1 d1
            n2 := max b2 d2
        in generateTensor
             (\match as list integer with
               | [$i & ?(<= a1), $j & ?(<= a2)] -> A_i_j
               | [$i & ?(<= m1), $j]            -> B_i_(j - a2)
               | [$i,            $j & ?(<= m2)] -> C_(i - a1)_j
               | [$i,            $j]            -> D_(i - m1)_(j - m2))
             [m1 + n1, m2 + n2]

--
-- Determinant
--
def M.determinant (m: Matrix MathValue) : MathValue :=
  match tensorShape m as list integer with
    | [#0, #0] -> 1
    | [$n, #n] ->
      let (es, os) := evenAndOddPermutations' n
       in sum (map (\e -> product (map2 (\i j -> m_i_j) (between 1 n) e)) es) -
            sum (map (\o -> product (map2 (\i j -> m_i_j) (between 1 n) o)) os)
    | _ -> undefined

def M.det (m: Matrix MathValue) : MathValue := M.determinant m