Theory Multivariate_Resultant

subsection ‹Resultants of Multivariate Polynomials›

text ‹We utilize the conversion of multivariate polynomials into univariate polynomials
  for the definition of the resultant of multivariate polynomials via
  the resultant for univariate polynomials. In this way, we can use the algorithm
  to efficiently compute resultants for the multivariate case.›

theory Multivariate_Resultant
  imports 
    Poly_Connection
    Algebraic_Numbers.Resultant
    Subresultants.Subresultant
    MPoly_Divide_Code
    MPoly_Container
begin

hide_const (open) 
  MPoly_Type.degree
  MPoly_Type.coeff
  Symmetric_Polynomials.lead_coeff

lemma det_sylvester_matrix_higher_degree: 
  "det (sylvester_mat_sub (degree f + n) (degree g) f g)
  = det (sylvester_mat_sub (degree f) (degree g) f g) * (lead_coeff g * (-1)^(degree g))^n"
proof (induct n)
  case (Suc n)
  let ?A = "sylvester_mat_sub (degree f + Suc n) (degree g) f g" 
  let ?d = "degree f + Suc n + degree g" 
  define h where "h i = ?A $$ (i,0) * cofactor ?A i 0" for i
  have mult_left_zero: "x = 0  x * y = 0" for x y :: 'a by auto
  have "det ?A = (i<?d. h i)" 
    unfolding h_def
    by (rule laplace_expansion_column[OF sylvester_mat_sub_carrier, of 0], force)
  also have " = sum h ({degree g}  ({..<?d} - {degree g}))" 
    by (rule sum.cong, auto)
  also have " = sum h {degree g} + sum h ({..<?d} - {degree g})" 
    by (rule sum.union_disjoint, auto)
  also have "sum h ({..<?d} - {degree g}) = 0" 
    unfolding h_def
    by (intro sum.neutral ballI mult_left_zero, auto simp: sylvester_mat_sub_def coeff_eq_0)
  also have "sum h {degree g} = h (degree g)" by simp
  also have " = lead_coeff g * cofactor ?A (degree g) 0" unfolding h_def
    by (rule arg_cong[of _ _ "λ x. x * _"], simp add: sylvester_mat_sub_def)
  also have "cofactor ?A (degree g) 0 = (-1)^(degree g) * det (sylvester_mat_sub (degree f + n) (degree g) f g)" 
    unfolding cofactor_def
  proof (intro arg_cong2[of _ _ _ _ "λ x y. (-1)^x * det y"], force)
    show "mat_delete ?A (degree g) 0 = sylvester_mat_sub (degree f + n) (degree g) f g" 
      unfolding sylvester_mat_sub_def
      by (intro eq_matI, auto simp: mat_delete_def coeff_eq_0)
  qed
  finally show ?case unfolding Suc by simp
qed simp

text ‹The conversion of multivariate into univariate polynomials permits us to define resultants in the multivariate
  setting. Since in our application one of the polynomials is already univariate, we use a non-symmetric definition
  where only one of the input polynomials is multivariate.›
definition resultant_mpoly_poly :: "nat  'a :: comm_ring_1 mpoly  'a poly  'a mpoly" where
  "resultant_mpoly_poly x p q = resultant (mpoly_to_mpoly_poly x p) (map_poly Const q)"

text ‹This lemma tells us that there is only a minor difference between computing the multivariate resultant and then
  plugging in values, or first inserting values and then evaluate the univariate resultant.›
lemma insertion_resultant_mpoly_poly: "insertion α (resultant_mpoly_poly x p q) = resultant (partial_insertion α x p) q * 
  (lead_coeff q * (-1)^ degree q)^(degree (mpoly_to_mpoly_poly x p) - degree (partial_insertion α x p))" 
