The Blackbox Solver

A blackbox solver runs with default settings and values of the tolerances, executing a selection of algorithms which has shown to work for a large collection of problems. As to what the solver computes, two different types of solvers are available

  1. to approximate all isolated solutions, typically of systems with as many equations as unknows; and

  2. to compute a numerical irreducible decomposition of the entire solution set, which includes the isolated solution points, but also all positive dimensional sets (curves and surfaces), factored into irreducible components.

Because the output of the two types is so vastly different and because the complexity of a numerical irreducible decomposition is much higher than computing only the isolated solutions, the user must decide in advance which solver function to call.

approximating all isolated solutions

The input to the solver is a list of strings, with symbolic representations of the polynomials in the system. The ; at the end signifies the = 0 of the equations.

polynomials = ['x^3 + 2*x*y - x^2;', 'x + y - x^3;']

To call the blackbox solver, we import the solve function from the solver module.

from phcpy.solver import solve
solutions = solve(polynomials)

The output of solve is also a list of strings.

for (idx, sol) in enumerate(solutions):
    print('Solution', idx+1, ':')
    print(sol)

has as output

Solution 1 :
t :  0.00000000000000E+00   0.00000000000000E+00
m : 2
the solution for t :
 x :  0.00000000000000E+00   0.00000000000000E+00
 y :  0.00000000000000E+00   0.00000000000000E+00
== err :  7.124E-17 = rco :  0.000E+00 = res :  0.000E+00 =
Solution 2 :
t :  1.00000000000000E+00   0.00000000000000E+00
m : 1
the solution for t :
 x :  1.00000000000000E+00   0.00000000000000E+00
 y :  0.00000000000000E+00   0.00000000000000E+00
== err :  2.100E-74 = rco :  5.348E-01 = res :  0.000E+00 =
Solution 3 :
t :  1.00000000000000E+00   0.00000000000000E+00
m : 1
the solution for t :
 x : -1.50000000000000E+00   0.00000000000000E+00
 y : -1.87500000000000E+00   3.30053725596684E-238
== err :  6.747E-80 = rco :  1.220E-01 = res : 1.073E-237 =

How did this actually work? We can ask to see more output of the solver, giving a value to the verbose level parameter, the vrblvl as argument to the solver.

solutions = solve(polynomials, vrblvl=1)

shows the following

in solve, tasks : 0, mvfocus : 0, precision : d
the polynomials on input :
x^3 + 2*x*y - x^2;
x + y - x^3;
nbr : 4, roco :
total degree : 9
2-homogeneous Bezout number : 6
  with with partition : { x }{ y }
general linear-product Bezout number : 5
  based on the set structure :
     { x }{ x y }{ x }
     { x y }{ x }{ x }
mixed volume : 2
stable mixed volume : 4

The roco is an abbreviation of root count. A root count is an upper bound on the number of solutions. In the example, the two input polynomials are cubics. Therefore, the largest number of isolated solutions equals nine, the product of the degrees of the polynomials. However, not all monomials of degree three or less that could appear with nonzero coefficient are present. The numbers 6 and 5 are better bounds computed on the degrees. Mixed volumes are generically sharp bounds for the number of isolated solutions with all coordinates different from zero. As in the example, there is one double root at (0, 0), which is counted by the stable mixed volume.

If the coefficients of the polynomials are sufficiently independent from each other, then the number of isolated solutions counted with multiplicity will match one of the computed root counts.

options of the solve function

The blackbox solver runs with default values, which can be changed when calling the solve function.

In addition to the list of polynomials, there are six parameters with default values:

  1. tasks equals the number of the threads and is by default set to zero. The solutions can approximated independently to each other using p threads could in the best case speed up the solver by a factor of p.

  2. mvfocus is a flag to apply only mixed volumes as root counts, if set to one. By default, this flag is set to zero and the solver will compute all bounds based on the degrees which may be too time consuming for sparse polynomial systems.

  3. precision is by default double (d). Other values are dd for double double and qd for quad double. While running in higher precision leads to more accurate results, the computational overhead can be significant. The overhead may be compensated (in part) by multithreading.

  4. checkin is the option to run some basic checks on the input. By default True, setting it to False is okay if the input polynomials are automatically generated in the correct way.

  5. dictionary_output is by default False and a list of strings is returned. If True, then the output is a list of dictionaries, often convenient for processing.

  6. vrblvl is the verbose level parameter to make the blackbox solver less black when set to higher values.

Of course, then there is always the help(solve):

help(solve)

which shows the documentation string of the function.

When changing the default values, consider the following.

  1. The number of threads should never be set to a value higher than the number of available cores on the system. To find out the number of available cores, do the following:

    from phcpy.dimension import get_core_count
    get_core_count()
    

    So, use up to the value returned by phcpy.dimension.get_core_count() as the value to assign to the parameter tasks.

  2. The focus on mixed volumes (in the option mvfocus) is automatically applied when the polynomials have negative exponents.

  3. When computing in higher precision, keep in mind that also the coefficients of the polynomials then must also be evaluated in higher precision. Consider 1/3 in double precision:

    1/3
    

    which evaluates to

    0.3333333333333333
    

    which is of course not equal to the rational number 1/3.

  4. One of the checks done by default (checkin=True) is whether the number of polynomials in the list equals the number of unknowns. At this stage, if the syntax of the polynomial is incorrect, an error message will be printed as well.

  5. If dictionary_output is wanted after a run, then it can be computed afterwards, the solve() should not be called again, but can be computed with the strsol2dict() of the solutions module. For example:

    from phcpy.solutions import strsol2dict
    strsol2dict(solutions[0])
    

    shows the output

    {'t': 0j, 'm': 2, 'err': 1.17e-16, 'rco': 0.0, 'res': 0.0, 'x': 0j, 'y': 0j}
    
  6. Higher values of the verbose level vrblvl are mainly meant for debugging purposes as it should procedures are executed. As the solving of a polynomial system could take a long time, the user can see which procedures are currently running if the solver appears to be stuck.