{
 "cells": [
  {
   "cell_type": "markdown",
   "source": [
    "# Positive dimensional roots at infinity\n",
    "**Adapted from**: Example 6.14 p. 102 of [D13]\n",
    "\n",
    "[D13] Dreesen, Philippe.\n",
    "*Back to the Roots: Polynomial System Solving Using Linear Algebra*\n",
    "Ph.D. thesis (2013)"
   ],
   "metadata": {}
  },
  {
   "outputs": [],
   "cell_type": "code",
   "source": [
    "using LinearAlgebra\n",
    "using TypedPolynomials\n",
    "using MacaulayMatrix\n",
    "using JuMP\n",
    "using MultivariateMoments"
   ],
   "metadata": {},
   "execution_count": 1
  },
  {
   "cell_type": "markdown",
   "source": [
    "Consider the system given in [D13, Example 6.14] which corresponds to `Systems/dreesen10` of [macaulaylab](http://www.macaulaylab.net/):"
   ],
   "metadata": {}
  },
  {
   "outputs": [
    {
     "output_type": "execute_result",
     "data": {
      "text/plain": "4-element Vector{MultivariatePolynomials.VectorPolynomial{Float64, MultivariatePolynomials.Term{Float64, TypedPolynomials.Monomial{(x₁, x₂, x₃, x₄), 4}}}}:\n -1.0 + x₂ + x₁\n x₂x₄ + x₁x₃\n -0.6666666666666666 + x₂x₄² + x₁x₃²\n x₂x₄³ + x₁x₃³"
     },
     "metadata": {},
     "execution_count": 2
    }
   ],
   "cell_type": "code",
   "source": [
    "@polyvar x[1:4]\n",
    "system = [\n",
    "    x[1] + x[2] - 1,\n",
    "    x[1] * x[3] + x[2] * x[4],\n",
    "    x[1] * x[3]^2 + x[2] * x[4]^2 - 2/3,\n",
    "    x[1] * x[3]^3 + x[2] * x[4]^3,\n",
    "]"
   ],
   "metadata": {},
   "execution_count": 2
  },
  {
   "cell_type": "markdown",
   "source": [
    "With the classical MacaulayMatrix approach, the nullity increases by 2 at every degree\n",
    "because of the positive dimensional solution set at infinity."
   ],
   "metadata": {}
  },
  {
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[ Info: Added 56 rows to complete columns up to degree 4\n",
      "[ Info: Nullspace of dimensions (65, 15) computed from Macaulay matrix of dimension (56, 65) in 0.000884172 seconds.\n",
      "[ Info: Added 69 rows to complete columns up to degree 5\n",
      "[ Info: Nullspace of dimensions (120, 17) computed from Macaulay matrix of dimension (125, 120) in 0.001944609 seconds.\n",
      "[ Info: Added 121 rows to complete columns up to degree 6\n",
      "[ Info: Nullspace of dimensions (203, 18) computed from Macaulay matrix of dimension (246, 203) in 0.007104496 seconds.\n",
      "[ Info: Found 2 real solutions\n"
     ]
    },
    {
     "output_type": "execute_result",
     "data": {
      "text/plain": "2-element Vector{Vector{Float64}}:\n [0.4999999999999999, 0.5000000000000001, 0.8164965809277263, -0.8164965809277261]\n [0.5000000000000001, 0.5, -0.816496580927726, 0.8164965809277256]"
     },
     "metadata": {},
     "execution_count": 3
    }
   ],
   "cell_type": "code",
   "source": [
    "solve_system(system, column_maxdegree = 6)"
   ],
   "metadata": {},
   "execution_count": 3
  },