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)