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
9
10
11
12
13
14 import httplib, urllib
15
16 from os.path import split as pathsplit
17 from urlparse import urlsplit
18
19
20
21
22
23 from com.xhaus.jyson import JysonCodec as json
24
25 from ConfigParser import SafeConfigParser
26
27 from xml.dom.minidom import parse, parseString
28
29 from textwrap import wrap as textwrap
30
31
32
33
34
35
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
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
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
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
111
112
113
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
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
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
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
168
169 conn = httplib.HTTPConnection(self._sd.url)
170
171
172
173 _path = self._sd.sqpath + "?" + urllib.urlencode(self._data)
174
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
183 self.text = response.read()
184
185 self.json = json.loads(self.text)
186
187 conn.close()
188
189
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
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
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
230
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
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
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
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
268
269
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
277
278
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
289
290
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
298
299
300
301 self.__constructJasObject()
302
304 """
305 Return the ideal as a Jas objects.
306 """
307
308
309 return self.jasRing.ideal(list=self.jasBasis)
310
312
313
314
315 path = link[23:]
316
317
318 url = self._sd.sdhost
319
320 conn = httplib.HTTPConnection(url)
321
322
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
330 if (xmlTree.getElementsByTagName("vars") == []):
331 raise IOERROR("The given XMLString does not contain variables for the IntPS System")
332 if (xmlTree.getElementsByTagName("basis") == []):
333 raise IOERROR("The given XMLString does not contain a basis for the IntPS System")
334
335
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
342
343 from jas import PolyRing, ZZ
344
345 if 'hasParameters' in self.__dict__ and self.hasParameters != '':
346
347
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
353 R = PolyRing(ZZ(), str(self.hasVariables) )
354 gens = str(self.hasVariables)
355
356
357 Rg = "(one," + gens + ") = R.gens();"
358
359 exec(str(Rg))
360
361 self.jasRing = R;
362
363
364 from edu.jas.poly import GenPolynomialTokenizer
365 vs = GenPolynomialTokenizer.expressionVariables(str(gens))
366 vs = sorted(vs)
367
368 vsb = set()
369 [ vsb.update(GenPolynomialTokenizer.expressionVariables(str(s))) for s in self.basis]
370 vsb = sorted(list(vsb))
371
372 if vs != vsb:
373 raise ValueError("invalid variables: expected " + str(vs) + ", got " + str(vsb))
374
375
376 self.jasBasis = []
377 for ps in self.basis:
378
379 ps = str(ps)
380 ps = ps.replace('^', '**')
381
382
383 pol = eval(ps)
384 self.jasBasis.append(pol)
385
386
387
388
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
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
430 """
431 This is the implementation of the predicate "sd:hasVariables". This
432 is actually not needed.
433 """
434
435
436 K = self.jasRing.ring.vars
437 return ",".join(sorted(list(set(K))))
438
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