Code Snippets

The functions are based on the code snippets of the menus of the Jupyter notebook extension of PHCpy. Every function is self contained and illustrates one particular feature.

the blackbox solver

def solve_random_trinomials():
    """
    Illustrates the solution of random trinomials.
    """
    from phcpy.solver import random_trinomials
    f = random_trinomials()
    for pol in f: print(pol)
    from phcpy.solver import solve
    sols = solve(f)
    for sol in sols: print(sol)
    print(len(sols), "solutions found")
def solve_specific_trinomials():
    """
    Illustrates the solution of specific trinomials.
    """
    f = ['x^2*y^2 + 2*x - 1;', 'x^2*y^2 - 3*y + 1;']
    from phcpy.solver import solve
    sols = solve(f)
    for sol in sols: print(sol)
def solution_from_string_to_dictionary():
    """
    Illustrates the dictionary format of a solution.
    """
    p = ['x + y - 1;', '2*x - 3*y + 1;']
    from phcpy.solver import solve
    sols = solve(p)
    print(sols[0])
    from phcpy.solutions import strsol2dict
    dsol = strsol2dict(sols[0])
    print(dsol.keys())
    for key in dsol.keys(): print('the value for', key, 'is', dsol[key])
def verify_by_evaluation():
    """
    Illustrates the verification of solutions by evaluation.
    """
    p = ['x + y - 1;', '2*x - 3*y + 1;']
    from phcpy.solver import solve
    sols = solve(p)
    from phcpy.solutions import strsol2dict, evaluate
    dsol = strsol2dict(sols[0])
    eva = evaluate(p, dsol)
    for val in eva: print(val)
def make_a_solution():
    """
    Illustrates the making of a solution.
    """
    from phcpy.solutions import make_solution
    s0 = make_solution(['x', 'y'], [float(3.14), complex(0, 2.7)])
    print(s0)
    s1 = make_solution(['x', 'y'], [int(2), int(3)])
    print(s1)
def filter_solution_lists():
    """
    Illustrates the filtering of solution lists.
    """
    from phcpy.solutions import make_solution, is_real, filter_real
    s0 = make_solution(['x', 'y'], [float(3.14), complex(0, 2.7)])
    print(is_real(s0, 1.0e-8))
    s1 = make_solution(['x', 'y'], [int(2), int(3)])
    print(is_real(s1, 1.0e-8))
    realsols = filter_real([s0, s1], 1.0e-8, 'select')
    for sol in realsols: print(sol)
def coordinates_names_values():
    """
    Illustrates coordinates, names, values of a solution.
    """
    from phcpy.solver import solve
    p = ['x^2*y^2 + x + 1;', 'x^2*y^2 + y + 1;']
    s = solve(p)
    print(s[0])
    from phcpy.solutions import coordinates, make_solution
    (names, values) = coordinates(s[0])
    print(names)
    print(values)
    s0 = make_solution(names, values)
    print(s0)
def fix_retrieve_seed():
    """
    Illustrates fixing and retrieving the seed.
    """
    from phcpy.dimension import set_seed, get_seed
    set_seed(2024)
    print(get_seed())
def solve_with_four_threads():
    """
    Ilustrates solving with four threads.
    """
    from phcpy.solver import solve
    from phcpy.families import cyclic
    nbrt = 4 # number of tasks
    pols = cyclic(6)
    print('solving the cyclic 6-roots problem :')
    for pol in pols: print(pol)
    from datetime import datetime
    starttime = datetime.now()
    sols = solve(pols)
    stoptime = datetime.now()
    elapsed = stoptime - starttime
    print(f'elapsed time with no multithreading :')
    print(elapsed)
    starttime = datetime.now()
    sols = solve(pols, tasks=nbrt)
    stoptime = datetime.now()
    elapsed = stoptime - starttime
    print(f'elapsed time with {nbrt} threads :')
    print(elapsed)
def four_root_counts():
    """
    Illustrates four root counts.
    """
    f = ['x^3*y^2 + x*y^2 + x^2;', 'x^5 + x^2*y^3 + y^2;']
    from phcpy.starters import total_degree
    print('the total degree :', total_degree(f))
    from phcpy.starters import m_homogeneous_bezout_number as mbz
    (bz, part) = mbz(f)
    print('a multihomogeneous Bezout number :', bz)
    from phcpy.starters import linear_product_root_count as lrc
    (rc, ssrc) = lrc(f)
    print('a linear-product root count :', rc)
    from phcpy.volumes import stable_mixed_volume
    (mv, smv) = stable_mixed_volume(f)
    print('the mixed volume :', mv)
    print('the stable mixed volume :', smv)
