Sequenzieller Algorithmus

Anders als der RetePlus-Algorithmus stellt der sequenzielle Ausführungsmodus keine Inferenz bereit. Der sequenzielle Algorithmus arbeitet nur auf eine vorhersehbaren Art und Weise, wenn die Regeln homogen sind, d. h., wenn sie dieselben Bindungen verwenden.

Um dieselbe Ausführung bei beiden Algorithmen zu gewährleisten, müssen die folgenden beiden Bedingungen erfüllt sein:

  • Die Regeln müssen homogen sein. Homogene Regeln bedeuten, dass diese Regeln für dieselben Typen und dieselbe Anzahl von Objekten gesetzt werden müssen. Wenn Sie versuchen, den sequenziellen Algorithmus auf heterogene Regeln anzuwenden, werden dadurch in der Regel mehr Tupelobjekte erzeugt, was dazu führen kann, dass mehr Regeln ausgeführt werden als im RetePlus-Modus. Die allgemeine Tupelsignatur kann auch dazu führen, dass heterogene Regeln nicht ausgelöst werden. Wenn im Arbeitsspeicher keine entsprechenden Instanzen vorhanden sind, können keine Tupel erstellt werden.

  • Die Regeln dürfen nicht verkettet sein, d. h. die Modifikation, die im Aktionsteil einer Regel ausgeführt wird, darf nicht zur Ausführung einer anderen Regel führen.

Wenn Sie den sequenziellen Ausführungsmodus für Regeln angeben, die mit diesem Modus nicht kompatibel sind, gibt die Engine einen Fehler aus.

Die Verwendung des sequenziellen Ausführungsmodus wird in den folgenden Abschnitten ausführlicher beschrieben:

Tupel

Ein Tupel ist eine Liste von Objekten, die einer bestimmten Struktur entspricht. Eine Tupelstruktur ist einfach eine Folge von Klassendeskriptoren. Jede Position in einer Tupelstruktur wird als Slot bezeichnet. Derselbe Klassendeskriptor kann mehrfach an verschiedenen Slots vorkommen.

Nehmen Sie beispielsweise eine Tupelstruktur an:

(Customer,Product)

Im Folgenden sehen Sie ein Tupel, das dieser Struktur entspricht:

(new Customer("Henry"),new DVD("Mickey"))

Beachten Sie, dass die Unterklasse berücksichtigt wird, da ein DVD ein Productist. Es gibt jedoch einige Tupel, die mit der Struktur nicht kompatibel sind:

Das folgende Tupel ist beispielsweise zu groß:


(new Customer("Henry"),new CD("Madona"),new DVD("Mickey"))

Dieses Tupel ist nicht ordnungsgemäß sortiert:


(new DVD("Lord of the rings"),new Customer("Henry"))

Und dieses Tupel ist zu klein:


(new Customer("Henry"))

In Java™wird ein Tupel einfach als Array von Objekten implementiert:


Java Tuple = Object[]

Der Java-Code zum Erstellen eines Tupels und seiner Objekte lautet wie folgt:


new Object[] {new Customer("Henry"),new DVD("Mickey")}

Zur Laufzeit werden die Tupel entweder manuell oder automatisch aus dem Inhalt des Arbeitsspeichers erstellt und dann an die Regelengine übergeben. Die Tupel werden nacheinander an die Tupelabgleichsmethode übergeben.

Tupel werden nur aus Objekten im Arbeitsspeicher erstellt, wenn sie der Tupelstruktur entsprechen: eine allgemeine Signatur, die von allen Regeln in der sequenziellen Aufgabe gemeinsam genutzt wird. Nach der Tupelerzeugung werden alle Regeln für alle Tupel ausgeführt. Aus diesem Grund wird die Verwendung homogener Regel mit dem sequenziellen Algorithmus empfohlen.

Wenn Sie ein eigenes Tupel bereitstellen, muss es immer der Signatur entsprechen, es muss aber nicht unbedingt vollständig sein. Ein partielles Tupel ist ein Tupel mit nicht belegten Slots. Nicht belegte Slots werden einfach auf null gesetzt. Das Tupel (new Customer("Henry")), das eigentlich zu klein ist, könnte als (new Customer("Henry"),null) codiert werden, um der Tupelstruktur zu entsprechen.

