Rule Engine #
Another adaptation approach, which is strongly intertwined with the OperatorAdaptation is the adaptation via the RuleEngine. ProCAKE differentiates between the usage of RuleAdaptation
for the adaptation for generic DataObjects
and RuleOperatorAdaptation
, which applies the conventional learning process of Adaptation Operators (as described here) in conjunction with a rule-based representation of adaptation knowledge for workflows. In the following, the basic concepts and usage in combination with the AdaptationManager
as well as standalone are shown.
Basic Concept #
Rules are store in so-called rulebases persisted as *.drl
files. In alignment with classic adaptation rules, every rule follows the same pattern: A precondition containing all the conditions that must hold in order for the rule to fire, and a conclusion which specifies the actions taken as a consequence. In the context of ProCAKE this means that the precondition of a rule can contain an arbitrary amount of domain-specific predicates defined by the user. These predicates can directly address attributes of the case object as well as the query object. Consequently, the execution of a rule leads to a manipulation of the object (or a copy of it) that was previously obtained by retrieval. In its current state, the rule engine makes use of a local search algorithm, i.e., Hillclimbing, in order to identify the rule whose application would lead to the greatest similarity improvement towards the query. For this purpose, there are two separate rule bases for a domain: AsimulationBase.drl
and an appplicationBase.drl
. The rules in those two rule bases only differ in terms of meta-predicates that are used to control the search process. The skeleton of both a simulation rule and an application rule are shown below.
rule "Exchange PAINT_COLOR yellow by silver (Simulation)"
activation-group "Exchange COLOR yellow by silver (Simulation)"
when
$queryCar: AggregateObject(id == "query") **
$caseCar: AggregateObject(id == "case") **
$ri: DroolsCarInterface( ** ** = Meta-predicates
bindObjectState($caseCar, true), **
hasColor("silver", $queryCar), ##
hasColor("yellow", $caseCar) ## ## = Domain-predicates
)
then
$ri.exchangeColorBy("silver", $caseCar); ##
$ri.evaluateSimulation(drools.getRule().getName()); **
end
Application-version of the same rule:
rule "Exchange PAINT_COLOR yellow by silver (Application)"
activation-group "application"
when
HillClimbingObject(getBestRuleId() == "Exchange COLOR yellow by silver (Application)") **
$queryCar: AggregateObject(id == "query") **
$caseCar: AggregateObject(id == "case") **
$ri: DroolsCarInterface( ** ** = Meta-predicates
bindObjectState($caseCar, false), **
hasColor("silver", $queryCar), ##
hasColor("yellow", $caseCar) ## ## = Domain-predicates
)
then
$ri.exchangeColorBy("silver", $caseCar); ##
$ri.concludeIteration(); **
end
In analogy to the principles of the Operator Adaptation, the applicable rules of the simulation base are applied to a copy of the retrieved object in order to determine the rule that improves the similarity the most. Once the ID of this rule has been determined, the corresponding rule of the application base is applied to the real case object. This process repeats until no further rule is applicable and/or no further improvement in similarity can be achieved.
For simpler application purposes that do not require the usage of a local search, RuleAdaptation
also provides the possibility to execute a single rule base by calling its method applyRuleBase(String pathRuleBase)
(more on this to follow). Note that this behavior is currently only supported in standalone mode.
RuleAdaptation #
RuleAdaptation
is used for the adaptation of generic DataObjects
, e.g., AggregateObjects
and can be executed via the AdaptationManager
or in standalone mode. Regardless of the selected mode, you need to provide an own implementation of a rule interface extending de.uni_trier.wi2.procake.adaptation.rules.impl.DroolsManualInterfaceImpl
. This rule interface represents the mediator between the RuleEngine and ProCAKE by defining domain-specific predicates used within the rules themselves. A simple predicate in the car domain which was also used by the rules above could look like this:
public boolean hasColor(String color, AggregateObject car) {
StringObject colorObject = (StringObject) car.getAttributeValue("paint_color");
return colorObject.getNativeString().equals(color);
}
Note that predicates used within the precondition of the rule need to return a primitive boolean
in order for the RuleEngine to evaluate them correctly.
A corresponding action used within the conclusion of the rule could look like this:
public void exchangeColorBy(String newColor, AggregateObject car) {
StringObject colorObject = ModelFactory.getDefaultModel().createObject("String");
colorObject.setNativeString(newColor);
((AggregateObject) getObject()).setAttributeValue("paint_color", colorObject);
}
Note: When manipulating the case objects in the conclusion of the rule (e.g., exchangeColor
) please make sure to use the helper method getObject()
from DroolsManualInterfaceImpl
as it needs to be distinguished if those changes are applied in the simulation phase or application phase. After defining the required set of predicates in the custom rule interface and importing this interface in your rule base, the method signatures can be used within the precondition (when
) and conclusion (then
) of the rule. A full implementation of a rule interface for the car domain is located at de.uni_trier.wi2.procake_demos.cars.adaptation.rules.DroolsCarInterface
.
Usage with the AdaptationManager #
When using the AdaptationManager
to perform RuleAdaptation
, simply create a custom AlgorithmConfiguration
in the XML configuration file.RuleAdaptation
expects the following three parameters to be set:
Parameter (Code, XML) | Type/Range | Default Value | Description |
---|---|---|---|
PATH_SIMULATION_BASE, pathSimulationBase | String | null | Path to the rule base used during the simulation phase. |
PATH_APPLICATION_BASE, pathApplicationBase | String | null | Path to the rule base used during the application phase. |
MANUAL_INTERFACE_IMPLEMENTATION, manualInterfaceImplementation | String | null | Custom implementation class of the domain-specific implementation of DroolsManualInferfaceImpl . |
A sample configuration can be found at de.uni_trier.wi2.procake_demos.domains.cars.adaptation.CarsRuleAdaptationTestConfig.xml
. Once configured, the adaptation process can be initiated as follows:
AdaptationManager<AggregateObject, AggregateObject> adaptationManager = new AdaptationManagerImpl<>(
pool);
AdaptationConfiguration adaptationConfiguration = IOUtil.readFile(
"/de/uni_trier/wi2/procake_demos/wiki/adaptation/cars/CarsRuleAdaptationTestConfig.xml",
AdaptationConfigReader.READER_NAME);
adaptationManager.init(adaptationConfiguration, null);
AdaptationSession<AggregateObject, AggregateObject> adaptationSession =
adaptationManager.execute(pool.getObject("C07"), pool.getObject("C20"));
System.out.println("Query:\t " + adaptationSession.getAdaptationQuery().toDetailedString());
System.out.println("Case:\t " + adaptationSession.getOriginCase().toDetailedString());
System.out.println("Adapted: " + adaptationSession.getAdaptedCase().toDetailedString());
In this case no retrieval is performed and adaptation is directly initiated with the case object C07
and the query object C20
. The demo class containing this code is located at de.uni_trier.wi2.procake_demos.cars.adaptation.rules.CarsRuleDemoAdaptationManager
.
Usage in Standalone mode #
It is also possible to use the RuleEngine without its integration into the AdaptationManager
. To choose this form of operation, RuleAdaptation
has to be instantiated directly. Due to the lack of XML configuration, the required parameters listed in the table above have to be set programmatically. Additionally, the method executeObjectAdaptation(orderedCategories)
accepts a sorted list of object attributes. If provided, the RuleEngine executes rules adapting the given attribute in the listed order, meaning that in this case rules adapting the attribute year
will be considered before rules adaptingfuel
. Attributes that are not contained in the list will not be adapted at all. If no such list is passed to the method, rules will be chosen according to their most significant similarity improvement towards the query (Hillclimbing).
AggregateObject caseCar = pool.getObject("C07");
AggregateObject queryCar = pool.getObject("C20");
DroolsCarInterface manualInterface = new DroolsCarInterface();
RuleAdaptation adaptation = new RuleAdaptation(caseCar, queryCar, manualInterface);
adaptation.init(
"de/uni_trier/wi2/procake_demos/wiki/adaptation/cars/ruleBaseSimulation.drl",
"de/uni_trier/wi2/procake_demos/wiki/adaptation/cars/ruleBaseApplication.drl");
List<String> orderedCategories = Arrays.asList(
"year", "fuel", "price"
);
DroolsAdaptationResult adaptationResult = adaptation.executeObjectAdaptation(orderedCategories);
System.out.println(adaptationResult);
System.out.println("Query:\t " + queryCar.toDetailedString());
System.out.println("Case:\t " + caseCar.toDetailedString());
System.out.println("Adapted: " + adaptationResult.getAdaptedObject().toDetailedString());
The demo class containing this code is located at de.uni_trier.wi2.procake_demos.cars.adaptation.rules.CarsRuleDemoStandalone
.
As mentioned previously running the RuleEngine in standalone mode also allows for the execution of a single rule base without applying a local search algorithm. To do this, simply call the method applyRuleBase()
instead of initiating the rule bases and executing executeObjectAdaptation()
as shown in the snippet below:
AggregateObject caseCar = pool.getObject("C07");
AggregateObject queryCar = pool.getObject("C20");
DroolsCarInterface manualInterface = new DroolsCarInterface();
RuleAdaptation adaptation = new RuleAdaptation(caseCar, queryCar, manualInterface);
DataObject adaptedCar = adaptation.applyRuleBase(
"de/uni_trier/wi2/procake_demos/wiki/adaptation/cars/singleRuleBase.drl");
The adapted object will be returned directly. Note that since there is no control mechanism over the rule execution process, all applicable rules present in the rule base will be applied to the provided case object. Hence, rules no longer need any meta predicates directing the search. As a consequence, rules can be as simple as:
rule "Paint case car grey"
when
$queryCar: AggregateObject(id == "query")
$caseCar: AggregateObject(id == "case")
$ri: DroolsCarInterface(
bindObjectState($queryCar, $caseCar),
hasColor("grey", $queryCar)
)
then
$ri.exchangeColorBy("grey", $caseCar);
System.out.println("Painted case car grey!");
end
RuleOperatorAdaptation #
RuleOperatorAdaptation
is used for the adaptation of NESTWorkflowObjects
and can be executed via the AdaptationManager
or in standalone mode. As the name suggests, it applies the principles of OperatorAdaptation
at heart, generating rules based on the learned operators. Consequently, rules are a different form of representing those operators in order to make them accessible for the RuleEngine. Just like with RuleAdaptation
you need to provide an own implementation of a rule interface extending de.uni_trier.wi2.procake.adaptation.rules.operator.impl.DroolsOperatorInterface
. This extension needs to provide methods that differ depending on the application domain, namely insertDataNode
, insertTaskNode
, insertDataflowEdge
, insertControlflowEdge
, exchange
, and cleanUp
. If you are unfamiliar with these concepts stemming from the operator adaptation, please refer to the corresponding paper available here. A sample implementation for the cooking domain can be found at de.uni_trier.wi2.procake_demos.recipes.workflows.adaptation.rules.DroolsCookingOperatorInterface
. If you wish to add custom domain preconditions that go beyond those defined in DroolsOperatorInterface
, they would need to be added to your custom implementation as well.
In addition to the rule interface, a custom utility class defining certain aspects about NEST graph handling in your domain needs to be provided. The generic utility class de.uni_trier.wi2.procake.adaptation.rules.impl.DomainUtils
contains methods for handing graph fragments that are independent of the application domain. Beyond these existing implementations, however, you need to implement the following methods, as they depend on the semantic description used within your domain: getNodeName
, getNodeDescription
, compareNodes
. A sample implementation for the cooking domain can be found at de.uni_trier.wi2.procake_demos.recipes.workflows.adaptation.rules.CookingDomainUtils
.
Usage with the AdaptationManager #
When using the AdaptationManager
to perform RuleOperatorAdaptation
, simply create a custom AlgorithmConfiguration
in the XML configuration file. The parameters required by RuleOperatorAdaptation
overlap with those required by OperatorAdaptatation
:
The following parameters can be set:
Parameter (Code, XML) | Type/Range | Default Value | Description |
---|---|---|---|
PATH_SIMULATION_BASE, pathSimulationBase | String | null | Path to the rule base used during the simulation phase. |
PATH_APPLICATION_BASE, pathApplicationBase | String | null | Path to the rule base used during the application phase. |
EXPERT_MODE, expertMode | Flag (boolean) | false | Sets whether conditional actions such as deleteEdgeIfPresent should be exposed in the conclusion. (currently not supported) |
TRANSFORMATION_MODE, transformationMode | TransformationModes | SIMILAR_STREAMLET | Sets the transformation mode used for identifying the deletion streamlets and generating the deletion/exchange rules for adaptation.SIMILAR_STREAMLET requires the presence of a sufficiently similar deletion streamlet in the graph while EXACT_STREAMLET requires the existence of an exact match. |
SIM_THRESHOLD_STREAMLETS, simThresholdStreamlets | Double | 0.5 | Sets the minimum similarity between the deletion streamlet and the current streamlet in the workflow. |
SIM_THRESHOLD_ANCHOR, simThresholdAnchor | Double | 0.5 | Sets the min similarity between the anchor task of the insertion streamlet in the workflow streamlet in order to be a valid anchor task. |
OPERATOR_ADAPTATION_IMPLEMENTATION, operatorAdaptationImplementation | String | null | Domain implementation of OperatorAdaptationImpl that is used for learning the operators from a casebase. |
OPERATOR_INTERFACE_IMPLEMENTATION, operatorInterfaceImplementation | String | null | Domain implementation of DroolsOperatorInterface that is used for providing custom predicates and other utility methods within rules. |
DOMAIN_UTILS_IMPLEMENTATION, domainUtilsImplementation | String | null | Domain implementation of DomainUtils providing custom utility methods for handling NEST graphs. |
DATA_NODE_DATA_CLASS, dataNodeDataClass | String | null | Data node class of the data items. |
TASK_NODE_DATA_CLASS, taskNodeDataClass | String | null | Task node class of the tasks. |
MIN_HEAD_DATA_SIM, minHeadDataSim | Double | 0.5 | Sets the minimum similarity between the data node in the workflow and the head data node in the streamlet. |
MIN_HEAD_PRODUCER_TASK_SIM, minHeadProducerTaskSim | Double | 0.5 | Sets the minimum similarity between the data node in the workflow and the head data node in the streamlet. |
MIN_WFPAIR_SIM, minWFPairSim | Double | 0.0 | Sets the min similarity between workflows that must be achieved in order to consider workflows as similar enough to learn adaptation operators based on the difference of these two workflows. |
A sample configuration can be found at de.uni_trier.wi2.procake_demos.domains.recipes.workflows.adaptation.RecipeRuleAdaptationTestConfig.xml
. Once configured, the adaptation process can be initiated as follows:
SimilarityModelFactory.getDefaultSimilarityModel()
.addSimilarityMeasure(new SMNESTQueryImpl(new CookingWorkflowSimilarityUtils()),
SMNESTQuery.NAME);
AdaptationManager<NESTWorkflowObject, NESTQuery> adaptationManager = new AdaptationManagerImpl<>(
pool);
AdaptationConfiguration adaptationConfiguration = IOUtil.readFile(
"/de/uni_trier/wi2/procake_demos/wiki/adaptation/recipes/RecipeRuleAdaptationTestConfig.xml",
AdaptationConfigReader.READER_NAME);
adaptationManager.init(adaptationConfiguration, null);
NESTWorkflowObject emptyWorkflow = new NESTWorkflowBuilderImpl<NESTWorkflowObject>().createNESTWorkflowGraphObject(
"Empty_Workflow", "CustomWorkflow", null);
NESTQuery query = new NESTQueryImpl();
query.setQueryGraph(pool.getObject("W01"));
query.setRestrictionGraph(emptyWorkflow);
AdaptationSession<NESTWorkflowObject, NESTQuery> adaptationSession =
adaptationManager.execute(pool.getObject("W02"), query);
The demo class containing this code is located at de.uni_trier.wi2.procake_demos.recipesworkflows.adaptation.RecipeRuleDemoAdaptationManager
.
Usage in Standalone mode #
It is also possible to use the operator-based rule adaptation without its integration into the AdaptationManager
. To use this mode of operation, a few more extra steps are required as the configuration process has to be done manually:
- Since
RuleOperatorAdaptation
uses regularAdaptationOperators
as a base for rule extraction, you need to provide those either directly in-memory or as serialized adaptation knowledge. If provided directly in-memory you can skip this step. Otherwise, RuleEngine provides a parser (de.uni_trier.wi2.procake_demos.recipes.workflows.adaptation.operator.xml.AdaptationOperatorParser.xml
) to read the operators. This can be done by calling:
AdaptationOperatorParser operatorParser = new AdaptationOperatorParser();
operatorParser.init(
"/de/uni_trier/wi2/procake_demos/wiki/adaptation/recipes/operatorRepository.xml");
List<AdaptationOperator> operators = operatorParser.parse();
- Additionally, your own implementations of
DomainUtils
andDroolsOperatorInterface
need to be instantiated and set up with the correspondingDataClass
of the data and task nodes in your domain. For the cooking domain this could look like the following:
CookingDomainUtils cookingDomainUtils = new CookingDomainUtils();
DroolsOperatorInterface cookingOperatorInterface = new DroolsCookingOperatorInterface(
cookingDomainUtils);
cookingOperatorInterface.setDataNodeDataClass("ingredientType");
cookingOperatorInterface.setTaskNodeDataClass("activityType");
- As a next step, the
RuleBaseGenerator
has to be set up. It expects the list of operatos and your custom implementation ofDomainUtils
as arguments. Additionally, you need to configure it by providing paths where thesimulationBase
andapplicationBase
should be written to. Note: You can add as many output paths as you like, however, you should always also write them to your projectstarget
in order for the RuleEngine being able to load them without any issues.
RuleBaseGenerator repository = new RuleBaseGenerator(operators, cookingDomainUtils);
repository.addCustomOutputPathSimulation(
"target/classes/de/uni_trier/wi2/procake_demos/wiki/adaptation/recipes/ruleBaseSimulation.drl");
repository.addCustomOutputPathApplication(
"target/classes/de/uni_trier/wi2/procake_demos/wiki/adaptation/recipes/ruleBaseApplication.drl");
repository.extractRuleBases();
- Finally,
RuleOperatorAdaptation
can be instantiated and executed. The result of the adaptation is wrapped in the classDroolsAdaptationOperatorResultImpl
providing an owntoString()
implementation for displaying the generated chain of rules in the console.
RuleOperatorAdaptation adaptation = new RuleOperatorAdaptation(retrievedGraph, queryGraph,
repository, false, cookingOperatorInterface);
adaptation.init(
"de/uni_trier/wi2/procake_demos/wiki/adaptation/recipes/ruleBaseSimulation.drl",
"de/uni_trier/wi2/procake_demos/wiki/adaptation/recipes/ruleBaseApplication.drl");
DroolsOperatorAdaptationResultImpl adaptationResult = adaptation.executeWorkflowAdaptation();
System.out.println(adaptationResult);
The entire demo implementation can be found at de.uni_trier.wi2.procake_demos.recipes.workflows.adaptation.RecipeRuleDemoStandalone
.