def __call__(self, assumptions=None): """ Run 'command' and collect output. INPUT: - ``assumptions`` - ignored, accepted for compatibility with other solvers (default: ``None``) TESTS: This class is not meant to be called directly:: sage: from sage.sat.solvers.dimacs import DIMACS sage: fn = tmp_filename() sage: solver = DIMACS(filename=fn) sage: solver.add_clause( (1, -2 , 3) ) sage: solver() Traceback (most recent call last): ... ValueError: No SAT solver command selected. """ if assumptions is not None: raise NotImplementedError("Assumptions are not supported for DIMACS based solvers.") self.write() output_filename = None self._output = [] command = self._command.strip() if not command: raise ValueError("No SAT solver command selected.") if "{output}" in command: output_filename = tmp_filename() command = command.format(input=self._headname, output=output_filename) args = shlex.split(command) try: process = subprocess.Popen(args, stdout=subprocess.PIPE) except OSError: raise OSError("Could run '%s', perhaps you need to add your SAT solver to $PATH?"%(" ".join(args))) try: while process.poll() is None: for line in iter(process.stdout.readline,''): if get_verbose() or self._verbosity: print line, sys.stdout.flush() self._output.append(line) sleep(0.1) if output_filename: self._output.extend(open(output_filename).readlines()) except KeyboardInterrupt: process.kill() raise KeyboardInterrupt
def __call__(self, model, outfile='sage.png', verbose=1, block=True, extra_opts=''): modelfile = tmp_filename() + '.dat' open(modelfile,'w').write(model) opts = '' ext = outfile[-4:].lower() if ext == '.png': opts += ' -format PNG ' elif ext == '.tga': opts += ' -format TARGA ' elif ext == '.bmp': opts += ' -format BMP ' elif ext == '.ppm': opts += ' -format PPM ' elif ext == '.rgb': opts += ' -format RGB ' opts += ' -o %s '%outfile opts += ' ' + extra_opts + ' ' if verbose >= 2: opts += ' +V ' elif verbose == 0: opts += ' 1>/dev/null' cmd = 'tachyon %s %s; rm -f "%s"'%(modelfile,opts, modelfile) if not block: cmd += ' &' if verbose: print cmd os.system(cmd)
def __call__(self, model, outfile='sage.png', verbose=1, block=True, extra_opts=''): """ This executes the tachyon program, given a scene file input. The default is to return the result as a PNG file called 'sage.png'. TESTS:: sage: from sage.interfaces.tachyon import TachyonRT sage: tgen = Tachyon() sage: tgen.texture('t1') sage: tgen.sphere((0,0,0),1,'t1') sage: tgen.str()[30:40] 'resolution' sage: t = TachyonRT() sage: import os sage: t(tgen.str(), outfile = os.devnull) tachyon ... Tachyon Parallel/Multiprocessor Ray Tracer... """ modelfile = tmp_filename(ext='.dat') open(modelfile, 'w').write(model) opts = '' ext = outfile[-4:].lower() if ext == '.png': opts += ' -format PNG ' elif ext == '.tga': opts += ' -format TARGA ' elif ext == '.bmp': opts += ' -format BMP ' elif ext == '.ppm': opts += ' -format PPM ' elif ext == '.rgb': opts += ' -format RGB ' opts += ' -o %s ' % outfile opts += ' ' + extra_opts + ' ' if verbose >= 2: opts += ' +V ' elif verbose == 0: opts += ' 1>/dev/null' cmd = 'tachyon %s %s; rm -f "%s"' % (modelfile, opts, modelfile) if not block: cmd = '( ' + cmd + ' ) &' if verbose: print cmd # One should always flush before system() sys.stdout.flush() sys.stderr.flush() os.system(cmd)
def __init__(self, command=None, filename=None, verbosity=0, **kwds): """ Construct a new generic DIMACS solver. INPUT: - ``command`` - a named format string with the command to run. The string must contain {input} and may contain {output} if the solvers writes the solution to an output file. For example "sat-solver {input}" is a valid command. If ``None`` then the class variable ``command`` is used. (default: ``None``) - ``filename`` - a filename to write clauses to in DIMACS format, must be writable. If ``None`` a temporary filename is chosen automatically. (default: ``None``) - ``verbosity`` - a verbosity level, where zero means silent and anything else means verbose output. (default: ``0``) - ``**kwds`` - accepted for compatibility with other solves, ignored. TESTS:: sage: from sage.sat.solvers.dimacs import DIMACS sage: DIMACS() DIMACS Solver: '' """ if filename is None: filename = tmp_filename() self._headname = filename self._verbosity = verbosity if command is not None: self._command = command else: self._command = self.__class__.command self._tail = open(tmp_filename(), 'w') self._var = 0 self._lit = 0
def __init__(self, command=None, filename=None, verbosity=0, **kwds): """ Construct a new generic DIMACS solver. INPUT: - ``command`` - a named format string with the command to run. The string must contain {input} and may contain {output} if the solvers writes the solution to an output file. For example "sat-solver {input}" is a valid command. If ``None`` then the class variable ``command`` is used. (default: ``None``) - ``filename`` - a filename to write clauses to in DIMACS format, must be writable. If ``None`` a temporary filename is chosen automatically. (default: ``None``) - ``verbosity`` - a verbosity level, where zero means silent and anything else means verbose output. (default: ``0``) - ``**kwds`` - accepted for compatibility with other solves, ignored. TESTS:: sage: from sage.sat.solvers.dimacs import DIMACS sage: DIMACS() DIMACS Solver: '' """ if filename is None: filename = tmp_filename() self._headname = filename self._verbosity = verbosity if command is not None: self._command = command else: self._command = self.__class__.command self._tail = open(tmp_filename(), "w") self._var = 0 self._lit = 0
def __call__(self, model, outfile='sage.png', verbose=1, block=True, extra_opts=''): """ This executes the tachyon program, given a scene file input. The default is to return the result as a PNG file called 'sage.png'. TESTS:: sage: from sage.interfaces.tachyon import TachyonRT sage: tgen = Tachyon() sage: tgen.texture('t1') sage: tgen.sphere((0,0,0),1,'t1') sage: tgen.str()[30:40] 'resolution' sage: t = TachyonRT() sage: import os sage: t(tgen.str(), outfile = os.devnull) tachyon ... Tachyon Parallel/Multiprocessor Ray Tracer... """ modelfile = tmp_filename(ext='.dat') open(modelfile,'w').write(model) opts = '' ext = outfile[-4:].lower() if ext == '.png': opts += ' -format PNG ' elif ext == '.tga': opts += ' -format TARGA ' elif ext == '.bmp': opts += ' -format BMP ' elif ext == '.ppm': opts += ' -format PPM ' elif ext == '.rgb': opts += ' -format RGB ' opts += ' -o %s '%outfile opts += ' ' + extra_opts + ' ' if verbose >= 2: opts += ' +V ' elif verbose == 0: opts += ' 1>/dev/null' cmd = 'tachyon %s %s; rm -f "%s"'%(modelfile,opts, modelfile) if not block: cmd = '( ' + cmd + ' ) &' if verbose: print cmd # One should always flush before system() sys.stdout.flush() sys.stderr.flush() os.system(cmd)
def runsnake(command): """ Graphical profiling with ``runsnake`` INPUT: - ``command`` -- the command to be run as a string. EXAMPLES:: sage: runsnake("list(SymmetricGroup(3))") # optional - runsnake ``command`` is first preparsed (see :func:`preparse`):: sage: runsnake('for x in range(1,4): print x^2') # optional - runsnake 1 4 9 :func:`runsnake` requires the program ``runsnake``. Due to non trivial dependencies (python-wxgtk, ...), installing it within the Sage distribution is unpractical. Hence, we recommend installing it with the system wide Python. On Ubuntu 10.10, this can be done with:: > sudo apt-get install python-profiler python-wxgtk2.8 python-setuptools > sudo easy_install RunSnakeRun See the ``runsnake`` website for instructions for other platforms. :func:`runsnake` further assumes that the system wide Python is installed in ``/usr/bin/python``. .. seealso:: - `The runsnake website <http://www.vrplumber.com/programming/runsnakerun/>`_ - ``%prun`` - :class:`Profiler` """ import cProfile, os from sage.misc.misc import tmp_filename, get_main_globals from sage.misc.preparser import preparse tmpfile = tmp_filename() cProfile.runctx(preparse(command.lstrip().rstrip()), get_main_globals(), locals(), filename=tmpfile) os.system("/usr/bin/python -E `which runsnake` %s &" % tmpfile)
def dump_to_tmpfile(s): """ Utility function to dump a string to a temporary file. EXAMPLE: sage: from spherical import * sage: file_loc = dump_to_tmpfile("boo") sage: os.remove(file_loc) """ file_loc = tmp_filename() f = open(file_loc,"w") f.write(s) f.close() return file_loc
def dump_to_tmpfile(s): """ Utility function to dump a string to a temporary file. EXAMPLE:: sage: from sage.combinat.designs import ext_rep sage: file_loc = ext_rep.dump_to_tmpfile("boo") sage: os.remove(file_loc) """ file_loc = tmp_filename() f = open(file_loc, "w") f.write(v2_b2_k2_icgsa) f.close() return file_loc
def dump_to_tmpfile(s): """ Utility function to dump a string to a temporary file. EXAMPLE:: sage: from sage.combinat.designs import ext_rep sage: file_loc = ext_rep.dump_to_tmpfile("boo") sage: os.remove(file_loc) """ file_loc = tmp_filename() f = open(file_loc,"w") f.write(v2_b2_k2_icgsa) f.close() return file_loc
def runsnake(command): """ Graphical profiling with ``runsnake`` INPUT: - ``command`` -- the command to be run as a string. EXAMPLES:: sage: runsnake("list(SymmetricGroup(3))") # optional - runsnake ``command`` is first preparsed (see :func:`preparse`):: sage: runsnake('for x in range(1,4): print x^2') # optional - runsnake 1 4 9 :func:`runsnake` requires the program ``runsnake``. Due to non trivial dependencies (python-wxgtk, ...), installing it within the Sage distribution is unpractical. Hence, we recommend installing it with the system wide Python. On Ubuntu 10.10, this can be done with:: > sudo apt-get install python-profiler python-wxgtk2.8 python-setuptools > sudo easy_install RunSnakeRun See the ``runsnake`` website for instructions for other platforms. :func:`runsnake` further assumes that the system wide Python is installed in ``/usr/bin/python``. .. seealso:: - `The runsnake website <http://www.vrplumber.com/programming/runsnakerun/>`_ - ``%prun`` - :class:`Profiler` """ import cProfile, os from sage.misc.misc import tmp_filename, get_main_globals from sage.misc.preparser import preparse tmpfile = tmp_filename() cProfile.runctx(preparse(command.lstrip().rstrip()), get_main_globals(), locals(), filename=tmpfile) os.system("/usr/bin/python -E `which runsnake` %s &"%tmpfile)
def __call__(self, n, watch=False): n = Integer(n) self._validate(n) cmd = 'echo "%s" | %s'%(n, self.__cmd) if watch: t = tmp_filename() os.system('%s | tee %s'%(cmd, t)) ou = open(t).read() os.unlink(t) else: i,o,e = os.popen3(cmd) i.close() ou = e.read() + '\n' + o.read() if 'command not found' in ou: err = ou + '\n' + 'You must install GMP-ECM.\n' err += sage.misc.package.package_mesg('ecm-6.1.3') raise RuntimeError, err return ou
def __call__(self, n, watch=False): n = Integer(n) self._validate(n) cmd = 'echo "%s" | %s' % (n, self.__cmd) if watch: t = tmp_filename() os.system("%s | tee %s" % (cmd, t)) ou = open(t).read() os.unlink(t) else: i, o, e = os.popen3(cmd) i.close() ou = e.read() + "\n" + o.read() if "command not found" in ou: err = ou + "\n" + "You must install GMP-ECM.\n" err += sage.misc.package.package_mesg("ecm-6.1.3") raise RuntimeError, err return ou
def __call__(self, n, watch=False): n = Integer(n) self._validate(n) cmd = 'echo "%s" | %s'%(n, self.__cmd) if watch: t = tmp_filename() os.system('%s | tee %s'%(cmd, t)) ou = open(t).read() os.unlink(t) else: from subprocess import Popen, PIPE x = Popen(cmd, shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True) x.stdin.close() ou = x.stderr.read() + '\n' + x.stdout.read() if 'command not found' in ou: err = ou + '\n' + 'You must install GMP-ECM.\n' err += sage.misc.package.package_mesg('ecm-6.1.3') raise RuntimeError, err return ou
def compile_and_load(code): r""" INPUT: - ``code`` -- string containing code that could be in a .pyx file that is attached or put in a %cython block in the notebook. OUTPUT: a module, which results from compiling the given code and importing it EXAMPLES:: sage: module = sage.misc.cython.compile_and_load("def f(int n):\n return n*n") sage: module.f(10) 100 """ from sage.misc.misc import tmp_filename file = tmp_filename() + ".pyx" open(file, 'w').write(code) from sage.server.support import cython_import return cython_import(file, create_local_c_file=False)
def tmp_filename(): """ Return a temporary file. OUTPUT: String. The absolute filename of the temporary file. EXAMPLES:: sage: from sage.dev.misc import tmp_filename sage: tmp_filename().startswith(str(SAGE_TMP)) True """ try: from sage.misc.misc import tmp_filename return tmp_filename() except ImportError: from tempfile import NamedTemporaryFile f = NamedTemporaryFile(dir=get_sage_tmp()) f.close() return f.name
def __call__(self, model, outfile='sage.png', verbose=1, block=True, extra_opts=''): modelfile = tmp_filename() + '.dat' open(modelfile, 'w').write(model) opts = '' ext = outfile[-4:].lower() if ext == '.png': opts += ' -format PNG ' elif ext == '.tga': opts += ' -format TARGA ' elif ext == '.bmp': opts += ' -format BMP ' elif ext == '.ppm': opts += ' -format PPM ' elif ext == '.rgb': opts += ' -format RGB ' opts += ' -o %s ' % outfile opts += ' ' + extra_opts + ' ' if verbose >= 2: opts += ' +V ' elif verbose == 0: opts += ' 1>/dev/null' cmd = 'tachyon %s %s; rm -f "%s"' % (modelfile, opts, modelfile) if not block: cmd += ' &' if verbose: print cmd os.system(cmd)
def compile_and_load(code): r""" INPUT: - ``code`` -- string containing code that could be in a .pyx file that is attached or put in a %cython block in the notebook. OUTPUT: a module, which results from compiling the given code and importing it EXAMPLES:: sage: module = sage.misc.cython.compile_and_load("def f(int n):\n return n*n") sage: module.f(10) 100 """ from sage.misc.misc import tmp_filename file = tmp_filename() + ".pyx" open(file, "w").write(code) from sage.server.support import cython_import return cython_import(file, create_local_c_file=False)
def __call__(self, program, complex, subcomplex=None, **kwds): """ Call a CHomP program to compute the homology of a chain complex, simplicial complex, or cubical complex. See :class:`CHomP` for full documentation. EXAMPLES:: sage: from sage.interfaces.chomp import CHomP sage: T = cubical_complexes.Torus() sage: CHomP()('homcubes', T) # indirect doctest, optional - CHomP {0: 0, 1: Z x Z, 2: Z} """ from sage.misc.misc import tmp_filename from sage.homology.all import CubicalComplex, cubical_complexes from sage.homology.all import SimplicialComplex, Simplex from sage.homology.cubical_complex import Cube from sage.homology.chain_complex import HomologyGroup, ChainComplex from subprocess import Popen, PIPE from sage.rings.all import QQ, ZZ from sage.modules.all import VectorSpace, vector from sage.combinat.free_module import CombinatorialFreeModule if not have_chomp(program): raise OSError, "Program %s not found" % program verbose = kwds.get('verbose', False) generators = kwds.get('generators', False) extra_opts = kwds.get('extra_opts', '') base_ring = kwds.get('base_ring', ZZ) if extra_opts: extra_opts = extra_opts.split() else: extra_opts = [] # type of complex: cubical = False simplicial = False chain = False # CHomP seems to have problems with cubical complexes if the # first interval in the first cube defining the complex is # degenerate. So replace the complex X with [0,1] x X. if isinstance(complex, CubicalComplex): cubical = True edge = cubical_complexes.Cube(1) original_complex = complex complex = edge.product(complex) if verbose: print "Cubical complex" elif isinstance(complex, SimplicialComplex): simplicial = True if verbose: print "Simplicial complex" else: chain = True base_ring = kwds.get('base_ring', complex.base_ring()) if verbose: print "Chain complex over %s" % base_ring if base_ring == QQ: raise ValueError, "CHomP doesn't compute over the rationals, only over Z or F_p." if base_ring.is_prime_field(): p = base_ring.characteristic() extra_opts.append('-p%s' % p) mod_p = True else: mod_p = False # # complex # try: data = complex._chomp_repr_() except AttributeError: raise AttributeError, "Complex can not be converted to use with CHomP." datafile = tmp_filename() f = open(datafile, 'w') f.write(data) f.close() # # subcomplex # if subcomplex is None: if cubical: subcomplex = CubicalComplex([complex.n_cells(0)[0]]) elif simplicial: m = re.search(r'\(([^,]*),', data) v = int(m.group(1)) subcomplex = SimplicialComplex([[v]]) else: # replace subcomplex with [0,1] x subcomplex. if cubical: subcomplex = edge.product(subcomplex) # # generators # if generators: genfile = tmp_filename() extra_opts.append('-g%s' % genfile) # # call program # if subcomplex is not None: try: sub = subcomplex._chomp_repr_() except AttributeError: raise AttributeError, "Subcomplex can not be converted to use with CHomP." subfile = tmp_filename() f = open(subfile, 'w') f.write(sub) f.close() else: subfile = '' if verbose: print "Popen called with arguments", print [program, datafile, subfile] + extra_opts print print "CHomP output:" print # output = Popen([program, datafile, subfile, extra_opts], cmd = [program, datafile] if subfile: cmd.append(subfile) if extra_opts: cmd.extend(extra_opts) output = Popen(cmd, stdout=PIPE).communicate()[0] if verbose: print output print "End of CHomP output" print if generators: gens = open(genfile, 'r').read() if verbose: print "Generators:" print gens # # process output # # output contains substrings of one of the forms # "H_1 = Z", "H_1 = Z_2 + Z", "H_1 = Z_2 + Z^2", # "H_1 = Z + Z_2 + Z" if output.find('trivial') != -1: if mod_p: return {0: VectorSpace(base_ring, 0)} else: return {0: HomologyGroup(0)} d = {} h = re.compile("^H_([0-9]*) = (.*)$", re.M) tors = re.compile("Z_([0-9]*)") # # homology groups # for m in h.finditer(output): if verbose: print m.groups() # dim is the dimension of the homology group dim = int(m.group(1)) # hom_str is the right side of the equation "H_n = Z^r + Z_k + ..." hom_str = m.group(2) # need to read off number of summands and their invariants if hom_str.find("0") == 0: if mod_p: hom = VectorSpace(base_ring, 0) else: hom = HomologyGroup(0) else: rk = 0 if hom_str.find("^") != -1: rk_srch = re.search(r'\^([0-9]*)\s?', hom_str) rk = int(rk_srch.group(1)) rk += len(re.findall("(Z$)|(Z\s)", hom_str)) if mod_p: rk = rk if rk != 0 else 1 if verbose: print "dimension = %s, rank of homology = %s" % (dim, rk) hom = VectorSpace(base_ring, rk) else: n = rk invts = [] for t in tors.finditer(hom_str): n += 1 invts.append(int(t.group(1))) for i in range(rk): invts.append(0) if verbose: print "dimension = %s, number of factors = %s, invariants = %s" %(dim, n, invts) hom = HomologyGroup(n, invts) # # generators # if generators: if cubical: g = process_generators_cubical(gens, dim) if verbose: print "raw generators: %s" % g if g: module = CombinatorialFreeModule(base_ring, original_complex.n_cells(dim), prefix="", bracket=True) basis = module.basis() output = [] for x in g: v = module(0) for term in x: v += term[0] * basis[term[1]] output.append(v) g = output elif simplicial: g = process_generators_simplicial(gens, dim, complex) if verbose: print "raw generators: %s" % gens if g: module = CombinatorialFreeModule(base_ring, complex.n_cells(dim), prefix="", bracket=False) basis = module.basis() output = [] for x in g: v = module(0) for term in x: if complex._numeric: v += term[0] * basis[term[1]] else: # if not numeric: have to # translate vertices from numbers # to their original form. translate = dict([(b,a) for (a,b) in complex._numeric_translation]) simplex = Simplex([translate[a] for a in tuple(term[1])]) v += term[0] * basis[simplex] output.append(v) g = output elif chain: g = process_generators_chain(gens, dim, base_ring) if verbose: print "raw generators: %s" % gens if g: d[dim] = (hom, g) else: d[dim] = hom else: d[dim] = hom if chain: new_d = {} bottom = min(complex.differential()) top = max(complex.differential()) for dim in d: if complex._degree == -1: # chain complex new_dim = bottom + dim else: # cochain complex new_dim = top - dim if isinstance(d[dim], tuple): # generators included. group = d[dim][0] gens = d[dim][1] new_gens = [] dimension = complex.differential(new_dim).ncols() # make sure that each vector is embedded in the # correct ambient space: pad with a zero if # necessary. for v in gens: v_dict = v.dict() if dimension - 1 not in v.dict(): v_dict[dimension - 1] = 0 new_gens.append(vector(base_ring, v_dict)) else: new_gens.append(v) new_d[new_dim] = (group, new_gens) else: new_d[new_dim] = d[dim] d = new_d return d
def eval(self,x,globals=None, locals=None): """ EXAMPLES:: sage: from sage.misc.inline_fortran import InlineFortran, _example sage: test_fortran = InlineFortran(globals()) # optional -- fortran sage: test_fortran(_example) # optional -- fortran sage: import numpy sage: n = numpy.array(range(10),dtype=float) sage: fib(n,int(10)) # optional -- fortran sage: n # optional -- fortran array([ 0., 1., 1., 2., 3., 5., 8., 13., 21., 34.]) """ if len(x.splitlines()) == 1 and os.path.exists(x): filename = x x = open(x).read() if filename.lower().endswith('.f90'): x = '!f90\n' + x global count # On linux g77_shared should be a script that runs sage_fortran -shared # On OS X it should be a script that runs gfortran -bundle -undefined dynamic_lookup path = os.environ['SAGE_LOCAL']+'/bin/sage-g77_shared' from numpy import f2py old_import_path=os.sys.path cwd=os.getcwd() os.sys.path.append(cwd) #name = tmp_dir() + '/fortran_module_%d'%count name = 'fortran_module_%d'%count if os.path.exists(name): os.unlink(name) s_lib_path="" s_lib="" for s in self.library_paths: s_lib_path=s_lib_path+"-L"+s+" " for s in self.libraries: s_lib=s_lib +"-l"+s + " " # if the first line has !f90 as a comment gfortran will treat it as # fortran 90 code if x.startswith('!f90'): fname = os.path.join(tmp_filename() +'.f90') else: fname = os.path.join(tmp_filename() +'.f') log = tmp_filename() extra_args = '--quiet --f77exec=%s --f90exec=%s %s %s 1>&2 >"%s"'%( path, path, s_lib_path, s_lib, log) f2py.compile(x, name, extra_args = extra_args, source_fn=fname) log_string = open(log).read() os.unlink(log) os.unlink(fname) if self.verbose: print log_string count += 1 try: m=__builtin__.__import__(name) except ImportError: if not self.verbose: print log_string return finally: os.sys.path=old_import_path os.unlink(name + '.so') for k, x in m.__dict__.iteritems(): if k[0] != '_': self.globals[k] = x
def __call__(self, program, complex, subcomplex=None, **kwds): """ Call a CHomP program to compute the homology of a chain complex, simplicial complex, or cubical complex. See :class:`CHomP` for full documentation. EXAMPLES:: sage: from sage.interfaces.chomp import CHomP sage: T = cubical_complexes.Torus() sage: CHomP()('homcubes', T) # indirect doctest, optional - CHomP {0: 0, 1: Z x Z, 2: Z} """ from sage.misc.misc import tmp_filename from sage.homology.all import CubicalComplex, cubical_complexes from sage.homology.all import SimplicialComplex, Simplex from sage.homology.cubical_complex import Cube from sage.homology.chain_complex import HomologyGroup, ChainComplex from subprocess import Popen, PIPE from sage.rings.all import QQ, ZZ from sage.modules.all import VectorSpace, vector from sage.combinat.free_module import CombinatorialFreeModule if not have_chomp(program): raise OSError("Program %s not found" % program) verbose = kwds.get('verbose', False) generators = kwds.get('generators', False) extra_opts = kwds.get('extra_opts', '') base_ring = kwds.get('base_ring', ZZ) if extra_opts: extra_opts = extra_opts.split() else: extra_opts = [] # type of complex: cubical = False simplicial = False chain = False # CHomP seems to have problems with cubical complexes if the # first interval in the first cube defining the complex is # degenerate. So replace the complex X with [0,1] x X. if isinstance(complex, CubicalComplex): cubical = True edge = cubical_complexes.Cube(1) original_complex = complex complex = edge.product(complex) if verbose: print "Cubical complex" elif isinstance(complex, SimplicialComplex): simplicial = True if verbose: print "Simplicial complex" else: chain = True base_ring = kwds.get('base_ring', complex.base_ring()) if verbose: print "Chain complex over %s" % base_ring if base_ring == QQ: raise ValueError( "CHomP doesn't compute over the rationals, only over Z or F_p." ) if base_ring.is_prime_field(): p = base_ring.characteristic() extra_opts.append('-p%s' % p) mod_p = True else: mod_p = False # # complex # try: data = complex._chomp_repr_() except AttributeError: raise AttributeError( "Complex can not be converted to use with CHomP.") datafile = tmp_filename() f = open(datafile, 'w') f.write(data) f.close() # # subcomplex # if subcomplex is None: if cubical: subcomplex = CubicalComplex([complex.n_cells(0)[0]]) elif simplicial: m = re.search(r'\(([^,]*),', data) v = int(m.group(1)) subcomplex = SimplicialComplex([[v]]) else: # replace subcomplex with [0,1] x subcomplex. if cubical: subcomplex = edge.product(subcomplex) # # generators # if generators: genfile = tmp_filename() extra_opts.append('-g%s' % genfile) # # call program # if subcomplex is not None: try: sub = subcomplex._chomp_repr_() except AttributeError: raise AttributeError( "Subcomplex can not be converted to use with CHomP.") subfile = tmp_filename() f = open(subfile, 'w') f.write(sub) f.close() else: subfile = '' if verbose: print "Popen called with arguments", print[program, datafile, subfile] + extra_opts print print "CHomP output:" print # output = Popen([program, datafile, subfile, extra_opts], cmd = [program, datafile] if subfile: cmd.append(subfile) if extra_opts: cmd.extend(extra_opts) output = Popen(cmd, stdout=PIPE).communicate()[0] if verbose: print output print "End of CHomP output" print if generators: gens = open(genfile, 'r').read() if verbose: print "Generators:" print gens # # process output # # output contains substrings of one of the forms # "H_1 = Z", "H_1 = Z_2 + Z", "H_1 = Z_2 + Z^2", # "H_1 = Z + Z_2 + Z" if output.find('trivial') != -1: if mod_p: return {0: VectorSpace(base_ring, 0)} else: return {0: HomologyGroup(0, ZZ)} d = {} h = re.compile("^H_([0-9]*) = (.*)$", re.M) tors = re.compile("Z_([0-9]*)") # # homology groups # for m in h.finditer(output): if verbose: print m.groups() # dim is the dimension of the homology group dim = int(m.group(1)) # hom_str is the right side of the equation "H_n = Z^r + Z_k + ..." hom_str = m.group(2) # need to read off number of summands and their invariants if hom_str.find("0") == 0: if mod_p: hom = VectorSpace(base_ring, 0) else: hom = HomologyGroup(0, ZZ) else: rk = 0 if hom_str.find("^") != -1: rk_srch = re.search(r'\^([0-9]*)\s?', hom_str) rk = int(rk_srch.group(1)) rk += len(re.findall("(Z$)|(Z\s)", hom_str)) if mod_p: rk = rk if rk != 0 else 1 if verbose: print "dimension = %s, rank of homology = %s" % (dim, rk) hom = VectorSpace(base_ring, rk) else: n = rk invts = [] for t in tors.finditer(hom_str): n += 1 invts.append(int(t.group(1))) for i in range(rk): invts.append(0) if verbose: print "dimension = %s, number of factors = %s, invariants = %s" % ( dim, n, invts) hom = HomologyGroup(n, ZZ, invts) # # generators # if generators: if cubical: g = process_generators_cubical(gens, dim) if verbose: print "raw generators: %s" % g if g: module = CombinatorialFreeModule( base_ring, original_complex.n_cells(dim), prefix="", bracket=True) basis = module.basis() output = [] for x in g: v = module(0) for term in x: v += term[0] * basis[term[1]] output.append(v) g = output elif simplicial: g = process_generators_simplicial(gens, dim, complex) if verbose: print "raw generators: %s" % gens if g: module = CombinatorialFreeModule(base_ring, complex.n_cells(dim), prefix="", bracket=False) basis = module.basis() output = [] for x in g: v = module(0) for term in x: if complex._is_numeric(): v += term[0] * basis[term[1]] else: translate = complex._translation_from_numeric( ) simplex = Simplex( [translate[a] for a in term[1]]) v += term[0] * basis[simplex] output.append(v) g = output elif chain: g = process_generators_chain(gens, dim, base_ring) if verbose: print "raw generators: %s" % gens if g: if not mod_p: # sort generators to match up with corresponding invariant g = [ _[1] for _ in sorted(zip(invts, g), key=lambda x: x[0]) ] d[dim] = (hom, g) else: d[dim] = hom else: d[dim] = hom if chain: new_d = {} diff = complex.differential() if len(diff) == 0: return {} bottom = min(diff) top = max(diff) for dim in d: if complex._degree_of_differential == -1: # chain complex new_dim = bottom + dim else: # cochain complex new_dim = top - dim if isinstance(d[dim], tuple): # generators included. group = d[dim][0] gens = d[dim][1] new_gens = [] dimension = complex.differential(new_dim).ncols() # make sure that each vector is embedded in the # correct ambient space: pad with a zero if # necessary. for v in gens: v_dict = v.dict() if dimension - 1 not in v.dict(): v_dict[dimension - 1] = 0 new_gens.append(vector(base_ring, v_dict)) else: new_gens.append(v) new_d[new_dim] = (group, new_gens) else: new_d[new_dim] = d[dim] d = new_d return d
def __call__(self, assumptions=None): """ Run 'command' and collect output. INPUT: - ``assumptions`` - ignored, accepted for compatibility with other solvers (default: ``None``) TESTS: This class is not meant to be called directly:: sage: from sage.sat.solvers.dimacs import DIMACS sage: fn = tmp_filename() sage: solver = DIMACS(filename=fn) sage: solver.add_clause( (1, -2 , 3) ) sage: solver() Traceback (most recent call last): ... ValueError: No SAT solver command selected. """ if assumptions is not None: raise NotImplementedError( "Assumptions are not supported for DIMACS based solvers.") self.write() output_filename = None self._output = [] command = self._command.strip() if not command: raise ValueError("No SAT solver command selected.") if "{output}" in command: output_filename = tmp_filename() command = command.format(input=self._headname, output=output_filename) args = shlex.split(command) try: process = subprocess.Popen(args, stdout=subprocess.PIPE) except OSError: raise OSError( "Could run '%s', perhaps you need to add your SAT solver to $PATH?" % (" ".join(args))) try: while process.poll() is None: for line in iter(process.stdout.readline, ''): if get_verbose() or self._verbosity: print line, sys.stdout.flush() self._output.append(line) sleep(0.1) if output_filename: self._output.extend(open(output_filename).readlines()) except KeyboardInterrupt: process.kill() raise KeyboardInterrupt