
  {
   "cell_type": "markdown",
   "source": [
    "Let's compute the moment matrix of degree 4 from the nullspace of\n",
    "the Macaulay matrix of degree 5 using the Clarabel SDP solver:"
   ],
   "metadata": {}
  },
  {
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[ Info: Nullspace of dimensions (120, 17) computed from Macaulay matrix of dimension (125, 120) in 0.001968263 seconds.\n",
      "-------------------------------------------------------------\n",
      "           Clarabel.jl v0.9.0  -  Clever Acronym              \n",
      "                   (c) Paul Goulart                          \n",
      "                University of Oxford, 2022                   \n",
      "-------------------------------------------------------------\n",
      "\n",
      "problem:\n",
      "  variables     = 17\n",
      "  constraints   = 121\n",
      "  nnz(P)        = 0\n",
      "  nnz(A)        = 2025\n",
      "  cones (total) = 2\n",
      "    : Zero        = 1,  numel = 1\n",
      "    : PSDTriangle = 1,  numel = 120\n",
      "\n",
      "settings:\n",
      "  linear algebra: direct / qdldl, precision: Float64\n",
      "  max iter = 200, time limit = Inf,  max step = 0.990\n",
      "  tol_feas = 1.0e-08, tol_gap_abs = 1.0e-08, tol_gap_rel = 1.0e-08,\n",
      "  static reg : on, ϵ1 = 1.0e-08, ϵ2 = 4.9e-32\n",
      "  dynamic reg: on, ϵ = 1.0e-13, δ = 2.0e-07\n",
      "  iter refine: on, reltol = 1.0e-13, abstol = 1.0e-12, \n",
      "               max iter = 10, stop ratio = 5.0\n",
      "  equilibrate: on, min_scale = 1.0e-04, max_scale = 1.0e+04\n",
      "               max iter = 10\n",
      "\n",
      "iter    pcost        dcost       gap       pres      dres      k/t        μ       step      \n",
      "---------------------------------------------------------------------------------------------\n",
      "  0   0.0000e+00  -0.0000e+00  0.00e+00  4.01e-01  3.42e+00  1.00e+00  3.22e+00   ------   \n",
      "  1   0.0000e+00  -1.1323e-03  1.13e-03  5.81e-02  4.66e-01  1.19e-01  4.64e-01  8.61e-01  \n",
      "  2   0.0000e+00   2.8687e-04  2.87e-04  1.56e-03  1.23e-02  3.48e-03  1.27e-02  9.74e-01  \n",
      "  3   0.0000e+00   2.8697e-06  2.87e-06  1.56e-05  1.23e-04  3.48e-05  1.28e-04  9.90e-01  \n",
      "  4   0.0000e+00   2.8697e-08  2.87e-08  1.56e-07  1.23e-06  3.48e-07  1.28e-06  9.90e-01  \n",
      "  5   0.0000e+00   2.8697e-10  2.87e-10  1.56e-09  1.23e-08  3.48e-09  1.28e-08  9.90e-01  \n",
      "  6   0.0000e+00   2.8697e-10  2.87e-10  1.56e-09  1.23e-08  3.48e-09  1.28e-08  0.00e+00  \n",
      "---------------------------------------------------------------------------------------------\n",
      "Terminated with status = solved (reduced accuracy)\n",
      "solve time = 12.2ms\n",
      "[ Info: Terminated with ALMOST_OPTIMAL (ALMOST_SOLVED) in 0.012238786000000001 seconds.\n",
      "┌ Warning: * Solver : Clarabel\n",
      "│ \n",
      "│ * Status\n",
      "│   Result count       : 1\n",
      "│   Termination status : ALMOST_OPTIMAL\n",
      "│   Message from the solver:\n",
      "│   \"ALMOST_SOLVED\"\n",
      "│ \n",
      "│ * Candidate solution (result #1)\n",
      "│   Primal status      : NEARLY_FEASIBLE_POINT\n",
      "│   Dual status        : NEARLY_FEASIBLE_POINT\n",
      "│   Objective value    : -0.00000e+00\n",
      "│   Dual objective value : -2.86973e-10\n",
      "│ \n",
      "│ * Work counters\n",
      "│   Solve time (sec)   : 1.22388e-02\n",
      "│   Barrier iterations : 6\n",
      "└ @ MacaulayMatrix ~/.julia/packages/MacaulayMatrix/2RDTD/src/hankel.jl:25\n"
     ]
    },
    {
     "output_type": "execute_result",
     "data": {
      "text/plain": "MomentMatrix with row/column basis:\n MonomialBasis([1, x[4], x[3], x[2], x[1], x[4]^2, x[3]*x[4], x[3]^2, x[2]*x[4], x[2]*x[3], x[2]^2, x[1]*x[4], x[1]*x[3], x[1]*x[2], x[1]^2])\nAnd entries in a 15×15 MultivariateMoments.SymMatrix{Float64}:\n  1.0                      3.725744452537372e-10   …   0.2500000002434462\n  3.725744452537372e-10    0.6666666666666679          9.315270776966145e-11\n -3.7257428739390086e-10  -0.6666666666666662         -9.313425725077096e-11\n  0.5000000000000245       1.8628763028688544e-10      0.12499999987823852\n  0.499999999999976        1.8628704828715925e-10      0.1250000003652074\n  0.6666666666666679       2.4838786583103456e-10  …   0.16666666650438633\n -0.6666666666666662      -2.4838329223259015e-10     -0.16666666682896436\n  0.6666666666666669       2.4838344055144734e-10      0.1666666665043526\n  1.8628763028688544e-10   0.333333333333317           4.6547795999607455e-11\n -1.8628716624835562e-10  -0.3333333333333493         -4.6587067537018356e-11\n  0.25000000024349434      9.315274246413097e-11   …   0.2555649744273872\n  1.8628704828715925e-10   0.33333333333335063         4.660461599970134e-11\n -1.8628748023330477e-10  -0.333333333333317          -4.654744515178444e-11\n  0.2499999997565302       9.313424337298315e-11      -0.13056497454914884\n  0.2500000002434462       9.315270776966145e-11       0.2555649749143563"
     },
     "metadata": {},
     "execution_count": 4
    }
   ],
   "cell_type": "code",
   "source": [
    "import Clarabel\n",
    "ν = moment_matrix(system, Clarabel.Optimizer, 5)"
   ],
   "metadata": {},
   "execution_count": 4
  },