Hi,

I agree that generic-general purpose RuleEngines can not hold this computation.

The problem is that you have dublication of Facts, which kill memory-space.
For instance, if you execute the Example with 4 Triples under Drools you get:

<0> <subClassOf> <1>
<1> <subClassOf> <2>
<2> <subClassOf> <3>
<3> <subClassOf> <4>
<0> <subClassOf> <2>
<2> <subClassOf> <4>
<1> <subClassOf> <3>
<0> <subClassOf> <3>
<1> <subClassOf> <4>
<0> <subClassOf> <4>
<0> <subClassOf> <3>
<1> <subClassOf> <4>
<0> <subClassOf> <4>
<0> <subClassOf> <4>
<0> <subClassOf> <4>
<0> <subClassOf> <4>


The marked Triples are dublicates. With a growing number of Triples the dublicates grow exponentialy.
Each of the Triple is represented by a TripleObject (see Triple Class in first email). Theoreticly, Drools does a correct Job. In Practice, this behaviour is not always desired.

Suppose the very common usecase, where you like to calculate all Transitiv relations of a Hierarchie, like in a Tree (with countless specific usecases). In most cases, you simply dont need dublicated Facts, because they dont offer any more information.....If you look at the Drools output from before, you Know that 0 is a subClass of 4....you dont need to be told 6 times (!)


One RuleEngine which does solve this problem very efficiently is Jena.
You can execute the example @ appendix. There I create a Class Hierachie, with a specific depth. You can also see the output of the inferencing as a proof (at the end of the class)
Try to set a dept of 1000, it only takes 500 ms (my pc: Intel Celeron 2 Ghz, 512 mb ram)

Jena is a general purpose RuleEngine as well and you can specify rules in a Prolog-like Syntax. Jena does not include dublicated Triples and this is why it is so fast.

>From my point of view, it would be a very very nice if Drools could also support such an optimization in Future releases. It could be implemented as an optional feature, for people who dont need dublicated Facts.


best regards,
Andreas


Peter Van Weert schrieb:
Hi,

I think there's not much you can do here. The complexity of the test you created is extremely exponential, both in time and in space. Here are the results I get from using another rule engine:

(nb Triples in query, nb Triples in result, execution time in msec)

(2, 3, 0)
(5, 39, 0)
(10, 9901, 46)
(11, 33615, 156)
(12, 116115, 578)
(13, 406627, 2380)
(14, ?, ?)

Here the point at which the memory just can't hold all the objects needed comes at 14, the same will happen to Drools at some point. Or maybe with Drools the first problem encountered will be the time complexity. At some point one of them just has to become to bad for a system to be of any practical use, but that's the case with all exponential problems. I doubt any rule engine will hold its own longer then, say, 20 with this test!

Greets,



Andreas Andreakis wrote:
Hallo,

In order to test the Reasoning performance of the rule engine, I wrote a small performance test.
Using a Triple BeanClass, containing Resource-Property-Object attributes all of them as String,  I defined a rule which does the following:

(X, link, Y) and (Y, link, Z) -> (X, link, Z)


Now I create a specific count of Triples (here 2), which are lineary connected:
(Triple1, link, Triple2)
(Triple2, link, Triple3)

the result I get when I fire the rule and read the Objects is as expected:
(Triple1, link, Triple2)
(Triple2, link, Triple3)
(Triple1, link, Triple3) //<---- new Fact, inferenced one


But currently, the performance is very poor.
If I set 8 Triples (lineryy connected one after another) it takes: 484 ms
9 Triples need: 2156 ms
10 Triples need: 20828 ms (!)


see the attachments for the rules file (rule.xml), the Triple Class and the Main class.


Is there a way to tune the process ? I doing something wrong ?

thanx in advance,
Andreas



package transitivity2;

public class Triple {


  private String resource;

  private String property;

  private String object;


  public String getObject() {

    return this.object;
  }


  public void setObject(String object) {

    this.object = object;
  }


  public String getProperty() {

    return this.property;
  }


  public void setProperty(String property) {

    this.property = property;
  }


  public String getResource() {

    return this.resource;
  }


  public void setResource(String resource) {

    this.resource = resource;
  }


  public String toString() {

    return "<" + resource + "> <" + property + "> <" + object + ">";
  }

}
  

<rule-set name="cheese rules" xmlns="http://drools.org/rules" xmlns:java="http://drools.org/semantics/java" xmlns:xs="http://www.w3.org/2001/XMLSchema-instance" xs:schemaLocation="http://drools.org/rules rules.xsd http://drools.org/semantics/java java.xsd"> <import>java.lang.String</import> <import>transitivity2.Triple</import> <rule name="Generic TransLink Rule"> <parameter identifier="X"> <class>transitivity2.Triple</class> </parameter> <parameter identifier="Y"> <class>transitivity2.Triple</class> </parameter> <parameter identifier="linkName"> <class>java.lang.String</class> </parameter> <java:condition>Y.getResource().equals(X.getObject())</java:condition> <java:consequence> Triple newTriple = new Triple(); newTriple.setResource(X.getResource()); newTriple.setProperty(linkName); newTriple.setObject(Y.getObject()); drools.assertObject(newTriple); </java:consequence> </rule> </rule-set>