def newton_and_deflation():
    """
    Illustrates Newton's method and deflation.
    """
    p = ['(29/16)*x^3 - 2*x*y;', 'x^2 - y;']
    from phcpy.solutions import make_solution
    s = make_solution(['x', 'y'],[float(1.0e-6), float(1.0e-6)])
    print(s)
    from phcpy.deflation import double_newton_step
    s2 = double_newton_step(p, [s])
    print(s2[0])
    s3 = double_newton_step(p, s2)
    print(s3[0])
    from phcpy.deflation import double_deflate
    sd = double_deflate(p, [s])
    print(sd[0])
def overconstrained_deflation():
    """
    Illustrates deflation of an overconstrained system.
    """
    from phcpy.solutions import make_solution
    from phcpy.deflation import double_deflate
    sol = make_solution(['x', 'y'], [float(1.0e-6), float(1.0e-6)])
    print(sol)
    pols = ['x**2;', 'x*y;', 'y**2;']
    sols = double_deflate(pols, [sol], tolrnk=1.0e-8)
    print(sols[0])
    sols = double_deflate(pols, [sol], tolrnk=1.0e-4)
    print(sols[0])
def equation_and_variable_scaling():
    """
    Illustrates equation and variable scaling.
    """
    print('solving without scaling ...')
    from phcpy.solver import solve
    p = ['0.000001*x^2 + 0.000004*y^2 - 4;', '0.000002*y^2 - 0.001*x;']
    psols = solve(p)
    for sol in psols: print(sol)
    print('solving after scaling ...')
    from phcpy.scaling import double_scale_system as scalesys
    from phcpy.scaling import double_scale_solutions as scalesols
    (q, c) = scalesys(p)
    print('the scaled polynomial system :')
    for pol in q: print(pol)
    qsols = solve(q)
    ssols = scalesols(len(q), qsols, c)
    for sol in ssols: print(sol)

path trackers

def total_degree_start_system():
    """
    The product of the degrees of the polynomials (the total degree)
    provide an upper bound on the number of solutions.
    A total degree start system is a simple system that has
    as many solutions as the product of the degrees.
    """
    from phcpy.starters import total_degree
    from phcpy.starters import total_degree_start_system
    from phcpy.trackers import double_track
    p = ['x^2 + 4*y^2 - 4;', '2*y^2 - x;']
    d = total_degree(p)
    print('the total degree :', d)
    (q, qsols) = total_degree_start_system(p)
    print('the number of start solutions :', len(qsols))
    print('the start system :', q)
    s = double_track(p, q, qsols)
    print('the number of solutions :', len(s))
    for sol in s: print(sol)
def track_one_path():
    """
    Illustrates the tracking of one solution path.
    The order in which the solutions appear at the end
    depends on the gamma constant.
    """
    from phcpy.starters import total_degree_start_system
    from phcpy.trackers import double_track
    p = ['x^2 + 4*y^2 - 4;', '2*y^2 - x;']
    (q, qsols) = total_degree_start_system(p)
    g1, s1 = double_track(p, q, [qsols[2]])
    print('first gamma :', g1)
    print(s1[0])
    g2, s2 = double_track(p, q, [qsols[2]])
    print('second gamma :', g2)
    print(s2[0])
def track_with_fixed_gamma():
    """
    Fixing the gamma in the homotopies fixes the order
    in which the solutions appear at the end of the paths.
    """
    from phcpy.starters import total_degree_start_system
    from phcpy.trackers import double_track
    p = ['x^2 + 4*y^2 - 4;', '2*y^2 - x;']
    (q, qsols) = total_degree_start_system(p)
    g3, s3 = double_track(p, q, [qsols[2]], \
        gamma=complex(0.824372806319,0.56604723848934))
    print('gamma :', g3)
    print('the solution at the end:')
    print(s3[0])
def get_next_point_on_path():
    """
    A step-by-step path tracker gives control to the user
    who can ask for the next point on a path.
    """
    from phcpy.starters import total_degree_start_system
    p = ['x**2 + 4*x**2 - 4;', '2*y**2 - x;']
    (q, s) = total_degree_start_system(p)
    from phcpy.trackers import initialize_double_tracker
    from phcpy.trackers import initialize_double_solution
    from phcpy.trackers import next_double_solution
    initialize_double_tracker(p, q)
    initialize_double_solution(len(p), s[0])
    s1 = next_double_solution()
    print('the next point on the solution path :')
    print(s1)
    print(next_double_solution())
    print(next_double_solution())
    initialize_double_solution(len(p), s[1])
    points = [next_double_solution() for i in range(11)]
    from phcpy.solutions import strsol2dict
    dicpts = [strsol2dict(sol) for sol in points]
    xvals = [sol['x'] for sol in dicpts]
    print('the x-coordinates on the path :')
    for x in xvals: print(x)
