eMoflon / emoflon-ibex

Shared, eMoflon-specific component for incremental unidirectional and bidirectional graph transformations
GNU General Public License v3.0
12 stars 4 forks source link

Democles Demo #104

Closed anthonyanjorin closed 7 years ago

anthonyanjorin commented 7 years ago

Hi @gervarro,

after spending a few hours trying to understand the trainbenchmark (and getting the incr. PM to run for my example based on it) I've basically given up.

I've started a minimal example here: https://github.com/eMoflon/emoflon-ibex/tree/democlesTrial and have added some todos for you.

Basically, all I want to understand for the moment is the initialisation code required to get the incremental pattern matcher up and running with minimal effort. All I need for the moment is to be able to then react to the matches found.

In the project there are trivial source, target and correspondence metamodels under /model. Under /instance there is a trivial input model basically consisting of exactly one source element.

I have also specified a very simple pattern that should match for exactly this source element, i.e., in this example a match should be found and I want to react to it by applying a forward rule (and creating the corresponding target element).

Thanks for your help! Tony

anthonyanjorin commented 7 years ago

FYI @erhanleblebici

gervarro commented 7 years ago

I uploaded a first version of the requested code, although there are some issues that we should clarify regarding the example.

anthonyanjorin commented 7 years ago

Great!

Thanks!

RolandKluge commented 7 years ago

@gervarro Thanks also from my side for providing a minimal working example. Currently, I am evaluating a little the integration of Democles-Interpreter with the Simonstrator (in comparison to the code generated by eMoflon and the dumb Simonstrator pattern matcher). A next step would be to also integrate Democles-Incremental.

anthonyanjorin commented 7 years ago

Hi Greg,