proof -
  let ?pa = "partial_insertion α x" 
  let ?a = "insertion α" 
  let ?q = "map_poly Const q" 
  let ?m = "mpoly_to_mpoly_poly x" 
  interpret a: comm_ring_hom ?a by (rule comm_ring_hom_insertion)
  define m where "m = degree (?m p) - degree (?pa p)" 
  from degree_partial_insertion_le_mpoly[of α x p] have deg: "degree (?m p) = degree (?pa p) + m" unfolding m_def by simp
  define k where "k = degree (?pa p) + m" 
  define l where "l = degree q" 
  have "resultant (?pa p) q = det (sylvester_mat_sub (degree (?pa p)) (degree q) (?pa p) q)" 
    unfolding resultant_def sylvester_mat_def by simp
  have "?a (resultant_mpoly_poly x p q) = ?a (det (sylvester_mat_sub (degree (?pa p) + m) (degree q) (?m p) ?q))" 
    unfolding resultant_mpoly_poly_def resultant_def sylvester_mat_def degree_map_poly_Const deg ..
  also have " =
    det (a.mat_hom (sylvester_mat_sub (degree (?pa p) + m) (degree q) (?m p) ?q))" 
    unfolding a.hom_det ..
  also have "a.mat_hom (sylvester_mat_sub (degree (?pa p) + m) (degree q) (?m p) ?q)
    = sylvester_mat_sub (degree (?pa p) + m) (degree q) (?pa p) q" 
    unfolding k_def[symmetric] l_def[symmetric] 
    by (intro eq_matI, auto simp: sylvester_mat_sub_def coeff_map_poly)
  also have "det  = det (sylvester_mat_sub (degree (?pa p)) (degree q) (?pa p) q) * (lead_coeff q * (- 1) ^ degree q) ^ m" 
    by (subst det_sylvester_matrix_higher_degree, simp)
  also have "det (sylvester_mat_sub (degree (?pa p)) (degree q) (?pa p) q) = resultant (?pa p) q" 
    unfolding resultant_def sylvester_mat_def by simp
  finally show ?thesis unfolding m_def by auto
qed

lemma insertion_resultant_mpoly_poly_zero: fixes q :: "'a :: idom poly" 
  assumes q: "q  0" 
  shows "insertion α (resultant_mpoly_poly x p q) = 0  resultant (partial_insertion α x p) q = 0" 
  unfolding insertion_resultant_mpoly_poly using q by auto

lemma vars_resultant: "vars (resultant p q)   (vars ` (range (coeff p)  range (coeff q)))" 
  unfolding resultant_def det_def sylvester_mat_def sylvester_mat_sub_def 
  apply simp
  apply (rule order.trans[OF vars_setsum]) 
  subgoal using finite_permutations by blast
  apply (rule UN_least)
  apply (rule order.trans[OF vars_mult]) 
  apply simp
  apply (rule order.trans[OF vars_prod])
  apply (rule UN_least)
  by auto

text ‹By taking the resultant, one variable is deleted.›
lemma vars_resultant_mpoly_poly: "vars (resultant_mpoly_poly x p q)  vars p - {x}" 
proof
  fix y
  assume "y  vars (resultant_mpoly_poly x p q)" 
  from set_mp[OF vars_resultant this[unfolded resultant_mpoly_poly_def]] obtain i 
    where "y  vars (coeff (mpoly_to_mpoly_poly x p) i)  y  vars (coeff (map_poly Const q) i)" 
    by auto
  moreover have "vars (coeff (map_poly Const q) i) = {}" 
    by (subst coeff_map_poly, auto)
  ultimately have "y  vars (coeff (mpoly_to_mpoly_poly x p) i)" by auto
  thus "y  More_MPoly_Type.vars p - {x}" using vars_coeff_mpoly_to_mpoly_poly by blast
qed

text ‹For resultants, we manually have to select the implementation that 
  works on integral domains, because there is no factorial ring instance for @{typ "int mpoly"}.›

lemma resultant_mpoly_poly_code[code]:
  "resultant_mpoly_poly x p q = resultant_impl_basic (mpoly_to_mpoly_poly x p) (map_poly Const q)"
  unfolding resultant_mpoly_poly_def div_exp_basic.resultant_impl by simp

end