Module sdjas
[hide private]
[frames] | no frames]

Source Code for Module sdjas

  1  ''' 
  2  Access to the database of ideals as provided by the 
  3  SymbolicData Project (http://symbolicdata.org). 
  4   
  5  Adapted from the Symbolicdata and Sage code 
  6  ''' 
  7  # 
  8  # $Id: sdjas.py 5803 2018-03-27 10:29:31Z kredel $ 
  9  # 
 10  # adapted from the Symbolicdata and Sage code 
 11  # 
 12  # communicate with the SPARQL endpoint (http request) 
 13  #import requests 
 14  import httplib, urllib 
 15  # easily handle URLs 
 16  from os.path import split as pathsplit 
 17  from urlparse import urlsplit 
 18  # for nice output of dictionaries: json.dumps(dict, indent = 4) 
 19  # mostly for debugging reasons (will be removed later) 
 20  # for old jython versions it must be loaded from an  
 21  # external library jyson-1.0.2.jar 
 22  #import json  
 23  from com.xhaus.jyson import JysonCodec as json 
 24  # parse the sd.ini file 
 25  from ConfigParser import SafeConfigParser 
 26  # parse the xml of the resource files 
 27  from xml.dom.minidom import parse, parseString 
 28  # output lists nicely (there might be a better way) 
 29  from textwrap import wrap as textwrap 
 30  # not needed 
 31  #from jas import * 
 32   
 33  # Some internal helper functions that are not meant to be 
 34  # called by the user 
 35   
