In this notebook there are presented examples of usage of shiroin, a python library for proving inequalities of multivariate polynomials.
At the beginning we need to load the packages.
from sympy import *
from shiroindev import *
from IPython.display import Latex
shiro.seed=1
shiro.display=lambda x:display(Latex(x))
shiro.seed=1
sets a seed for proving functions. If you don't write it, you can get a slightly different proof each time you run a function. This line is here only for the sake of reproducibility.
The next line provides a nicer display of proofs, i.e. formulas will be shown instead of LaTeX code of these formulas. Note that this works on Jupyter, but not on the git page.
Now let's make some proofs. We will use problems from https://www.imomath.com/index.php?options=593&lmm=0.
Prove the inequality , if are real numbers.
Function prove
tries to prove that given formula is nonnegative, assuming all variables are nonnegative. In this case the nonnegativity assumption is not a problem, since all powers on the left side are even, so if then .
prove('(a^2+b^2+c^2-a*b-a*c-b*c)')
Function prove prints several things. The first two gives us a formula after expanding it. The next one is status, which is the return status of the first use of scipy.optimize.linprog
. Possible outputs and explanations are
Then we've got a hint. So let's use it!
prove('(a^2+b^2+c^2-a*b-a*c-b*c)*2')
Find all real numbers such that .
At first glance it doesn't look like an inequality problem, but actually it is one. If you try to calculate both sides for different values, you can see that the left side of the equation is never less than the right one. So let's try
prove('a^2+b^2+c^2+d^2-a*(b+c+d)')
This time prove
didn't found the proof. But it doesn't mean that the inequality is not true! prove
uses a list of positive values, where is a number of variables in the formula. List of values should correspond to the list of variables in alphabetical order. Here are a few tips how to choose the right values.
In the formula above are in one orbit and the formula is homogenous, so let's try and .
prove('a^2+b^2+c^2+d^2-a*(b+c+d)','2,1,1,1')
Function makes a substitution and try to prove new inequality. This time it succeeded. Moreover, if starting formula is equal to 0, then all these inequalities have to be equalities, so and eventually . We can also try a little bit lower value for .
prove('a^2+b^2+c^2+d^2-a*(b+c+d)','7/4,1,1,1')
Now we can see that if , then and eventually . Note that inequality is proved only for positive numbers (which, by continuity, can be expanded to nonnegative numbers). But using similar argumentation to the one in previous problem, if is the solution of , then is a solution, too. Since the only nonnegative solution is , it means that it is the only solution.
It is worth noting that this time function prove
used as a new variable instead of . If you want to start a new proof and you don't care about the collision of variables from previous proofs, you can use newproof
function, which clears the set of used variables.
We can also use the findvalues
function to find values for the proof more automatically. It looks for (local) minimum of the value defined above.
formula=S('a^2+b^2+c^2+d^2-a*(b+c+d)')
numvalues=findvalues(formula)
numvalues
If the value were less than 1, that would mean that the formula is negative for given values. If were equal to 1, then we have to choose exactly these values (or other values for which the is equal to 1. But here is greater than 1, so we can take a point near it and (probably) still have a proof. The values given to the prove
function must not be floating point numbers, so we can rationalize them.
values=nsimplify(numvalues,tolerance=0.1,rational=True)
values
newproof()
prove(formula,values)
If you set the tolerance bigger, then the values will have smaller numerators and denominators, so coefficients in the proof will be smaller, too. But if the tolerance is too big, then proof will not be found.
Let's skip the problem 3 and look solve the problem 4 instead.
If and are two positive numbers less than 1, prove that
prove('1/(1-x^2)+1/(1-y^2)-2/(1-x*y)')
prove
assumes that formula is well-defined if all variables are positive, so it doesn't have to analyze the denominator (except of choosing the right sign). In this case it is not true, since if , then . Also denominator is equal to which is negative for . So we need to make some substitution after which new variables can have all positive values, not just these inside (0,1) interval.
We will use a function makesubs
to generate these substitutions. It has three basic parameters: formula
, intervals
and values
. intervals
are current limitations of variables, values
are values of variables for which of formula
is small. values
should be inside corresponding intervals
. This argument is optional but it's better to use it.
Let's go back to our problem. If , then , so it's the minimum value of the formula. So let values=(1/2,1/2)
(warning: do not use decimal point, for example '0.5,0.5').
newproof()
newformula,newvalues=makesubs('1/(1-x^2)+1/(1-y^2)-2/(1-x*y)','[0,1],[0,1]','1/2,1/2')
prove(newformula*3,newvalues)
Now let's get back to problem 3.
If are positive real numbers that satisfy , find the minimal value of
The problem is equivalent to finding minimum of assuming and . The first idea is to suppose that the minimum is reached when . In that case, and formula is equal to 1. Now we can substitute . Constraints for variables are , , . We can rewrite it as , . These two conditions have two important properties:
If these two conditions hold, then you can use makesubs
function.
newproof()
formula=Sm('xy/z+yz/x+zx/y-1').subs('z',S('1-x-y'))
newformula,values=makesubs(formula,'[0,1-y],[0,1]','1/3,1/3')
prove(newformula,values)
The proof is found, so the assumption that 1 is the minimum of xy/z+yz/x+zx/y
was good.
Functions S
and Sm
creates a SymPy object from a string. The only difference is that Sm
assumes that there are no multi-letter variables and adds a multiplication sign between every two terms which has no operator sign, so object Sm(xy/z+yz/x+zx/y)
has 3 variables x,y,z
and S('xy/z+yz/x+zx/y')
has 6 variables x,y,z,xy,yz,zx
.
As you may have noticed, formulas are often cyclic or symmetric. Therefore you can use cyclize
or symmetrize
function to reduce the length of the written formula. Here are a few commands which will do the same as each other.
prove('(a^2+b^2+c^2-a*b-a*c-b*c)*2')
#prove(S('(a^2+b^2+c^2-a*b-a*c-b*c)*2'))
#prove(Sm('2(a^2+b^2+c^2-ab-ac-bc)'))
#prove(cyclize('2*a^2-2*a*b'))
#prove(symmetrize('a^2-a*b'))
Now look at formula . It's quite obvious that it's nonnegative, but prove
fails to show this!
prove('(x-1)^4')
But there is a relatively simple method to generate a proof using this library. We will make to proofs: one for and the second one for .
newproof()
prove(makesubs('(x-1)^4','(1,oo)'))
newproof()
prove(makesubs('(x-1)^4','(-oo,1)'))
formula=cyclize('a/(b+c)',variables='a,b,c,d')-2
formula
prove(formula)
This problem, like the previous one, can be solved by splitting the domain of variables to several subdomains. But we can also use the symmetry of this inequality. For example, without loss of generality we can assume that and , so , .
newproof()
prove(makesubs(formula,'[c,oo],[d,oo]'))
newproof()
prove(makesubs(formula,'[c,oo],[d,oo]')*2)
It's a good idea to use intervals that are unbounded from one side (i.e. those which contain ). In this problem we could assume that , as well. But as you can see, in this case the proof is several times longer.
newproof()
prove(makesubs(formula,'[0,c],[0,d]')*2)
Function powerprove
is a shortcut for splitting domain on several subdomains and proving the inequality for each of them. This function uses of -dimensional intervals with a common point (by default it's ), where is a number of variables. Here there are two examples of using it. As you can see, proofs are very long.
newproof()
#this is equivalent to
#prove(makesubs('(x-1)^4','[1,oo]'))
#prove(makesubs('(x-1)^4','[1,0]'))
#you can write ends of interval in any order, so [1,0] is the same as [0,1]
#but the substitution is slightly simpler when 0 is on the right side
powerprove('(x-1)^4')
newproof()
formula=Sm('-(3a+2b+c)(2a^3+3b^2+6c+1)+(4a+4b+4c)(a^4+b^3+c^2+3)')
#this is equivalent to
#prove(makesubs(formula,'[1,oo],[1,oo],[1,oo]'))
#prove(makesubs(formula,'[1,0],[1,oo],[1,oo]'))
#prove(makesubs(formula,'[1,oo],[1,0],[1,oo]'))
#prove(makesubs(formula,'[1,0],[1,0],[1,oo]'))
#prove(makesubs(formula,'[1,oo],[1,oo],[1,0]'))
#prove(makesubs(formula,'[1,0],[1,oo],[1,0]'))
#prove(makesubs(formula,'[1,oo],[1,0],[1,0]'))
#prove(makesubs(formula,'[1,0],[1,0],[1,0]'))
powerprove(formula)
Now let's take a look at slightly another kind of the problem.
Let be a convex function. Prove that
To create a proof, we will use provef
function. It assumes that is convex and nonnegative, then it tries to find a proof. However, if the last inequality is , then the proof works for any convex function.
provef('(-f(1,2,3)-f(2,3,1)-f(3,1,2)+f(4,3,-1)+f(3,-1,4)+f(-1,4,3))*21')
Let's try to solve problem 6 from the finals of LXIII Polish Mathematical Olympiad. It was one of the hardest inequality in the history of this contest, solved only by 3 finalists.
Prove the inequality
The first observation is that the formula is cyclic, so without loss of generality we may assume that . We can go a step further and divide it into two cases: and .
shiro.display=lambda x:None #turn off printing of proofs
newproof()
formula=cyclize('((a-b)/c)^2-2*sqrt(2)*(a-b)/c')
formula1=makesubs(formula,'[b,oo],[c,oo]',variables='a,b') #a>=b>=c
prove(formula1)
formula2=makesubs(formula,'[c,oo],[b,oo]',variables='a,c') #a>=c>=b
prove(formula2)
So the case is done, but is not. But maybe we can adjust values.
values=findvalues(formula1)
values
First and second value is approximately equal to 0, so we can try to replace 0 with 1.
prove(formula1,values='1,1,7')
The key observation is that the formula1
is homogenous, so we can scale values.
newvalues=(1,values[1]/values[0],values[2]/values[0])
newvalues
newvalues[1]**2
Now the third value is very big. Technically we could use it, but it would run into overflow error, so we will use 1 instead of it. Second value is very close to , so this value will be our next try.
prove(formula1,values='1,sqrt(2),1')
So after getting the code all together we have got the following proof.
newproof()
shiro.display=lambda x:display(Latex(x)) #turn on printing proofs
formula=cyclize('((a-b)/c)^2-2*sqrt(2)*(a-b)/c')
display(Latex('Case $a\ge c\ge b$'))
formula1=makesubs(formula,'[c,oo],[b,oo]',variables='a,c,b')
prove(formula1)
display(Latex('Case $a\ge b\ge c$'))
formula2=makesubs(formula,'[b,oo],[c,oo]')
prove(formula2,values='1,2**(1/2),1')