eclipse-qvtd / org.eclipse.qvtd

Eclipse Public License 2.0
0 stars 0 forks source link

QVTi needs nested mappings #179

Closed eclipse-qvtd-bot closed 17 hours ago

eclipse-qvtd-bot commented 17 hours ago

| --- | --- | | Bugzilla Link | 493791 | | Status | RESOLVED WORKSFORME | | Importance | P3 normal | | Reported | May 17, 2016 06:50 EDT | | Modified | May 29, 2016 05:43 EDT | | Reporter | Horacio Hoyos |

Description

In the MTC from QVTc to QVTi the normalization step (u to m) flattens the mappings eliminating nested (local) mappings and hence in QVTi they where removed from the CS and AS. I have found a case for which flatenning can not be performed and hence QVTi requires nested mappings.

After debugging Modelmorf's HstmToStm I realized my translation to QVTc was flawed. After going over it, this is the final version of CStateToState:

map CStateToState in HstmToStm {\ hstm (fs1:State, ts1:State) {\ t1:Trans,\ ms1:State,\ ms2:State |\ t1.fromState = fs1;\ t1.toState = ts1;\ }\ enforce stm (ts2:State) {\ realize fs2:State,\ realize t2 : Trans |\ t2.fromState := fs2;\ t2.toState := ts2;\ }\ where (s2s1:HstateToState, s2s2:HstateToState |) {\ realize s2s : HstateToState,\ realize t2t : HtransToTrans |\ ms1 := s2s1.hstate;\ ms2 := s2s2.hstate;\ s2s.hstate := fs1;\ s2s.state := fs2;\ t2t.htrans := t1;\ t2t.trans := t2;\ }\ map {\ hstm(\ ms1 = fs1 or StateContainsState(fs1, ms1);\ ms2 = ts1 or StateContainsState(ts1, ms2);) {}\ where ( )\ {\ s2s.name := fs1.name;\ fs1.name := s2s.name;\ s2s.name := fs2.name;\ fs2.name := s2s.name;\ t2t.name := t1.name;\ t1.name := t2t.name;\ t2t.name := t2.name;\ t2.name := t2t.name;

    }\
}\

}

Notice that ms1 and ms2 are actually local variables that we assign to the trace values of s2s1 and s2s2, hence they must be kept in the bottom pattern of the hstm domain. Since these variables are assigned values from the middle model, their assignments can not be changed to initializers. For these two reason, the predicates in the guard patttern of hstm domain in the nested mapping can not be moved to the guard of the parent hstm domain and the nested mappig must be kept on QVTi.

This would also affect evaluation, as nested mappings predicates should be tested in the check phase (befor the commit phase).

eclipse-qvtd-bot commented 17 hours ago

By Ed Willink on May 17, 2016 07:28

QVTr2QVTc demonstrates that nested mappings can be avoided by using the invoker's trace class as part of the invoked guard, thereby expressing a strong called-from restriction. I think this can always work.

This does not necessarily mean that QVTi may not benefit from nested mappings. QVTi certainly lacks a good "if" construct for use in the mapping statements. This can be worked around by looping over an empty collection selection. This latter arises while attempting to define the missing Collection support for QVTr2QVTc with member and rest variables.

... A revisit of the QVTi 'imperative' control language is appropriate ...

However I cannot follow your bug description since I cannot tell what the "final version of CStateToState". I would expect it to be the qvtu that you claim cannot be translated to qvtm, but it has bidirectional behaviour so it is qvtc.

You need to attach a complete machine readable example or GIT branch.

eclipse-qvtd-bot commented 17 hours ago

By Horacio Hoyos on May 17, 2016 07:54

Created attachment 261785 (attachment deleted)\ THe complete HstmToStm Example

Indeed some transformations can always be written in a different way, and perhaps some QVTr/QVTc "best practices" could be provided to guide users. Non the less, if the transformation is valid (i.e. valid CS) the engine should execute it.

