1 | package felix.dstruct; |
2 | |
3 | import java.io.BufferedReader; |
4 | import java.util.ArrayList; |
5 | import java.util.HashMap; |
6 | import java.util.HashSet; |
7 | import java.util.Hashtable; |
8 | import java.util.TreeMap; |
9 | import java.util.concurrent.ConcurrentHashMap; |
10 | |
11 | import tuffy.db.RDB; |
12 | import tuffy.mln.Literal; |
13 | import tuffy.mln.Predicate; |
14 | import tuffy.mln.Type; |
15 | import tuffy.ra.ConjunctiveQuery; |
16 | import tuffy.ra.Function; |
17 | import tuffy.util.DebugMan; |
18 | import tuffy.util.ExceptionMan; |
19 | import tuffy.util.FileMan; |
20 | import tuffy.util.Timer; |
21 | import tuffy.util.UIMan; |
22 | import felix.parser.FelixInputParser; |
23 | import felix.task.ConcurrentEvidenceLoader; |
24 | import felix.util.*; |
25 | |
26 | |
27 | /** |
28 | * An object of FelixQuery consists of program, query and evidence |
29 | * from inputs. |
30 | * @author Ce Zhang |
31 | * |
32 | */ |
33 | public class FelixQuery { |
34 | |
35 | /** |
36 | * Map from predicate name to rules used for coref that is |
37 | * difficult to be represented by first order logic. |
38 | */ |
39 | HashMap<String, ArrayList<ConjunctiveQuery>> specialClusteringRules = |
40 | new HashMap<String, ArrayList<ConjunctiveQuery>>(); |
41 | |
42 | /** |
43 | * All input clauses. |
44 | */ |
45 | ArrayList<FelixClause> clauses = new ArrayList<FelixClause>(); |
46 | |
47 | /** |
48 | * Map from integer IDs to clauses |
49 | */ |
50 | public HashMap<Integer, FelixClause> id2clause = new HashMap<Integer, FelixClause>(); |
51 | |
52 | /** |
53 | * All input predicates. |
54 | */ |
55 | ArrayList<FelixPredicate> predicates = new ArrayList<FelixPredicate>(); |
56 | |
57 | //TODO: scoping rule in statOperator? |
58 | /** |
59 | * All input scoping rules |
60 | */ |
61 | ArrayList<ConjunctiveQuery> scopingRules = new ArrayList<ConjunctiveQuery>(); |
62 | |
63 | /** |
64 | * All input datalog rules |
65 | */ |
66 | ArrayList<ConjunctiveQuery> datalogRules = new ArrayList<ConjunctiveQuery>(); |
67 | |
68 | /** |
69 | * Map from predicate's name to predicate. |
70 | */ |
71 | HashMap<String, FelixPredicate> nameMapPred = new HashMap<String, FelixPredicate>(); |
72 | |
73 | /** |
74 | * Map from type's name to type. |
75 | */ |
76 | private Hashtable<String, Type> nameMapType = new Hashtable<String, Type>(); |
77 | |
78 | /** |
79 | * Map from function's name to function. |
80 | */ |
81 | private Hashtable<String, Function> nameMapFunc = new Hashtable<String, Function>(); |
82 | |
83 | /** |
84 | * Map from constant to constant ID. |
85 | */ |
86 | public ConcurrentHashMap<String, Integer> mapConstantID = new ConcurrentHashMap<String, Integer>(); |
87 | |
88 | /** |
89 | * Map from constant ID to constant. |
90 | */ |
91 | public ConcurrentHashMap<Integer, String> mapIDConstant = new ConcurrentHashMap<Integer, String>(); |
92 | |
93 | /** |
94 | * @deprecated |
95 | */ |
96 | public ConcurrentHashMap<String, Integer> newMapConstantID = new ConcurrentHashMap<String, Integer>(); |
97 | |
98 | |
99 | /** |
100 | * The parser used to parse the input. |
101 | */ |
102 | FelixInputParser parser = new FelixInputParser(this); |
103 | |
104 | /** |
105 | * Add a clause to FelixQuery. |
106 | * @param fc |
107 | */ |
108 | public void addFelixClause(FelixClause fc) { |
109 | fc.setId(this.clauses.size() + 1); |
110 | this.clauses.add(fc); |
111 | this.id2clause.put(fc.getId(), fc); |
112 | } |
113 | |
114 | /** |
115 | * Add a predicate to FelixQuery. |
116 | * @param fp |
117 | */ |
118 | public void addFelixPredicate(FelixPredicate fp) { |
119 | |
120 | if (nameMapPred.containsKey(fp.getName())) { |
121 | ExceptionMan.die("Duplicate predicate definitions - " |
122 | + fp.getName()); |
123 | } |
124 | |
125 | fp.setID(predicates.size()); |
126 | predicates.add(fp); |
127 | nameMapPred.put(fp.getName(), fp); |
128 | |
129 | } |
130 | |
131 | /** |
132 | * Get all scoping rules. |
133 | * @return |
134 | */ |
135 | public ArrayList<ConjunctiveQuery> getScopingRules(){ |
136 | return this.scopingRules; |
137 | } |
138 | |
139 | /** |
140 | * Add a scoping rule to FelixQuery. |
141 | * @param sr |
142 | */ |
143 | public void addScopingRule(ConjunctiveQuery sr) { |
144 | this.scopingRules.add(sr); |
145 | } |
146 | |
147 | /** |
148 | * Add a Datalog rule to FelixQuery. |
149 | * @param dr |
150 | */ |
151 | public void addDatalogRule(ConjunctiveQuery dr) { |
152 | this.datalogRules.add(dr); |
153 | } |
154 | |
155 | /** |
156 | * Get predicate by name. |
157 | * @param name |
158 | * @return |
159 | */ |
160 | public FelixPredicate getPredByName(String name) { |
161 | return nameMapPred.get(name); |
162 | } |
163 | |
164 | /** |
165 | * Get all predicates. |
166 | * @return |
167 | */ |
168 | public ArrayList<FelixPredicate> getPredicates(){ |
169 | return this.predicates; |
170 | } |
171 | |
172 | /** |
173 | * Get or create type by name. |
174 | * @param name |
175 | * @return |
176 | */ |
177 | public Type getOrCreateTypeByName(String name) { |
178 | Type t = nameMapType.get(name); |
179 | if (t == null) { |
180 | t = new Type(name); |
181 | nameMapType.put(name, t); |
182 | } |
183 | return t; |
184 | } |
185 | |
186 | /** |
187 | * Get function by name. |
188 | * @param name |
189 | * @return |
190 | */ |
191 | public Function getFunctionByName(String name) { |
192 | Function f = Function.getBuiltInFunctionByName(name); |
193 | if (f != null) { |
194 | return f; |
195 | } |
196 | return nameMapFunc.get(name); |
197 | } |
198 | |
199 | /** |
200 | * Add a constant to FelixQuery. |
201 | * @param symbol |
202 | * @return |
203 | */ |
204 | public int addConstant(String symbol) { |
205 | |
206 | Integer id = mapConstantID.get(symbol); |
207 | boolean isnew = false; |
208 | isnew = (id==null); |
209 | if (id == null) { |
210 | id = mapConstantID.size() + 1; |
211 | mapConstantID.putIfAbsent(symbol, id); |
212 | // Clause.mappingFromID2Const.put(id, symbol); |
213 | |
214 | if(FelixConfig.evidDBSchema != null && isnew && loadingEvid == true){ |
215 | newMapConstantID.putIfAbsent(symbol, id); |
216 | } |
217 | } |
218 | mapIDConstant.putIfAbsent(id, symbol); |
219 | return id; |
220 | |
221 | } |
222 | |
223 | /** |
224 | * Whether we consider the evidence file while parsing operators. |
225 | */ |
226 | public boolean loadingEvid = false; |
227 | |
228 | /** |
229 | * Get or add (if not exists) a symbol to a type's constant table. |
230 | * @param symbol |
231 | * @param type |
232 | * @return |
233 | */ |
234 | public int getSymbolID(String symbol, Type type) { |
235 | |
236 | Integer id = mapConstantID.get(symbol); |
237 | boolean isnew = false; |
238 | isnew = (id==null); |
239 | if (id == null) { |
240 | id = mapConstantID.size() + 1; |
241 | mapConstantID.putIfAbsent(symbol, id); |
242 | |
243 | if(FelixConfig.evidDBSchema != null && isnew && loadingEvid == true){ |
244 | newMapConstantID.putIfAbsent(symbol, id); |
245 | } |
246 | |
247 | // Clause.mappingFromID2Const.put(id, symbol); |
248 | } |
249 | if (type != null && !type.isNonSymbolicType()) { |
250 | if(FelixConfig.evidDBSchema != null && isnew && loadingEvid == false){ |
251 | return id; |
252 | } |
253 | type.addConstant(id); |
254 | } |
255 | return id; |
256 | |
257 | } |
258 | |
259 | /** |
260 | * Load MLN programs. |
261 | * @param progFiles |
262 | */ |
263 | public void loadPrograms(String[] progFiles) { |
264 | for (String f : progFiles) { |
265 | String g = FileMan.getGZIPVariant(f); |
266 | if (g == null) { |
267 | ExceptionMan.die("non-existent file: " + f); |
268 | } else { |
269 | f = g; |
270 | } |
271 | UIMan.println(">>> Parsing program file: " + f); |
272 | parser.parseProgramFile(f); |
273 | } |
274 | } |
275 | |
276 | /** |
277 | * Load MLN queries. |
278 | * @param queryFiles |
279 | */ |
280 | public void loadQueries(String[] queryFiles) { |
281 | for (String f : queryFiles) { |
282 | String g = FileMan.getGZIPVariant(f); |
283 | if (g == null) { |
284 | ExceptionMan.die("non-existent file: " + f); |
285 | } else { |
286 | f = g; |
287 | } |
288 | UIMan.println(">>> Parsing query file: " + f); |
289 | parser.parseQueryFile(f); |
290 | } |
291 | } |
292 | |
293 | /** |
294 | * Load MLN queries specified by command lines. |
295 | * @param queryAtoms |
296 | */ |
297 | public void parseQueryCommaList(String queryAtoms) { |
298 | parser.parseQueryCommaList(queryAtoms); |
299 | } |
300 | |
301 | /** |
302 | * Load MLN queries specified by command lines. |
303 | * @param queryAtoms |
304 | */ |
305 | public void parseProgFromString(String prog) { |
306 | parser.parseProgramFileFromString(prog); |
307 | } |
308 | |
309 | /** |
310 | * Close all predicate's loadingFile. |
311 | */ |
312 | public void closeFiles() { |
313 | for (FelixPredicate p : predicates) { |
314 | p.closeFiles(); |
315 | } |
316 | } |
317 | |
318 | /** |
319 | * Get all predicates in this FelixQuery. |
320 | * @return |
321 | */ |
322 | public HashSet<FelixPredicate> getAllPred() { |
323 | return new HashSet<FelixPredicate>(predicates); |
324 | } |
325 | |
326 | /** |
327 | * Get all predicates which are open in this FelixQuery. |
328 | * @return |
329 | */ |
330 | public HashSet<FelixPredicate> getAllOpenPred(){ |
331 | HashSet<FelixPredicate> ret = new HashSet<FelixPredicate>(); |
332 | |
333 | for(FelixPredicate fp : this.getAllPred()){ |
334 | if(fp.isClosedWorld() == false){ |
335 | ret.add(fp); |
336 | } |
337 | } |
338 | |
339 | return ret; |
340 | } |
341 | |
342 | /** |
343 | * Get all clauses in this FelixQuery. |
344 | * @return |
345 | */ |
346 | public HashSet<FelixClause> getAllClause(){ |
347 | return new HashSet<FelixClause>(clauses); |
348 | } |
349 | |
350 | /** |
351 | * Load MLN evidences. |
352 | * @param evidFiles |
353 | * @throws InterruptedException |
354 | */ |
355 | public void loadEvidences(String[] evidFiles) throws InterruptedException { |
356 | |
357 | ArrayList<ConcurrentEvidenceLoader> loaders = |
358 | new ArrayList<ConcurrentEvidenceLoader>(); |
359 | |
360 | for(String f : evidFiles){ |
361 | ConcurrentEvidenceLoader loader = |
362 | new ConcurrentEvidenceLoader(f, this); |
363 | loaders.add(loader); |
364 | loader.run(); |
365 | } |
366 | |
367 | } |
368 | |
369 | //TODO: think about whether there are repeated materializations |
370 | //that can be reuse. |
371 | /** |
372 | * Materialize database tables. |
373 | * @param db |
374 | */ |
375 | public void materializeTables(RDB db) { |
376 | FelixUIMan.println(1, ">>> Storing symbol tables..."); |
377 | FelixUIMan.println(2, "# constants = " + mapConstantID.size()); |
378 | if(FelixConfig.evidDBSchema == null){ |
379 | db.createConstantTable(mapConstantID); |
380 | }else{ |
381 | db.insertConstantTable(newMapConstantID); |
382 | } |
383 | mapConstantID = null; |
384 | try { |
385 | DebugMan.runGC(); |
386 | DebugMan.runGC(); |
387 | DebugMan.runGC(); |
388 | } catch (Exception e) { |
389 | e.printStackTrace(); |
390 | } |
391 | |
392 | for (Type t : nameMapType.values()) { |
393 | if(FelixConfig.evidDBSchema == null){ |
394 | t.storeConstantList(db); |
395 | }else{ |
396 | t.storeConstantList(db,true); |
397 | } |
398 | //db.execute("CREATE INDEX index_type_" + t.name + "_id ON " + t.getRelName() + "(constantid)"); |
399 | //db.execute("CREATE INDEX index_type_" + t.name + "_value ON " + t.getRelName() + "(constantvalue)"); |
400 | //db.analyze(t.getRelName()); |
401 | } |
402 | |
403 | UIMan.println(">>> Storing evidence..."); |
404 | |
405 | for (Predicate p : getAllPred()) { |
406 | if(FelixConfig.evidDBSchema == null){ |
407 | p.flushEvidence(); |
408 | }else{ |
409 | p.flushEvidence(true); |
410 | } |
411 | |
412 | } |
413 | } |
414 | |
415 | /** |
416 | * Execute all Datalog rules. |
417 | * @param db |
418 | */ |
419 | public void executeAllDatalogRules(RDB db) { |
420 | if (datalogRules.isEmpty()) |
421 | return; |
422 | |
423 | long total = 0; |
424 | UIMan.println(">>> Executing Datalog rules..."); |
425 | for (ConjunctiveQuery cq : datalogRules) { |
426 | UIMan.print("."); |
427 | Predicate p = cq.head.getPred(); |
428 | FelixUIMan.println(1, cq.toString()); |
429 | Timer.start("datalogq"); |
430 | //cq.buildIndexes(db, null, null, null, false, new ArrayList<String>()); |
431 | cq.materialize(db, true, new ArrayList<String>()); |
432 | int ni = db.getLastUpdateRowCount(); |
433 | total += ni; |
434 | FelixUIMan.println(1, "### inserted " + UIMan.comma(ni) |
435 | + (ni != 1 ? " new tuples" : " new tuple")); |
436 | FelixUIMan.println(1, "### current cardinality of '" + p.getName() |
437 | + "' = " + UIMan.comma(db.countTuples(p.getRelName()))); |
438 | String tm = Timer.elapsed("datalogq"); |
439 | FelixUIMan.println(1, "### took time " + tm + "\n"); |
440 | db.analyze(p.getRelName()); |
441 | } |
442 | UIMan.println(); |
443 | } |
444 | |
445 | /** |
446 | * Get Class and Tag rules for clustering predicates. |
447 | * @param _predName |
448 | * @return |
449 | */ |
450 | public ArrayList<ConjunctiveQuery> getSpecialClusteringRules(String _predName){ |
451 | if(this.specialClusteringRules.containsKey(_predName)){ |
452 | return this.specialClusteringRules.get(_predName); |
453 | }else{ |
454 | return new ArrayList<ConjunctiveQuery>(); |
455 | } |
456 | } |
457 | |
458 | /** |
459 | * Add Class or Tag rules for clustering predicates. |
460 | * @param cq |
461 | */ |
462 | public void registerClusteringRule(ConjunctiveQuery cq){ |
463 | |
464 | Literal lhead = cq.head; |
465 | String _pname = lhead.getPred().getName(); |
466 | if(!this.specialClusteringRules.containsKey(_pname)){ |
467 | this.specialClusteringRules.put(_pname, new ArrayList<ConjunctiveQuery>()); |
468 | } |
469 | this.specialClusteringRules.get(_pname).add(cq); |
470 | } |
471 | |
472 | } |
473 | |
474 | |
475 | |
476 | |
477 | |