Современные алгоритмы позволяют достаточно эффективно решать следующие задачи:
Одной из библиотек, которая умеет делать такие вещи является SumOfSquares.jl на языке Julia.
using SumOfSquares
using DynamicPolynomials
using Mosek
using MosekTools
Пример 1. Неравенство о средних для двух чисел
@polyvar x y
opt = optimizer_with_attributes(Mosek.Optimizer, "QUIET" => true);
model = SOSModel(opt)
con_ref = @constraint(model, x^2 - 2 * x * y + y^2 >= 0)
optimize!(model)
@show termination_status(model)
round(sos_decomposition(con_ref)[1], digits=3)
Пример 2. Неравенство о средних для трех чисел
@polyvar x y z
opt = optimizer_with_attributes(Mosek.Optimizer, "QUIET" => true);
model = SOSModel(opt)
con_ref = @constraint(model, x^6 + y^6 + z^6 - 3 * x^2 * y^2 * z^2 >= 0)
optimize!(model)
@show termination_status(model)
for p = sos_decomposition(con_ref)
print(round(p, digits=3), "\n")
end
Заметим, что разложение отличается от того, которое было в анонсе курса, их, вообще говоря, бесконечно много разных.
Пример 3. Многочлен Моцкина
opt = optimizer_with_attributes(Mosek.Optimizer, "QUIET" => true);
model = SOSModel(opt)
moz = x^4*y^2 + x^2*y^4 - 3*x^2*y^2 + 1
@constraint(model, moz >= 0)
optimize!(model)
@show termination_status(model)
Это означает, что разложения в сумму квадратов нет.
Пример 4. А теперь умножим на
opt = optimizer_with_attributes(Mosek.Optimizer, "QUIET" => true);
model = SOSModel(opt)
moz = x^4*y^2 + x^2*y^4 - 3*x^2*y^2 + 1
con_ref = @constraint(model, moz * (x^2 + y^2) >= 0)
optimize!(model)
@show termination_status(model)
for p = sos_decomposition(con_ref)
print(round(p, digits=3), "\n")
end
Утверждается, что многочлен Моцкина умноженный на является суммой квадратов многочленов выше. Заметим, что это приближенное равенство (и нулевые многочлены на самом деле не нулевые, а очень близкие к нулю). Однако, мы можем округлить числа и проверить, вдруг получится точное разложение:
((-x^3*y - x*y^3 + 2*x*y)^2 + (-x^2*y + y)^2 + (x*y^2 - x)^2) - moz * (x^2 + y^2)
И действительно получилось! То, как округлять, чтобы получать верные равенства - отдельная история.
Простешим примером использования SOS оптимизации является поиск нижних оценко на значение многочлена. Например, если мы хотим найти нижнюю оценку на , мы можем поискать максимум такой, что является SOS
opt = optimizer_with_attributes(Mosek.Optimizer, "QUIET" => true);
model = SOSModel(opt)
@variable(model, c)
moz = x^4*y^2 + x^2*y^4 - 3*x^2*y^2 + 1
@constraint(model, moz - c >= 0)
@objective(model, Max, c)
optimize!(model)
@show termination_status(model)
@show round(value(c), digits = 3)
Так мы выяснили, что многочлен Моцкина всегда . Но ведь мы же знаем, что он неотрицателен! Чтобы получить оценку получше, можно прибегнуть к теореме Артина (а точнее Пойа-Резника)
opt = optimizer_with_attributes(Mosek.Optimizer, "QUIET" => true);
model = SOSModel(opt)
@variable(model, c)
moz = x^4 * y^2 + x^2 * y^4 - 3 * x^2 * y^2 + 1
@constraint(model, moz * (x^2 + y^2) - c * (x^2 + y^2) >= 0)
@objective(model, Max, c)
optimize!(model)
@show termination_status(model)
@show round(value(c), digits = 3)