
  {
   "cell_type": "markdown",
   "source": [
    "The nullspace of this moment matrix is a Macaulay matrix"
   ],
   "metadata": {}
  },
  {
   "outputs": [
    {
     "output_type": "execute_result",
     "data": {
      "text/plain": "0×0 Macaulay matrix for polynomials:\n  5.897711921664038e-14 + 0.9999999999999994*x[4] + x[3] - 1.4682525325425826e-14*x[4]^2\n  -0.5000000000001729 + 3.2596772937398524e-16*x[4] + x[2] + 1.0484472896961149e-13*x[4]^2\n  -0.49999999999983163 + 6.156031452731937e-17*x[4] + x[1] - 1.0394986062844766e-13*x[4]^2\n  0.666666666666666 + 5.268577352792162e-16*x[4] - 2.2195756829484477e-16*x[4]^2 + x[3]*x[4]\n  -1.536525535226813e-14 - 0.49999999999999983*x[4] + 9.01468757230224e-15*x[4]^2 + x[2]*x[4]\n  1.6090974574336504e-14 - 0.5000000000000008*x[4] - 1.0732798282105085e-14*x[4]^2 + x[1]*x[4]\nThe row shifts are:\nMonomialBasis([])\nThe column basis is:\nMonomialBasis([])\n"
     },
     "metadata": {},
     "execution_count": 5
    }
   ],
   "cell_type": "code",
   "source": [
    "M = nullspace(ν, ShiftNullspace())"
   ],
   "metadata": {},
   "execution_count": 5
  },
  {
   "cell_type": "markdown",
   "source": [
    "Each coefficient is close to a rational number, after some clean up,\n",
    "we get the following system:"
   ],
   "metadata": {}
  },
  {
   "outputs": [
    {
     "output_type": "execute_result",
     "data": {
      "text/plain": "6-element Vector{MultivariatePolynomials.VectorPolynomial{Float64, MultivariatePolynomials.Term{Float64, TypedPolynomials.Monomial{(x₁, x₂, x₃, x₄), 4}}}}:\n x₄ + x₃\n -1.0 + 2.0x₂\n -1.0 + 2.0x₁\n 2.0 + 3.0x₃x₄\n -x₄ + 2.0x₂x₄\n -x₄ + 2.0x₁x₄"
     },
     "metadata": {},
     "execution_count": 6
    }
   ],
   "cell_type": "code",
   "source": [
    "real_system = clean(M; tol = 1e-6).polynomials"
   ],
   "metadata": {},
   "execution_count": 6
  },
  {
   "cell_type": "markdown",
   "source": [
    "We have the equations `2x[1] = 1` and `2x[2] = 1` from which we get\n",
    "`x[1] = 1/2` and `x[2] = 1/2`.\n",
    "We then have the equations `x[3] = -x[4]` and `3x[3] * x[4] = -2` from\n",
    "which we get `x[3] = ±√(2/3)` and `x[4] = ∓√(2/3)`.\n",
    "Notice that the complex solutions are not solutions of the system anymore.\n",
    "The Macaulay solver indeed find these real solutions direction at degree 2."
   ],
   "metadata": {}
  },
  {
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[ Info: Added 18 rows to complete columns up to degree 2\n",
      "[ Info: Nullspace of dimensions (15, 2) computed from Macaulay matrix of dimension (18, 15) in 9.5116e-5 seconds.\n",
      "[ Info: Found 2 real solutions\n"
     ]
    },
    {
     "output_type": "execute_result",
     "data": {
      "text/plain": "2-element Vector{Vector{Float64}}:\n [0.4999999999999999, 0.5000000000000001, 0.8164965809277269, -0.8164965809277265]\n [0.5, 0.4999999999999998, -0.8164965809277267, 0.8164965809277263]"
     },
     "metadata": {},
     "execution_count": 7
    }
   ],
   "cell_type": "code",
   "source": [
    "solve_system(real_system, column_maxdegree = 2)"
   ],
   "metadata": {},
   "execution_count": 7
  },
  {
   "cell_type": "markdown",
   "source": [
    "---\n",
    "\n",
    "*This notebook was generated using [Literate.jl](https://github.com/fredrikekre/Literate.jl).*"
   ],
   "metadata": {}
  }
 ],
 "nbformat_minor": 3,
 "metadata": {
  "language_info": {
   "file_extension": ".jl",
   "mimetype": "application/julia",
   "name": "julia",
   "version": "1.11.0"
  },
  "kernelspec": {
   "name": "julia-1.11",
   "display_name": "Julia 1.11.0",
   "language": "julia"
  }
 },
 "nbformat": 4
}