I have now completed all TODOs and got the code running... but the handleEvent method is not being called :(

What could be the problem? I expect a single match for the trivial pattern and model.

Thanks, Tony

anthonyanjorin commented 7 years ago

ok the URI in the pattern might be the problem....

anthonyanjorin commented 7 years ago

ok, I've fixed all URIs now (in model and pattern). Still no events :(

Any ideas @gervarro ?

gervarro commented 7 years ago

I have just updated the demo code (at 22:15). I set a breakpoint in the new code in the handleEvent method, and the match was found. (It was probably not a good idea from me to use 2 resource sets in the original code.)

gervarro commented 7 years ago

@anthonyanjorin @RolandKluge One important additional info if you want to debug / get an overview what is going on in the pattern matcher. The IncrementalUpdateSession class contains 3 methods where you can analyze the overall behaviour.

  1. appendToHistory: Enqueues / collects notifications arriving from the (not necessarily EMF-based) model. No processing takes place yet.
  2. call: Main entry point / loop for the transactional incremental processing (invoked from performIncrementalUpdates()). Feeds notification events into the Rete network. If the auto-commit mode is switched on, this method is automatically invoked when a notification event arrives. This method already collects the match events/deltas (in the transaction) for each pattern, however, this kind of result is not yet returned by the performIncrementalUpdates() method.
  3. handleEvent: This code is called if a match (for any of the patterns) is found.
gervarro commented 7 years ago

Yes, I planned to use only .ecore files for the example (although I expect to be able to register generated code if this is preferred without changing anything). Being flexible in this regard would be great (so the end user should take over adjusting registration as required).

The engine can already handle reflective models without any problems/changes.

anthonyanjorin commented 7 years ago

Thanks, Greg.

My problem now is understanding the match life cycle.

What I want to do is call performIncrementalUpdates(), act on all determined matches in handleEvent, and then call performIncrementalUpdates() again to obtain the next round of events, etc.

The problem I am having now is that I seem to be getting all the old matches I have already handled over and over again :(

Perhaps a related question: what exactly does retePatternMatcherModule.getSession().setAutoCommitMode(true); do and when should I use it instead of false?

anthonyanjorin commented 7 years ago

(seems to have been my error, I have to check this again)

anthonyanjorin commented 7 years ago

IT'S WORKING!!! YEAAAHH 👍

gervarro commented 7 years ago

What I want to do is call performIncrementalUpdates(), act on all determined matches in handleEvent, and then call performIncrementalUpdates() again to obtain the next round of events, etc.

Notification events arriving from the model are blocked/collected on the input side of the Rete network. performIncrementalUpdates() enforces the Rete network to actually process the notifications arriving from the model (since the last invocation of the performIncrementalUpdates() call). The consequent changes in the match set are sent to the attached handleEvent() method. When all the notifications events are processed (i.e., the performIncrementalUpdates() method returns), you can invoke it again to process the model changes that are waiting on the input side (if the handleEvent() performed some changes).

Perhaps a related question: what exactly does retePatternMatcherModule.getSession().setAutoCommitMode(true); do and when should I use it instead of false?

Auto-commit mode: The performIncrementalUpdates() method is automatically invoked after a notification event has been enqueued on the input side. It was basically a performance test from my side, but I do not really see any really practical use cases. The only thing you should avoid is to invoke performIncrementalUpdates() in the handleEvent() method as it most probably results in an infinite recursion in the current engine. I should probably build in some mechanisms to avoid this situation in the future.

anthonyanjorin commented 7 years ago

Another question: I have used insert events up until now. As far as I can see you also have delete events. How about updates? I.e. something changed in the match but it is still valid? Or is that a delete+insert?

gervarro commented 7 years ago

An update is internally a delete + insert (as that is the way how events come from EMF), which cannot be noticed externally (no match events), if the match set does not change (considering only the values assigned to the formal parameters of the pattern). At least this would be the intended behaviour.

anthonyanjorin commented 7 years ago

Hi Greg,

The incremental pattern matcher has version conflicts with the pattern matcher in eMoflon. I committed a target definition file. (Window, Preferences, type target, select Target Definition, check Running platform + Democles, OK)

Where exactly did you commit this target definition file? I can't find it :(

thanks, Tony

gervarro commented 7 years ago

Hi Tony,

Where exactly did you commit this target definition file? I can't find it :(

https://github.com/eMoflon/emoflon-ibex/blob/democlesTrial/org.emoflon.ibex.tgg.tests/DemoclesTrial/SimpleFamiliesToPersons/democles.target

Eclipse should automatically import/find the target file when you open preferences, although I am not sure.

anthonyanjorin commented 7 years ago

Hi Greg,

do you think Democles can handle completely empty pattern bodies? This might sound like a silly question but since we systematically generate a network of patterns, in many cases, some of them are simply empty. E.g., if a rule is an axiom (everything is green) then SRC_CONTEXT is an empty pattern, which is, however, still invoked from other patterns as usual.

At the moment, I am getting:

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: -1
    at org.gervarro.plan.dynprog.Algorithm.generatePlan(Algorithm.java:30)
    at org.gervarro.democles.plan.incremental.leaf.ReteSearchPlanAlgorithmSession.run(ReteSearchPlanAlgorithmSession.java:55)
    at org.gervarro.democles.interpreter.incremental.rete.RetePatternMatcherModule.getSession(RetePatternMatcherModule.java:93)

and am wondering what the problem could be.

Please find my /models folder attached (it contains a generated Democles pattern file): model.zip

FYI @erhanleblebici

anthonyanjorin commented 7 years ago

Hi,

I have now filled such empty patterns with a trivial EQ(0,0) constraint.

The error I am now getting is:

Exception in thread "main" java.lang.NullPointerException
    at org.gervarro.democles.common.runtime.PatternMatcherModule.lookupAdornedConstraintOperations(PatternMatcherModule.java:93)
    at org.gervarro.democles.plan.incremental.leaf.ReteSearchPlanAlgorithmSession.createInitialPlan(ReteSearchPlanAlgorithmSession.java:172)
    at org.gervarro.democles.plan.incremental.leaf.ReteSearchPlanAlgorithmSession.createInitialPlan(ReteSearchPlanAlgorithmSession.java:167)
    at org.gervarro.democles.plan.incremental.leaf.ReteSearchPlanAlgorithmSession.run(ReteSearchPlanAlgorithmSession.java:52)

The NPE is flying here:

throw new RuntimeException("No handler exists for constraint type " + constraintType.toString());
anthonyanjorin commented 7 years ago

ok the problem was because I had trivial pattern invocations. So pattern invocations where I was not passing on any variables. I have removed them now.

anthonyanjorin commented 7 years ago

A problem we have to discuss is how best to handle TGG axioms, i.e., trivial matches that have no context at all (I have the feeling that Democles does not produce any match events at the moment).

anthonyanjorin commented 7 years ago

I'm still having problems with pattern invocations. They seem fine for me but I keep getting "no handler exists"...

After stripping down all patterns to an almost trivial version I'm getting

Exception in thread "main" java.lang.RuntimeException
    at org.gervarro.democles.notification.emf.NotificationModule.issueNotificationsForInsertion(NotificationModule.java:169)
    at org.gervarro.democles.incremental.emf.NotificationCollector.issueNotificationsForInsertion(NotificationCollector.java:159)
    at org.gervarro.democles.incremental.emf.NotificationCollector.add(NotificationCollector.java:141)

and I don't have any sources to debug.

anthonyanjorin commented 7 years ago

@gervarro: I have now created a minimal isolated example (attached as a zip below).
You can open the project in an Eclipse installation with eMoflon (also without if you create the resourceSet directly) and Democles.

democlesTest.zip

The democles pattern file is under DemoclesTest/patterns.xmi and is really very basic.

The main method is in FamiliesToPersonsDeterministicTransformation.java

I'm getting the RuntimeException given above, and unfortunately I can't seem to find the sources to debug the code.

gervarro commented 7 years ago

Hi Tony,

do you think Democles can handle completely empty pattern bodies? This might sound like a silly question but since we systematically generate a network of patterns, in many cases, some of them are simply empty. E.g., if a rule is an axiom (everything is green) then SRC_CONTEXT is an empty pattern, which is, however, still invoked from other patterns as usual.

Before you dig into the details... I have never tested empty pattern bodies. A general question: What would be the expected behaviour? What does the interface of an empty pattern look like? How is an empty pattern "invoked", and what is the expected result? What kind of matches should it produce, if it has no content?

and am wondering what the problem could be.

It is most probably a bug as I have never experimented with empty patterns due to the above-mentioned conceptual issues. I will check your example.

anthonyanjorin commented 7 years ago

ok, thanks! The example is probably something else (RuntimeException with notifications or similar), but I can then send you updates with generated patterns. I think exchanging this simple demo project is the best way to experiment for the moment.

yes, empty patterns are pretty silly if they are directly specified, but when generating things systematically, they do pop up quite often.

I would say that an empty pattern should result in a match (an insert event), the interface is empty, it is invoked by passing nothing, and it's match is also empty.

In this manner we can handle axioms (rules that are always applicable) uniformly.

Could you comment on this @erhanleblebici ?

erhanleblebici commented 7 years ago

Yes, I agree with "empty interface and passing nothing in case of invoking empty patterns" @anthonyanjorin I would expect that an empty pattern matches trivially, and its occurence is fired only once.

I can actually understand that it does not make any sense to define empty bodies. An empty pattern could perhaps have a trivial constraint (that is always satisfied) in the pattern body, and no interface as the trivial constraint should not be related to any (EMF) variable.

gervarro commented 7 years ago

Cannot one suppress empty patterns during the pattern generation when they are so easily identifiable (pattern without interface variables and constraints)?

The runtime problem is probably a 'nice' and well-known proxy issue again.

gervarro commented 7 years ago

Cannot one suppress empty patterns during the pattern generation when they are so easily identifiable (pattern without interface variables and constraints)?

Would it be an acceptable solution if the Democles pattern builder automatically eliminated the empty patterns? Or do you really need matches from empty patterns?

anthonyanjorin commented 7 years ago

Yes, I think it'll be best if we handle empty patterns explicitly ourselves.

We'll have to handle the fact that these rules are applicable, but it's more flexible to do this for each scenario in a suitable way.

anthonyanjorin commented 7 years ago

Do you agree @erhanleblebici? Doesn't seem to make sense to push this to the PM or hack a "trivial" constraint. For the model generator these matches should come again and again (and so we would have some handling any way)...

gervarro commented 7 years ago

Hi Tony,

there were actually two EMF related problems in the code

Hint: it is never a good idea to reorganize my EMF initialization code. ;-)

What is the best way to share the corrected solution with you?

anthonyanjorin commented 7 years ago

@gervarro: Note that the current problem with the example has nothing to do with empty patterns as I had already weeded them out (at least I believe so :))

