Skip to content

Commit 65999b4

Browse files
committed
Fixes
1 parent 02bfd28 commit 65999b4

File tree

1 file changed

+89
-96
lines changed

1 file changed

+89
-96
lines changed

src/Certificate/newton_polytope.jl

Lines changed: 89 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -209,70 +209,6 @@ function half_newton_polytope(basis::SA.ExplicitBasis, newton::NewtonFilter)
209209
return post_filter(gram_monos, monos)
210210
end
211211

212-
# If `mono` is such that there is no other way to have `mono^2` by multiplying
213-
# two different monomials of `monos` and `mono` is not in `X` then, the corresponding
214-
# diagonal entry of the Gram matrix will be zero hence the whole column and row
215-
# will be zero hence we can remove this monomial.
216-
# See [Proposition 3.7, CLR95], [Theorem 2, L09] or [Section 2.4, BKP16].
217-
218-
# [CLR95] Choi, M. D. and Lam, T. Y. and Reznick, B.
219-
# *Sum of Squares of Real Polynomials*.
220-
# Proceedings of Symposia in Pure mathematics (1995)
221-
#
222-
# [L09] Lofberg, Johan.
223-
# *Pre-and post-processing sum-of-squares programs in practice*.
224-
# IEEE transactions on automatic control 54.5 (2009): 1007-1011.
225-
#
226-
# [BKP16] Sabine Burgdorf, Igor Klep, and Janez Povh.
227-
# *Optimization of polynomials in non-commuting variables*.
228-
# Berlin: Springer, 2016.
229-
function post_filter(monos, X)
230-
num = Dict(mono => 1 for mono in X)
231-
function _increase(mono)
232-
return num[mono] = get(num, mono, 0) + 1
233-
end
234-
function _decrease(mono)
235-
value = num[mono] - 1
236-
if iszero(value)
237-
delete!(num, mono)
238-
else
239-
num[mono] = value
240-
end
241-
return iszero(value)
242-
end
243-
for a in monos
244-
for b in monos
245-
if a != b
246-
_increase(a * b)
247-
end
248-
end
249-
end
250-
back = Dict{eltype(monos),Int}()
251-
keep = ones(Bool, length(monos))
252-
function _delete(i)
253-
keep[i] = false
254-
a = monos[i]
255-
for (j, b) in enumerate(monos)
256-
if keep[j] && a != b
257-
for w in [a * b, b * a]
258-
if _decrease(w) && haskey(back, w)
259-
_delete(back[w])
260-
end
261-
end
262-
end
263-
end
264-
end
265-
for (i, mono) in enumerate(monos)
266-
w = mono^2
267-
if haskey(num, w)
268-
back[w] = i
269-
else
270-
_delete(i)
271-
end
272-
end
273-
return monos[findall(keep)]
274-
end
275-
276212
struct DegreeBounds{M}
277213
mindegree::Int
278214
maxdegree::Int
@@ -305,7 +241,6 @@ function min_degree(p::SA.AlgebraElement, v)
305241
return mapreduce(Base.Fix2(min_degree, v), min, SA.supp(p))
306242
end
307243
min_degree(p::MB.Polynomial{MB.Monomial}, v) = MP.degree(p.monomial, v)
308-
minus_min_degree(p, v) = -min_degree(p, v)
309244

310245
function _monomial(vars, exps)
311246
if any(Base.Fix2(isless, 0), exps)
@@ -389,13 +324,21 @@ function _interval(a, b)
389324
end
390325
end
391326

327+
function _gram_shift_min(d_g, d_s, g::SA.AlgebraElement)
328+
if _is_monomial_basis(typeof(g))
329+
return d_g + 2minimum(d_s)
330+
else
331+
return 0
332+
end
333+
end
334+
392335
function deg_range(deg, p, gs, gram_deg, truncation)
393336
d_max = min(deg(p), truncation)
394337
sign = deg_sign(deg, p, d_max)
395338
for g in gs
396339
d_g, sign_g = deg_sign(deg, g)
397340
d_s = gram_deg(g)
398-
if isempty(d_s) || d_g + 2minimum(d_s) > truncation
341+
if isempty(d_s) || _gram_shift_min(d_g, d_s, g) > truncation
399342
continue
400343
end
401344
d = d_g + 2maximum(d_s)
@@ -429,7 +372,7 @@ end
429372

