Theory More_LLL

section ‹ Preliminary results for LLL ›

text ‹ In this file, we prove some additional results involving
  lattices and a bound for LLL. ›

theory More_LLL
imports
  "LLL_Basis_Reduction.LLL"
begin 

context vec_module begin

lemma in_lattice_ofD:
  assumes x: "x  lattice_of a"
  assumes a: "set a  carrier_vec n"
  obtains v where
    "v  carrier_vec (length a)"
    "mat_of_cols n a *v map_vec of_int (v::int vec) = x"
proof -
  from in_latticeE[OF x]
  obtain c where
    c: "x = sumlist (map (λ i. of_int (c i) v a ! i) [0 ..< length a])" .

  have "x = lincomb_list (of_int  c) a "
    unfolding lincomb_list_def c by auto
  also have "... = 
    mat_of_cols n a *v vec (length a) (of_int  c)"
    apply (subst lincomb_list_as_mat_mult)
    using a by auto
  also have "... = 
    mat_of_cols n a *v map_vec of_int (vec (length a) c)"
    by (auto simp add: map_vec)
  finally have "x = mat_of_cols n a *v map_vec of_int (vec (length a) c)" .
  thus ?thesis
    by (auto intro!: that)
qed

lemma exists_list_all2:
  assumes "x. (x  set xs  (y. P x y))"
  obtains ys where "list_all2 P xs ys"
  using assms
proof (induction xs arbitrary: thesis)
  case Nil
  then show ?case
    by auto
next
  case c: (Cons x xs)
  obtain y where y: "P x y"
    using c(3) by auto
  obtain ys where ys: "list_all2 P xs ys"
    using c
    by auto 
  show ?case using c y ys by auto
qed
  
lemma subset_lattice_ofD:
  assumes xs: "set xs  lattice_of a" "set xs  carrier_vec n"
  assumes a: "set a  carrier_vec n"
  obtains vs where
    "vs  carrier_mat (length a) (length xs)"
    "mat_of_cols n a * map_mat of_int vs = mat_of_cols n xs"
proof -
  define P where "P = 
    (λx v. v  carrier_vec (length a) 
    mat_of_cols n a *v map_vec of_int (v::int vec) = x)"
  obtain ys where ys: "list_all2 P xs ys"
    apply (subst exists_list_all2[where P = "P", where xs = "xs"])
    unfolding P_def
    apply (meson a in_lattice_ofD subsetD xs)
    by auto

  then have len: "length xs = length ys"
    using list_all2_lengthD by blast

  define vs where "vs = mat_of_cols (length a) ys"
  have 1: "vs  carrier_mat (length a) (length xs)"
    unfolding vs_def
    by (metis len mat_of_cols_carrier(1))

  have "i < length ys 
    mat_of_cols n a *v
    of_int_hom.vec_hom (col (mat_of_cols (length a) ys) i) = xs ! i" for i
    by (metis P_def col_mat_of_cols list_all2_conv_all_nth ys)
  then have "i < length ys 
   col (mat_of_cols n a * of_int_hom.mat_hom (mat_of_cols (length a) ys)) i =
   col (mat_of_cols n xs) i" for i
    apply (subst col_mat_of_cols)
    subgoal using len by auto
    subgoal using len xs(2) by fastforce
    apply (subst col_mult2)
    by auto
  then have 2: "mat_of_cols n a * map_mat of_int vs = mat_of_cols n xs"
    unfolding vs_def
    apply (intro mat_col_eqI)
    using len by auto

  show ?thesis
    apply (intro that)
    using 1 2 by auto
qed

lemma mk_coeff_list_nth:
  assumes "i < length ls" "distinct ls"
  shows "mk_coeff ls f (ls ! i) = f i"
  using assms
proof (induction ls arbitrary: f i)
  case Nil
  then show ?case
    by auto
next
  case (Cons l ls)
  then show ?case
    by (auto simp add: mk_coeff_Cons nth_equal_first_eq)