def plot_trajectories():
    """
    The step-by-step path tracker is applied to plot the
    trajectories of the solutions using matplotlib.
    """
    import matplotlib.pyplot as plt
    p = ['x^2 + y - 3;', 'x + 0.125*y^2 - 1.5;']
    print('constructing a total degree start system ...')
    from phcpy.starters import total_degree_start_system
    q, qsols = total_degree_start_system(p)
    print('number of start solutions :', len(qsols))
    from phcpy.trackers import initialize_double_tracker
    from phcpy.trackers import initialize_double_solution
    from phcpy.trackers import next_double_solution
    initialize_double_tracker(p, q, False)
    from phcpy.solutions import strsol2dict
    plt.ion()
    fig = plt.figure()
    for k in range(len(qsols)):
        if(k == 0):
           axs = fig.add_subplot(221)
        elif(k == 1):
           axs = fig.add_subplot(222)
        elif(k == 2):
            axs = fig.add_subplot(223)
        elif(k == 3):
           axs = fig.add_subplot(224)
        startsol = qsols[k]
        initialize_double_solution(len(p),startsol)
        dictsol = strsol2dict(startsol)
        xpoints =  [dictsol['x']]
        ypoints =  [dictsol['y']]
        for k in range(300):
            ns = next_double_solution()
            dictsol = strsol2dict(ns)
            xpoints.append(dictsol['x'])
            ypoints.append(dictsol['y'])
            tval = dictsol['t'].real
            if(tval >= 1.0):
                break
        print(ns)
        xre = [point.real for point in xpoints]
        yre = [point.real for point in ypoints]
        axs.set_xlim(min(xre)-0.3, max(xre)+0.3)
        axs.set_ylim(min(yre)-0.3, max(yre)+0.3)
        dots, = axs.plot(xre,yre,'r-')
        fig.canvas.draw()
    fig.canvas.draw()
    ans = input('hit return to continue')
def polyhedral_homotopies():
    """
    Polyhedral homotopies solve random coefficient systems
    tracking an optimal number of solution paths,
    that is equal to the mixed volume.
    """
    from phcpy.volumes import mixed_volume
    from phcpy.volumes import double_polyhedral_homotopies
    from phcpy.trackers import double_track
    p = ['x^3*y^2 - 3*x^3 + 7;','x*y^3 + 6*y^3 - 9;']
    print('the mixed volume :', mixed_volume(p))
    (q, qsols) = double_polyhedral_homotopies()
    print('the number of start solutions :', len(qsols))
    gamma, psols = double_track(p, q, qsols)
    print('the number of solutions at the end :', len(psols))
    for sol in psols: print(sol)

sweep homotopies

def quadratic_turning_point():
    """
    Arc length parameter continuation is applied in a real sweep
    which ends at a quadratic turning point.
    """
    from phcpy.sweepers import double_real_sweep
    from phcpy.solutions import make_solution
    circle = ['x^2 + y^2 - 1;', 'y*(1-s) + (y-2)*s;']
    first = make_solution(['x', 'y', 's'], [1.0, 0.0, 0.0])
    second = make_solution(['x', 'y', 's'], [-1.0, 0.0, 0.0])
    startsols = [first, second]
    newsols = double_real_sweep(circle, startsols)
    for sol in newsols: print(sol)
def complex_parameter_homotopy_continuation():
    """
    Sweeps the parameter space with a convex linear combination
    of the parameters.  By a random gamma constant, no singularities
    are encountered during this complex sweep.
    """
    from phcpy.sweepers import double_complex_sweep
    from phcpy.solutions import make_solution
    circle = ['x^2 + y^2 - 1;']
    first = make_solution(['x', 'y'], [1.0, 0.0])
    second = make_solution(['x', 'y'], [-1.0, 0.0])
    startsols = [first, second]
    par = ['y']
    start = [0, 0]
    target = [2, 0]
    newsols = double_complex_sweep(circle, startsols, 2, par, start, target)
    for sol in newsols: print(sol)

Schubert calculus

def lines_meeting_four_lines():
    """
    Applies Pieri homotopies to compute all lines meeting
    four given lines in 3-space.
    """
    from phcpy.schubert import pieri_root_count
    from phcpy.schubert import random_complex_matrix, run_pieri_homotopies
    (m, p, q) = (2, 2, 0)
    dim = m*p + q*(m+p)
    roco = pieri_root_count(m, p, q)
    print('the root count :', roco)
    L = [random_complex_matrix(m+p, m) for _ in range(dim)]
    (f, fsols) = run_pieri_homotopies(m, p, q, L)
    for sol in fsols: print(sol)
    print('number of solutions :', len(fsols))