430373
# deg_range(deg, p, gs, gram_deg, range)
431374
#
432-
# Maximum value of `deg(s_0 = p - sum s_i g_i for g in gs) in range` where
375+
# Maximum value of `deg(s_0 = p - sum s_i g_i for i in eachindex(gs)) in range` where
433376
# `s_0, s_i` are SOS and `deg(s_i) <= gram_deg(g)`.
434377
# Note that `range` should be in increasing order.
435378
function deg_range(deg, p, gs, gram_deg, range::UnitRange)
@@ -451,6 +394,40 @@ _maxdegree(a::SA.AlgebraElement) = maximum(_maxdegree, SA.supp(a))
451394
_mindegree(p::MB.Polynomial{MB.Monomial}) = MP.mindegree(p.monomial)
452395
_maxdegree(p::MB.Polynomial{MB.Monomial}) = MP.maxdegree(p.monomial)
453396

397+
_is_monomial_basis(::Type{<:SA.AlgebraElement{<:MB.Algebra{BT,B}}}) = true
398+
_is_monomial_basis(::Type{<:SA.AlgebraElement}) = false
399+
400+
# Minimum degree of a gram basis for a gram matrix `s`
401+
# such that the minimum degree of `s * g` is at least `mindegree`.
402+
function _multiplier_mindegree(
403+
mindegree,
404+
g::SA.AlgebraElement,
405+
)
406+
if _is_monomial_basis(typeof(g))
407+
return _min_half(min_shift(mindegree, _mindegree(g)))
408+
else
409+
# For instance, with `Chebyshev` a square already produces
410+
# a monomial of degree 0 so let's just give up here
411+
# The Chebyshev Newton polytope of the product of polynomials
412+
# with Chebyshev Newton polytope `N1` and `N2` is a subset
413+
# of `(N1 + N2 - R_+^n) ∩ R_+^n` so we compute
414+
# `σ(y, cheby(N1) + cheby(N2))` as `σ(max.(y, 0), N1 + N2)`
415+
return 0
416+
end
417+
end
418+
419+
# Maximum degree of a gram basis for a gram matrix `s`
420+
# such that the maximum degree of `s * g` is at most `maxdegree`.
421+
# Here, we assume that the degree of `*(a::MB.Polynomial, b::MB.Polynomial)`
422+
# is bounded by the sum of the degree of `a` and `b` for any basis.
423+
function _multiplier_maxdegree(maxdegree, g::SA.AlgebraElement)
424+
return _max_half(maxdegree - _maxdegree(g))
425+
end
426+
427+
function _multiplier_deg_range(range, g::SA.AlgebraElement)
428+
return _multiplier_mindegree(minimum(range), g):_multiplier_maxdegree(maximum(range), g)
429+
end
430+
454431
function putinar_degree_bounds(
455432
p::SA.AlgebraElement,
456433
gs::AbstractVector{<:SA.AlgebraElement},
@@ -459,13 +436,11 @@ function putinar_degree_bounds(
459436
)
460437
mindegree = 0
461438
# TODO homogeneous case
462-
mindeg(g) = _min_half(min_shift(mindegree, _mindegree(g)))
463-
maxdeg(g) = _max_half(maxdegree - _maxdegree(g))
464-
degrange(g) = mindeg(g):maxdeg(g)
465-
minus_degrange(g) = -maxdeg(g):-mindeg(g)
439+
degrange(g) = _multiplier_deg_range(mindegree:maxdegree, g)
440+
minus_degrange(g) = (-).(degrange(g))
466441
# The multiplier will have degree `0:2fld(maxdegree - _maxdegree(g), 2)`
467442
mindegree =
468-
-deg_range(p -> -_mindegree(p), p, gs, minus_degrange, -maxdegree:0)
443+
-deg_range((-) _mindegree, p, gs, minus_degrange, -maxdegree:0)
469444
if isnothing(mindegree)
470445
return
471446
end
@@ -474,13 +449,17 @@ function putinar_degree_bounds(
474449
return
475450
end
476451
vars_mindeg = map(vars) do v
477-
return -deg_range(
478-
Base.Fix2(minus_min_degree, v),
479-
p,
480-
gs,
481-
minus_degrange,
482-
-maxdegree:0,
483-
)
452+
return if _is_monomial_basis(eltype(gs))
453+
-deg_range(
454+
(-) Base.Fix2(min_degree, v),
455+
p,
456+
gs,
457+
minus_degrange,
458+
-maxdegree:0,
459+
)
460+
else
461+
0
462+
end
484463
end
485464
if any(isnothing, vars_mindeg)
486465
return
@@ -501,29 +480,24 @@ function putinar_degree_bounds(
501480
)
502481
end
503482

504-
function multiplier_basis(g::SA.AlgebraElement, bounds::DegreeBounds)
483+
function multiplier_basis(g::SA.AlgebraElement{<:MB.Algebra{BT,B}}, bounds::DegreeBounds) where {BT,B}
505484
shifted = minus_shift(bounds, g)
506485
if isnothing(shifted)
507486
halved = nothing
508487
else
509488
halved = _half(shifted)
510489
end
511-
basis = MB.FullBasis{MB.Monomial,MP.monomial_type(typeof(g))}()
490+
basis = MB.FullBasis{B,MP.monomial_type(typeof(g))}()
512491
if isnothing(halved)
513-
# TODO add `MB.empty_basis` to API
514-
return MB.maxdegree_basis(
515-
basis,
516-
MP.variables(bounds.variablewise_mindegree),
517-
-1,
518-
)
492+
return MB.empty_basis(MB.explicit_basis_type(typeof(basis)))
519493
else
520494
return maxdegree_gram_basis(basis, halved)
521495
end
522496
end
523497

524498
function half_newton_polytope(
525-
p::MP.AbstractPolynomialLike,
526-
gs::AbstractVector{<:MP.AbstractPolynomialLike},
499+
p::SA.AlgebraElement,
500+
gs::AbstractVector{<:SA.AlgebraElement},
527501
vars,
528502
maxdegree,
529503
::NewtonDegreeBounds,
@@ -554,10 +528,10 @@ function half_newton_polytope(
554528
gs::AbstractVector{<:SA.AlgebraElement},
555529
vars,
556530
maxdegree,
557-
::NewtonFilter{<:NewtonDegreeBounds},
531+
filter::NewtonFilter{<:NewtonDegreeBounds},
558532
)
559533
bounds = putinar_degree_bounds(p, gs, vars, maxdegree)
560-
bases = [multiplier_basis(g, bounds) for g in gs]
534+
bases = half_newton_polytope(p, gs, vars, maxdegree, filter.outer_approximation)
561535
push!(
562536
bases,
563537
maxdegree_gram_basis(
@@ -604,6 +578,7 @@ function Base.:+(c::SignCount, a::SignChange{Missing})
604578
@assert c.unknown >= -a.Δ
605579
return SignCount(c.unknown + a.Δ, c.positive, c.negative)
606580
end
581+
607582
function Base.:+(c::SignCount, a::SignChange{<:Number})
608583
if a.sign > 0
609584
@assert c.positive >= -a.Δ
@@ -642,6 +617,29 @@ function increase(cache, counter, generator_sign, monos, mult)
642617
end
643618
end
644619

620+
# If `mono` is such that there is no other way to have `mono^2` by multiplying
621+
# two different monomials of `monos` and `mono` is not in `X` then, the corresponding
622+
# diagonal entry of the Gram matrix will be zero hence the whole column and row
623+
# will be zero hence we can remove this monomial.
624+
# See [Proposition 3.7, CLR95], [Theorem 2, L09] or [Section 2.4, BKP16].
625+
626+
# This is generalized here to the case with constraints as detailed in [L23].
627+
628+
# [CLR95] Choi, M. D. and Lam, T. Y. and Reznick, B.
629+
# *Sum of Squares of Real Polynomials*.
630+
# Proceedings of Symposia in Pure mathematics (1995)
631+
#
632+
# [L09] Löfberg, Johan.
633+
# *Pre-and post-processing sum-of-squares programs in practice*.
634+
# IEEE transactions on automatic control 54.5 (2009): 1007-1011.
635+
#
636+
# [BKP16] Sabine Burgdorf, Igor Klep, and Janez Povh.
637+
# *Optimization of polynomials in non-commuting variables*.
638+
# Berlin: Springer, 2016.
639+
#
640+
# [L23] Legat, Benoît
641+
# *Exploiting the Structure of a Polynomial Optimization Problem*
642+
# SIAM Conference on Applications of Dynamical Systems, 2023
645643
function post_filter(poly::SA.AlgebraElement, generators, multipliers_gram_monos)
646644
counter = MB.algebra_element(
647645
zero(MP.polynomial_type(MP.monomial_type(typeof(SA.basis(poly))), SignCount)),
@@ -737,8 +735,3 @@ end
737735
function _sub(basis::SubBasis{B}, I) where {B}
738736
return SubBasis{B}(basis.monomials[I])
739737
end
740-
741-
function half_newton_polytope(a::SA.AlgebraElement, args...)
742-
@assert SA.basis(a) isa MB.SubBasis{MB.Monomial}
743-
half_newton_polytope(SA.coeffs(a, MB.FullBasis{MB.Monomial,MP.monomial_type(typeof(a))}()), args...)
744-
end

0 commit comments

Comments
 (0)