)abbrev domain VSBASIS VectorSpaceBasis
++ Author: Francois Maltey, Martin Rubey
++ Basic Operations:
++ Keywords:
++ Description:
++  The domain \spad{VectorSpaceBasis F} implements subspaces of
++  finite dimensional vector spaces over a field F.
++  A subspace is represented by a list of linearly independent
++  coordinate vectors which constitute a basis.
++  The empty list represents the space of dimension 0.
VectorSpaceBasis(R : Field) : Exports == Implementation where
    Exports ==> Join(BasicType, CoercibleTo OutputForm) with

        basis : List Vector R -> %
          ++ \spad{basis LV} extracts a basis of the subspace spanned by
          ++ the list of vectors \spad{LV}.
        coerce : % -> List Vector R
          ++ \spad{coerce b} returns the basis as a list of vectors.
        nullSpace : Matrix R -> %
          ++ \spad{nullSpace M} builds a basis of the kernel of
          ++ the linear transformation defined by the matrix \spad{M}.
          ++ This function calls the function \spad{nullSpace}
          ++ from the \spad{Matrix} domain. Only the signature changes.
        columnSpace : Matrix R -> %
          ++ \spad{columnSpace M} builds a basis of the image space of
          ++ the linear transformation defined by the matrix \spad{M}.
          ++ This function calls the \spad{columnSpace} from the \spad{Matrix}
          ++ domain. Only the signature changes.

        sumBasis : (%, %) -> %
          ++ \spad{sumBasis(B1, B2)} computes a basis of the sum
          ++ of the two subspaces.
        sumBasis : (List Vector R, List Vector R) -> %
          ++ \spad{sumBasis(Lv1, Lv2)} computes a basis of the sum
          ++ of the two subspaces spanned by the vectors of \spad{Lv1}
          ++ and \spad{Lv2}.
        sumBasis : List % -> %
          ++ \spad{sumBasis Lb} computes a basis of the sum of list of
          ++ subspaces.
          ++ This function is slightly more efficient than
          ++ \spad{reduce(sumBasis, Lb)}.
        sumBasis : (List List Vector R) -> %
          ++ \spad{sumBasis LLv} computes a basis of the sum of the list of
          ++ subspaces represented by LLv.
          ++ This function is slightly more efficient than
          ++ \spad{reduce(sumBasis, LLv)}.
        "+" : (%,%) -> %
          ++ \spad{B1+B2} computes a basis of the sum of two subspaces.

        intBasis : (List Vector R, List Vector R) -> %
          ++ \spad{intBasis(Lv1, Lv2)} computes a basis of the intersection
          ++ of the two subspaces spanned by the vectors of \spad{Lv1}
          ++ and \spad{Lv2}.
        intBasis : (%, %) -> %
          ++ \spad{intBasis(B1, B2)} computes a basis of the intersection
          ++ of the two subspaces.
        intBasis : List % -> %
          ++ \spad{intBasis Lb} computes a basis of the intersection
          ++ of the list of subspaces represented by LLv.
        intBasis : List List Vector R -> %
          ++ \spad{intBasis LLv} computes a basis of the intersection
          ++ of the list of subspaces represented by LLv.
        "*" : (%, %) -> %
          ++ \spad{b1*b2} computes a basis of the intersection
          ++ of two subspaces.

        rank : List Vector R -> NonNegativeInteger
          ++ \spad{rank Lv} computes the rank of a list of vectors.
        rank : % -> NonNegativeInteger
          ++ \spad{rank b} computes the rank of a basis, i.e., the number of
          ++ vectors in the basis.
        isBasis? : List Vector R -> Boolean
          ++ \spad{isBasis? Lv} tests for linear independence
          ++ of a list of vectors.
        subspace? : (%, %) -> Boolean
          ++ \spad{subspace?(B1, B2)} tests if the first space is included in
          ++ the second.
        0 : constant -> %
          ++ \spad{0} creates the empty basis describing the space with the zero
          ++ vector. The length of this zero vector is undefined.
        canonicalBasis : NonNegativeInteger -> %
          ++ \spad{canonicalBasis n} builds the canonical basis of the
          ++ vector space of dimension \spad{n}.

        complementSpace : % -> %
          ++ \spad{complementSpace B} builds a complement space of the basis
          ++ \spad{B} from some vectors of the canonical basis.
          ++ This function fails with the empty basis.
        complementSpace : (%, NonNegativeInteger) -> %
          ++ \spad{complementSpace(B, n)} builds a complement space of the basis
          ++ \spad{B} from some vectors of the canonical basis \spad{B}.
          ++ The integer \spad{n} provides the length of the vectors in
          ++ the case of the empty basis.
        complementSpace : List Vector R -> %
          ++ \spad{complementSpace Lv} builds a complement space of the subspace
          ++ spanned by the list of vectors \spad{LV}.
          ++ This function fails with the empty basis.
        complementSpace : (List Vector R, NonNegativeInteger) -> %
          ++ \spad{complementSpace(Lv, n)} builds a complement space
          ++ of the subspace spanned by the list of vectors \spad{LV}.
          ++ The integer \spad{n} provides the length of the vectors in
          ++ the case of the empty basis.

        member? : (Vector R, %) -> Boolean
          ++ \spad{member?(v, B)} tests if the vector \spad{v}
          ++ is in the subspace spanned by \spad{B}.
        coordinatesIfCan : (Vector R, %) -> Union (Vector R, "failed")
          ++ \spad{coordinatesIfCan(v, B)} computes the coordinates of
          ++ the vector \spad{v} in the basis \spad{B} if possible
          ++ and returns \spad{"failed"} otherwise.
        coordinates : (Vector R, %) -> Vector R
          ++ \spad{coordinates(v, B)} computes the coordinates of
          ++ the vector \spad{v} in the basis \spad{B} if possible
          ++ and produces an error otherwise.

    Implementation ==> List Vector R add

        LSMP1 ==> LinearSystemMatrixPackage1 R

        -- a basis is represented as a list of vectors
        -- the length of each vector must be the same
        -- this test is done by the call of column2matrix

        Rep := List Vector R

        -- the empty list is a basis of the space with only the null vector.
        -- in this case length of vectors is undefined.

        0 : % == (empty()$(List Vector R))

        -- retracting a basis to a list of vectors is obvious.
        -- building a basis from a list of vectors may change this list,
        -- so there is no coerce from List Vector R to VectorSpaceBasis R.

        coerce (B : %) : List Vector R == B::Rep

        -- axiom displays a basis,
        -- the display claims that a basis isn't a list of vectors.

        coerce (B : %) : OutputForm ==
          a1 : OutputForm := message("VectorSpace ")
          a2 : OutputForm := coerce(B pretend List Vector R)$(List Vector R)
          hconcat(a1, a2)$OutputForm

        -- this function builds a matrix from its column vectors.

        column2matrix(Lv : List Vector R) : Matrix R ==
          n := #(Lv.1)
          M : Matrix R := new(n, #Lv, 0)
          for k in 1..#Lv for v in Lv repeat
            n ~= #v => _
              error _
                "Length of vectors in VectorSpaceBasis must have the same size"
            setColumn!(M, k, v)
          M

        -- basis calls in fact the columnSpace function in Matrix R domain.
        -- columnSpace is based over rowEchelon.
        -- the empty list is a separate case.

        basis Lv ==
          #Lv = 0 => 0
          (columnSpace(column2matrix Lv)$(Matrix R))::%

        -- VectorSpaceBasis R includes columnSpace and nullSpace functions of
        -- Matrix R domain.

        columnSpace M == ((columnSpace M)$(Matrix R))::%
        nullSpace M == ((nullSpace M)$(Matrix R))::%

        -- A basis of the sum of subspaces is a basis extract from the
        -- union of the basis.
        -- Parameters can be 2 lists of vectors, or two basis, or
        -- a list of list of vector, or a list of basis.
        -- the + operator is also allowed.

        sumBasis(Lv : List Vector R, Lw : List Vector R) : % ==
          basis concat (Lv, Lw)
        sumBasis(B1 : %, B2 : %) : % == basis concat (B1::Rep, B2::Rep)

        -- the empty sum of subspace is the null vector subspace

        sumBasis(LLv : List List Vector R) : % == basis concat LLv
        sumBasis(LB : List %) : % == basis (concat LB::Rep)

        B1 + B2 == sumBasis (B1, B2)

        -- subvector creates the vector [v.a, v.(a+1), ..., v.b]

        subVector(v : Vector R, a : Integer, b : Integer) : Vector R ==
            vv : Vector R := new((b-a+1)::NonNegativeInteger, 0)
            for k in 1..b-a+1 repeat qsetelt!(vv, k, qelt(v, k+a-1))
            vv

        -- local function to computes t.1*Lv.1 + t.2*Lv.2 + ...

        linearSum(t : Vector R, Lv : List Vector R) : Vector R ==
            vv : Vector R := new(#(Lv.1), 0)
            for k in 1..#Lv for v2 in Lv repeat
                t2 := t.k
                for j in 1..#vv repeat vv.j := vv.j + t2*v2.j
            vv

        -- The columns of M are vectors.
        -- The kernel of M describes linear relationships between vectors.
        -- These relations in the nullSpace of the matrix obtained
        -- by the union of B1 and B2 supply null-vectors.
        -- Divide this sum between vectors of B1 and vectors of B2
        -- produce a vector in the intersectionSpace.
        -- This method builds a basis of the intersection.
        -- vectors of B1 are at the end of the matrix,
        -- so the nullSpace function advantges the vectors of B1.

        intBasis0(Lv : List Vector R, Lw : List Vector R) : % ==
            empty?(Lv) or empty?(Lw) => 0
            d1 := #Lv
            d2 := #Lw
            #(first Lv) ~= #(first Lw)
               => error "vectors have different lengths in intBasis"
            M := new(#(first Lv), d1+d2, 0)
            for k in 1..d2 for w in Lw repeat setColumn! (M, k, w)
            for k in 1..d1 for v in Lv repeat setColumn! (M, d2+k, v)
            lker : List Vector R := (nullSpace M)$(Matrix R)
            LcoeffB1 : List Vector R :=
              [subVector (kv, d2+1, d2+d1) for kv in lker]
            [linearSum (cc, Lv) for cc in LcoeffB1]

        intBasis(Lv : List Vector R, Lw : List Vector R) : % ==
            intBasis0(Lv, Lw)

        intBasis(B1 : %, B2 : %) : % ==
            Lv : Rep := B1::Rep
            Lw : Rep := B2::Rep
            intBasis0(Lv, Lw)

        -- the empty intersection of subspaces is not defined.

        intBasis (LLB : List List Vector R) : % ==
          #LLB = 0 => error "intersect no subspace is impossible"
          res := basis LLB.1
          for LB in rest LLB repeat res := intBasis (res, basis LB)
          res

        intBasis (LLB : List %) : % ==
          #LLB = 0 => error "intersect no subspace is impossible"
          res := LLB.1
          for LB in rest LLB repeat res := intBasis (res, LB)
          res

        B1 * B2 == intBasis (B1, B2)

        -- rank and dimension are synonym.

        rank (B : %) : NonNegativeInteger == #(B::Rep)
        rank (Lv : List Vector R) : NonNegativeInteger == rank basis Lv

        -- vectors without linear relationship is a basis of the subspace
        -- spanned by theses vectors.

        isBasis? Lv == (rank Lv = # Lv)

        -- dimension theory justifies these tests for inclusion
        -- and equality of spaces.

        subspace? (B1, B2) == (rank B2 = rank sumBasis (B1, B2))

        B1 = B2 ==
           (rk1 := rank B1) ~= (rk2 := rank B2) => return false
           rks := rank sumBasis (B1, B2)
           (rks = rk1) and (rks = rk2)

        -- This function builds the canonical basis of a vector space
        -- of length n

        canonicalBasis n ==
          L : List Vector R := []
          for k in 1..n repeat
            v : Vector R := new(n, 0)
            v.(n+1-k) := 1
            L := cons (v, L)
          L

        -- This function concats all the vectors of the canonical basis
        -- and looks for vectors to complete a basis of the vector space.
        -- The last reverse call makes order the result in the order of bc.

        complementSpace (Lv : List Vector R) : % ==
          Lv = [] =>
            error
              "Can't get vector length of an empty family for complementSpace"
          dim := #(Lv.1)
          bc : List Vector R := (canonicalBasis dim)::List Vector R
          Lw : List Vector R := concat (Lv, bc)
          M := rowEchelon column2matrix Lw
          n := #Lv
          ind : NonNegativeInteger := 1
          for k in 1..n for v in Lw repeat
            if ind <= dim and M.(ind, k) ~= 0 then ind := ind+1
          RES := []
          for k in n+1..n+dim for v in bc repeat
            if ind <= dim and M.(ind, k)~=0
              then
                RES := cons (v, RES)
                ind := ind+1
          reverse! RES

        complementSpace (Lv : List Vector R, n : NonNegativeInteger) : % ==
          Lv = [] => canonicalBasis n
          n ~= #(Lv.1) => error "Inconsistant vector length in complementSpace"
          complementSpace Lv

        -- the argument B is a basis, computation is a little easier.

        complementSpace (B : %) : % ==
          B = 0 =>
            error
              "Can't get vector length of an empty basis for complement Space"
          dim := #(B.1)
          bc : List Vector R := (canonicalBasis dim)::List Vector R
          Lw : List Vector R := concat (B::List Vector R, bc)
          M := rowEchelon column2matrix Lw
          n := #B
          ind : NonNegativeInteger := n+1
          RES := []
          for k in n+1..n+dim for v in bc repeat
            if ind <= dim and M.(ind, k) ~= 0
              then
                RES := cons (v, RES)
                ind := ind+1
          reverse! RES

        complementSpace (B : %, n : NonNegativeInteger) : % ==
          B = [] => canonicalBasis n
          n ~= #(B.1) => error "Inconsistant vector length in complementSpace"
          complementSpace B

        -- member?, coordinatesIfCan and coordinates test if a vector is
        -- in a subspace and consider it.

        member? (v, B) ==
          hasSolution?(column2matrix (B::Rep), v)$LSMP1

        coordinatesIfCan (v, B) ==
          particularSolution(column2matrix(B::Rep), v)$LSMP1

        coordinates (V, B) ==
          res := particularSolution(column2matrix(B::Rep), V)$LSMP1
          res case "failed" => error "Vector not in the subspace"
          res::Vector R
