PHCpack.m2
needsPackage "NAGtypes"
newPackage(
"PHCpack",
Version => "1.5",
Date => "7 September 2012",
Authors => {
{Name => "Elizabeth Gross",
Email => "egross7@uic.edu",
HomePage => "http://www.math.uic.edu/~lizgross"},
{Name => "Sonja Petrovic",
Email => "sonja@psu.edu",
HomePage => "http://www.personal.psu.edu/sxp61"},
{Name => "Jan Verschelde",
Email => "jan@math.uic.edu",
HomePage => "http://www.math.uic.edu/~jan"},
{Name => "Contributing Author: Anton Leykin",
HomePage => "http://www.math.gatech.edu/~leykin"}
},
Headline => "Interface to PHCpack",
Configuration => {
"path" => "",
"PHCexe"=>"phc",
"keep files" => true
},
DebuggingMode => false,
AuxiliaryFiles => true,
CacheExampleOutput => true
)
needsPackage "NAGtypes"
export {
"cascade",
"constructEmbedding",
"gamma",
"factorWitnessSet",
"isCoordinateZero",
"isWitnessSetMember",
"mixedVolume",
"nonZeroFilter",
"numericalIrreducibleDecomposition",
"refineSolutions",
"solveRationalSystem",
"solveSystem",
"StableMixedVolume",
"StartDimension",
"StartSystem",
"tDegree",
"toLaurentPolynomial",
"topWitnessSet",
"trackPaths",
"Verbose",
"zeroFilter"
}
protect ErrorTolerance, protect Iterations,
protect Bits, protect ResidualTolerance,
protect Append
--##########################################################################--
-- GLOBAL VARIABLES
--##########################################################################--
DBG = 0; -- debug level (10=keep temp files)
path'PHC = (options PHCpack).Configuration#"path";
PHCexe=path'PHC|(options PHCpack).Configuration#"PHCexe";
-- this is the executable string that make sures that calls to PHCpack run:
-- NOTE: the absolute path should be put into the init-PHCpack.m2 file
needsPackage "SimpleDoc"
needsPackage "NAGtypes"
--##########################################################################--
-- INTERNAL METHODS
--##########################################################################--
----------------------------------
--- File read/write operations ---
----------------------------------
getFilename = () -> (
filename := temporaryFileName();
while fileExists(filename)
or fileExists(filename|"PHCinput")
or fileExists(filename|"PHCoutput") do filename = temporaryFileName();
filename
)
parseSolutions = method(TypicalValue => Sequence, Options => {Bits => 53})
parseSolutions (String,Ring) := o -> (s,R) -> (
-- parses solutions in PHCpack format
-- IN: s = string of solutions in PHCmaple format
-- V = list of variable names
-- OUT: List of solutions, each of type Point,
-- carrying also other diagnostic information about each.
oldprec := defaultPrecision;
defaultPrecision = o.Bits;
L := get s;
L = replace("=", "=>", L);
L = replace("I", "ii", L);
L = replace("E\\+","e",L);
L = replace("E", "e", L);
L = replace("time", "\"time\"", L);
L = replace("rco", "\"rco\"", L);
L = replace("multiplicity", "\"mult\"", L);
L = replace("res", "\"residual\"", L);
L = replace("resolution", "\"residual\"", L);
-- because M2 automatically thinks "res"=resolution
sols := toList apply(value L, sol->new HashTable from toList sol);
defaultPrecision = oldprec;
apply(sols, sol->point( {apply(gens R, v->sol#v)} | outputToPoint sol ))
)
pointsToFile = method(TypicalValue => Nothing, Options => {Append => false})
pointsToFile (List,Ring,String) := o -> (S,R,name) -> (
-- writes list of points to file in PHCpack format
-- IN: S, list of points, e.g.: obtained as points(WitnessSet);
-- R, ring (with symbols for the variables);
-- name, string with file name.
file := if o.Append then openOutAppend name else openOut name;
if o.Append then
file << endl << "THE SOLUTIONS :" << endl;
file << #S << " " << numgens R << endl <<
"===========================================================" << endl;
scan(#S, i->(
file << "solution " << i+1 << " :" << endl <<
"t : 0.00000000000000E+00 0.00000000000000E+00" << endl <<
"m : 1" << endl <<
"the solution for t :" << endl;
scan(numgens R, v->(
L := " "|toString R_v|" : "|
format(0,-1,9,9,realPart toCC S#i#v)|" "|
format(0,-1,9,9,imaginaryPart toCC S#i#v);
file << L << endl;
));
file << "== err : 0 = rco : 1 = res : 0 ==" << endl;
));
close file;
)
solutionsToFile = method(TypicalValue => Nothing, Options => {Append => false})
solutionsToFile (List,Ring,String) := o -> (S,R,name) -> (
-- writes solutions to file in PHCpack format
-- IN: S, list of solutions;
-- R, ring (with symbols for the variables);
-- name, string with file name.
file := if o.Append then openOutAppend name else openOut name;
if o.Append then
file << endl << "THE SOLUTIONS :" << endl;
file << #S << " " << numgens R << endl <<
"===========================================================" << endl;
scan(#S, i->(
file << "solution " << i << " :" << endl <<
"t : 0.00000000000000E+00 0.00000000000000E+00" << endl <<
"m : 1" << endl <<
"the solution for t :" << endl;
scan(numgens R, v->(
L := " "|toString R_v|" : "|
format(0,-1,9,9,realPart toCC S#i#Coordinates#v)|" "|
format(0,-1,9,9,imaginaryPart toCC S#i#Coordinates#v);
file << L << endl;
));
file << "== err : 0 = rco : 1 = res : 0 ==" << endl;
));
close file;
)
startSystemFromFile = method(TypicalValue => List)
startSystemFromFile (String) := (name) -> (
-- IN: file name starting with a random coefficient start system
-- OUT: list of polynomials in a ring with coefficients in CC
-- REQUIRED: the format of the system on file is a random coefficient
-- system produced by phc -m, every term starts on a separate line
-- with the "+" sign. The coefficient field must be CC.
s := get name;
s = replace("i","ii",s);
s = replace("E","e",s);
L := lines(s);
n := value L_0;
result := {};
i := 0; j := 1;
local stop;
local term;
local p;
while i < n do (
stop = false; p = 0;
while not stop do (
if #L_j != 0 then (
-- we have to bite off the first "+" sign of the term
if (L_j_(#L_j-1) != ";") then (
term = value substring(1,#L_j-1,L_j); p = p + term;
) else ( -- in this case (L_j_(#L_j-1) == ";") holds
term = value substring(1,#L_j-2,L_j); p = p + term;
stop = true; result = result | {p}
);
); j = j + 1;
stop = stop or (j >= #L);
);
i = i + 1;
);
result
)
systemFromFile = method(TypicalValue => List)
systemFromFile (String) := (name) -> (
-- IN: file name starting with a polynomial system
-- OUT: list of polynomials in a ring with coefficients in CC
-- NOTE: the format of the system on file may be such that the
-- first term starts with +1*x which cannot be digested by M2.
-- In contrast to the startSystemFromFile, the first term could
-- also be "-1*x" so we must be a bit more careful...
-- Another problem are constants as "3.0e+00", at least on a Mac.
s := get name;
s = replace("i","ii",s);
s = replace("E","e",s);
--s = replace("e+00","",s); -- on Mac: M2 crashes at 3.0e+00 as constant
L := lines(s);
n := value L_0;
result := {};
i := 0; j := 1;
local stop;
local term;
local p;
while i < n do (
stop = false; p = 0;
while not stop do (
if #L_j != 0 then (
if (L_j_(#L_j-1) != ";") then (
-- we have to bite off the first "+" sign of the term
term =
value substring(1,#L_j-1,L_j);
if (L_j_0 == "+") then p = p + term else p = p - term;
) else ( -- in this case (L_j_(#L_j-1) == ";") holds
term = value substring(1,#L_j-2,L_j);
if (L_j_0 == "+") then p = p + term else p = p - term;
stop = true; result = result | {p}
);
); j = j + 1;
stop = stop or (j >= #L);
);
i = i + 1;
);
result
)
systemToFile = method(TypicalValue => Nothing)
systemToFile (List,String) := (F,name) -> (
file := openOut name;
if (#F == numgens ring F#0)
then file << #F << endl
else file << #F << " " << numgens ring F#0 << endl;
scan(F, f->(
L := toExternalString f;
L = replace("ii", "I", L);
L = replace("e", "E", L);
L = replace("p53","",L);
L = replace("p[0-9]+","",L);
file << L << ";" << endl;
));
close file;
)
witnessSetFromFile = method(TypicalValue => WitnessSet)
witnessSetFromFile (String) := (name) -> (
-- IN: file name which contains a witness set in PHCpack format
-- OUT: a witness set
e := systemFromFile(name);
d := dimEmbedding(e);
witnessPointsFile := temporaryFileName() | "PHCwitnessPoints";
if fileExists witnessPointsFile then removeFile witnessPointsFile;
run(PHCexe|" -z " | name | " "|witnessPointsFile);
eR := ring first e;
g := parseSolutions(witnessPointsFile,eR);
w := witnessSet(ideal(take(e,{0,#e-d-1})),ideal(take(e,{#e-d,#e-1})),g);
return w;
)
witnessSetToFile = method()
witnessSetToFile (WitnessSet,String) := (witset,name) -> (
-- IN: name, a file name where to write a witness set to,
-- witset, a witness set.
R := ring ideal(witset);
s := equations(witset)|slice(witset);
p := points(witset);
systemToFile(s,name);
pointsToFile(p,R,name,Append=>true);
)
----------------------------
-- embedding dimension -----
----------------------------
dimEmbedding = method(TypicalValue => ZZ)
dimEmbedding (List) := (system) -> (
-- IN: embedded system with slack variables.
-- OUT: returns the number of slack variables = the dimension.
eR := ring first system;
v := gens eR;
slack := v_(#v-1);
zz := toString(slack);
ds := substring(2,#zz-1,zz);
dimension := if (value(ds)===null) then 0 else value(ds);
return dimension;
)
-----------------------------
--- conversion to Point ---
-----------------------------
outputToPoint = method()
outputToPoint HashTable := (H)->{
SolutionStatus => if H#"mult" == 1 then Regular else Singular,
ConditionNumber => (H#"rco")^(-1),
LastT => H#"time"
}
----------------------------------
----add slack variables-----------
----------------------------------
addSlackVariables = method()
addSlackVariables WitnessSet := (W) -> (
-- creates a new system of polynomials, in variables:
-- old set of variables, and zz1, ..., zzd, where
-- d is the dimension of W.
R := ring W;
n := numgens R;
d := dim W; -- this will be the number of slack variables to add
W1 := generalEquations W;
-- Add in new variables zz1, ..., zzd,
-- this changes the equations, the slice, and the points
slackvars := apply(d, i->getSymbol("zz"|toString (i+1)));
newR := (coefficientRing R)[gens R, slackvars];
newvars := (vars newR)_{n..n+d-1};
-- new slice:
newSlice := apply(d, i -> sub(W1.Slice#i,newR) + newR_(n + i));
-- add a linear matrix
A := random(newR^(d),newR^(n-d));
AZ := newvars * A;
newEqns := (sub(gens ideal W1, newR) + AZ) | newvars;
-- new points
zeros := toList apply(d, i -> 0_(coefficientRing R));
newPoints := apply(W1.Points, pt -> point({join(coordinates(pt),zeros)}));
witnessSet(ideal newEqns, ideal newSlice, newPoints)
)
-------------------------------------------------------
------------ Witness Superset Filters ---------------
-------------------------------------------------------
witnessSuperSetFilter = method()
witnessSuperSetFilter (WitnessSet,List) := (witset,pts) -> (
-- This is an auxiliary procedure to witnessSuperSetsFilter,
-- applying isWitnessSetMember to filter points in a witness superset.
-- IN: witset, a witness set to represent a witness set;
-- pts, a list of points to be tested for membership.
-- OUT: a list of points in pts for which isWitnessSetMember is false.
result := new MutableList from {};
for p in pts do (
if not isWitnessSetMember(witset,p)
then result = append(result,p);
);
return result;
)
witnessSuperSetsFilter = method()
witnessSuperSetsFilter (MutableList,List) := (witsets,pts) -> (
-- This is an auxiliary procedure to cascade,
-- applying witnessSuperFilter to filter points in the
-- witness supersets given in the list.
-- IN: witsets, a list of tuples (dimension,witness set),
-- pts, a list of points to be tested for membership.
-- OUT: a list of points in pts for which isWitnessSetMember is false.
result := new MutableList from {};
local c;
for p in pts do (
found := false;
if instance(p,Point)
then c = p
else c = point{p};
for w in witsets when (not found) do (
ws := w#1;
found = isWitnessSetMember(ws,c);
);
if not found then result = append(result,p);
);
return toList(result);
)
--##########################################################################--
-- EXPORTED METHODS
--
-- NOTE:
-- trackPaths and refineSolutions are methods adapted
-- from NumericalAlgebraicGEometry/PHCpack.interface.m2
--##########################################################################--
-----------------------------------------------
------------ CASCADE ------------------------
-----------------------------------------------
cascade = method(TypicalValue => NumericalVariety, Options => {StartDimension => -1})
cascade (List) := o -> (system) -> (
-- IN: system, a polynomial system;
-- dimension, top dimension of the solution set.
-- OUT: a hash table with keys the dimension of each component,
-- values are witness sets for positive dimensions,
-- or a list of isolated solutions for key equal to zero.
if not(class coefficientRing ring ideal system===ComplexField) then
error "coefficient ring is not complex";
R := ring ideal system;
if # system > numgens R then error "the system is overdetermined";
if o.StartDimension==-1 then startdim:=(numgens R)-1
else startdim=o.StartDimension;
PHCinputFile := temporaryFileName() | "PHCinput";
PHCoutputFile := temporaryFileName() | "PHCoutput";
PHCbatchFile := temporaryFileName() | "PHCbatch";
PHCsolsFile := temporaryFileName() | "PHCsols";
PHCsessionFile := temporaryFileName() | "PHCsession";
{PHCinputFile, PHCoutputFile, PHCbatchFile, PHCsolsFile, PHCsessionFile} / (f->if fileExists f
then removeFile f);
toList (0..startdim) / (i->if fileExists (PHCoutputFile | "_sw" | i)
then removeFile (PHCoutputFile | "_sw" | i) );
stdio << "writing output to file " << PHCoutputFile << endl;
systemToFile(system,PHCinputFile);
s := concatenate("0\ny\n",PHCinputFile);
s = concatenate(s,"\n",PHCoutputFile);
s = concatenate(s,"\n");
s = concatenate(s,toString(startdim));
s = concatenate(s,"\nn\n");
bat := openOut PHCbatchFile;
bat << s;
close bat;
stdio << "calling phc -c < " << PHCbatchFile;
stdio << " > " << PHCsessionFile << endl;
run(PHCexe|" -c < " | PHCbatchFile | " > " | PHCsessionFile);
stdio << "output of phc -c is in file " << PHCoutputFile << endl;
stdio << "... constructing witness sets ... " << endl;
local slackvars;
local RwithSlack;
--get solutions
result := new MutableList from {};
dims:=select(toList (0..startdim),j->(fileExists (PHCoutputFile | "_sw" | j) and
match("THE SOLUTIONS",get (PHCoutputFile | "_sw" | j))));
topdimension := max dims;
i := topdimension;
while i>=0 do
( if member(i,dims) then (
fil := (PHCoutputFile | "_sw" | i);
if i > 0 then
(
slackvars = apply(i, k->getSymbol("zz"|toString(k+1)));
RwithSlack = (coefficientRing R)monoid(gens R | slackvars);
use RwithSlack;
supwit := witnessSetFromFile(fil);
if i == topdimension then (
result = append(result,(i,supwit));
) else (
supsols := points(supwit);
genpts := witnessSuperSetsFilter(result,supsols);
g := toList(apply(genpts,x->point{x}));
ws := witnessSet(ideal(equations(supwit)),ideal(slice(supwit)),g);
if #g!=0 then result = append(result,(i,ws));
);
) else (
run(PHCexe | " -z " | fil | " " | PHCsolsFile);
use R;
supwit = witnessSetFromFile(fil);
psols := parseSolutions(PHCsolsFile,R);
isols := witnessSuperSetsFilter(result,psols);
ws = witnessSet(ideal(equations(supwit)),ideal(slice(supwit)),isols);
if #isols!=0 then result = append(result,(0,ws));
);
);
i = i-1
);
use R;
numericalVariety(toList (apply(result,i-> last i)))
)
-----------------------------------------------
-------- CONSTRUCT EMBEDDING ------------------
-----------------------------------------------
constructEmbedding = method(TypicalValue => List)
constructEmbedding (List, ZZ) := (system, dimension) -> (
-- IN: system, a polynomial system with complex coefficients;
-- dimension, expected dimension of the solution set.
-- OUT: system with as many random hyperplanes at the end
-- as the value of dimension.
if not(class coefficientRing ring first system===ComplexField) then
error "coefficient ring of system is not complex";
PHCinputFile := temporaryFileName() | "PHCinput";
PHCoutputFile := temporaryFileName() | "PHCoutput";
PHCbatchFile := temporaryFileName() | "PHCbatch";
PHCsessionFile := temporaryFileName() | "PHCsession";
{PHCinputFile, PHCoutputFile, PHCbatchFile,
PHCsessionFile} / (f->if fileExists f then removeFile f);
systemToFile(system,PHCinputFile);
s := concatenate("1\ny\n",PHCinputFile);
s = concatenate(s,"\n",PHCoutputFile);
s = concatenate(s,"\n");
s = concatenate(s,toString(dimension));
s = concatenate(s,"\nn\n");
bat := openOut PHCbatchFile;
bat << s;
close bat;
stdio << "calling phc -c < " << PHCbatchFile;
stdio << " > " << PHCsessionFile << endl;
run(PHCexe|" -c < " | PHCbatchFile | " > " | PHCsessionFile);
stdio << "output of phc -c is in file " << PHCoutputFile << endl;
-- extending the ring with slack variables
slackvars := apply(dimension, i->getSymbol("zz"|toString(i+1)));
R := ring ideal system;
-- surplus variables are used when the initial system is overconstrained
nv := numgens R;
nq := #system;
nbss := nq - nv;
local RwithSlack;
if (nbss > 0) then (
surplusvars := apply(nbss, i->getSymbol("ss"|toString(i+1)));
RwithSlack = (coefficientRing R)[gens R, surplusvars, slackvars];
) else (
RwithSlack = (coefficientRing R)[gens R, slackvars];
);
use RwithSlack;
return systemFromFile(PHCoutputFile);
)
---------------------------------------------
----------- FACTOR WITNESS SET --------------
---------------------------------------------
factorWitnessSet = method(TypicalValue=>List)
factorWitnessSet (WitnessSet ) := w -> (
-- IN: a witness set properly embedded with slack variables.
-- OUT: a list of witness sets, every element is irreducible.
PHCinputFile := temporaryFileName() | "PHCinput";
PHCoutputFile := temporaryFileName() | "PHCoutput";
PHCbatchFile := temporaryFileName() | "PHCbatch";
PHCsessionFile := temporaryFileName() | "PHCsession";
stdio << "preparing input file to " << PHCinputFile << endl;
system := equations(w) | slice(w);
systemToFile(system,PHCinputFile);
R := ring first system;
use R;
L := toList(points(w));
pointsToFile(L,R,PHCinputFile,Append=>true);
stdio << "preparing batch file to " << PHCbatchFile << endl;
s := concatenate("2\n",PHCinputFile); -- option 2 of phc -f
s = concatenate(s,"\n",PHCoutputFile);
s = concatenate(s,"\n1\n"); -- use monodromy to factor
s = concatenate(s,"0\n"); -- default settings of path trackers
bat := openOut PHCbatchFile;
bat << s;
close bat;
stdio << "... calling monodromy breakup ..." << endl;
run(PHCexe|" -f < " | PHCbatchFile | " > " | PHCsessionFile);
stdio << "session information of phc -f is in " << PHCsessionFile << endl;
stdio << "output of phc -f is in file " << PHCoutputFile << endl;
-- counting the number of factors
count := 0;
result := new MutableList from {};
name := PHCinputFile | "_f" | toString(count+1);
while (fileExists name) do (
result = append(result,witnessSetFromFile(name));
count = count + 1;
name = PHCinputFile | "_f" | toString(count+1);
);
stdio << "found " << count << " irreducible factors " << endl;
for ws in result do ws#IsIrreducible=true;
return numericalVariety(toList(result));
)
----------------------------
---- IS COORDINATE ZERO ----
----------------------------
isCoordinateZero = method(TypicalValue => Boolean)
isCoordinateZero (Point,ZZ,RR) := (sol,k,tol) -> (
-- IN: sol, a solution of a polynomial system;
-- k, index to a coordinate must be within range;
-- tol, tolerance for the absolute value of the k-th coordinate.
-- OUT: true, if the k-th coordinate has abs less than tol;
-- false, otherwise.
L := sol#Coordinates;
return abs(L_k)<=tol;
)
----------------------------------------------
----------IS WITNESS SET MEMBER -------------
----------------------------------------------
isWitnessSetMember = method(TypicalValue => Boolean, Options => {Verbose => false})
isWitnessSetMember (WitnessSet,Point) := o-> (witset,testpoint) -> (
-- IN: witset, a witness set for a positive dimensional solution set,
-- testpoint, does it belong to the solution set?
-- OUT: true if testpoint is a member of the solution set,
-- false otherwise.
PHCwitnessFile := temporaryFileName() | "PHCwitset";
PHCtestpointFile := temporaryFileName() | "PHCtestpoint";
PHCbatchFile := temporaryFileName() | "PHCbatch";
PHCoutputFile := temporaryFileName() | "PHCoutput";
PHCsessionFile := temporaryFileName() | "PHCsession";
if o.Verbose then
stdio << "writing witness set to file " << PHCwitnessFile << endl;
witnessSetToFile(witset,PHCwitnessFile);
d := dim(witset); -- pad test point with zero values for slack variables
dzeros := toList(apply(0..d,i->0));
L := {coordinates(testpoint)|dzeros};
R := ring ideal(witset);
if o.Verbose then
stdio << "writing test point to file " << PHCtestpointFile << endl;
pointsToFile(L,R,PHCtestpointFile);
s := concatenate("1\n",PHCwitnessFile);
s = concatenate(s,"\n",PHCtestpointFile);
s = concatenate(s,"\n",PHCoutputFile);
s = concatenate(s,"\n0\n");
bat := openOut PHCbatchFile;
bat << s;
close bat;
if o.Verbose then (
stdio << "calling phc -f < " << PHCbatchFile;
stdio << " > " << PHCsessionFile << endl;
);
run(PHCexe|" -f < " | PHCbatchFile | " > " | PHCsessionFile);
if o.Verbose then
stdio << "output of phc -f is in file " << PHCoutputFile << endl;
-- if the point does not belong to the witness set,
-- then the output file contains the word "not"
r := get PHCoutputFile;
result := not match("not",r);
return result;
)
-----------------------------------
-------- MIXED VOLUME -------------
-----------------------------------
mixedVolume = method(Options => {StableMixedVolume => false, StartSystem => false})
mixedVolume List := Sequence => opt -> system -> (
-- IN: system = list of polynomials in the system
-- OUT: mixed volume of the system. if optional inputs specified, then output is
-- a sequence containing a subset of: mixed volume, stable mixed volume,
-- start system, and solutions to start system.
-- Calls an Ada translation of ACM TOMS Algorithm 846:
-- "MixedVol: a software package for mixed-volume computation"
-- by Tangan Gao, T. Y. Li, Mengnien Wu, ACM TOMS 31(4):555-560, 2005.
R := ring ideal system;
n := #system;
if n < numgens R then error "the system is underdetermined";
if n > numgens R then error "the system is overdetermined";
if not(class coefficientRing R===ComplexField) then
error "coefficient ring is not complex";
filename := getFilename();
<< "using temporary files " << filename|"PHCinput" << " and " << filename|"PHCoutput" << endl;
infile := filename|"PHCinput";
outfile := filename|"PHCoutput";
cmdfile := filename|"PHCcommands";
sesfile := filename|"PHCsession";
if opt.StartSystem then startfile := filename|"PHCstart";
-- writing data to the corresponding files
file := openOut cmdfile;
file << "4" << endl; -- call MixedVol in PHCpack
if opt.StableMixedVolume
then (file << "y" << endl) -- stable mixed volume wanted
else (file << "n" << endl); -- no stable mixed volume
file << "n" << endl; -- no mixed-cell configuration on file
if opt.StartSystem then (
file << "y" << endl; -- random coefficient start system wanted
file << startfile << endl;
file << "0" << endl << "1" << endl;
)
else (file << "n" << endl); -- no random coefficient start system
close file;
systemToFile(system,infile);
-- launching mixed volume calculator :
execstr := PHCexe|" -m "|infile|" "|outfile|" < "|cmdfile|" > "|sesfile;
ret := run(execstr);
if ret =!= 0 then
error "error occurred while executing PHCpack command: phc -m";
F := get outfile;
-- search lines of outfile for: " mixed volume : "
-- once found, extract just the number and return its value:
local mixvol;
scanLines(line ->
if substring(0,21,line) == "common mixed volume :" then (
mixvol = value replace("common mixed volume : ","",line);
break
), outfile);
local stabmv;
if opt.StableMixedVolume then (
scanLines(line ->
if substring(0,21,line) == "stable mixed volume :" then (
stabmv = value replace("stable mixed volume : ","",line);
break
), outfile);
);
local result;
if not opt.StartSystem then (
if opt.StableMixedVolume then result = (mixvol, stabmv) else result = mixvol;
)
else (
solsfile := startfile | ".sols";
p := startSystemFromFile(startfile);
execstr = PHCexe|" -z "|startfile|" "|solsfile;
ret = run(execstr);
if ret =!= 0 then
error "error occurred while executing PHCpack command: phc -z";
sols := parseSolutions(solsfile, ring ideal system);
if opt.StableMixedVolume
then result = (mixvol,stabmv,p,sols)
else result = (mixvol,p,sols);
);
result
)
-------------------------------------------
------------NON ZERO FILTER----------------
-------------------------------------------
nonZeroFilter = method(TypicalValue => List)
nonZeroFilter (List,ZZ,RR) := (sols,k,tol) -> (
-- IN: sols, solutions of a polynomial system;
-- k, index to a coordinate of a solution
-- tol, tolerance for the absolute value of k-th coordinate
-- OUT: list of solutions in sols where the k-th coordinate
-- is less than the tolerance.
return select(sols,t->(not isCoordinateZero(t,k,tol)));
)
-------------------------------------------
--NUMERICAL IRREDUCIBLE DECOMPOSITION------
-------------------------------------------
numericalIrreducibleDecomposition=method(TypicalValue=>NumericalVariety, Options=>{StartDimension=>-1})
numericalIrreducibleDecomposition (List) := o -> (L) -> (
--IN: an ideal, top dimension
--OUT: a NumericalVariety
setRandomSeed(random ZZ);
startdim:=o.StartDimension;
W:=cascade(L,StartDimension=>startdim);
witsets:=apply(keys W, i->if i!=0 then (factorWitnessSet((W#i)_0))#i else W#i);
numericalVariety(flatten witsets)
)
-----------------------------------
----- REFINING SOLUTIONS ----------
-----------------------------------
refineSolutions = method(TypicalValue=>List)
refineSolutions (List,List,ZZ) := (f,sols,dp) -> (
-- IN: f, a polynomial system with complex coefficients;
-- sols, list of points, initial approximations for the solutions of f;
-- dp, number of decimal places in the working precision.
-- OUT: a list of refined solutions.
if not(class coefficientRing ring first f===ComplexField) then
error "coefficient ring of system is not complex";
PHCinputFile := temporaryFileName() | "PHCinput";
PHCoutputFile := temporaryFileName() | "PHCoutput";
PHCbatchFile := temporaryFileName() | "PHCbatch";
PHCsessionFile := temporaryFileName() | "PHCsession";
PHCsolutions := temporaryFileName() | "PHCsolutions";
stdio << "writing input system to " << PHCinputFile << endl;
systemToFile(f,PHCinputFile);
stdio << "appending solutions to " << PHCinputFile << endl;
R := ring first f;
solutionsToFile(sols, R, PHCinputFile, Append=>true);
stdio << "preparing input data for phc -v in " << PHCbatchFile << endl;
s := concatenate("3\n",PHCinputFile);
s = concatenate(s,"\n");
s = concatenate(s,PHCoutputFile);
s = concatenate(s,"\n3\n1.0E-");
s = concatenate(s,toString(dp-4)); -- tolerance for correction term
s = concatenate(s,"\n4\n1.0E-");
s = concatenate(s,toString(dp+16)); -- tolerance for residual
s = concatenate(s,"\n6\n");
nit := ceiling(dp/10.0);
s = concatenate(s,toString(nit)); -- number of Newton iterations
s = concatenate(s,"\n7\n");
s = concatenate(s,toString(dp)); -- decimal places in working precision
s = concatenate(s,"\n0\n");
bat := openOut PHCbatchFile;
bat << s;
close bat;
-- stdio << "running phc -v, writing output to " << PHCsessionFile << endl;
run(PHCexe|" -v < " | PHCbatchFile | " > " | PHCsessionFile);
stdio << "using temporary file " << PHCoutputFile;
stdio << " for storing refined solutions " << endl;
stdio << "solutions in Maple format in " << PHCsolutions << endl;
run(PHCexe|" -z " | PHCoutputFile | " " | PHCsolutions);
b := ceiling(log_2(10^dp));
result := parseSolutions(PHCsolutions,R,Bits=>b);
result
)
----------------------------------
------ SOLVE SYSTEM --------------
----------------------------------
solveSystem = method(TypicalValue => List)
solveSystem List := List => system -> (
-- IN: system = list of polynomials with complex coeffiecients,
-- i.e. the system to solved
-- OUT: solutions to the system, a list of Points
if instance(ring ideal system, FractionField) then
error "ring is a fraction field, use solveRationalSystem";
if not(class coefficientRing ring first system===ComplexField) then
error "coefficient ring is not complex";
filename := getFilename();
<< "using temporary files " << filename|"PHCinput" << " and " << filename|"PHCoutput" << endl;
infile := filename|"PHCinput";
outfile := filename|"PHCoutput";
solnsfile := filename|"PHCsolns";
R := ring ideal system;
n := #system;
if n < numgens R then
error "the system is underdetermined, positive dimensional";
-- add slack variables if needed (i.e. if system is overdetermined)
if n > numgens R then (
nSlacks := n - numgens R;
slackVars := apply(nSlacks, i->getSymbol("OOOO"|toString i));
newR := CC(monoid[gens R, slackVars]);
rM := random(CC^n,CC^nSlacks);
system = apply(#system, i->sub(system#i,newR)
+(rM^{i}*transpose submatrix'(vars newR,toList(0..numgens R - 1)))_(0,0))
) else newR=R;
-- writing data to the corresponding files:
systemToFile(system,infile);
-- launching blackbox solver:
execstr := PHCexe|" -b " |infile|" "|outfile;
ret := run(execstr);
if ret =!= 0 then
error "error occurred while executing PHCpack command: phc -b";
execstr = PHCexe|" -z "|infile|" " |solnsfile;
ret = run(execstr);
if ret =!= 0 then
error "error occurred while executing PHCpack command: phc -z";
-- parse and output the solutions:
result := parseSolutions(solnsfile, R);
result
)
------------------------------------
------SOLVE RATIONAL SYSTEM--------
-----------------------------------
solveRationalSystem = method(TypicalValue => List)
solveRationalSystem List := system -> (
-- IN: system = list of rational equations with complex coeffiecients,
-- i.e. the system to solved
-- OUT: solutions to the system, a list of Points
origRing := ring ideal system;
--convert to laurent polynomial
if instance(ring ideal system, FractionField)
then system = toLaurentPolynomial(system, getSymbol "LLLL");
filename := getFilename();
<< "using temporary files " << filename|"PHCinput" << " and " << filename|"PHCoutput" << endl;
infile := filename|"PHCinput";
outfile := filename|"PHCoutput";
solnsfile := filename|"PHCsolns";
R := ring ideal system;
vars R;
n := #system;
if n < numgens R then
error "the system is underdetermined, positive dimensional";
-- add slack variables if needed (i.e. if system is overdetermined)
if n > numgens R then (
nSlacks := n - numgens R;
slackVars := apply(nSlacks, i->getSymbol("OOOO"|toString i));
newR := QQ(monoid(gens R | slackVars, Inverses=>true, MonomialOrder=>RevLex));
rM := random(QQ^n,QQ^nSlacks);
system = apply(#system, i->sub(system#i,newR)
+(rM^{i}*transpose submatrix'(vars newR,toList(0..numgens R - 1)))_(0,0))
) else newR=R;
-- writing data to the corresponding files:
systemToFile(system,infile);
-- launching blackbox solver:
execstr := PHCexe|" -b " |infile|" "|outfile;
ret := run(execstr);
if ret =!= 0 then
error "error occurred while executing PHCpack command: phc -b";
execstr = PHCexe|" -z "|infile|" " |solnsfile;
ret = run(execstr);
if ret =!= 0 then
error "error occurred while executing PHCpack command: phc -z";
-- parse and output the solutions:
result := parseSolutions(solnsfile, QQ[gens origRing]);
result
)
-----------------------------------
------ TO LAURENT POLYNOMIAL ------
-----------------------------------
toLaurentPolynomial = method(TypicalValue => List)
toLaurentPolynomial (List, Symbol) := (system, var) -> (
-- IN: system (or a polynomial) which is rational
-- (i.e. lives in some field of fractions of a polynomial ring)
-- and a symbol to be used for new indexed variables (e.g. x)
-- OUT: same system converted to a Laurent polynomial system,
-- where denominators are replaced with new indexed variables.
R := ring ideal system;
P := R.baseRings_(#R.baseRings-1);
-- P is the polynomial ring whose field of fractions the system lives in
counter := 0;
scan(system, f-> (
if instance(class f, FractionField) then
--if f is already polynomial, do not do anything!
if liftable(f,P) then
--if it can be lifted to P, then do so and update the system
system = system-set{f} | {lift(f,P)}
else (
-- add one new variable "var_counter", and define the
-- appropriate Laurent polynomial ring:
P = (coefficientRing P) monoid(flatten entries vars P | {var_counter},
Inverses=>true, MonomialOrder=>RevLex);
-- add the new Laurent polynomial to replace the rational equation:
newvar := P_(numgens P - 1);
system = system - set{f} | {sub(numerator(f),P)*newvar^(-1)};
system = system | {newvar - sub(denominator(f),P)};
counter = counter+1;
)
)
);
-- For consistency, we make sure that everyone lives in the Laurent
-- polynomial ring. This is not necessary for PHCpack,
-- but for any further M2 calculations for the system, it is.
system=apply(system,f-> sub(f,P));
system
)
-----------------------------------------------
------------ TOP WITNESS SET ----------------
-----------------------------------------------
topWitnessSet = method()
topWitnessSet (List,ZZ) := (system,dimension) -> (
-- IN: system, a polynomial system;
-- dimension, top dimension of the solution set.
-- OUT: a witness set for the top dimensional component,
-- a list of nonsolutions
stdio << "... calling constructEmbedding ..." << endl;
e := constructEmbedding(system,dimension);
stdio << "... calling solveSystem ..." << endl;
s := solveSystem(e);
g := zeroFilter(s,#e-1,1.0e-10);
ns := nonZeroFilter(s,#e-1,1.0e-10);
stdio << "... constructing a witness set ... " << endl;
w := witnessSet(ideal(take(e,{0,#e-dimension-1})),
ideal(take(e,{#e-dimension,#e-1})),g);
return (w,ns);
)
----------------------------------
-------- TRACK PATHS -----------
----------------------------------
trackPaths = method(TypicalValue => List, Options=>{gamma=>0, tDegree=>2})
trackPaths (List,List,List) := List => o -> (T,S,Ssols) -> (
-- IN: T, target system to be solved;
-- S, start system with solutions in Ssols;
-- Ssols, solutions at the start of the paths.
-- OPT INTPUTS: gamma, constant for gamma trick
-- tDegree, degree of continuation parameter
-- OUT: Tsols, solutions at the end of the paths.
if not(class coefficientRing ring first T===ComplexField) then
error "coefficient ring of target system is not complex";
if not(class coefficientRing ring first S===ComplexField) then
error "coefficient ring of start system is not complex";
R := ring first T;
n := #T;
targetfile := temporaryFileName() | "PHCtarget";
startfile := temporaryFileName() | "PHCstart";
outfile := temporaryFileName() | "PHCoutput";
Ssolsfile := temporaryFileName() | "PHCstartsols";
Tsolsfile := temporaryFileName() | "PHCtargetsols";
batchfile := temporaryFileName() | "PHCbat";
<< "using temporary files " << outfile << " and " << Tsolsfile << endl;
if n < numgens R then error "the system is underdetermined";
if n> numgens R then error "the system is overdetermined";
-- writing data to the corresponding files
systemToFile(T,targetfile);
systemToFile(S,startfile);
solutionsToFile(Ssols,R,Ssolsfile);
-- making batch file
bat := openOut batchfile;
bat << targetfile << endl << outfile << endl <<"n"<< endl
<< startfile << endl << Ssolsfile << endl;
-- first menu with settings of the construction of the homotopy
bat << "k" << endl << o.tDegree << endl;
if o.gamma != 0 then (
bat << "a" << endl << realPart o.gamma << endl;
bat << imaginaryPart o.gamma << endl;
);
bat << "0" << endl;
-- second menu
bat << "0" << endl; -- exit for now
-- third menu
bat << "0" << endl; -- exit for now
-- fourth menu
bat << "0" << endl; -- exit for now
close bat;
run(PHCexe|" -p <"|batchfile|" >phc_session.log");
run(PHCexe|" -z "|outfile|" "|Tsolsfile);
-- parse and output the solutions
result := parseSolutions(Tsolsfile, R);
if n > numgens R then (
result = apply(result, s->(
if any(drop(first s, numgens R), x->abs x > 0.01)
then error "slack value is nonzero";
{take(first s, numgens R)}|drop(s,1)
));
totalN := #result;
scan(result, s->(
if s#1#"mult">1 then error "mutiple root encountered";
if s#1#"mult"<0 then error "negative mutiplicity";
));
result = select(result,
s->
max(s#0/abs)<10000 -- path failed and/or diverged
);
if DBG>0 and #result < totalN
then -- error "discarded!"
<< "track[PHCpack]: discarded "<<
totalN-#result << " out of " << totalN << " solutions" << endl;
);
return result;
)
-----------------------------------------------
------------ ZERO FILTER -----------------------
-----------------------------------------------
zeroFilter = method(TypicalValue => List)
zeroFilter (List,ZZ,RR) := (sols,k,tol) -> (
-- IN: sols, solutions of a polynomial system;
-- k, index to a coordinate of a solution
-- tol, tolerance for the absolute value of k-th coordinate
-- OUT: list of solutions in sols where the k-th coordinate
-- is less than the tolerance.
return select(sols,t->isCoordinateZero(t,k,tol));
)
--##########################################################################--
-- DOCUMENTATION
--##########################################################################--
beginDocumentation()
load "./PHCpack/PHCpackDoc.m2";
--##########################################################################--
-- TESTS
--##########################################################################
-----------------------------------
-- cascade
-----------------------------------
TEST///
R=CC[x11,x22,x21,x12,x23,x13,x14,x24]
L={x11*x22-x21*x12,x12*x23-x22*x13,x13*x24-x23*x14}
assert( # cascade L == 1 )--there is one component of dim.5.
///;
-----------------------------------
--constructEmbedding
-----------------------------------
TEST///
R=CC[x,y,z]
L={x^2+y^2, x+y+z}
Emb=constructEmbedding(L,1)
assert( # Emb == 4) -- there are four equations
assert ( member(zz1, Emb) == true ) --one of the eqns is zz1
///;
-----------------------------------
--factorWitnessSet
-----------------------------------
TEST///
R = CC[x,y];
system = {x*y};
(w, ns)=topWitnessSet(system,1);
V=factorWitnessSet(w);
assert ((# V#1) == 2)
///;
-----------------------------------
--isCoordinateZero
-----------------------------------
TEST///
P=point({{0,1.0e-12}})
assert (isCoordinateZero(P,1,1.0e-10) == true)
///;
-----------------------------------
-- isWitnessSetMember
-----------------------------------
TEST///
R = CC [x,y]
system = {2*y+2*x, 4*y + 4*x}
(W, ns)=topWitnessSet(system, 1)
assert isWitnessSetMember(W,point{{0,0}})
///;
-----------------------------------
-- mixedVolume
-----------------------------------
TEST///
R=CC[x,y,z]
S={y-x^2,z-x^3,x+y+z-1}
m=mixedVolume(S)
assert(m==3)
R=CC[x,y,z]
S1={y^3+z^2+3,z^2+x^4+x^4*z^2+4,x^4+y^3+x^4*y^3+5}
M=mixedVolume(S1)
assert(M==48)--testing output against by-hand calculation
R=CC[x,y]
S2={x^2+x*y+y^2+x+y+1,x^5+x^4*y+x^3*y^2+x^2*y^3+x*y^4+y^5} --another example
M=mixedVolume(S2)
assert(M==10)--testing output against by-hand calculation
///;
-----------------------------------
-- nonZeroFilter
-----------------------------------
TEST///
R = CC[x,y];
f = { x^3*y^5 + y^2 + x^2*y, x*y + x^2 - 1};
fSols = solveSystem(f);
nonzeroSols = nonZeroFilter(fSols,0,1.0e-10);
assert(# nonzeroSols ==10) -- this is just complement of zeroFilter which is tested in more detail. Here we are just counting the number of solutions to make sure the method ran.
///;
-------------------------------------
--numericalIrreducibleDecomposition
-------------------------------------
TEST///
R = CC [x,y]
system={x^2*y}
V=numericalIrreducibleDecomposition(system)
assert (dim V == 1) --there are two components of dimension 1
assert ((# V#1)==2)
///;
-----------------------------------
-- refineSolutions
-----------------------------------
TEST///
R = CC[x,y];
S = {x^2 - 1/3, x*y - 1};
roots = solveSystem(S);
r0 = roots#0#Coordinates#1
newRoots = refineSolutions(S,roots,64) --recall that solutions are of type Point.
--check if precision increased:
assert(precision newRoots#0#Coordinates#1 > precision roots#0#Coordinates#1)
--check if input number of decimal places, 64, used correctly:
assert(precision newRoots#0#Coordinates#1 == ceiling(log_2(10^64)))
///;
-----------------------------------
-- solveRationalSystem
-----------------------------------
TEST///
QQ[x,y,z];
sys = {y-x^2, z-x^3, (x+y+z-1)/x};
sols = solveRationalSystem(sys);
assert(# sols == 3); --there are 3 solutions
real = realPoints(sols);
assert(# real ==1); --one solution is real
///;
-----------------------------------
-- solveSystem
-----------------------------------
TEST///
R=CC[x,y,z]
S={x^2-y*z-3,y^2-x*z-4,z^2-x*y-5}
L=solveSystem(S)
n=# L
assert(n==2)
sol1={11/6.,-1/6.,-13/6.}
sol2={-11/6.,1/6.,13/6.}
assert((abs((sol1-L_0#Coordinates)_0)<.00000000001 and abs((sol1-L_0#Coordinates)_1)<.00000000001 and abs((sol1-L_0#Coordinates)_2)<.00000000001) or
(abs((sol1-L_1#Coordinates)_0)<.00000000001 and abs((sol1-L_1#Coordinates)_1)<.00000000001 and abs((sol1-L_1#Coordinates)_2)<.00000000001))
assert((abs((sol2-L_0#Coordinates)_0)<.00000000001 and abs((sol2-L_0#Coordinates)_1)<.00000000001 and abs((sol2-L_0#Coordinates)_2)<.00000000001) or
(abs((sol2-L_1#Coordinates)_0)<.00000000001 and abs((sol2-L_1#Coordinates)_1)<.00000000001 and abs((sol2-L_1#Coordinates)_2)<.00000000001))
///;
-----------------------------------
-- toLaurentPolynomial
-----------------------------------
TEST///
QQ[x,y,z];
sys = {y-x^2, z-x^3, (x+y+z-1)/x};
convertedSys = toLaurentPolynomial(sys,w);
R=ring ideal convertedSys
assert(isPolynomialRing(R)) --make sure it is a poly ring
O=options(R);
assert(O#Inverses) -- want to make sure this is a Laurent ring
///;
-----------------------------------
--topWitnessSet
-----------------------------------
TEST///
R = CC[x,y];
system = {x*y};
(w, ns)=topWitnessSet(system,1);
assert (dim w == 1)
assert (degree w == 2)
///;
-----------------------------------
-- trackPaths
-----------------------------------
TEST///
R = CC[x,y];
f = { x^3*y^5 + y^2 + x^2*y, x*y + x^2 - 1};
(m,q,qsols) = mixedVolume(f,StartSystem=>true);
fsols = trackPaths(f,q,qsols)
assert(# fsols == 8)
///;
-----------------------------------
-- zeroFilter
-----------------------------------
TEST///
R = CC[x,y];
f = { x^3*y^5 + y^2 + x^2*y, x*y + x^2 - 1};
fSols = solveSystem(f);
zeroSols = zeroFilter(fSols,1,1.0e-10);
assert( sort {zeroSols_0#Coordinates,zeroSols_1#Coordinates} == {{-1, 0}, {1, 0}}
)
///;
--##########################################################################--
end -- terminate reading ...
--##########################################################################--
restart
installPackage("PHCpack",RemakeAllDocumentation=>true)
installPackage ("PHCpack",RerunExamples=>true)
viewHelp PHCpack
--many errors seem to be due to the fact some things are not exported:
--error: mutable unexported unset symbol(s) in package PHCpack: 'generalEquations', 'NumericalVariety', 'numericalVariety', 'IsIrreducible'
/Users/sxp61/m2svn/trunk/M2/Macaulay2/packages/PHCpack.m2:329:12-329:28: here is the first use of 'generalEquations'
/Users/sxp61/m2svn/trunk/M2/Macaulay2/packages/PHCpack.m2:401:34-401:50: here is the first use of 'NumericalVariety'
/Users/sxp61/m2svn/trunk/M2/Macaulay2/packages/PHCpack.m2:488:3-488:19: here is the first use of 'numericalVariety'
/Users/sxp61/m2svn/trunk/M2/Macaulay2/packages/PHCpack.m2:588:26-588:39: here is the first use of 'IsIrreducible'