package transitivity2; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import org.drools.FactHandle; import org.drools.RuleBase; import org.drools.WorkingMemory; import org.drools.io.RuleBaseLoader; public class Main { /** * @param args */ public static void main(String[] args) throws Exception{ RuleBase base = RuleBaseLoader.loadFromInputStream(Triple.class .getResourceAsStream("rules.xml")); WorkingMemory memory = base.newWorkingMemory(); //set here the linkName String linkName = "subClassOf"; //set here the list depth int triplesCount = 9; List<Triple> tripleList = new ArrayList<Triple>(); for (int i = 1; i <=triplesCount; i++) { Triple t = new Triple(); t.setResource(String.valueOf(i-1)); t.setProperty(linkName); t.setObject(String.valueOf(i)); tripleList.add(t); } // hold time long time = System.currentTimeMillis(); //assert all Triples into WorkingMemory for (Triple t : tripleList) { FactHandle handle = memory.assertObject(t); } memory.assertObject(linkName); memory.fireAllRules(); //Display calculation time: System.out.println("Trans-calculation: "+(System.currentTimeMillis()-time)+" ms"); //Display all Triples. Old + new infered (over transitivity) List objects = memory.getObjects(Triple.class); for (Object object : objects) { Triple currentTriple = (Triple)object; System.out.println(currentTriple); } }//end main }//end class


import java.util.ArrayList;
import java.util.List;

import com.hp.hpl.jena.ontology.OntClass;
import com.hp.hpl.jena.ontology.OntModel;
import com.hp.hpl.jena.ontology.OntModelSpec;
import com.hp.hpl.jena.rdf.model.ModelFactory;
import com.hp.hpl.jena.util.iterator.ExtendedIterator;


public class Main2 {


  /**
   * @param args
   */
  public static void main(String[] args) {

    //Create Model WITHOUT Inferencing
    OntModel model = ModelFactory.createOntologyModel(OntModelSpec.RDFS_MEM);
   
    //set the number of linear connected Triples (here a Classes hierachie, 
with subClassOf Properties)
    int depth = 1000;
    
    //create the Classes
    List<OntClass> classList = new ArrayList<OntClass>();
    for(int i=0; i<=depth; i++){
      
      OntClass clazz = model.createClass("Class"+i);
      classList.add(clazz);
    }
    
    //create subClassOf relations. Constructs the hierarchie with the specified 
depth
    Object[] classArr = classList.toArray();
    for(int i=1; i<classArr.length; i++){
      
      OntClass current = (OntClass)classArr[i-1];
      OntClass next = (OntClass)classArr[i];
      
      current.addSuperClass(next);
    }
    
    long time = System.currentTimeMillis();
    
    //Apply RDFS Rules on the model. RDFS Rules define transitivity of the 
subClassOf Property
    //The rdfsModel contains the predefined Facts + inferenced ones
    OntModel rdfsModel = 
ModelFactory.createOntologyModel(OntModelSpec.RDFS_MEM_RDFS_INF, model);
  
    long time2 = (System.currentTimeMillis()-time);
    System.out.println("RDFSModel creation Time: "+time2+" ms\n");    
  
    //Proof - display Results
    //displayConnectionsPerClass(rdfsModel, classList);
    displayAllTriples(rdfsModel, classList);

  }//end main

  
  
  private static void displayAllTriples(OntModel model, List classList){
    
    Object[] classArr2 = classList.toArray();
    for(int i=0; i<classArr2.length; i++){
      
      OntClass current = model.getOntClass("Class"+i);
      ExtendedIterator iterator = current.listSuperClasses();
      
      while (iterator.hasNext()) {
        OntClass element = (OntClass) iterator.next();
        System.out.println(current.getURI()+" subClassOf "+element.getURI());
      }
      
      System.out.println();
    }
  }
  
  private static void displayConnectionsPerClass(OntModel model, List 
classList){
    
    Object[] classArr2 = classList.toArray();
    for(int i=0; i<classArr2.length; i++){
      
      OntClass current = model.getOntClass("Class"+i);
      ExtendedIterator iterator = current.listSuperClasses();
      
      System.out.println(current);
      int count = 0;
      
      while (iterator.hasNext()) {
        iterator.next();
        count++;
      }
      System.out.println(count+"\n");
    }
  }
  
  
}//end class

Reply via email to