qed

text ‹ This next lemma is trivial when the cols are not distinct. ›
lemma mat_mul_non_zero_col_lin_dep:
  assumes A: "A  carrier_mat n y" "distinct (cols A)"
  assumes U: "U  carrier_mat y z"
  assumes i: "i < z" "col U i  0v y"
  assumes eqz: "A * U = 0m n z"
  shows "lin_dep (set (cols A))"
proof -
  obtain j where j: "j < y" "U $$ (j,i)  0"
    using U i
    unfolding vec_eq_iff
    by auto

  have Aeq: "A = mat_of_cols n (cols A)"
    using A by auto
  have "0v n = A *v (col U i)"  
    using eqz
    unfolding mat_eq_iff vec_eq_iff
    using A U i by auto
  also have "... = mat_of_cols n (cols A) *v vec (length (cols A)) (λia. U $$ (ia, i))"
    unfolding col_def Aeq[symmetric] using A U by auto
  also have "... =
    lincomb_list (λia. U $$ (ia, i)) (cols A)"
    apply (subst lincomb_list_as_mat_mult[symmetric])
     apply (metis assms(1) carrier_matD(1) carrier_vecD cols_dim subsetD)
    by auto
  also have "... =
    lincomb (mk_coeff (cols A) (λia. U $$ (ia, i))) (set (cols A))"
    apply (subst lincomb_list_as_lincomb)
    using assms(1) cols_dim apply blast
    by auto
  finally have
    lc:"lincomb (mk_coeff (cols A) (λia. U $$ (ia, i))) (set (cols A)) = 0v n" by auto

  have mc: "mk_coeff (cols A) (λia. U $$ (ia, i)) (col A j)  0"
    using mk_coeff_list_nth
    by (metis assms(1) assms(2) carrier_matD(2) cols_length cols_nth j)

  have "col A j  set (cols A)"
    by (metis assms(1) carrier_matD(2) cols_length cols_nth j(1) nth_mem)
  thus ?thesis
    apply (intro lin_dep_crit [OF _ _ _ _ mc lc])
    using A j by auto
qed

lemma lin_indpt_mul_eq_ident:
  assumes a: "distinct a" "lin_indpt (set a)"
    "set a  carrier_vec n" "length a = m"
  assumes u: "u  carrier_mat m m"
  assumes e: "mat_of_cols n a = mat_of_cols n a * u"
  shows "u = 1m m"
proof (rule ccontr)
  assume "u  1m m"
  then obtain i where i: "i < m"
    "col (u - 1m m) i  0v m"
    unfolding vec_eq_iff mat_eq_iff
    using u by auto
  have 1:"mat_of_cols n a  carrier_mat n m"
    using assms(4) mat_of_cols_carrier(1) by blast
  have 2:"distinct (cols (mat_of_cols n a))"
    by (simp add: assms(1) assms(3))

  have "mat_of_cols n a * (u - 1m m) = 0m n m"
    by (smt (verit, ccfv_SIG) assms(4) e mat_of_cols_carrier(1) minus_r_inv_mat mult_minus_distrib_mat one_carrier_mat right_mult_one_mat u)
  from mat_mul_non_zero_col_lin_dep[OF 1 2 _ i this]
  show False
    using a by (simp add: assms(3) minus_carrier_mat)
qed

end

text ‹ Set up a locale: vec\_module where the ring has characteristic zero ›
locale ring_char_0_vec_module = vec_module f_ty n for
  f_ty::"'a:: {comm_ring_1, ring_char_0} itself"
  and n
begin                                                           

text ‹ This next lemma shows that different basis a, b of the same lattice 
  have the same Gram determinant. ›
lemma lattice_of_eq_gram_det_eq:
  fixes a b::"'a vec list"
  assumes a: "distinct a" "lin_indpt (set a)" "set a  carrier_vec n"
  assumes b: "set b  carrier_vec n" "length a = length b"
  assumes lat_eq: "lattice_of a = lattice_of b"
  defines A: "A  mat_of_cols n a"
  defines B: "B  mat_of_cols n b"
  shows "det (AT * A) = det (BT * B)"
