@@ -73,21 +73,26 @@ function is_commutative(vars)
7373 return length (vars) < 2 || prod (vars[1 : 2 ]) == prod (reverse (vars[1 : 2 ]))
7474end
7575
76+ abstract type AbstractNewtonPolytopeApproximation end
77+
78+ # `maxdegree` and `mindegree` on each variable separately
79+ # and on each group of variable.
80+ struct NewtonDegreeBounds{NPT} <: AbstractNewtonPolytopeApproximation
81+ variable_groups:: NPT
82+ end
83+
7684# Multipartite
7785# TODO we might do this recursively : do 2 parts, merge them, merge with next
7886# one and so on so that the filter at the end prunes more.
79- function half_newton_polytope (
80- X:: AbstractVector ,
81- parts:: Tuple ;
82- apply_post_filter = true ,
83- )
87+ function half_newton_polytope (X:: AbstractVector , newton:: NewtonDegreeBounds )
8488 if ! is_commutative (MP. variables (X))
8589 throw (
8690 ArgumentError (
8791 " Multipartite Newton polytope not supported with noncommutative variables." ,
8892 ),
8993 )
9094 end
95+ parts = newton. variable_groups
9196 if ! all (
9297 i -> all (j -> i == j || isempty (parts[i] ∩ parts[j]), 1 : length (parts)),
9398 1 : length (parts),
@@ -108,25 +113,23 @@ function half_newton_polytope(
108113 end
109114 if length (all_parts) == 1
110115 # all variables on same part, fallback to shortcut
111- return half_newton_polytope (
112- X,
113- tuple ();
114- apply_post_filter = apply_post_filter,
115- )
116+ return half_newton_polytope (X, NewtonDegreeBounds (tuple ()))
116117 end
117118 monovecs = map (vars -> sub_half_newton_polytope (X, vars), all_parts)
118119 # Cartesian product of the newton polytopes of the different parts
119120 product = [prod (monos) for monos in Iterators. product (monovecs... )]
120121 mindeg, maxdeg = cfld (MP. extdegree (X), 2 )
121122 # We know that the degree inequalities are satisfied variable-wise and
122123 # part-wise but for all variables together so we filter with that
123- monos =
124- MP. monovec (filter (mono -> mindeg <= MP. degree (mono) <= maxdeg, product))
125- if apply_post_filter
126- return post_filter (monos, X)
127- else
128- return monos
129- end
124+ gram_monos = filter (mono -> mindeg <= MP. degree (mono) <= maxdeg, product)
125+ return MP. monovec (gram_monos)
126+ end
127+
128+ # Filters out points ouside the Newton polytope from the
129+ # outer approximation given by `outer_approximation`.
130+ struct NewtonFilter{N<: AbstractNewtonPolytopeApproximation } < :
131+ AbstractNewtonPolytopeApproximation
132+ outer_approximation:: N
130133end
131134
132135function __chip (cur, i, vars, exps, n, op)
@@ -162,16 +165,12 @@ end
162165
163166# Shortcut for more efficient `extdeg` and `exp` function in case all the
164167# variables are in the same part
165- function half_newton_polytope (
166- X:: AbstractVector ,
167- :: Tuple{} ;
168- apply_post_filter = true ,
169- )
168+ function half_newton_polytope (X:: AbstractVector , :: NewtonDegreeBounds{Tuple{}} )
170169 vars = MP. variables (X)
171170 if is_commutative (vars)
172171 # Commutative variables
173172 exp (mono, i) = MP. exponents (mono)[i]
174- monos = _sub_half_newton_polytope (X, MP. extdegree (X), exp, vars)
173+ return _sub_half_newton_polytope (X, MP. extdegree (X), exp, vars)
175174 else
176175 # Non-commutative variables
177176 # We use Newton chip method of [Section 2.3, BKP16].
@@ -201,15 +200,15 @@ function half_newton_polytope(
201200 end
202201 end
203202 end
204- monos = MP. monovec (_monos)
205- end
206- if apply_post_filter
207- return post_filter (monos, X)
208- else
209- return monos
203+ return MP. monovec (_monos)
210204 end
211205end
212206
207+ function half_newton_polytope (monos:: AbstractVector , newton:: NewtonFilter )
208+ gram_monos = half_newton_polytope (monos, newton. outer_approximation)
209+ return post_filter (gram_monos, monos)
210+ end
211+
213212# If `mono` is such that there is no other way to have `mono^2` by multiplying
214213# two different monomials of `monos` and `mono` is not in `X` then, the corresponding
215214# diagonal entry of the Gram matrix will be zero hence the whole column and row
@@ -271,15 +270,10 @@ function post_filter(monos, X)
271270end
272271
273272function monomials_half_newton_polytope (
274- X:: AbstractVector ,
275- parts;
276- apply_post_filter = true ,
273+ monos:: AbstractVector ,
274+ newton:: AbstractNewtonPolytopeApproximation ,
277275)
278- return half_newton_polytope (
279- MP. monovec (X),
280- parts;
281- apply_post_filter = apply_post_filter,
282- )
276+ return half_newton_polytope (MP. monovec (monos), newton)
283277end
284278
285279struct DegreeBounds{M}
@@ -289,6 +283,27 @@ struct DegreeBounds{M}
289283 variablewise_maxdegree:: M
290284end
291285
286+ # TODO add to MP
287+ function _map_powers (f, mono)
288+ exps = map (f, MP. powers (mono))
289+ return _monomial (MP. variables (mono), exps)
290+ end
291+ function _map_exponents (f, mono)
292+ exps = map (f, MP. exponents (mono))
293+ return _monomial (MP. variables (mono), exps)
294+ end
295+
296+ _min_half (d:: Integer ) = cld (d, 2 )
297+ _max_half (d:: Integer ) = fld (d, 2 )
298+ function _half (d:: DegreeBounds )
299+ return DegreeBounds (
300+ _min_half (d. mindegree),
301+ _max_half (d. maxdegree),
302+ _map_exponents (_min_half, d. variablewise_mindegree),
303+ _map_exponents (_max_half, d. variablewise_maxdegree),
304+ )
305+ end
306+
292307function min_degree (p, v)
293308 return mapreduce (Base. Fix2 (MP. degree, v), min, MP. monomials (p))
294309end
@@ -306,35 +321,30 @@ function max_degree(p, v)
306321end
307322
308323function min_shift (d, shift)
309- return max (0 , cld (d - shift, 2 ))
310- end
311- function max_shift (d, shift)
312- return fld (d - shift, 2 )
324+ return max (0 , d - shift)
313325end
314326
315327function minus_shift (
316328 deg,
317- m :: MP.AbstractMonomial ,
329+ mono :: MP.AbstractMonomial ,
318330 p:: MP.AbstractPolynomialLike ,
319331 shift,
320332)
321- exps = map (MP . powers (m) ) do (v, d)
333+ return _map_powers (mono ) do (v, d)
322334 return shift (d, deg (p, v))
323335 end
324- return _monomial (MP. variables (m), exps)
325336end
326337
327338function minus_shift (d:: DegreeBounds , p:: MP.AbstractPolynomialLike )
328339 var_mindegree =
329340 minus_shift (min_degree, d. variablewise_mindegree, p, min_shift)
330- var_maxdegree =
331- minus_shift (max_degree, d. variablewise_maxdegree, p, max_shift)
341+ var_maxdegree = minus_shift (max_degree, d. variablewise_maxdegree, p, - )
332342 if isnothing (var_maxdegree)
333343 return
334344 end
335345 return DegreeBounds (
336346 min_shift (d. mindegree, MP. mindegree (p)),
337- max_shift ( d. maxdegree, MP. maxdegree (p) ),
347+ d. maxdegree - MP. maxdegree (p),
338348 var_mindegree,
339349 var_maxdegree,
340350 )
@@ -436,8 +446,8 @@ function putinar_degree_bounds(
436446)
437447 mindegree = 0
438448 # TODO homogeneous case
439- mindeg (g) = min_shift (mindegree, MP. mindegree (g))
440- maxdeg (g) = max_shift (maxdegree, MP. maxdegree (g))
449+ mindeg (g) = _min_half ( min_shift (mindegree, MP. mindegree (g) ))
450+ maxdeg (g) = _max_half (maxdegree - MP. maxdegree (g))
441451 degrange (g) = mindeg (g): maxdeg (g)
442452 minus_degrange (g) = - maxdeg (g): - mindeg (g)
443453 # The multiplier will have degree `0:2fld(maxdegree - MP.maxdegree(g), 2)`
@@ -477,3 +487,52 @@ function putinar_degree_bounds(
477487 _monomial (vars, vars_maxdeg),
478488 )
479489end
490+
491+ function multiplier_basis (g:: MP.AbstractPolynomialLike , bounds:: DegreeBounds )
492+ shifted = minus_shift (bounds, g)
493+ if isnothing (shifted)
494+ halved = nothing
495+ else
496+ halved = _half (shifted)
497+ end
498+ basis = MB. MonomialBasis
499+ if isnothing (halved)
500+ # TODO add `MB.empty_basis` to API
501+ return MB. maxdegree_basis (
502+ basis,
503+ MP. variables (bounds. variablewise_mindegree),
504+ - 1 ,
505+ )
506+ else
507+ return maxdegree_gram_basis (basis, halved)
508+ end
509+ end
510+
511+ function half_newton_polytope (
512+ p:: MP.AbstractPolynomialLike ,
513+ gs:: AbstractVector{<:MP.AbstractPolynomialLike} ,
514+ vars,
515+ maxdegree,
516+ :: NewtonDegreeBounds ,
517+ )
518+ # TODO take `variable_groups` into account
519+ bounds = putinar_degree_bounds (p, gs, vars, maxdegree)
520+ return [multiplier_basis (g, bounds) for g in gs]
521+ end
522+
523+ function half_newton_polytope (
524+ p:: MP.AbstractPolynomialLike ,
525+ gs:: AbstractVector{<:MP.AbstractPolynomialLike} ,
526+ vars,
527+ maxdegree,
528+ newton:: NewtonFilter{<:NewtonDegreeBounds} ,
529+ )
530+ # TODO
531+ return half_newton_polytope (
532+ p,
533+ gs,
534+ vars,
535+ maxdegree,
536+ newton. outer_approximation,
537+ )
538+ end
0 commit comments