Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/interfaces/phc.py
8814 views
1
r"""
2
Interface to PHC.
3
4
PHC computes numerical information about systems of polynomials over
5
the complex numbers.
6
7
PHC implements polynomial homotopy methods to exploit structure in
8
order to better approximate all isolated solutions. The package also
9
includes extra tools to handle positive dimensional solution
10
components.
11
12
AUTHORS:
13
14
- PHC was written by J. Verschelde, R. Cools, and many others (?)
15
16
- William Stein and Kelly ?? -- first version of interface to PHC
17
18
- Marshall Hampton -- second version of interface to PHC
19
20
- Marshall Hampton and Alex Jokela -- third version, path tracking
21
22
"""
23
24
#*****************************************************************************
25
# Copyright (C) 2006 William Stein <[email protected]>
26
# Copyright (C) 2008 Marshall Hampton <[email protected]>
27
#
28
# Distributed under the terms of the GNU General Public License (GPL)
29
# as published by the Free Software Foundation; either version 2 of
30
# the License, or (at your option) any later version.
31
# http://www.gnu.org/licenses/
32
#*****************************************************************************
33
34
import os
35
36
import sage.misc.misc
37
from sage.rings.real_mpfr import RR
38
from sage.rings.all import CC
39
from sage.rings.integer import Integer
40
from sage.plot.plot import *
41
import re
42
43
import pexpect
44
45
def get_solution_dicts(output_file_contents, input_ring, get_failures = True):
46
"""
47
Returns a list of dictionaries of variable:value (key:value)
48
pairs. Only used internally; see the solution_dict function in
49
the PHC_Object class definition for details.
50
51
INPUT:
52
output_file_contents -- phc solution output as a string
53
input_ring -- a PolynomialRing that variable names can be coerced into
54
55
OUTPUT:
56
a list of dictionaries of solutions
57
58
EXAMPLES::
59
60
sage: from sage.interfaces.phc import *
61
sage: R2.<x1,x2> = PolynomialRing(QQ,2)
62
sage: test_sys = [(x1-1)^5-x2, (x2-1)^5-1]
63
sage: sol = phc.blackbox(test_sys, R2) # optional -- phc
64
sage: test = get_solution_dicts(sol.output_file_contents,R2) # optional -- phc
65
sage: str(sum([q[x1].real() for q in test]))[0:4] # optional -- phc
66
'25.0'
67
"""
68
output_list = output_file_contents.splitlines()
69
test = 'False'
70
solution_dicts = []
71
for solution_line in range(len(output_list)-1,-1,-1):
72
if output_list[solution_line].find('THE SOLUTIONS') == 0:
73
break
74
try:
75
var_number = int(output_list[solution_line+2].split(' ')[1])
76
sol_number = int(output_list[solution_line+2].split(' ')[0])
77
except IndexError:
78
var_number = int(output_list[solution_line+1].split(' ')[1])
79
sol_number = int(output_list[solution_line+1].split(' ')[0])
80
for i in range(solution_line + 1,len(output_list)):
81
if output_list[i].count('the solution for t') == 1:
82
if output_list[i-3].count('success') > 0 or get_failures == True:
83
temp_dict = {}
84
for j in range(1,var_number+1):
85
rawsplit = output_list[i+j].split(': ')[1].split(' ')
86
for extras in range(rawsplit.count('')):
87
rawsplit.remove('')
88
temp_var = output_list[i+j].split(': ')[0].replace(' ','')
89
temp_dict[input_ring(temp_var)] = CC(rawsplit[0],rawsplit[1])
90
solution_dicts.append(temp_dict)
91
return solution_dicts
92
93
def get_classified_solution_dicts(output_file_contents, input_ring, get_failures = True):
94
"""
95
Returns a dictionary of lists of dictionaries of variable:value (key:value)
96
pairs. Only used internally; see the classified_solution_dict function in
97
the PHC_Object class definition for details.
98
99
INPUT:
100
output_file_contents -- phc solution output as a string
101
input_ring -- a PolynomialRing that variable names can be coerced into
102
103
OUTPUT:
104
a dictionary of lists if dictionaries of solutions, classifies by type
105
106
EXAMPLES::
107
108
sage: from sage.interfaces.phc import *
109
sage: R2.<x1,x2> = PolynomialRing(QQ,2)
110
sage: test_sys = [(x1-2)^5-x2, (x2-1)^5-1]
111
sage: sol = phc.blackbox(test_sys, R2) # optional -- phc
112
sage: sol_classes = get_classified_solution_dicts(sol.output_file_contents,R2) # optional -- phc
113
sage: len(sol_classes['real']) # optional -- phc
114
1
115
"""
116
output_list = output_file_contents.splitlines()
117
test = 'False'
118
solution_dicts = {}
119
solution_types = ['complex', 'real','failure']
120
for sol_type in solution_types:
121
solution_dicts[sol_type] = []
122
for solution_line in range(len(output_list)-1,-1,-1):
123
if output_list[solution_line].find('THE SOLUTIONS') == 0:
124
break
125
var_number = int(output_list[solution_line+2].split(' ')[1])
126
sol_number = int(output_list[solution_line+2].split(' ')[0])
127
for i in range(solution_line + 1,len(output_list)):
128
if output_list[i].count('the solution for t') == 1:
129
phc_type = output_list[i+var_number+1].split(' = ')[-1]
130
if phc_type.find('complex') != -1:
131
phc_type = 'complex'
132
elif phc_type.find('real') != -1:
133
phc_type = 'real'
134
else:
135
phc_type = 'failure'
136
temp_dict = {}
137
for j in range(1,var_number+1):
138
rawsplit = output_list[i+j].split(': ')[1].split(' ')
139
for extras in range(rawsplit.count('')):
140
rawsplit.remove('')
141
temp_var = output_list[i+j].split(': ')[0].replace(' ','')
142
if phc_type == 'real':
143
temp_dict[input_ring(temp_var)] = RR(rawsplit[0])
144
else:
145
temp_dict[input_ring(temp_var)] = CC(rawsplit[0],rawsplit[1])
146
solution_dicts[phc_type].append(temp_dict)
147
return solution_dicts
148
149
def get_variable_list(output_file_contents):
150
"""
151
Returns the variables, as strings, in the order in which PHCpack has processed them.
152
153
EXAMPLES::
154
155
sage: from sage.interfaces.phc import *
156
sage: R2.<x1,x2> = PolynomialRing(QQ,2)
157
sage: test_sys = [(x1-2)^5-x2, (x2-1)^5-1]
158
sage: sol = phc.blackbox(test_sys, R2) # optional -- phc
159
sage: get_variable_list(sol.output_file_contents) # optional -- phc
160
['x1', 'x2']
161
"""
162
output_list = output_file_contents.splitlines()
163
for solution_line in range(len(output_list)-1,-1,-1):
164
if output_list[solution_line].find('THE SOLUTIONS') == 0:
165
break
166
var_number = int(output_list[solution_line+2].split(' ')[1])
167
varlist = []
168
for var_ind in range(var_number):
169
var = output_list[solution_line + 8 + var_ind].split(' ')[1]
170
varlist.append(var)
171
return varlist
172
173
class PHC_Object:
174
175
def __init__(self, output_file_contents, input_ring):
176
"""
177
A container for data from the PHCpack program - lists of float
178
solutions, etc. Currently the file contents are kept as a string;
179
for really large outputs this would be bad.
180
181
INPUT:
182
output_file_contents: the string output of PHCpack
183
input_ring: for coercion of the variables into the desired ring.
184
185
EXAMPLES::
186
187
sage: from sage.interfaces.phc import phc
188
sage: R2.<x,y> = PolynomialRing(QQ,2)
189
sage: start_sys = [(x-1)^2+(y-1)-1, x^2+y^2-1]
190
sage: sol = phc.blackbox(start_sys, R2) # optional -- phc
191
sage: str(sum([x[0] for x in sol.solutions()]).real())[0:3] # optional -- phc
192
'2.0'
193
"""
194
self.output_file_contents = output_file_contents
195
self.input_ring = input_ring
196
197
198
def save_as_start(self, start_filename = None, sol_filter = ''):
199
"""
200
Saves a solution as a phcpack start file. The usual output is
201
just as a string, but it can be saved to a file as well. Even
202
if saved to a file, it still returns the output string.
203
204
EXAMPLES::
205
206
sage: from sage.interfaces.phc import phc
207
sage: R2.<x,y> = PolynomialRing(QQ,2)
208
sage: start_sys = [x^3-y^2,y^5-1]
209
sage: sol = phc.blackbox(start_sys, R2) # optional -- phc
210
sage: start_save = sol.save_as_start() # optional -- phc
211
sage: end_sys = [x^7-2,y^5-x^2] # optional -- phc
212
sage: sol = phc.start_from(start_save, end_sys, R2) # optional -- phc
213
sage: len(sol.solutions()) # optional -- phc
214
15
215
"""
216
start_data = ''
217
output_list = self.output_file_contents.splitlines()
218
for a_line in output_list:
219
if a_line.find('ROOT COUNTS') != -1 or a_line.find('START SOLUTIONS') != -1:
220
break
221
else:
222
start_data += a_line + '\n'
223
for index in range(len(output_list)-1,0,-1):
224
a_line = output_list[index]
225
if a_line.find('THE SOLUTIONS') != -1:
226
found_solutions = index
227
break
228
start_data += output_list[found_solutions] + '\n\n'
229
try:
230
var_number = int(output_list[found_solutions+1].split(' ')[1])
231
except Exception:
232
# bad error handling
233
var_number = int(output_list[found_solutions+2].split(' ')[1])
234
sol_count = 0
235
sol_data = ''
236
for i in range(found_solutions + 2, len(output_list)):
237
if output_list[i].count('the solution for t') == 1 and output_list[i+1+var_number].find(sol_filter) != -1:
238
phc_type = output_list[i+var_number+1].split(' = ')[-1]
239
if phc_type.find('no solution') == -1:
240
sol_count += 1
241
for ind2 in range(i-3,i+var_number+2):
242
sol_data += output_list[ind2] + '\n'
243
jan_bar = '===========================================================================\n'
244
sol_data += jan_bar
245
start_data += str(sol_count) + ' ' + str(var_number) + '\n'
246
start_data += jan_bar + sol_data
247
if start_filename != None:
248
start_file = file(start_filename,'w')
249
start_file.write(start_data)
250
start_file.close()
251
return start_data
252
253
def classified_solution_dicts(self):
254
"""
255
Returns a dictionary of lists of dictionaries of solutions.
256
Its not as crazy as it sounds; the keys are the types of solutions as
257
classified by phcpack: regular vs. singular, complex vs. real
258
259
INPUT:
260
None
261
262
OUTPUT:
263
A dictionary of lists of dictionaries of solutions
264
265
EXAMPLES::
266
267
sage: from sage.interfaces.phc import phc
268
sage: R.<x,y> = PolynomialRing(CC,2)
269
sage: p_sys = [x^10-y,y^2-1]
270
sage: sol = phc.blackbox(p_sys,R) # optional -- phc
271
sage: classifieds = sol.classified_solution_dicts() # optional -- phc
272
sage: str(sum([q[y] for q in classifieds['real']]))[0:3] # optional -- phc
273
'2.0'
274
"""
275
try:
276
return self.__classified_sols
277
except AttributeError:
278
pass
279
classified_sols = get_classified_solution_dicts(self.output_file_contents, self.input_ring)
280
self.__classified_sols = classified_sols
281
return classified_sols
282
283
def solution_dicts(self, get_failures = False):
284
"""
285
Returns a list of solutions in dictionary form: variable:value.
286
287
INPUT:
288
self -- for access to self_out_file_contents, the string
289
of raw PHCpack output.
290
291
get_failures (optional) -- a boolean. The default (False)
292
is to not process failed homotopies. These either lie on
293
positive-dimensional components or at infinity.
294
295
OUTPUT:
296
solution_dicts: a list of dictionaries. Each dictionary
297
element is of the form variable:value, where the variable
298
is an element of the input_ring, and the value is in
299
ComplexField.
300
301
EXAMPLES::
302
303
sage: from sage.interfaces.phc import *
304
sage: R.<x,y,z> = PolynomialRing(QQ,3)
305
sage: fs = [x^2-1,y^2-x,z^2-y]
306
sage: sol = phc.blackbox(fs,R) # optional -- phc
307
sage: s_list = sol.solution_dicts() # optional -- phc
308
sage: s_list.sort() # optional -- phc
309
sage: s_list[0] # optional -- phc
310
{y: 1.00000000000000, z: -1.00000000000000, x: 1.00000000000000}
311
"""
312
try:
313
return self.__solution_dicts
314
except AttributeError:
315
pass
316
solution_dicts = get_solution_dicts(self.output_file_contents, self.input_ring, get_failures = get_failures)
317
self.__solution_dicts = solution_dicts
318
return solution_dicts
319
320
def solutions(self, get_failures = False):
321
"""
322
Returns a list of solutions in the ComplexField. Use the variable_list function to get the order of variables used by PHCpack, which is usually different than the term order of the input_ring.
323
324
INPUT:
325
self -- for access to self_out_file_contents, the string
326
of raw PHCpack output.
327
get_failures (optional) -- a boolean. The default (False)
328
is to not process failed homotopies. These either lie on
329
positive-dimensional components or at infinity.
330
331
OUTPUT:
332
solutions: a list of lists of ComplexField-valued solutions.
333
334
EXAMPLES::
335
336
sage: from sage.interfaces.phc import *
337
sage: R2.<x1,x2> = PolynomialRing(QQ,2)
338
sage: test_sys = [x1^5-x1*x2^2-1, x2^5-x1*x2-1]
339
sage: sol = phc.blackbox(test_sys, R2) # optional -- phc
340
sage: len(sol.solutions()) # optional -- phc
341
25
342
"""
343
try:
344
return self.__solutions
345
except AttributeError:
346
pass
347
solution_dicts = get_solution_dicts(self.output_file_contents, self.input_ring, get_failures = get_failures)
348
self.__solution_dicts = solution_dicts
349
solutions = [sol_dict.values() for sol_dict in solution_dicts]
350
self.__solutions = solutions
351
return solutions
352
353
def variable_list(self):
354
"""
355
Returns the variables, as strings, in the order in which
356
PHCpack has processed them.
357
358
EXAMPLES::
359
360
sage: from sage.interfaces.phc import *
361
sage: R2.<x1,x2> = PolynomialRing(QQ,2)
362
sage: test_sys = [x1^5-x1*x2^2-1, x2^5-x1*x2-1]
363
sage: sol = phc.blackbox(test_sys, R2) # optional -- phc
364
sage: sol.variable_list() # optional -- phc
365
['x1', 'x2']
366
"""
367
try:
368
return self.__var_list
369
except AttributeError:
370
pass
371
var_list = get_variable_list(self.output_file_contents)
372
self.__var_list = var_list
373
return var_list
374
375
class PHC:
376
"""
377
A class to interface with PHCpack, for computing numerical
378
homotopies and root counts.
379
380
EXAMPLES::
381
382
sage: from sage.interfaces.phc import phc
383
sage: R.<x,y> = PolynomialRing(CDF,2)
384
sage: testsys = [x^2 + 1, x*y - 1]
385
sage: phc.mixed_volume(testsys) # optional -- phc
386
2
387
sage: v = phc.blackbox(testsys, R) # optional -- phc
388
sage: sols = v.solutions() # optional -- phc
389
sage: sols.sort() # optional -- phc
390
sage: sols # optional -- phc
391
[[-1.00000000000000*I, 1.00000000000000*I], [1.00000000000000*I, -1.00000000000000*I]]
392
sage: sol_dict = v.solution_dicts() # optional -- phc
393
sage: x_sols_from_dict = [d[x] for d in sol_dict] # optional -- phc
394
sage: x_sols_from_dict.sort(); x_sols_from_dict # optional -- phc
395
[-1.00000000000000*I, 1.00000000000000*I]
396
sage: residuals = [[test_equation.change_ring(CDF).subs(sol) for test_equation in testsys] for sol in v.solution_dicts()] # optional -- phc
397
sage: residuals # optional -- phc
398
[[0, 0], [0, 0]]
399
"""
400
401
def _output_from_command_list(self, command_list, polys, verbose = False):
402
"""
403
A pexpect interface to phcpack, given a command list for
404
interactive dialogs. The input file is supplied from the
405
polynomial list, output file is also supplied. This is
406
only used as a building block for the interface.
407
408
INPUT:
409
command_list -- a list of commands to phc
410
polys -- a polynomial system as a list of polynomials
411
412
OUTPUT:
413
an output string from phc
414
415
EXAMPLES::
416
417
sage: from sage.interfaces.phc import *
418
sage: R2.<x,y> = PolynomialRing(QQ,2)
419
sage: start_sys = [(x-1)^2+(y-1)-1, x^2+y^2-1] # optional -- phc
420
sage: a = phc._output_from_command_list(['phc -m','4','n','n','n'], start_sys) # optional -- phc
421
"""
422
# Get temporary file names (these will be in SAGE_HOME/.sage/tmp/pid)
423
input_filename = sage.misc.misc.tmp_filename()
424
output_filename = sage.misc.misc.tmp_filename()
425
426
# Get the input polynomial text
427
input = self._input_file(polys)
428
if verbose:
429
print "Writing the input file to %s"%input_filename
430
open(input_filename,'w').write(input)
431
432
if verbose:
433
print "The following file will be the input polynomial file to phc."
434
print input
435
436
# Create a phc process
437
child_phc = pexpect.spawn(command_list[0])
438
# feed it the commands
439
child_phc.sendline('y')
440
child_phc.sendline(input_filename)
441
child_phc.sendline(output_filename)
442
for command_string in command_list[1:]:
443
if verbose:
444
print command_string
445
child_phc.sendline(command_string)
446
child_phc.expect('results')
447
read_stuff = child_phc.read()
448
if verbose:
449
print read_stuff
450
child_phc.close()
451
if not os.path.exists(output_filename):
452
raise RuntimeError, "The output file does not exist; something went wrong running phc."
453
454
# Delete the input file
455
os.unlink(input_filename)
456
457
# Return the output filename
458
return output_filename
459
460
def _input_file(self, polys):
461
"""
462
This is used internally to implement the PHC interface.
463
464
INPUT:
465
polys -- a list of polynomials in a Sage polynomial ring
466
over a field that embeds into the complex
467
numbers.
468
469
OUTPUT:
470
-- a PHC input file (as a text string) that
471
describes these polynomials.
472
473
EXAMPLES::
474
475
sage: from sage.interfaces.phc import *
476
sage: R2.<x,y> = PolynomialRing(QQ,2)
477
sage: start_sys = [(x-1)^2+(y-1)-1, x^2+y^2-1]
478
sage: phc._input_file(start_sys) # optional -- phc
479
'2\nx^2 - 2*x + y - 1;\nx^2 + y^2 - 1;\n'
480
"""
481
if not isinstance(polys, (list, tuple)):
482
raise TypeError, 'polys must be a list or tuple'
483
s = '%s\n'%len(polys)
484
for f in polys:
485
s += f._repr_() + ';\n' # note the semicolon *terminators*
486
487
return s
488
489
def _parse_path_file(self, input_filename, verbose = False):
490
"""
491
Takes a phpack output file containing path tracking information
492
and parses it into a list of lists of dictionaries - i.e. a
493
list of solutions paths, where each solution path is a list of
494
dictionaries of variable and homotopy parameter values.
495
496
INPUT:
497
input_filename -- file must have path-tracking information
498
499
OUTPUT:
500
a list of lists of dictionaries, described above
501
502
EXAMPLES::
503
504
sage: from sage.interfaces.phc import *
505
sage: R2.<x,y> = PolynomialRing(QQ,2)
506
sage: start_sys = [x^5-y^2,y^5-1]
507
sage: sol = phc.blackbox(start_sys, R2) # optional -- phc
508
sage: start_save = sol.save_as_start() # optional -- phc
509
sage: end_sys = [x^5-2,y^5-x^2] # optional -- phc
510
sage: path_track_filename = phc._path_track_file(start_save, end_sys, R2, c_skew = .001) # optional -- phc
511
sage: sol_paths = phc._parse_path_file(path_track_filename) # optional -- phc
512
sage: len(sol_paths) # optional -- phc
513
25
514
"""
515
516
if not os.path.exists(input_filename):
517
raise RuntimeError, "The file containing output from phc (" + input_filename + ") cannot be found"
518
519
fh = open(input_filename)
520
line_idx = 0
521
begin = 0
522
count = 0
523
solutions_dicts = []
524
steps_dicts = []
525
526
# regular expressions for matching certain output types
527
var_cnt_regex = re.compile('^ +([0-9]+)')
528
output_regex = re.compile('^OUTPUT INFORMATION DURING')
529
t_regex = re.compile('(^t +: +(-{0,1}[0-9]+\.[0-9]+E[-+][0-9]+) +(-{0,1}[0-9]+\.[0-9]+E[-+][0-9]+)$)', re.IGNORECASE)
530
sols_regex = re.compile('(^ *(([a-z]|[0-9])+) +: +(-?[0-9]+\.[0-9]+E[-+][0-9]+) +(-?[0-9]+\.[0-9]+E[-+][0-9]+)$)', re.IGNORECASE)
531
complete_regex= re.compile('^TIMING INFORMATION')
532
533
breakfast = False
534
a_line = fh.readline()
535
end_test = ''
536
while a_line:
537
# processing....
538
a_line = a_line.replace("\n", '')
539
if line_idx == 0:
540
m = var_cnt_regex.match(a_line)
541
if m:
542
count = Integer(m.group(1))
543
if count > 0:
544
m = output_regex.match(a_line)
545
if m:
546
begin = 1
547
if begin:
548
m = t_regex.match(a_line)
549
if m:
550
# put the t-values into a dict
551
# m.group(2) contains the real val
552
# m.group(3) contains the imaginary val
553
# fh_w.write( "T=> G1(" + m.group(2) + '),G2(' + m.group(3) + ")\n")
554
# read off two lines - this should be 'm' and 'the solution for t :'
555
a_line = fh.readline()
556
end_test = a_line # store this to check for end of solution
557
a_line = fh.readline()
558
t_val = CC(m.group(2), m.group(3))
559
temp_dict = {}
560
temp_dict["t"] = t_val
561
for i in range(0, count):
562
a_line = fh.readline()
563
m = sols_regex.match(a_line)
564
if m:
565
# m.group(2) contains our var name
566
# m.group(4) contains our real val
567
# m.group(5) contains our imaginary val
568
temp_dict[m.group(2)] = CC(m.group(4),m.group(5))
569
steps_dicts.append(temp_dict)
570
# check if its the end of a solution
571
if end_test.find('Length of path') != -1:
572
if verbose: print "recording sol"
573
if steps_dicts != []:
574
solutions_dicts.append(steps_dicts)
575
steps_dicts = []
576
m = complete_regex.match(a_line)
577
if m:
578
breakfast = True
579
if breakfast:
580
break
581
line_idx += 1
582
a_line = fh.readline()
583
fh.close()
584
return solutions_dicts
585
586
def _path_track_file(self, start_filename_or_string, polys, input_ring, c_skew = 0.001, verbose = False):
587
"""
588
Returns the filename which contains path tracking output.
589
590
EXAMPLES::
591
592
sage: from sage.interfaces.phc import *
593
sage: R2.<x,y> = PolynomialRing(QQ,2)
594
sage: start_sys = [x^6-y^2,y^5-1]
595
sage: sol = phc.blackbox(start_sys, R2) # optional -- phc
596
sage: start_save = sol.save_as_start() # optional -- phc
597
sage: end_sys = [x^7-2,y^5-x^2] # optional -- phc
598
sage: path_track_filename = phc._path_track_file(start_save, end_sys, R2, c_skew = .001) # optional -- phc
599
sage: sol_paths = phc._parse_path_file(path_track_filename) # optional -- phc
600
sage: len(sol_paths) # optional -- phc
601
30
602
"""
603
# Probably unnecessarily redundant from the start_from function
604
if start_filename_or_string.find('THE SOLUTIONS') != -1:
605
start_filename = sage.misc.misc.tmp_filename()
606
start_file = file(start_filename,'w')
607
start_file.write(start_filename_or_string)
608
start_file.close()
609
elif os.path.exists(start_filename_or_string):
610
start_filename = start_filename_or_string
611
else:
612
raise RuntimeError, "There is something wrong with your start string or filename"
613
614
return self._output_from_command_list(['phc','0','0','A',start_filename, 'y','1','0','n','k','2','a','1',str(c_skew),'0','0','2'], polys, verbose = verbose)
615
616
def path_track(self, start_sys, end_sys, input_ring, c_skew = .001, saved_start = None):
617
"""
618
This function computes homotopy paths between the solutions of start_sys and end_sys.
619
620
INPUT:
621
start_sys -- a square polynomial system, given as a list of polynomials
622
end_sys -- same type as start_sys
623
input_ring -- for coercion of the variables into the desired ring.
624
c_skew -- optional. the imaginary part of homotopy multiplier; nonzero values
625
are often necessary to avoid intermediate path collisions
626
saved_start -- optional. A phc output file. If not given, start system solutions
627
are computed via the phc.blackbox function.
628
629
OUTPUT:
630
a list of paths as dictionaries, with the keys variables and t-values on the path.
631
632
EXAMPLES::
633
634
sage: from sage.interfaces.phc import *
635
sage: R2.<x,y> = PolynomialRing(QQ,2)
636
sage: start_sys = [x^6-y^2,y^5-1]
637
sage: sol = phc.blackbox(start_sys, R2) # optional -- phc
638
sage: start_save = sol.save_as_start() # optional -- phc
639
sage: end_sys = [x^7-2,y^5-x^2] # optional -- phc
640
sage: sol_paths = phc.path_track(start_sys, end_sys, R2, saved_start = start_save) # optional -- phc
641
sage: len(sol_paths) # optional -- phc
642
30
643
"""
644
645
if not saved_start:
646
sol = phc.blackbox(start_sys, input_ring)
647
saved_start = sol.save_as_start()
648
path_track_filename = phc._path_track_file(saved_start, end_sys, input_ring = input_ring, c_skew = c_skew)
649
sol_paths = phc._parse_path_file(path_track_filename)
650
os.unlink(path_track_filename)
651
return sol_paths
652
653
def plot_paths_2d(self, start_sys, end_sys, input_ring, c_skew = .001, endpoints = True, saved_start = None, rand_colors = False):
654
"""
655
This returns a graphics object of solution paths in the complex plane.
656
657
INPUT:
658
start_sys -- a square polynomial system, given as a list of polynomials
659
end_sys -- same type as start_sys
660
input_ring -- for coercion of the variables into the desired ring.
661
c_skew -- optional. the imaginary part of homotopy multiplier; nonzero values
662
are often necessary to avoid intermediate path collisions
663
endpoints -- optional. Whether to draw in the ends of paths as points.
664
saved_start -- optional. A phc output file. If not given, start system solutions
665
are computed via the phc.blackbox function.
666
667
OUTPUT:
668
lines and points of solution paths
669
670
EXAMPLES::
671
672
sage: from sage.interfaces.phc import *
673
sage: from sage.structure.sage_object import SageObject
674
sage: R2.<x,y> = PolynomialRing(QQ,2)
675
sage: start_sys = [x^5-y^2,y^5-1]
676
sage: sol = phc.blackbox(start_sys, R2) # optional -- phc
677
sage: start_save = sol.save_as_start() # optional -- phc
678
sage: end_sys = [x^5-25,y^5-x^2] # optional -- phc
679
sage: testing = phc.plot_paths_2d(start_sys, end_sys, R2) # optional -- phc
680
sage: type(testing) # optional -- phc (normally use plot here)
681
<class 'sage.plot.graphics.Graphics'>
682
"""
683
paths = phc.path_track(start_sys, end_sys, input_ring, c_skew = c_skew, saved_start = saved_start)
684
path_lines = []
685
sol_pts = []
686
if rand_colors:
687
r_color = {}
688
for a_var in input_ring.gens():
689
var_name = str(a_var)
690
r_color[var_name] = (random(),random(),random())
691
for a_sol in paths:
692
for a_var in input_ring.gens():
693
var_name = str(a_var)
694
temp_line = []
695
for data in a_sol:
696
temp_line.append([data[var_name].real(), data[var_name].imag()])
697
if rand_colors:
698
path_lines.append(line(temp_line, rgbcolor = r_color[var_name]))
699
else:
700
path_lines.append(line(temp_line))
701
if endpoints:
702
sol_pts = []
703
for a_sol in paths:
704
for a_var in input_ring.gens():
705
var_name = str(a_var)
706
sol_pts.append(point([a_sol[0][var_name].real(), a_sol[0][var_name].imag()]))
707
sol_pts.append(point([a_sol[-1][var_name].real(), a_sol[-1][var_name].imag()]))
708
return sum(sol_pts) + sum(path_lines)
709
else:
710
return sum(path_lines)
711
712
def mixed_volume(self, polys, verbose=False):
713
"""
714
Computes the mixed volume of the polynomial system given by the input polys.
715
716
INPUT:
717
polys -- a list of multivariate polynomials (elements of a multivariate
718
polynomial ring).
719
verbose -- print lots of verbose information about what this function does.
720
721
OUTPUT:
722
The mixed volume.
723
724
EXAMPLES::
725
726
sage: from sage.interfaces.phc import *
727
sage: R2.<x,y,z> = PolynomialRing(QQ,3)
728
sage: test_sys = [(x+y+z)^2-1,x^2-x,y^2-1]
729
sage: phc.mixed_volume(test_sys) # optional -- phc
730
4
731
"""
732
output_filename = self._output_from_command_list(['phc -m','4','n','n','n'], polys, verbose = verbose)
733
734
out = open(output_filename).read()
735
# All done
736
out_lines = out.split('\n')
737
for a_line in out_lines:
738
# the two conditions below are necessary because of changes in output format
739
if a_line.find('The mixed volume equals :') == 0 or a_line.find('common mixed volume :') == 0:
740
if verbose: print 'found line: ' + a_line
741
mixed_vol = Integer(a_line.split(':')[1])
742
break
743
744
try:
745
return mixed_vol
746
except NameError:
747
raise RuntimeError, "Mixed volume not found in output; something went wrong running phc."
748
749
750
def start_from(self, start_filename_or_string, polys, input_ring, path_track_file = None, verbose = False):
751
"""
752
This computes solutions starting from a phcpack solution file.
753
754
INPUT:
755
start_filename_or_string -- the filename for a phcpack start system, or the contents of such a file as a string. Variable names must match the inputring variables. The value of the homotopy variable t should be 1, not 0.
756
polys -- a list of multivariate polynomials (elements of a multivariate
757
polynomial ring).
758
input_ring: for coercion of the variables into the desired ring.
759
path_track_file: whether to save path-tracking information
760
verbose -- print lots of verbose information about what this function does.
761
762
OUTPUT:
763
A solution in the form of a PHCObject.
764
765
EXAMPLES::
766
767
sage: from sage.interfaces.phc import *
768
sage: R2.<x,y> = PolynomialRing(QQ,2)
769
sage: start_sys = [x^6-y^2,y^5-1]
770
sage: sol = phc.blackbox(start_sys, R2) # optional -- phc
771
sage: start_save = sol.save_as_start() # optional -- phc
772
sage: end_sys = [x^7-2,y^5-x^2] # optional -- phc
773
sage: sol = phc.start_from(start_save, end_sys, R2) # optional -- phc
774
sage: len(sol.solutions()) # optional -- phc
775
30
776
"""
777
input_filename = sage.misc.misc.tmp_filename()
778
output_filename = sage.misc.misc.tmp_filename()
779
780
if start_filename_or_string.find('THE SOLUTIONS') != -1:
781
start_filename = sage.misc.misc.tmp_filename()
782
start_file = file(start_filename,'w')
783
start_file.write(start_filename_or_string)
784
start_file.close()
785
elif os.path.exists(start_filename_or_string):
786
start_filename = start_filename_or_string
787
else:
788
raise RuntimeError, "There is something wrong with your start string or filename"
789
790
# Get the input polynomial text
791
input = self._input_file(polys)
792
if verbose:
793
print "Writing the input file to %s"%input_filename
794
open(input_filename,'w').write(input)
795
796
if verbose:
797
print "The following file will be the input polynomial file to phc."
798
print input
799
800
# Create a phc process
801
child_phc = pexpect.spawn('phc')
802
child_phc.sendline('y')
803
child_phc.sendline(input_filename)
804
child_phc.sendline(output_filename)
805
child_phc.sendline('0')
806
child_phc.sendline('0')
807
child_phc.expect('Nonlinear Reduction')
808
child_phc.sendline('A')
809
child_phc.sendline(start_filename)
810
child_phc.sendline('y')
811
child_phc.sendline('1')
812
child_phc.sendline('0')
813
if verbose:
814
phc_dialog = child_phc.read(size = 40)
815
print phc_dialog
816
child_phc.sendline('n')
817
child_phc.sendline('0')
818
if verbose:
819
child_phc.expect('CURRENT CONTINUATION')
820
phc_dialog = child_phc.read(size = 40)
821
print phc_dialog
822
child_phc.sendline('0')
823
if path_track_file == None:
824
child_phc.sendline('0')
825
else:
826
child_phc.sendline('2')
827
child_phc.expect('results')
828
dots = child_phc.read()
829
if verbose:
830
print "should be . : " + dots
831
832
#close down the process:
833
child_phc.close()
834
if not os.path.exists(output_filename):
835
raise RuntimeError, "The output file does not exist; something went wrong running phc."
836
837
# Read the output produced by PHC
838
out = open(output_filename).read()
839
840
# Delete the temporary files
841
os.unlink(output_filename)
842
os.unlink(input_filename)
843
844
# All done
845
return PHC_Object(out, input_ring)
846
847
def blackbox(self, polys, input_ring, verbose = False):
848
"""
849
Returns as a string the result of running PHC with the given polynomials
850
under blackbox mode (the '-b' option).
851
852
INPUT:
853
polys -- a list of multivariate polynomials (elements of a multivariate
854
polynomial ring).
855
input_ring: for coercion of the variables into the desired ring.
856
verbose -- print lots of verbose information about what this function does.
857
858
OUTPUT:
859
a PHC_Object object containing the phcpack output string.
860
861
EXAMPLES::
862
863
sage: from sage.interfaces.phc import *
864
sage: R2.<x,y> = PolynomialRing(QQ,2)
865
sage: start_sys = [x^6-y^2,y^5-1]
866
sage: sol = phc.blackbox(start_sys, R2) # optional -- phc
867
sage: len(sol.solutions()) # optional -- phc
868
30
869
"""
870
871
# Get three temporary file names (these will be in SAGE_HOME/.sage/tmp/pid)
872
input_filename = sage.misc.misc.tmp_filename()
873
output_filename = input_filename + ".phc"
874
log_filename = sage.misc.misc.tmp_filename()
875
876
# Get the input polynomial text
877
input = self._input_file(polys)
878
if verbose:
879
print "Writing the input file to %s"%input_filename
880
open(input_filename,'w').write(input)
881
882
if verbose:
883
print "The following file will be the input polynomial file to phc."
884
print input
885
886
# Create the phc command line>
887
cmd = 'phc -b %s %s'%(input_filename, output_filename)
888
889
if verbose:
890
print "The phc command line is:"
891
print cmd
892
893
# Do it -- make the system call.
894
e = os.system(cmd)
895
896
# Was there an error?
897
if e:
898
from sage.misc.sage_ostools import have_program
899
if not have_program('phc'):
900
print os.system('which phc') + ' PHC needs to be installed and in your path'
901
raise RuntimeError
902
# todo -- why? etc.
903
raise RuntimeError, open(log_filename).read() + "\nError running phc."
904
905
if not os.path.exists(output_filename):
906
raise RuntimeError, "The output file does not exist; something went wrong running phc."
907
908
# Read the output produced by PHC
909
out = open(output_filename).read()
910
911
# All done
912
return PHC_Object(out, input_ring)
913
914
915
################################
916
917
# The unique phc interface instance.
918
phc = PHC()
919
920
921