def line_producing_interpolating_curves():
    """
    Applies Pieri homotopies to compute line producing curves,
    interpolating at given lines in 3-space.
    """
    from phcpy.schubert import pieri_root_count
    from phcpy.schubert import random_complex_matrix, run_pieri_homotopies
    (m, p, q) = (2, 2, 1)
    dim = m*p + q*(m+p)
    roco = pieri_root_count(m, p, q)
    print('the root count :', roco)
    L = [random_complex_matrix(m+p, m) for _ in range(dim)]
    points = random_complex_matrix(dim, 1)
    (f, fsols) = run_pieri_homotopies(m, p, q, L, 0, points)
    print('number of solutions :', len(fsols))
def resolve_some_schubert_conditions():
    """
    Resolves an example of a Schubert condition,
    applying the Littlewood-Richardson rule.
    """
    from phcpy.schubert import resolve_schubert_conditions
    brackets = [[2, 4, 6], [2, 4, 6], [2, 4, 6]]
    roco = resolve_schubert_conditions(6, 3, brackets)
    print('number of solutions :', roco)
def solve_generic_schubert_problem():
    """
    Runs the Littlewood-Richardson homotopies to solve
    a generic instance of a Schubert problem.
    """
    brackets = [[2, 4, 6], [2, 4, 6], [2, 4, 6]]
    from phcpy.schubert import double_littlewood_richardson_homotopies as lrh
    (count, flags, sys, sols) = lrh(6, 3, brackets, verbose=False)
    print('the root count :', count)
    for sol in sols: print(sol)
    print('the number of solutions :', len(sols))

power series expansions

def example4pade():
    """
    The function f(z) = ((1 + 1/2*z)/(1 + 2*z))^(1/2) is
    a solution x(s) of (1-s)*(x^2 - 1) + s*(3*x^2 - 3/2) = 0
    """
    from phcpy.solutions import make_solution
    from phcpy.series import double_newton_at_point
    from phcpy.series import double_pade_approximants
    pol = ['(x^2 - 1)*(1-s) + (3*x^2 - 3/2)*s;']
    variables = ['x', 's']
    sol1 = make_solution(variables, [1, 0])
    sol2 = make_solution(variables, [-1, 0])
    sols = [sol1, sol2]
    print('start solutions :')
    for sol in sols: print(sol)
    srs = double_newton_at_point(pol, sols, idx=2)
    print('The series :')
    for ser in srs: print(ser)
    pad = double_pade_approximants(pol, sols, idx=2)
    print('the Pade approximants :')
    for app in pad: print(app)
def viviani_expansion(vrblvl=0):
    """
    Computes the power series expansion for the Viviani curve,
    from a natural parameter perspective, in double precision.
    """
    from phcpy.series import double_newton_at_series
    pols = [ '2*t^2 - x;', \
             'x^2 + y^2 + z^2 - 4;' , \
             '(x-1)^2 + y^2 - 1;']
    lser = [ '2*t^2;', '2*t;', '2;']
    nser = double_newton_at_series(pols, lser, maxdeg=12, nbr=8)
    variables = ['x', 'y', 'z']
    for (var, pol) in zip(variables, nser): print(var, '=', pol)
def apollonius_expansions():
    """
    Compare the series expansions at two solutions
    for the problem of Apollonius.
    """
    from phcpy.series import double_newton_at_series
    pols = [ 'x1^2 + 3*x2^2 - r^2 - 2*r - 1;', \
             'x1^2 + 3*x2^2 - r^2 - 4*x1 - 2*r + 3;', \
    '3*t^2 + x1^2 - 6*t*x2 + 3*x2^2 - r^2 + 6*t - 2*x1 - 6*x2 + 2*r + 3;']
    lser1 = ['1;', '1 + 0.536*t;', '1 + 0.904*t;']
    lser2 = ['1;', '1 + 7.464*t;', '1 + 11.196*t;']
    nser1 = double_newton_at_series(pols, lser1, idx=4, nbr=7)
    nser2 = double_newton_at_series(pols, lser2, idx=4, nbr=7)
    variables = ['x', 'y', 'z']
    print('the first solution series :')
    for (var, pol) in zip(variables, nser1): print(var, '=', pol)
    print('the second solution series :')
    for (var, pol) in zip(variables, nser2): print(var, '=', pol)

positive dimensional solution sets