proof -
  let ?m = "length a"
  have "set b  lattice_of a"
    by (simp add: lat_eq b basis_in_latticeI subsetI)    
  from subset_lattice_ofD[OF this]
  obtain vs where
    vs: "vs  carrier_mat (length a) (length b)"
    "mat_of_cols n a * map_mat of_int vs = mat_of_cols n b"
    using a b by blast
  then have vsn: "vs  carrier_mat ?m ?m" using b(2) by auto
  have "set a  lattice_of b"
    using a(3) lat_eq basis_in_latticeI by auto
  from subset_lattice_ofD[OF this]
  obtain us where
    us: "us  carrier_mat (length b) (length a)"
    "mat_of_cols n b * map_mat of_int us = mat_of_cols n a"
    using a b by blast
  then have usn: "us  carrier_mat ?m ?m" using b(2) by auto
  have "mat_of_cols n a =
    (mat_of_cols n a * map_mat of_int vs) * map_mat of_int us"
    using us vs by simp
  also have "... = mat_of_cols n a * (map_mat of_int vs * map_mat of_int us)"
    by (auto intro!: assoc_mult_mat us vs)
  also have "... = mat_of_cols n a * (map_mat of_int (vs * us))"
    by (metis of_int_hom.mat_hom_mult us(1) vs(1))
  finally have "mat_of_cols n a = mat_of_cols n a * (map_mat of_int (vs * us))" .

  from lin_indpt_mul_eq_ident[OF a(1-3) _ _ this]
  have "((map_mat of_int (vs * us))::'a mat) = (1m ?m::'a mat)"
    using vsn usn by auto
  then have "vs * us = 1m ?m"
    by (simp add: of_int_hom.mat_hom_inj of_int_hom.mat_hom_one)

  then have "det vs * det us = 1"
    using det_mult[OF vsn usn] by simp
  then have "(det us)^2 = 1"
    using power2_eq_1_iff zmult_eq_1_iff by blast
  then have us1: "(det (map_mat of_int us))^2 = 1"
    by (metis of_int_hom.hom_det of_int_hom.hom_one of_int_power)

  have Bcarr:"B  carrier_mat n ?m"
    unfolding B using b by auto
  then have BtBcarr:"BT * B  carrier_mat ?m ?m" by auto
  then have uBtBcarr: " (of_int_hom.mat_hom us)T * (BT * B)  carrier_mat ?m ?m"
    using usn
    by simp

  have d: "of_int_hom.mat_hom us  carrier_mat (length a) (length a)"
    using b(2) us(1) by auto
  have "det (AT * A) =
    det ((B * map_mat of_int us)T * (B * map_mat of_int us))"
      using us A B by auto
  also have "... = det ( (map_mat of_int us)T * (BT * B)  * map_mat of_int us)"
      apply (subst transpose_mult[OF Bcarr d])
    using us b
    by (smt (verit, ccfv_threshold) Bcarr assoc_mult_mat map_carrier_mat mult_carrier_mat transpose_carrier_mat)
  also have "... = det (map_mat of_int us)^2 * det (BT * B)"
    apply (subst det_mult[OF uBtBcarr d])
    apply (subst det_mult[OF _ BtBcarr])
    using usn by (auto simp add: det_transpose power2_eq_square)
  finally show ?thesis
    using us1
    by (metis l_one)
qed

lemma lattice_of_eq_gram_det_rows_eq:
  fixes a b::"'a vec list"
  assumes a: "distinct a" "lin_indpt (set a)" "set a  carrier_vec n"
  assumes b: "set b  carrier_vec n"  "length a = length b"
  assumes lat_eq: "lattice_of a = lattice_of b"
  defines A: "A  mat_of_rows n a"
  defines B: "B  mat_of_rows n b"
  shows "det (A * AT) = det (B * BT)"