36 -def _uri_to_name(uri):
37 """ 38 Converts a uri to a name or key by only taking everything 39 after the last / or (if present) #. 40 41 Examples: 42 - http://example.com/test -> test 43 - http://example.com/model#testedBy -> testedBy 44 """ 45 usplit = urlsplit(uri) 46 if usplit.fragment != '': 47 return usplit.fragment 48 else: 49 return pathsplit(usplit.path)[-1]
50
51 -def _pprint(l):
52 """ 53 Formats a list l to be displayed in a tabular layout. It is 54 possible to pass an integer width to the textwrap function. 55 The width of the terminal window could be obtained via the 56 Python console module. However, since it is not included 57 in Jas, we decided not to use it. The default width that 58 textwrap uses is set to 70. There might be a better way to 59 do this. 60 """ 61 col = max([len(x) for x in l]) + 3 62 padded = ''.join([x.ljust(col) for x in l]) 63 print '\n'.join(textwrap(padded))
64
65 -def get_value_for_URI(sd, URI, predicate):
66 """ 67 A quick convienience function to retrieve a single value 68 of a given triple (object, predicate, ...) 69 70 The parameter sd is a SymbolicData object that contains 71 information about the SPARQL endpoint. 72 """ 73 result = None 74 query = '''SELECT * WHERE { <%s> <%s> ?x }''' % (URI, predicate) 75 try: 76 result = SPARQL(sd, query).json['results']['bindings'][0]['x']['value'] 77 except: 78 pass 79 return result
80 81 82 83 # Class definitions start here
84 -class SymbolicData:
85 """ 86 Access to the database of ideals as provided by the 87 SymbolicData Project (http://symbolicdata.org). 88 """ 89
90 - def __init__(self, sparql = 'symbolicdata.org'):
91 """ 92 The constructor parses the sd.ini file and sets up some variables. 93 An optional parameter can be passed to select the SPARQL endpoint 94 that should be used. The keywords for different SPARQL endpoints are 95 defined in the sd.ini file. 96 97 The default SPARQL endpoint is the one from symbolicdata.org 98 """ 99 self._sparql = sparql 100 self._ideals = None 101 self._parser = SafeConfigParser() 102 self._parser.read(['sd.ini','examples/sd.ini']) 103 self.sd = self._parser.get('symbolicdata', 'sd') 104 try: 105 self.url = self._parser.get('sparql', self._sparql) 106 except: 107 raise ValueError("The SPARQL endpoint referenced by '%s' was not found in the sd.ini file." % self._sparql) 108 self.sdhost = self._parser.get('DEFAULT', 'sdhost') 109 self.sqpath = self._parser.get('sparql', 'path')
110 #print "SymbolicData() initialized" 111 #print "url = " + str(self.url) 112 #print "sdhost = " + str(self.sdhost) 113
114 - def get_ideals(self, force_reload = False):
115 """ 116 Returns a Python list of ideals. 117 """ 118 if self._ideals == None or force_reload == True: 119 self.list_ideals(False, force_reload) 120 return self._ideals
121
122 - def list_ideals(self, output = True, force_reload = False):
123 """ 124 Lists all the available ideals. 125 """ 126 if self._ideals == None or force_reload == True: 127 r = SPARQL(self, self._parser.get('queries', 'list_ideals')) 128 self._ideals = [_uri_to_name(x['ideal']['value']) for 129 x in r.json['results']['bindings']] 130 if output: 131 _pprint(self._ideals)
132
133 - def get_ideal(self, uri):
134 """ 135 Returns an ideal as a Jas object that is ready to be used by 136 Jas. 137 """ 138 return SD_Ideal(self, uri).get_ideal();
139
140 - def get_sd_ideal(self, uri):
141 """ 142 Returns an internal object that represents the SymbolicData 143 database object. (See below for details) 144 """ 145 return SD_Ideal(self, uri)
146 147 148
149 -class SPARQL:
150 """ 151 This is a 'wrapper' class for SPARQL queries. A class might be 152 a slight overkill. It was made with the idea, that one can store 153 the query and the result together, to re-evaluate both without 154 having to access the server. However, in the end this feature 155 was not really needed. 156 """
157 - def __init__(self, sd, query, output = 'json'):
158 """ 159 Execute the query and store the results. 160 """ 161 self._sd = sd; 162 self._query = query; 163 self._data = { 164 'output' : output, 165 'query' : query 166 } 167 #self.response = requests.get(self._sd.url, params = self._data) 168 #print "url = " + str(self._sd.url) 169 conn = httplib.HTTPConnection(self._sd.url) 170 #print "conn = " + str(conn) 171 172 #print "query = " + str(query) 173 _path = self._sd.sqpath + "?" + urllib.urlencode(self._data) 174 #print "path = " + str(_path) 175 conn.request("GET", _path ); 176 response = conn.getresponse(); 177 if response.status != 200: 178 print response.status, response.reason, "\n" 179 raise IOError, "HTTP GET %s not successful" % _path 180 181 head = response.msg 182 #print "head = " + str(head) 183 self.text = response.read() 184 #print "body = " + str(self.text) 185 self.json = json.loads(self.text) 186 #print "json = " + str(self.json) 187 conn.close()
188 189
190 -class SD_Ideal:
191 """ 192 This class represents a SymbolicData database object. The 193 constructor takes a complete URI or a name SUBJ (the latter of which 194 will be prefixed with the 'ideal' value from the sd.ini) 195 196 Any triple of the form (SUBJ, PRED, OBJ) will yield a field PRED* 197 for the SD_Ideal object with the value OBJ, where PRED* is the 198 ending piece of PRED URI as defined by the function _uri_to_name() 199 200 A SPARQL endpoint is needed. As a future improvement, it could be 201 nice to directly parse an RDF in a convienient serialization. 202 """ 203
204 - def __init__(self, sd, name):
205 """ 206 sd is a SymbolicData object, the name can be a complete URI or shortened 207 name as defined by _uri_to_name(). The latter will be prefixed with the 208 'ideal' value from the sd.ini. Namespaces like "sd:Wu-90" are not 209 (yet) supported. 210 211 Appart from retrieving the information from the SPARQL endpoint, the 212 resource data (XML files) is needed as well. While the SPARQL endpoint 213 can be substituted by another SPARQL endpoint, the links to the resource 214 files are 'hard-coded' into the RDF data. The possibility to use a 215 (possibly 'hand-filled') cache will be included in the next update. 216 """ 217 self._sd = sd 218 # quick test, if the given name already is an uri 219 if name[:7] == 'http://': 220 self.uri = name 221 else: 222 self.uri = "%s%s" % (self._sd._parser.get("symbolicdata", "ideal"), name) 223 224 self.hasXMLResource = False 225 self.hasLengthsList = '' 226 self.hasDegreeList = '' 227 self.hasParameters = '' 228 229 # we set up the query to get all predicate values 230 # of the URI/polynomial system/ideal 231 query = ''' 232 PREFIX sd: <%s> 233 SELECT ?p ?o WHERE { 234 <%s> ?p ?o 235 }''' % (self._sd.sd, self.uri) 236 self._request = SPARQL(self._sd, query) 237 238 if len(self._request.json['results']['bindings']) == 0: 239 raise ValueError("No data found for <%s>.\nMaybe the name was misspelled or the SPARQL endpoint is unavailable." % self.uri) 240 241 # append the keys to the self.__dict__. 242 for t in self._request.json['results']['bindings']: 243 uri = t['p']['value'] 244 obj = t['o']['value'] 245 self.__dict__[_uri_to_name(uri)] = obj 246 247 # Next we need a resource file with the actual expressions that are 248 # used to generate the ideal. 249 # 250 # There are four cases that need to be dealt with 251 # (1) the ideal is constructed direclty 252 # from an IntPS with related XML resource 253 # (2) the ideal is a flat variant of another 254 # ideal 255 # (3) the ideal is obtained by homogenizing 256 # another ideal 257 # (4) the ideal is obtained by parameterizing another 258 # ideal 259 # Please note: While it might seem that only one of (2) and (4) 260 # should be included, both are needed to map the actual history 261 # of how these ideals were obtained. 262 263 # case 1 264 if 'relatedPolynomialSystem' in self.__dict__.keys(): 265 self.__addXMLResource(get_value_for_URI(self._sd, self.relatedPolynomialSystem, self._sd.sd+'relatedXMLResource')) 266 self.hasXMLResource = True 267 #print "relatedPolynomialSystem " + str(name) 268 269 # case 2 270 if 'flatten' in self.__dict__.keys(): 271 parent_name = self.flatten 272 parent = SD_Ideal(self._sd, parent_name) 273 self.variablesCSV = self.hasVariables 274 self.variables = map(lambda x: str(x).strip(), self.variablesCSV.rsplit(",")) 275 self.basis = parent.basis 276 #print "flatten " + str(parent_name) + ", name = " + str(name) 277 278 # case 3 279 if 'homogenize' in self.__dict__.keys(): 280 parent_name = self.homogenize 281 if 'homogenizedWith' in self.__dict__.keys(): 282 hv = self.homogenizedWith 283 parent = SD_Ideal(self._sd, parent_name) 284 self.variablesCSV = parent.variablesCSV + "," + hv 285 self.variables = parent.variables 286 self.variables.append(hv) 287 self.basis = parent.jas_homogenize(hv) 288 #print "homogenize " + str(parent_name) + ", name = " + str(name) 289 290 # case 4 291 if 'parameterize' in self.__dict__.keys(): 292 parent_name = self.parameterize 293 parent = SD_Ideal(self._sd, parent_name) 294 self.variablesCSV = self.hasVariables 295 self.variables = map(lambda x: str(x).strip(), self.variablesCSV.rsplit(",")) 296 self.basis = parent.basis 297 #print "parameterize " + str(parent_name) + ", name = " + str(name) 298 299 # now we got the variables, the parameters and 300 # the strings/expressions for the polynomials 301 self.__constructJasObject()
302
303 - def get_ideal(self):
304 """ 305 Return the ideal as a Jas objects. 306 """ 307 #return ideal(self.sageBasis) 308 #print "jasRing = " + str(self.jasRing) 309 return self.jasRing.ideal(list=self.jasBasis)
310
311 - def __addXMLResource(self, link):
312 #xml = requests.get(link).text 313 #print "link = " + str(link) 314 #url = link[0:23] 315 path = link[23:] # hack for lost domain 316 #print "url = " + str(url) 317 #url = self._sd.url[:-5] 318 url = self._sd.sdhost 319 #print "url = " + str(url) 320 conn = httplib.HTTPConnection(url) 321 #print "conn = " + str(conn) 322 #print "path = " + str(path) 323 conn.request("GET", path ); 324 xml = conn.getresponse().read(); 325 print _uri_to_name(link) + " = " + str(xml) 326 327 xmlTree = parseString(xml) 328 329 # Code snipped borrowed from Albert Heinle 330 if (xmlTree.getElementsByTagName("vars") == []): # Check, if vars are there 331 raise IOERROR("The given XMLString does not contain variables for the IntPS System") 332 if (xmlTree.getElementsByTagName("basis") == []): # Check, if we have a basis 333 raise IOERROR("The given XMLString does not contain a basis for the IntPS System") 334 # -------------------- Input Check finished -------------------- 335 # From here, we can assume that the input is given correct 336 self.variablesCSV = (xmlTree.getElementsByTagName("vars")[0]).firstChild.data 337 self.variables = map(lambda x: str(x).strip(), self.variablesCSV.rsplit(",")) 338 polynomials = xmlTree.getElementsByTagName("basis")[0] 339 self.basis = map(lambda poly: str(poly.firstChild.data).strip(),polynomials.getElementsByTagName("poly"))
340
341 - def __constructJasObject(self):
342 #from types import StringType 343 from jas import PolyRing, ZZ 344 # set up the polynomial ring (Jas syntax) 345 if 'hasParameters' in self.__dict__ and self.hasParameters != '': 346 #K = 'K.<%s> = PolynomialRing(ZZ)' % self.hasParameters 347 #R = K + '; R.<%s> = PolynomialRing(K)' % self.hasVariables 348 K = PolyRing(ZZ(), str(self.hasParameters) ) 349 R = PolyRing(K, str(self.hasVariables)) 350 gens = '%s,%s' % (self.hasParameters, self.hasVariables) 351 else: 352 #R = 'R.<%s> = PolynomialRing(ZZ)' % (self.hasVariables) 353 R = PolyRing(ZZ(), str(self.hasVariables) ) 354 gens = str(self.hasVariables) 355 # translate Jas syntax to pure Python and execute 356 #exec(preparse(R)) 357 Rg = "(one," + gens + ") = R.gens();" 358 #print str(R) 359 exec(str(Rg)) # safe here since R did evaluate 360 #print "R = " + str(R) 361 self.jasRing = R; 362 363 # avoid XSS: check if polynomials are clean 364 from edu.jas.poly import GenPolynomialTokenizer 365 vs = GenPolynomialTokenizer.expressionVariables(str(gens)) 366 vs = sorted(vs) 367 #print "vs = " + str(vs) 368 vsb = set() 369 [ vsb.update(GenPolynomialTokenizer.expressionVariables(str(s))) for s in self.basis] 370 vsb = sorted(list(vsb)) 371 #print "vsb = " + str(vsb) 372 if vs != vsb: 373 raise ValueError("invalid variables: expected " + str(vs) + ", got " + str(vsb)) 374 # construct polynomials in the constructed ring from 375 # the polynomial expressions 376 self.jasBasis = [] 377 for ps in self.basis: 378 #print "ps = " + str(ps) 379 ps = str(ps) 380 ps = ps.replace('^', '**') 381 #exec(preparse("symbdata_ideal = %s" % ps)) 382 #exec("symbdata_poly = %s" % ps) 383 pol = eval(ps) 384 self.jasBasis.append(pol)
385 #print "jasBasis = " + str([ str(p) for p in self.jasBasis]) 386 387 # the following functions will all use Jas to 388 # calculate metadata
389 - def jas_hasLengthsList(self):
390 """ 391 This is the implementation of the predicate "sd:hasLengthsList". 392 The lengths lists is the sorted list of the number of monomials of 393 the generator of the ideal. 394 395 Along with the output, there will also be generated a field 396 FROM_JAS_hasLengthsList which can be used to later access the 397 data without recalculating. The main reason for this is that the 398 SymbolicData properties are converted into field, not getter 399 functions. So to have some symmetry, the Jas calculations will 400 end up in fields as well. 401 """ 402 try: 403 LL = sorted(map(lambda x : len(x.monomials()), self.jasBasis)) 404 self.FROM_JAS_hasLengthsList = ",".join(map(lambda x: str(x), LL)) 405 except: 406 self.FROM_JAS_hasLengthsList = '' 407 return self.FROM_JAS_hasLengthsList
408
409 - def jas_hasDegreeList(self):
410 """ 411 This is the implementation of the predicate "sd:hasDegreeList". 412 The degree list is the sorted list of the degree of the generator 413 of the ideal. 414 415 Along with the output, there will also be generated a field 416 FROM_JAS_hasDegreeList which can be used to later access the 417 data without recalculating. The main reason for this is that the 418 SymbolicData properties are converted into field, not getter 419 functions. So to have some symmetry, the Jas calculations will 420 end up in fields as well. 421 """ 422 try: 423 LL = sorted(map(lambda x : x.degree(), self.jasBasis)) 424 self.FROM_JAS_hasDegreeList = ",".join(map(lambda x: str(x), LL)) 425 except: 426 self.FROM_JAS_hasDegreeList = '' 427 return self.FROM_JAS_hasDegreeList
428
429 - def jas_hasVariables(self):
430 """ 431 This is the implementation of the predicate "sd:hasVariables". This 432 is actually not needed. 433 """ 434 #K = [] 435 #DL = map(lambda m : K.extend(map(lambda l : str(l), m.variables())), self.sageBasis) 436 K = self.jasRing.ring.vars 437 return ",".join(sorted(list(set(K))))
438
439 - def jas_homogenize(self, hv):
440 """ 441 Homogenize a basis, which here means actually nothing more than 442 homogenizing every element of the basis. 443 """ 444 homBasis = map(lambda x : x.homogenize(hv), self.jasBasis) 445 return homBasis
446