Rounding numerical solutions to exact optimal solutions
In certain situations there are reasons to believe that there is a 'nice' optimal solution. That is, the kernel of every optimal solution can be defined over a number field of low algebraic degree and low bit size. In that case, we can use the rounding procedure from [1] to obtain such a solution from a precise enough solution returned by a semidefinite programming solver. This is implemented in the function exact_solution
.
See also the following examples:
- A very basic example of minimizing a univariate polynomial
- A more involved example of minimizing a multivariate polynomial
- Rounding the Delsarte LP bound
ClusteredLowRankSolver.exact_solution
— Functionexact_solution(problem::Problem, primalsol::PrimalSolution, dualsol::DualSolution; transformed=false, FF = QQ, g=1, settings::RoundingSettings=RoundingSettings(), monomial_bases=nothing)
Compute and return an exact solution to the problem, given a primal solution, dual solution and a field FF
with approximate generator g
. Return (success, exactdualsol)
if transformed=false
, and (success, pd_transformed_exactsolution, transformations)
if transformed=true
.
A short introduction to the rounding procedure
The rounding procedure consists of three steps. First a nice basis for the kernel of the provided solution is found; this defines the optimal face. In the second step, this basis is used as part of a basis transformation, to obtain a smaller semidefinite program where the affine hull of the constraints has a one-to-one correspondence with the original optimal face. The provided solution is also transformed, to obtain a strictly feasible solution of the new semidefinite program. In the last step, we take an approximation of the numerical, transformed solution over the rationals (or a provided number field), and find an exact solution close to it. See [1] for a more detailed description.
Settings for the rounding procedure
For ease of use, all settings which can be used to tweak the rounding procedure are collected in the RoundingSettings
structure.
ClusteredLowRankSolver.RoundingSettings
— TypeRoundingSettings(settings...)
Settings for the rounding procedure:
- Finding the kernel
kernel_errbound
: (default:1e-10
) the allowed error for the kernel vectors. That is, the maximum entry of Xv is in absolute value at most thiskernel_round_errbound
: (default:1e-15
) the maximum allowed error when rounding the reduced row-echelon form entrywisekernel_use_primal
: (default:true
) use the primal solution to find the kernel vectors (otherwise, use an SVD of the dual solution)kernel_lll
: (default:false
) if true, use the LLL algorithm to find the nullspace of the kernel. Otherwise, use the reduced row-echelon form to find kernel vectors.kernel_bits
: (default:1000
) the maximum number of bits to be used in the LLL algorithm (for finding relations or finding the entries of the RREF)
- Reducing the kernel vectors
reduce_kernelvectors
: (default:true
) apply the reduction step or notreduce_kernelvectors_cutoff
: (default:400
) do reduction on the full matrix if its size is at most this cutoff. Otherwise do it on a submatrixreduce_kernelvectors_stepsize
: (default:200
) the number of extra columns to take into account in each iteration of the reduction step
- Transforming the problem and the solution
unimodular_transform
: (default:true
) use a unimodular transform obtained in the reduction stepnormalize_transformation
: (default:true
) multiply by a diagonal matrix to get an integer transformation for the problem (for problems over QQ)
- Finding an approximate solution in the field
regularization
: (default:1e-20
) use this regularization for solving the extended systemapproximation_decimals
: (default:40
) Approximate the numerical solution using this many digits, entrywise
- Rounding the solution to the affine space of constraints
redundancyfactor
: (default:10
) take at least this times the number of constraints columns as potential pivotspseudo
: (default:true
) use the psuedo inverse for rounding (this may give solutions with larger bitsize than needed)pseudo_columnfactor
: (default:1.05
) For a system of r rows, use r * pseudo_columnfactor number of columns for the pseudo inverseextracolumns_linindep
: (default:false
) if true, take the extra columns linearly independent of each other (otherwise, random columns)
Finding the right settings
Depending on the problem, the default parameters will suffice. In the following cases small changes are needed:
- The kernel vectors are not found correctly, or have significantly higher maximum number after reduction than the maximum numerator and denominator.
- Try to solve to a smaller duality gap, and/or decrease the setting
kernel_round_errbound
. - Try the settings
kernel_use_primal=false
.
- Try to solve to a smaller duality gap, and/or decrease the setting
- The final solution does not satisfy the constraints. (Or not enough pivots were found)
- increase
redundancyfactor
, or set it to-1
to take all variables into account. - This might also be an indication that the kernel vectors are not found correctly (see item 1).
- increase
- The final solution is not positive semidefinite.
- Increase the setting
approximation_decimals
. Make sure that the provided solution has at least that many digits correct (the duality gap should be lower than10^(-approximation_digits)
) - Increase the setting
pseudo_columnfactor
. The higher this setting, the closer the exact solution will be to the provided solution. However, this also increases the bit size of the exact solution. - In some cases this is also an indication that the kernel vectors are not found correctly (see item 1), especially when the reported negative eigenvalues are close to zero.
- Increase the setting
Using coefficient matching
Although the semidefinite program used in the solver is defined using sampling, it is possible to use a semidefinite program defined using coefficient matching in a monomial basis for the rounding procedure. This is not yet fully automated; the user needs to provide the monomial basis for each polynomial constraint to the rounding procedure in order to use this. Using coefficient matching instead of sampling in the rounding heuristic generally decreases the size of the exact solutions. See below for an example using the slightly modified code for the Delsarte LP bound in the example for rounding. Here we have one univariate constraint with polynomials up to degree $2d$.
d = 3
problem, primalsol, dualsol = delsarte(8, 1//2, d; duality_gap_threshold=1e-30)
R, (x,) = polynomial_ring(QQ, 1)
mon_basis = [x^k for k=0:2d]
success, exactdualsol = exact_solution(problem, primalsol, dualsol, monbases = [mon_basis])
Finding the appropriate number field for the rounding procedure
It is in general not directly clear over which number field the optimal face can be defined. The function find_field
can help to find such a field. See Section 2.5 of [1] for an explanation of the procedure.
ClusteredLowRankSolver.find_field
— Functionfind_field(primalsol, dualsol, max_degree=4; valbound=1e-15, errbound=1e-15, bits=100, max_coeff=1000)
Heuristically find a field over which the kernel can probably be defined.
Only consider values at least valbound
in absolute value. Find minimal polynomials such that the chosen entries are approximately generators with an error bound of errbound
. Use bits
number of bits and reject minimal polynomials with a maximum coefficient of more than max_coeff
.
Getting an exact feasible solution close to the optimum
In general, the kernel of the optimal solutions can only be defined using a number field of high algebraic degree, or with large bit size. In this case it is unlikely that the exact kernel can be found, so that the rounding procedure will fail. However, it is possible to get an exact feasible solution with objective value close to the optimum using the rounding procedure. To do this, solve the semidefinite program as a feasibility problem (no objective), with the extra constraint that the (original) objective should be a rational number close to the optimum value. Then the solver will return a strictly feasible solution if it exists (that is, with empty kernel). The function exact_solution
will now essentially skip the steps of finding the kernel vectors and transforming the problem, since there are no kernel vectors. Therefore, it will find an exact feasible solution close to the provided solution such that the objective of the original problem is equal to the rational number in the extra constraint. See Rounding the Delsarte LP bound for a worked example.