Wenn Sie beispielsweise die folgenden beiden Regeln haben, ist die Signatur [Integer, Integer]. Wenn nur ein Integer im Arbeitsspeicher vorhanden ist, wir die Regel "r2" nicht ausgelöst.
rule r1 {
when{
  Integer();
  Integer();
}
then {...


rule r2 {
when{
  Integer();
}
then {...
Wenn Sie beispielsweise die folgenden beiden Regeln haben, ist die Signatur [String, Integer]. Wenn Sie zwei Strings (S1, S2) und ein Integer (I1) im Arbeitsspeicher haben, wird die Regel "r2" einmal für jedes Tupel [S1, I1] [S2, I1] ausgelöst. Das bedeutet, dass r2 zweimal für I1 ausgelöst wird.
rule r1 {
when{
  Integer();
  String();
}
then {...


rule r2 {
when{
  Integer();
}
then {...

Regelauswahl im sequenziellen Modus

Die Regeln einer Regelaufgabe, die mit der Eigenschaft algorithm gleich sequential festgelegt werden, werden dynamisch in Java-Bytecode umgesetzt. Der Bytecode ist in der Lage, die sequenzielle Verarbeitung auszuführen, wenn die Aufgabe ausgeführt wird.

Die Eigenschaft body einer Regelaufgabe ist verbindlich. Sie wählt die Regeln des Regelsatzes aus, die von der Regelaufgabe berücksichtigt werden.

Siehe auch Laufzeitregelauswahl.

Steuerungseigenschaften im sequenziellen Modus

Steuerungseigenschaften wirken sich auf die Ausführung im sequenziellen Modus und auf die Tupelstruktur aus. Weitere allgemeine Informationen finden Sie unter Steuerungseigenschaften für Regelaufgaben in Ausführungsmodi .

Die Anzahl auszuführender Regeln pro Tupel und die Ausführungsreihenfolge können mit den Steuerungseigenschaften ordering, firing und firinglimit festgelegt werden.

Eigenschaft "ordering"

Die Eigenschaft ordering ist für die sequenzielle Verarbeitung verbindlich. Sie beschreibt die Reihenfolge, in der die Regeln der Regelaufgabe ausgeführt werden.

Es sind zwei Werte verfügbar:

  • literal: Die Regeln bleiben in der Reihenfolge, in der sie im Aufgabenhauptteil angegeben werden (mit Aufwärts-/Abwärtspfeilen).

  • sorted: Die Regeln werden entsprechend ihren statischen Prioritäten sortiert.

Es ist ein weiterer Wert, dynamic, verfügbar, der nur für Regelaufgaben gilt, die den Ausführungsmodus "RetePlus" verwenden.

Eigenschaft "firing"

Die Eigenschaft firing gibt an, ob für jedes Tupel alle Regeln oder nur die erste zutreffende Regel ausgeführt werden soll. Die Angabe dieser Eigenschaft ist optional.

Es gibt zwei mögliche Werte:

  • allrules

    Dieser Wert bedeutet, dass alle zutreffenden Regeln ausgeführt werden müssen, bevor mit dem nächsten Tupel fortgefahren wird. Dies ist der Standardwert.

  • rule

    Dieser Wert bedeutet, dass nur die erste zutreffende Regel im ersten Tupel ausgeführt werden muss.

Eigenschaft "firinglimit"

Die Eigenschaft firinglimit legt eine weitere Steuerungsebene fest, wenn die Eigenschaft firing auf allrules gesetzt ist. Mit dieser Eigenschaft wird angegeben, wie viele Regeln maximal ausgeführt werden, bevor mit dem nächsten Tupel fortgefahren wird. Diese Zahl muss größer als null sein.

Bekannte Einschränkungen des sequenziellen Algorithmus

In fast allen Fällen gibt es keine Einschränkung bezüglich der Anzahl der Regeln, die von einer einzelnen Aufgabe verarbeitet werden können, deren Eigenschaft "algorithm" auf "sequential" gesetzt ist. Die einzige Ausnahme bilden extrem große Entscheidungstabellen mit einer Klausel otherwise. Die Verarbeitung ist wie folgt: Alle Bedingungen aller Zeilen werden in einem einzigen Ausdruck verneint. Dieser einzige Ausdruck wird dann nach der Übersetzung in Bytecode in den Hauptteil einer einzelnen Methode übernommen. Dies ist ein Problem, da die Java-Prüffunktion Methoden mit einem zu großen Hauptteil zurückweist. Der für die Regeln verwendete Skalierbarkeitsalgorithmus wird nicht auf den Methodenhauptteil angewendet. Der Benutzer wird über diese Einschränkung benachrichtigt, wenn das Klassenladeprogramm die Regeln zurückweist. Die Einschränkung bezüglich der Regelanzahl ist nicht eindeutig festgelegt, bezieht sich aber wahrscheinlich auf mehrere Tausend Regeln.

In der folgenden Tabelle werden die Einschränkungen der sequenziellen Verarbeitung beschrieben, insbesondere ihre Verwendung in Kombination mit bestimmten IRL-Konstrukten (ILOG ® Rule Language).

Einschränkung Beschreibung
IRL-Einschränkungen (ILOG Rule Language)

Nicht alle Regelmuster, die für den statusabhängigen Rete-Abgleich konzipiert wurden, sind verfügbar. Es treten Kompilierzeitfehler auf, wenn eine Regel nicht mit der aktuellen Tupelabgleichsimplementierung kompatibel ist.

Deshalb unterstützt IRL bei der sequenziellen Verarbeitung die folgenden Features nicht:

  • Dynamische Prioritäten

    Da es bei der sequenziellen Verarbeitung keine Agenda gibt, sind nur die Regeln mit statischen Prioritäten zulässig.

  • not-, exists-, collect-Bedingungen ohne Aufzählungsausdruck

    Die Bedingungen für not, existsund collect mit einem Aufzählungsausdruck (from oder in) werden in Java-Bytecode umgesetzt. Wenn ein Aufzählungsausdruck angegeben wird, ist der Satz von Objekten als Wert verfügbar, der an die Bedingung gebunden ist. (Dies ist keine Einschränkung für RetePlus und Fastpath.)

Bedingungskonstrukte

Die folgenden Bedingungskonstrukte sind bei der sequenziellen Verarbeitung verfügbar:

  • Einfache Bedingungsbindungen und Tests

  • from

  • in

Ausnahmebehandlung

Die Ausnahmebehandlung wird in Regelbedingungsteilen im sequenziellen Modus und im Modus "Fastpath" nicht unterstützt.

Wenn Sie beispielsweise einen Ausnahmehandler in einem Kontext angeben, wird dieser im sequenziellen Modus und im Modus "Fastpath" nicht berücksichtigt:

IlrContext mycontext="....";
IlrExceptionHandler myhandler="....";
mycontext.setExceptionHandler(myhandler);
Refraktion

Die Dateneinheit für einen Enginezyklus ist das Tupel. Die Engine zeichnet die Tupel nicht während des Abgleichs auf, sondern verwirft sie zwischen den Zyklen. Deshalb gibt es keine integrierte Unterstützung für Refraktion.

Im sequenziellen Ausführungsmodus wird aus der vorherigen Regel Folgendes:


ruletask main {
   algorithm = sequential;
   ordering = literal;
   body = {Person,PersonProduct}
}

Berechnete Tupelstruktur:


(Person,Product)

Generierter Abgleichcode:


void matchObjects(Object[] tuple) {
   rule_Person((Person)tuple[0]);
   rule_PersonProduct((Person)tuple[0],(Product)tuple[1]);
}

Der Hauptcode bleibt gleich:

  1. context.insert(new Person("Henry"));

  2. context.insert(new CD("Madona"));

  3. context.insert(new DVD("Mickey"));

  4. context.execute();

Iteratorschleife für den Arbeitsspeicher:

  1. matcher.matchObjects((Person("Henry"),CD("Madona")));

  2. matcher.matchObjects((Person("Henry"),DVD("Mickey")));

Ausführungstrace:

  1. // matcher.matchObjects((Person("Henry"),CD("Madona")));

  2. Person(Henry)

  3. PersonProduct(Henry,Madona)

  4. // matcher.matchObjects((Person("Henry"),DVD("Mickey")));

  5. Person(Henry)

  6. PersonProduct(Henry,Mickey)

Der Ausführungstrace zeigt, dass bei Verwendung des Modus für sequenzielle Verarbeitung dieselbe Regel zweimal für dieselben Teile zweier verschiedener Tupel ausgeführt wird.

IRL-Funktionen (ILOG Rule Language)

Im Gegensatz dazu sind praktisch alle Ausdrücke und Anweisungen auf Scriptebene der IRL für die sequenzielle Verarbeitung verfügbar. Sie dienen hauptsächlich dazu, die Konsistenz der anderen Regelaufgaben, die nicht auf den sequenziellen Algorithmus eingestellt sind, zu gewährleisten.

Im Folgenden sehen Sie eine Liste dieser Features:

Funktionen

Funktionsdefinitionen und -aufrufe sind vollständig verfügbar.

Ausdrücke auf Scriptebene

Alle IRL-Ausdrücke sind verfügbar. (Beachten Sie, dass ?instance in jedem Ausführungsmodus funktioniert.)

Anweisungen auf Scriptebene

Alle IRL-Anweisungen sind verfügbar.

Aktualisieren!

Das Aktualisierungskonstrukt ist verfügbar, da falsche Aktualisierungen in Regelaufgaben vorhanden sein können, die auf den sequenziellen Algorithmus gesetzt sind. Eine unberechtigte Aktualisierung ist eine Aktualisierung, die an einem Objekt vorgenommen wird, das von keinem Bedingungsteil einer Regelaufgabe, die auf den sequenziellen Algorithmus eingestellt ist, zugeordnet werden kann, aber für andere Regelaufgaben, die den RetePlus-Algorithmus verwenden, wichtig ist. Alle Regelaufgaben werden im Regelsatz angegeben und müssen mit der aktuellen Engine konsistent sein. Es ist jedoch wichtig zu bemerken, dass die gewöhnliche Aktualisierung vom statusunabhängigen sequenziellen Verarbeitungsalgorithmus überhaupt nicht verarbeitet wird.

Update refresh

Der Modus "refresh" wird vom sequenzielle Verarbeitungsalgorithmus gar nicht verarbeitet, kann aber für andere Regelaufgaben, die den RetePlus-Algorithmus verwenden, relevant sein.

Einfügen

Das Einfügen von Objekten, die Bedingungsteilen von Regelaufgaben mit sequenziellem Algorithmus entsprechen könnten, kann zu Inkonsistenzen führen, wenn die Tupel der Objekte aus dem Arbeitsspeicher extrahiert werden. Dies ist darauf zurückzuführen, dass die Objekte an der ersten Position eingefügt werden und der Iterator, der die Tupel erstellt, nicht darüber benachrichtigt wird. Der Iterator übergibt die Tupel mit dem neuen Objekt deshalb nicht an die Engine für sequenzielle Verarbeitung. Die Einfügung ist jedoch für andere Regelaufgaben, die den RetePlus-Algorithmus verwenden, relevant.

Die folgenden Beispiele zeigen Fälle, die JIT-Kompilierungsfehler (Just-in-time) verursachen, d. h. Fehler zur Ausführungszeit, keine Parsing-Fehler.

Beispiel 1: IRL-Einschränkung, die zu Kompilierungsfehlern führt

Dieses Beispiel führt zu JIT-Kompilierungsfehlern, weil Aufzählungsausdrücke fehlen.


ruletask T {
  algorithm = sequential;
  body = { R1, R2, R3, R4, R5, R6, R7, R8, R9 }
}

rule R1 {
  when {
    not MyObject();  // No enumerator, an error will occur
  }
  then {
  }
}

rule R2 {
  when {
    exists MyObject();  // No enumerator, an error will occur
  }
  then {
  }
}

rule R3 {
  when {
    collect MyObject() where (size() == 1);  // No enumerator, an error
                                                will occur
  }
  then {
  }
}

function Object getObject() {
  return new MyObject();
}

function Object[] getObjects() {
  Object objs = new Object[1];
  objs[0] = getObject();
  return objs;
}

rule R4 {
  when {
    not MyObject() from getObject();  // An enumerator, no error
  }
  then {
  }
} 

rule R5 {
  when {
    not MyObject() in getObjects();  // An enumerator, no error
  }
  then {
  }
} 

rule R6 {
  when {
    exists MyObject() from getObject();  // An enumerator, no error
  }
  then {
  }
} 

rule R7 {
  when {
    exists MyObject() in getObjects();  // An enumerator, no error
  }
  then {
  }
} 

rule R8 {
  when {
    collect MyObject() from getObject() where (size() == 1); 
    // An enumerator, no error
  }
  then {
  }
} 

rule R9 {
  when {
    collect MyObject() in getObjects() where (size() == 1);  
    // An enumerator, no error
  }
  then {
  }
}

Beispiel 2: IRL mit Konstrukteinschränkungen

Im Folgenden sehen Sie ein Beispiel mit einem Konstrukt mit collect, das den Geltungsbereich der Bindungen demonstriert. Dies gilt für not und exists ebenfalls. Dieses Beispiel führt ebenfalls zu JIT-Kompilierungsfehlern, weil nicht unterstützte Konstrukte verwendet werden.


rule R10 {
  when {
    r:MyRootObject();
    c:collect MyObject(n:name; n.startsWith("ilog")) in r.getObjects(); 
    where (s:size(); s > 1);
  }
  then {
    out.println(r);  // correct
    out.println(c);  // correct
    out.println(s);  // correct
    out.println(n);  // error
  }
}