def twisted_cubic_witness_set():
    """
    Makes a witness set for the twisted cubic.
    """
    twisted = ['x^2 - y;', 'x^3 - z;']
    from phcpy.sets import double_embed
    embtwist = double_embed(3, 1, twisted)
    print('embedded system augmented with a random hyperplane :')
    for pol in embtwist:
        print(pol)
    from phcpy.solver import solve
    sols = solve(embtwist)
    print('number of generic points :', len(sols))
    for sol in sols: print(sol)
def homotopy_membership_test():
    """
    Homotopies to decide whether a point belongs to a positive
    dimensional solution set.
    """
    from phcpy.families import cyclic
    c4 = cyclic(4)
    from phcpy.sets import double_embed
    c4e1 = double_embed(4, 1, c4)
    from phcpy.solver import solve
    sols = solve(c4e1)
    from phcpy.solutions import filter_zero_coordinates as filter
    genpts = filter(sols, 'zz1', 1.0e-8, 'select')
    print('generic points on the cyclic 4-roots set :')
    for sol in genpts: print(sol)
    from phcpy.sets import double_membertest
    pt0 = [1, 0, -1, 0, 1, 0, -1, 0]
    ismbr = double_membertest(c4e1, sols, 1, pt0)
    print('Is', pt0, 'a member?', ismbr)
    pt1 = [1, 0, 1, 0, -1, 0, -1, 0]
    ismbr = double_membertest(c4e1, sols, 1, pt1)
    print('Is', pt1, 'a member?', ismbr)
def factor_a_cubic():
    """
    Illustrates the numerical factorization of a cubic.
    """
    cubic = '(x+1)*(x^2 + y^2 + 1);'
    from phcpy.sets import double_hypersurface_set
    (wit, pts) = double_hypersurface_set(2, cubic)
    for pol in wit:
        print(pol)
    print('number of witness points :', len(pts))
    for (idx, sol) in enumerate(pts):
        print('Solution', idx+1, ':')
        print(sol)
    from phcpy.factor import double_monodromy_breakup, write_factorization
    deco = double_monodromy_breakup(wit, pts, dim=1)
    write_factorization(deco)
def cascades_of_homotopies():
    """
    A cascade of homotopies computes generic points on all positive
    components of the solution set, for all dimensions.
    """
    pol1 = '(x^2 + y^2 + z^2 - 1)*(y - x^2)*(x - 0.5);'
    pol2 = '(x^2 + y^2 + z^2 - 1)*(z - x^3)*(y - 0.5);'
    pol3 = '(x^2 + y^2 + z^2 - 1)*(z - x*y)*(z - 0.5);'
    pols = [pol1, pol2, pol3]
    from phcpy.cascades import double_top_cascade, double_cascade_filter
    (embpols, sols0, sols1) = double_top_cascade(3, 2, pols)
    print('at dimension 2, degree :', len(sols0))
    (wp1, ws0, ws1) = double_cascade_filter(2, embpols, sols1, tol=1.0e-8)
    print('at dimension 1, candidate generic points :', len(ws0))
    (wp0, ws0, ws1) = double_cascade_filter(1, wp1, ws1, tol=1.0e-8)
    print('candidate isolated points :', len(ws0))
def numerical_irreducible_decomposition():
    """
    Runs a test example on a numerical irreducible decomposition.
    """
    pol0 = '(x1-1)*(x1-2)*(x1-3)*(x1-4);'
    pol1 = '(x1-1)*(x2-1)*(x2-2)*(x2-3);'
    pol2 = '(x1-1)*(x1-2)*(x3-1)*(x3-2);'
    pol3 = '(x1-1)*(x2-1)*(x3-1)*(x4-1);'
    pols = [pol0, pol1, pol2, pol3]
    from phcpy.decomposition import solve, write_decomposition
    deco = solve(pols, verbose=False)
    write_decomposition(deco)
def sphere_cylinder_intersection():
    """
    A sphere intersected by a cylinder defines a quartic curve.
    """
    sphere = 'X^2 + Y^2 + Z^2 - 1;'
    cylinder = 'X^2 + 1.0e-14*Y^2 + (Z - 0.5)^2 - 1;'
    from phcpy.sets import double_hypersurface_set
    (spheqs, sphpts) = double_hypersurface_set(3, sphere)
    (cyleqs, cylpts) = double_hypersurface_set(3, cylinder)
    from phcpy.diagonal import double_diagonal_solve
    quaeqs, quapts = double_diagonal_solve\
        (3, 2, spheqs, sphpts, 2, cyleqs, cylpts)
    for pol in quaeqs: print(pol)
    for sol in quapts: print(sol)