anthonyanjorin commented 7 years ago

Hmm... interesting. The actual code I'm working on does not load the patterns but generates them directly. And I got the same exception (at least from the same module and line in code).

Could you just export the project as a zip and upload it here? I'll share a checked in project with you next time.

Btw: is the source code for the plugin available? I could have debugged and probably guessed such problems myself.

gervarro commented 7 years ago

Hmm... interesting. The actual code I'm working on does not load the patterns but generates them directly. And I got the same exception (at least from the same module and line in code).

The pattern matcher relies on the package registry located at resourceSet.getEPackageRegistry() to look up metamodel elements. If the metamodel element cannot be found (or collide with another metamodel), then the pattern matcher will be unable to create operations.

Could you just export the project as a zip and upload it here? I'll share a checked in project with you next time.

Yes. Here it is CorrectedDemoclesTest.zip

anthonyanjorin commented 7 years ago

Hi Greg,

I'm making some progress! I am now getting events for flat patterns without pattern invocation.

As soon as I add pattern invocation, however, I get the following error:

Exception in thread "main" java.lang.RuntimeException: No handler exists for constraint type org.gervarro.democles.constraint.PatternInvocationConstraintType@41e36e46

You can find a folder with a folder with the pattern file and a subfolder with all referenced metamodels here: FamiliesToPersonsDeterministic.zip