I have attached the complete QVTc transformation, which is my translation from the ModelMorg QVTr example to QVTc. In this paritcualr case I don't think the nested mapping can be expressed by more elaborate guards, as explained in my OP.

eclipse-qvtd-bot commented 17 hours ago

By Ed Willink on May 17, 2016 08:03

(In reply to Horacio Hoyos from comment #2)

Indeed some transformations can always be written in a different way, and perhaps some QVTr/QVTc "best practices" could be provided to guide users. Non the less, if the transformation is valid (i.e. valid CS) the engine should execute it.

Yes but your bug report is on QVTi nested mappings. I suggest that it is only a QVTu2QVTm limitation. I'm inclined to think that there should be no nested mappings in the scheduler; just dependencies. Need mappings might be created by QVTs2QVTi to improve excution performance.\

I have attached the complete QVTc transformation, which is my translation from the ModelMorg QVTr example to QVTc. In this paritcualr case I don't think the nested mapping can be expressed by more elaborate guards, as explained in my OP.

Far from a complete example. It's a qvtcas without any .ecore or invocation procedures - JUnit tests case. You imply a manual transformation. Did you really create a *.qvtcas directly?

eclipse-qvtd-bot commented 17 hours ago

By Horacio Hoyos on May 17, 2016 08:38

(In reply to Ed Willink from comment #3)\

Far from a complete example. It's a qvtcas without any .ecore or invocation procedures - JUnit tests case. You imply a manual transformation. Did you really create a *.qvtcas directly?

No I wrote the qvtc manually. The metamodels are in the modelmorf examples plugin, which you sould have as part of the examples folder:

examples\org.eclipse.qvtd.examples.qvtrelation.modelmorf\qvtrsrc\HstmToStm

I will atach the qvtc source so you don't have references problems in the qvtcas due to metamodel location differences.

Yes but your bug report is on QVTi nested mappings.

My bug report is in QVTi but I supplied the QVTc because the specific characteristics of this transformation requires that QVTi supports nested mappings.

I suggest that it is only a QVTu2QVTm limitation.

It is not a problem of how the nested mappings are flattened, is a case in which I argue the nested mappings can't be flattened. I can highlight main problem in a simplre mapping:

map NeedsNesting in tx {\ domA (z : Z) {\ x : X; \ } ...\ where(y : Y) {\ x := y.a;\ }\ map {\ domA(x = z or z.p = x) {\ } ...\ }\ }

To flatten the mapping, currently we move variable x and the predicate x = z (in the nested mapping) to the guard of domA in the top mapping. Of course this won't work because x is assigned a value in the bottom pattern of the mapping. \ Currently what happens is that the assignment x := y.a is transformned into a predicate. However this is not correct in this case, because the guard has an or condition, hence we are intersted in either x being z (which will be covered by the predicate) or z.p = x, which will always be false if we change the assignment to a predicate.

A counter argument is that the assignment and the predicates could be re-wrtten as two predicatws that respect the intial logic, but I am always against this type of solution as it might be impossible to forsee all possible assignment+predicate scenarios that follow this pattern and how to transform them.

eclipse-qvtd-bot commented 17 hours ago

By Horacio Hoyos on May 17, 2016 08:39

Created attachment 261793 (attachment deleted)\ The qvtc source

eclipse-qvtd-bot commented 17 hours ago

By Ed Willink on May 17, 2016 09:02

(In reply to Horacio Hoyos from comment #4)

The metamodels are in the modelmorf examples plugin, which you sould have as part of the examples folder:

examples\org.eclipse.qvtd.examples.qvtrelation.modelmorf\qvtrsrc\HstmToStm

so where is hstm2stmMM.ecore?

Sorry. I do not have time to go on playing ping-pong to get all the missing information.

Please attach a zipped project that can be imported, with a launch configuration that can be run to demonstrate your observations.

eclipse-qvtd-bot commented 17 hours ago

By Horacio Hoyos on May 17, 2016 11:30

Created attachment 261803 Patch with test case

(In reply to Ed Willink from comment #6)

Please attach a zipped project that can be imported, with a launch configuration that can be run to demonstrate your observations.

Sorry, I sometimes forget we are on different development trees.

I have craeted a patch for org.eclipse.qvtd.build.etl.tests that adds the qvtc source, metamodels and sample, and new method to the test class to invoke the HStmToStm test. Since I am unfamiliar with the latest developments I don't know if anything else is needed. I don't have an expected model atm.

I ran the test and got: Missing OperationCallExp sources were fixed up for 'platform:/resource/org.eclipse.qvtd.build.etl.tests/bin/org/eclipse/qvtd/build/etl/tests/HstmToStm/HstmToStm.qvtcas'

Can't tell if is a result of the problem I discuss.

:notepad_spiral: patch.diff

eclipse-qvtd-bot commented 17 hours ago

By Ed Willink on May 17, 2016 11:46

(In reply to Horacio Hoyos from comment #7)

I ran the test and got: Missing OperationCallExp sources were fixed up for 'platform:/resource/org.eclipse.qvtd.build.etl.tests/bin/org/eclipse/qvtd/ build/etl/tests/HstmToStm/HstmToStm.qvtcas'

This is benign. I decided to eliminate the Function call incompatibility with the inherited Operation call by putting a "this" context variable in the Transformation; just like QVTo. Function calls should have a VariableExp to "this" as their source. The diagnostic warns that the missing source has been auto-repaired. QVTbaseUtil.getContextVariable provides Transformation/Domain context variables.

eclipse-qvtd-bot commented 17 hours ago

By Ed Willink on May 17, 2016 12:47

Failing test case pushed to ewillink/493791

I don't understand why you have put ms1 and ms2 in an input domain if you want them to be used.

Seems to work a bit better if ms1 and ms2 moved to the middle but I get an AS2CS failure on the qvtm.qvtcas so something (else) has gone wrong.

(In reply to Horacio Hoyos from comment #0)

This would also affect evaluation, as nested mappings predicates should be tested in the check phase (befor the commit phase).

No. A nested mapping is executed with respect to the stable truth of the outer mapping, i.e. after the outer mapping has been committed. The inner predicates may act as an 'if' or 'loop' wrt the outer mapping.

eclipse-qvtd-bot commented 17 hours ago

By Horacio Hoyos on May 18, 2016 05:27

(In reply to Ed Willink from comment #9)

Failing test case pushed to ewillink/493791

I don't understand why you have put ms1 and ms2 in an input domain if you want them to be used.

The are in the input domain beacuse their type belongs to the Hstm metamodel.

Seems to work a bit better if ms1 and ms2 moved to the middle but I get an AS2CS failure on the qvtm.qvtcas so something (else) has gone wrong.

Aren't all variables in the middle supposed to be of types in the middle metamodel?

(In reply to Horacio Hoyos from comment #0)

This would also affect evaluation, as nested mappings predicates should be tested in the check phase (befor the commit phase).

No. A nested mapping is executed with respect to the stable truth of the outer mapping, i.e. after the outer mapping has been committed. The inner predicates may act as an 'if' or 'loop' wrt the outer mapping.

If you look at the original QVTr transformation, the or test is in the when condition. Hence, the stm pattern is only enforced when the when condition is true. This means that in the QVTc version I propose, the outter mapping stm domain can only be comitted if the local mapping holds. Perhaps what needs to be done is change the order of the mappings. First check that ms1 and ms2 conditions hold, and the nested mapping will have the realized variables and assignments.

eclipse-qvtd-bot commented 17 hours ago

By Horacio Hoyos on May 18, 2016 05:31

The new version of the CStateToState looks like this:

map CStateToState in HstmToStm {\ hstm (fs1:State, ts1:State) {\ t1:Trans,\ ms1:State,\ ms2:State |\ t1.fromState = fs1;\ t1.toState = ts1;\ }\ -- s2s1 -> ms1\ -- s2s1 -> ms2\ where (s2s1:HstateToState, s2s2:HstateToState |) {\ ms1 := s2s1.hstate;\ ms2 := s2s2.hstate;\ }\ map {\ hstm(\ ms1 = fs1 or StateContainsState(fs1, ms1);\ ms2 = ts1 or StateContainsState(ts1, ms2);) {}\ enforce stm (ts2:State) {\ realize fs2:State,\ realize t2 : Trans |\ t2.fromState := fs2;\ t2.toState := ts2;\ }\ where ( )\ {\ realize s2s : HstateToState,\ realize t2t : HtransToTrans |\ s2s.hstate := fs1;\ s2s.state := fs2;\ t2t.htrans := t1;\ t2t.trans := t2;\ }\ map {\ where () {\ s2s.name := fs1.name;\ fs1.name := s2s.name;\ s2s.name := fs2.name;\ fs2.name := s2s.name;\ t2t.name := t1.name;\ t1.name := t2t.name;\ t2t.name := t2.name;\ t2.name := t2t.name;\ }\ }\ }\ }

eclipse-qvtd-bot commented 17 hours ago

By Ed Willink on May 18, 2016 06:56

I've now studied the original QVTr.

The top relation CStateToState is a 3D search:

for all t1:Trans\ fs1 := t1.fromState\ ts1 := t1.toState\ for all ms1:State | ms1 is a leaf state of fs1\ for all ms2:State | ms2 is a leaf state of ts1 ... create t2

so ms1,ms2 need to be guard variables in the QVTc equivalent so that the QVTc declarative match does the 3D search on 3 guard variables.

The above is perhaps what RelToCore could produce by mindlessly looping on the innermost check domain variable.

Much better code localizing two of the dimensions would be

for all t1:Trans\ fs1 := t1.fromState\ ts1 := t1.toState\ for all ms1:State in fs1->closure(containsState)->select(containsState->isEmpty())\ for all ms2:State in ts1->closure(containsState)->select(containsState->isEmpty()) ... create t2

which could be produced if the original QVTr test was better. The better code is near-linear wrt the output model rather than cubic wrt the input model.

I see no need for nested mappings.

eclipse-qvtd-bot commented 17 hours ago

By Horacio Hoyos on May 18, 2016 09:19

(In reply to Ed Willink from comment #12)\

Much better code localizing two of the dimensions would be

for all t1:Trans fs1 := t1.fromState ts1 := t1.toState for all ms1:State in fs1->closure(containsState)->select(containsState->isEmpty()) for all ms2:State in ts1->closure(containsState)->select(containsState->isEmpty()) ... create t2

Are you proposing that the QVTc to QVTi compiliation should detect that the guards (as expressed in my version) translate to the triple for loop?

I see no need for nested mappings.

Regardless of any improvements that could be done in the original QVTr code and if the compiler can generate QVTi with the proposed loops (probably without going through QVTc), I think that my QVTc version (with the last modification) is a valid transformation and the engine should be able to execute it. And for this, imho, the only solution (without needed to implement the super smart optimization that can detect and provide the triple for loop) is to keep the nested mapping in QVTi.

eclipse-qvtd-bot commented 17 hours ago

By Ed Willink on May 18, 2016 09:44

(In reply to Horacio Hoyos from comment #13)

Are you proposing that the QVTc to QVTi compiliation should detect that the guards (as expressed in my version) translate to the triple for loop?

If the QVTp mapping is triple-headed, then a triple loop will be generated by QVTs2QVTi.

eclipse-qvtd-bot commented 17 hours ago

By Ed Willink on May 19, 2016 15:39

(In reply to Ed Willink from comment #14)

If the QVTp mapping is triple-headed, then a triple loop will be generated by QVTs2QVTi.

This was such a nice simple example that I polished it and added it to the QVTr test suite. After a bit of debugging, it works in the ewillink/master branch.

See testQVTrCompiler_HierarchicalStateMachine2FlatStateMachine_CG\ and testQVTrCompiler_HierarchicalStateMachine2FlatStateMachine\ in org.eclipse.qvtd.xtext.qvtrelation.tests.QVTrCompilerTests.

After running, the auto-generated *.qvtcas etc can be seen in the bin folder.

It does indeed generate a triple loop,

for (org.eclipse.qvtd.xtext.qvtrelation.tests.HierarchicalStateMachine2FlatStateMachine.PHierarchicalStateMachine2FlatStateMachine.@org.eclipse.jdt.annotation.NonNull TLeafState2FlatState loop2 : ValueUtil.typedIterable(TLeafState2FlatState.class, jo_TLeafState2FlatState_1)) {\ for (org.eclipse.qvtd.xtext.qvtrelation.tests.HierarchicalStateMachine2FlatStateMachine.FlatStateMachine.@org.eclipse.jdt.annotation.NonNull State loop0_1 : ValueUtil.typedIterable(org.eclipse.qvtd.xtext.qvtrelation.tests.HierarchicalStateMachine2FlatStateMachine.FlatStateMachine.State.class, jo_State_1)) {\ for (org.eclipse.qvtd.xtext.qvtrelation.tests.HierarchicalStateMachine2FlatStateMachine.HierarchicalStateMachine.@org.eclipse.jdt.annotation.NonNull Transition loop1 : ValueUtil.typedIterable(org.eclipse.qvtd.xtext.qvtrelation.tests.HierarchicalStateMachine2FlatStateMachine.HierarchicalStateMachine.Transition.class, ji_Transition)) {\ MAP_m_THierachicalTransition2FlatTransition_Transition(loop0_1, loop1, loop2);\ }\ }\ }

which demonstrates again the need for the optimization that detects that some 'independent' heads are actually dependent.

eclipse-qvtd-bot commented 17 hours ago

By Ed Willink on May 26, 2016 05:43

(In reply to Ed Willink from comment #15)

it works in the ewillink/master branch.

Now on master with much more sensible names. Who would guess that CStateToState meant HierachicalTransition2FlatTransition?

See GIT\org.eclipse.qvtd\examples\org.eclipse.qvtd.examples.qvtrelation.hstm2fstm\src\org\eclipse\qvtd\examples\qvtrelation\hstm2fstm\HierarchicalStateMachine2FlatStateMachine.qvtr

eclipse-qvtd-bot commented 17 hours ago

By Horacio Hoyos on May 29, 2016 04:53

Hi, what started as a QVTc issue resolved in a QVTr example and solution. My issues about the original QVTc example (as coded by me) still remain.

I would add nested mappings back to QVTi in my fork, but I don't know all the tools well enough to modify the AST metamodel and regenrate all the code. If you think that they will be needed in the future, would you mind adding them now?

If not any guides on how to regenerate all the code if I try to modify the AST metamodel would be welcome.

eclipse-qvtd-bot commented 17 hours ago

By Ed Willink on May 29, 2016 05:43

In principle the "Generate QVTd All" MWE launch regenerates everything though I haven't tried it recently. The finer grained "Generate QVTd AS/CG/CG/..." are used regularly.

In practice you probably just need to merge the QVTc/QVTi Mapping up into AbstractMapping. This will affect the CS so you will need to improve the editors and all the CS2AS and AS2CS code. NB. Use Xtext <= 2.8.4 when regenerating editors. Expect to have a number of excitements since you are modifying contrary to a design that you are not familiar with.

However I think what you are trying to do is very misguided. The aim of the QVT/c/u/m/s/i chain is progressive simplification. Your motivating use case is without foundation.

I am fairly certain that 'nested' mappings will continue to be flattened by QVTu2QVTm. Any nesting that might be synthesized by QVTs2QVTi will be rather different with interesting complexities with respect to correctly maintaining the incremental state of outer / inner mappings. Again I am pretty certain that nested mappings will not appear until incremental execution is working and it is clear how nested mappings enhance rather than confuse the solution.