proof -
  have transpose_mat_a: "A = transpose_mat (mat_of_cols n a)"
    by (simp add: transpose_mat_of_cols A)
  have transpose_mat_b: "B = transpose_mat (mat_of_cols n b)"
    by (simp add: transpose_mat_of_cols B)
  from lattice_of_eq_gram_det_eq[OF assms(1-6)]
  have "det ((mat_of_cols n a)T * mat_of_cols n a) =
    det ((mat_of_cols n b)T * mat_of_cols n b)" by blast
  then show ?thesis
  using transpose_mat_a transpose_mat_b
  by auto
qed

lemma lattice_of_eq_sq_det_eq:
  fixes a b::"'a vec list"
  assumes a: "distinct a" "lin_indpt (set a)" "set a  carrier_vec n" "length a = n"
  assumes b: "set b  carrier_vec n" "length b = n"
  assumes lat_eq: "lattice_of a = lattice_of b"
  shows "(det (mat_of_cols n a))^2 = (det (mat_of_cols n b))^2"
proof -
  from lattice_of_eq_gram_det_eq[OF a(1-3) b(1) _ lat_eq]
  have "det ((mat_of_cols n a)T * (mat_of_cols n a)) =
    det ((mat_of_cols n b)T * (mat_of_cols n b))"
    using a b by auto

  thus ?thesis
    by (metis assms(4) b(2) carrier_matI det_mult det_transpose index_transpose_mat(2) index_transpose_mat(3) mat_of_cols_carrier(2) mat_of_cols_carrier(3) power2_eq_square)
qed

lemma lattice_of_eq_sq_det_rows_eq:
  fixes a b::"'a vec list"
  assumes a: "distinct a" "lin_indpt (set a)" "set a  carrier_vec n" "length a = n"
  assumes b: "set b  carrier_vec n" "length b = n"
  assumes lat_eq: "lattice_of a = lattice_of b"
  shows "(det (mat_of_rows n a))^2 = (det (mat_of_rows n b))^2"
proof - 
  have transpose_mat_a: "mat_of_rows n a = transpose_mat (mat_of_cols n a)"
    by (simp add: transpose_mat_of_cols)
  have transpose_mat_b: "mat_of_rows n b = transpose_mat (mat_of_cols n b)"
    by (simp add: transpose_mat_of_cols)
  have "(det (mat_of_cols n a))2 = (det (mat_of_cols n b))2"
    using lattice_of_eq_sq_det_eq assms by blast
  then show ?thesis
  using transpose_mat_a transpose_mat_b det_transpose 
  by (metis assms(4) b(2) mat_of_rows_carrier(1) transpose_mat_of_rows)
qed

end

context LLL_with_assms
begin

text ‹ This next lemma bounds the size of the shortest vector 
  by the determinant. ›
lemma short_vector_det_bound:
  assumes m: "m  0"
  assumes k: "k  m"
  shows "
    (rat_of_int short_vector2) ^ k  
    α ^ (k * (k-1) div 2) * rat_of_int (gs.Gramian_determinant reduce_basis k)"