I hope this is just a misunderstanding from my side of how to construct pattern invocations -- or the patterns are again so simple that the invocation is somehow trivial and not supported (I don't think so though -- seems to make sense to me).

Once I remove the pattern invocations everything works as it should.

Thanks! Tony

gervarro commented 7 years ago

Hi Tony,

I hope this is just a misunderstanding from my side of how to construct pattern invocations -- or the patterns are again so simple that the invocation is somehow trivial and not supported (I don't think so though -- seems to make sense to me).

This is not a misunderstanding. The pattern invocation constructs are OK. This is a bug. I forgot to implement one method of two that should configure positive pattern invocations. I have to check whether it is easy to fix or not.

Bests, Greg

gervarro commented 7 years ago

Hi Tony,

This is not a misunderstanding. The pattern invocation constructs are OK. This is a bug. I forgot to implement one method of two that should configure positive pattern invocations. I have to check whether it is easy to fix or not.

The bug has been fixed. You can download the latest fixed version from the Democles update site. While experimenting for fixing the bug, I noticed that I should call your attention to the extremely strict nature of the EMF notification processing layer, which throws RuntimeException immediately if the resource set is inconsistent. Before installing the notification adapter, it should always be checked whether the models (resources in the resource set) and the metamodels (package registry of the resource set) are completely separated. The notification layer assumes that metamodels only appear in the package registry while models only occur in the resources, however, in our typical usage scenario it is extremely easy to mix them in an improper way.

I experienced the following 2 cases

Summary: if you get a RuntimeException from the notification layer, then double check the content of the ResourceSet and its EPackageRegistry before the notification adapter is installed. Metamodels should only be located in the EPackageRegistry but not in the ResourceSet.

Bests, Greg

anthonyanjorin commented 7 years ago

Hi Greg,

thanks for the fix. Now I can run the code with patterns using pattern invocation. Strangely enough though, I am getting the events for normal matches (in the example ending with _CONTEXT), but not for the pattern (ending with _MODELGEN), which uses pattern invocation (same zip as before).

This is confusing because all three matches are found, and the last match that simply invokes exactly these three matches is not.

Any ideas what could be the matter? Is the pattern invocation wrong or somehow trivial and therefore ignored? Or am I not understanding correctly the matches to be found?

I am not getting any exceptions -- everything seems to be working and I'm getting all other matches as expected, just not the final match that (trivially) combines these three matches.

Thanks, Tony

gervarro commented 7 years ago

Hi Tony,

could you please include a model in the zip file? (If I remember correctly the zip file only contains metamodels and patterns.) I will analyze what can go wrong.

Bests, Greg

anthonyanjorin commented 7 years ago

Hi Greg,

with these models, I get matches for all context patterns, but also expect to get one match for the MODELGEN pattern:

models.zip

Cheers, Tony

anthonyanjorin commented 7 years ago

Hi Greg,

Is there anything else I can provide? Is the problem again on my side (setup, etc), or is it reproducible?

Thanks, Tony

gervarro commented 7 years ago

Hi Tony,

I am sorry for the delay, but I had an extreme busy period last week at work, and I did not have time to have a look at the situation. The busy period is tomorrow hopefully over, and I can work on the pattern matcher again.

Bests, Greg

gervarro commented 7 years ago

Hi Tony,

I forgot to wire pattern invocations at one location when the Rete network is constructed. A quick fix that constructs a correct network for this simple setup is already available on the two update sites, however, I just recognized that an additional (pattern specification) analysis algorithm might be more urgently needed than I originally expected to provide a really fail-safe solution. Until I am ready with this, please try to specify/feed the patterns in a bottom-up order (patterns without pattern invocations first, patterns invoking patterns without dependencies second, etc.)

Bests, Greg

anthonyanjorin commented 7 years ago

Ok thanks - if I understand correctly:

I'll give that a shot!

Cheers, Tony

anthonyanjorin commented 7 years ago

Hi Greg,

Next problem. When applying a rule (I'm now getting all expected matches) and manipulating the models, I'm now getting the following exception:

Exception in thread "main" java.lang.RuntimeException
    at org.gervarro.democles.incremental.emf.NotificationCollector.handleReferenceAddition(NotificationCollector.java:369)
    at org.gervarro.democles.incremental.emf.NotificationCollector.notifyChanged(NotificationCollector.java:214)
    at org.eclipse.emf.common.notify.impl.BasicNotifierImpl.eNotify(BasicNotifierImpl.java:374)
    at org.eclipse.emf.common.notify.impl.NotificationImpl.dispatch(NotificationImpl.java:1027)
    at org.eclipse.emf.common.notify.impl.NotificationImpl.dispatch(NotificationImpl.java:1032)
    at org.eclipse.emf.common.notify.impl.NotifyingListImpl.addUnique(NotifyingListImpl.java:299)
    at org.eclipse.emf.common.util.AbstractEList.add(AbstractEList.java:303)

Unfortunately I don't have the code so I can't check what exactly is causing the exception. The resource set only contains the models being manipulated, and I made sure that performIncrementalUpdates is not invoked while handling an event (the event handling process terminates before we start manipulating the models).

Any ideas? If you need to be able to reproduce the error then I'll have to prepare a jar.

Thanks, Tony

anthonyanjorin commented 7 years ago

Some more info:

The edge (FamilyRegister ---families--->Family) is a containment edge, and is being created via EMF reflection (no generated code). This might be a tricky situation as the contained node remains in the resource (EMF is sort of strange here).

gervarro commented 7 years ago

Hi Tony,

EMF is sort of buggy here (more precisely at line 1303 in the org.eclipse.emf.ecore.impl.BasicEObjectImpl.eBasicSetContainer(InternalEObject newContainer, int newContainerFeatureID, NotificationChain msgs) method. Fred already reported some strange behaviour 5-6 years ago, when you move an object that is directly contained in a resource to another location in the containment hierarchy, which is probably the case here. As you noticed, the contained node remains in the resource, no corresponding notification is sent, and EMF continues with an inconsistent state, and destroys the reliable operation of all incremental engines. This is exactly the reason why the current notification processing layer is as strict as possible to be able to detect cases when EMF is buggy.

I will try to fix it in the notification processing layer.

Bests, Greg

anthonyanjorin commented 7 years ago

Is there some way we can try to avoid this? How should we create new nodes and then add them to a container? In general we cannot avoid what is happening right now but I could try to avoid it as much as possible...

gervarro commented 7 years ago

The problem in the buggy case is that the newly created EObject is already contained by a Resource, and the removal is not handled correctly.

Is there some way we can try to avoid this? How should we create new nodes and then add them to a container?

The best (most reliable) programmatic way is to look up the EPackage in the package registry of the ResourceSet, then to query the EFactory instance and to invoke the create method with the EClass you want to instantiate. At this point, the newly created EObject is hanging in the air, but that is not a problem as you can simply add it to an EObject container by using the eSet method.

EPackage ePackage = rs.getPackageRegistry().getEPackage(packageURL);
EStructuralFeature feature = ((EClass) ePackage.getEClasssifier(className)).getEStructuralFeature(featureName);
EObject newEObject = ePackage.getEFactoryInstance().create(eClass);
if (feature.isMany()) {
    final List<EObject> list = new ArrayList<EObject>();
    list.add(newEObject);
    containerEObject.eSet(feature, list);
} else {
    containerEObject.eSet(feature, newEObject);
}