proof -

  from reduce_basis
  have rb: "lattice_of reduce_basis = L" 
    "reduced reduce_basis m" 
    "lin_indep reduce_basis" 
    "length reduce_basis = m" by auto

  have sv0: "short_vector = reduce_basis ! 0"
    using rb m unfolding short_vector_def
    by (metis hd_conv_nth list.size(3))

  have "map of_int_hom.vec_hom reduce_basis ! 0 = of_int_hom.vec_hom short_vector"
    apply (subst nth_map)
    using m by (auto simp add: sv0 rb)

  then have sve: "rat_of_int  short_vector 2 =
    gram_schmidt_fs.gso n (map of_int_hom.vec_hom reduce_basis) 02"
    by (smt (verit, del_insts) LLL_invD(2) assms(1) cof_vec_space.lin_indpt_list_def gram_schmidt_fs_Rn.fs0_gso0 gram_schmidt_fs_Rn.intro not_gr0 rb(3) reduce_basis_inv sq_norm_of_int)

  from reduce_basis_inv
  have LLLi: "LLL_invariant True m reduce_basis" by auto

  then have "LLL_invariant_weak reduce_basis"
    using LLL_inv_imp_w by auto

  from Gramian_determinant[OF this k]
  have gd: "rat_of_int (gs.Gramian_determinant reduce_basis k) =
    (i<k.
        gram_schmidt_fs.gso n (map of_int_hom.vec_hom reduce_basis) i2) "
    "0 < gs.Gramian_determinant reduce_basis k" by auto
                
  have "weakly_reduced reduce_basis m"
    using LLL.LLL_invD(8) LLLi by blast

  then have *: "( i. Suc i < m  
   gram_schmidt_fs.gso n (map of_int_hom.vec_hom reduce_basis) i2 
    α * gram_schmidt_fs.gso n (map of_int_hom.vec_hom reduce_basis) (Suc i)2)"
    using gram_schmidt_fs.weakly_reduced_def by blast

  have **: "i < k 
    gram_schmidt_fs.gso n (map of_int_hom.vec_hom reduce_basis) 02 
    α^i * gram_schmidt_fs.gso n (map of_int_hom.vec_hom reduce_basis) i2" for i
  proof (induction i)
    case 0
    then show ?case
      by auto
  next
    case (Suc i)
    from Suc
    have 1: "gram_schmidt_fs.gso n (map of_int_hom.vec_hom reduce_basis) 02
       α ^ i * gram_schmidt_fs.gso n (map of_int_hom.vec_hom reduce_basis) i2"
      by auto

    have "Suc i < m"
      using Suc(2) k order_less_le_trans by blast
    then
    have "gram_schmidt_fs.gso n (map of_int_hom.vec_hom reduce_basis) i2
       α * gram_schmidt_fs.gso n (map of_int_hom.vec_hom reduce_basis) (Suc i)2"
      by (meson "*" Suc(2) less_Suc_eq not_less_eq)

    then have 2: "α ^ i *gram_schmidt_fs.gso n (map of_int_hom.vec_hom reduce_basis) i2
       α ^ (i+1) * gram_schmidt_fs.gso n (map of_int_hom.vec_hom reduce_basis) (Suc i)2"
      by (simp add: α0(1))

    show ?case using 1 2 by auto
  qed

  have "(rat_of_int  short_vector 2) ^ k =
   (i<k. gram_schmidt_fs.gso n (map of_int_hom.vec_hom reduce_basis) 02)"
    using prod_pow sve
    by simp

  also have "...  (i<k. α^i * gram_schmidt_fs.gso n (map of_int_hom.vec_hom reduce_basis) i2)"
    apply (intro prod_mono)
    using ** by auto

  also have "... = (i<k. α^i) * rat_of_int (gs.Gramian_determinant reduce_basis k)"
    unfolding gd
    by simp

  also have "... =  α^(k * (k-1) div 2) * rat_of_int (gs.Gramian_determinant reduce_basis k)"
    unfolding power_sum[symmetric]
    by (metis Sum_Ico_nat atLeast0LessThan mult_eq_0_iff verit_minus_simplify(2))

  finally show ?thesis .
qed

lemma square_Gramian_determinant_eq_det_square:
  assumes sq:"n = m"
  shows "gs.Gramian_determinant fs_init m =
    (det (mat_of_rows m fs_init))^2"
  unfolding gs.Gramian_determinant_def
  apply (subst gs.Gramian_matrix_alt_def)
  apply (simp add: len)
  by (metis LLL_invD(9) assms det_mult det_transpose len mat_of_cols_carrier(1) mat_of_rows_carrier(1) power2_eq_square reduce_basis_inv take_all transpose_mat_of_rows)

end

end