Conformance
===========

The conformance relation takes a central role in the Modelverse.
In contrast to most other tools, the conformance relation is, as all other things, explicitly modelled.
As such, it can be altered quite easily and offers many degrees of flexibility not found in other tools.

The primary operation to use is *model_types*, which lists the types with which the model is typed::

    >>> model_types("models/my_pn")
    [("metamodels/PetriNets", "type_mappings/1234", None)]

This operation returns a list containing tuples.
In the tuple, the first entry denotes the metamodels location in the Modelverse.
The second entry denotes the type mapping used to conform to this specific metamodel.
The third and last entry denotes the conformance semantics that is used for this conformance, and will be discussed separately.

The type mapping relation is itself a model, typed by the *formalisms/TypeMapping* metamodel.
As such, it is possible to open the model denoted over there and even alter it, thereby directly altering the type mapping in the model.
This is useful in a variety of scenarios, where direct access to the type mapping relation is required.

Note that these conformance relations are the result of the stored links, which might be correct.
Indeed, the local and global constraints might be invalidated later on, or even in the context of language evolution, where for example the metamodel changes, these relations can be out of date.
To verify that the relation still holds, users can invoke the *verify* operation with the model and metamodel::

    >>> verify("models/my_pn", "metamodels/PetriNets")
    "OK"

If the result is the string "OK", everything is fine.
Otherwise, the string value denotes the error message, which contains the name of the element that violates conformance (e.g., which has no type anymore).
While the metamodel has to be specified in the verify operation, the type mapping is automatically determined based on the stored information of the *model_types* operation.

Multi-Conformance
-----------------

As indicated by the fact that the *model_types* operation returns a list, 
The Modelverse supports multiple conformance relations to hold simultaneously.
Upon creation, every model is typed by the metamodel used for its instantiation.
For example, when creating a PetriNets metamodel::

    >>> model_add("formalisms/PetriNets", "models/my_pn")
    >>> model_types("models/my_pn")
    [("metamodels/PetriNets", "type_mappings/1234", None)]

Additionally, different conformance relations can be defined.
For example, if there is a new metamodel called *formalisms/PetriNets_inhibitor*, it is possible to force a conformance relation there as well::

    >>> conformance_add("models/my_pn", "formalisms/PetriNets_inhibitor")
    >>> model_types("models/my_pn")
    [("metamodels/PetriNets", "type_mappings/1234", None),
     ("metamodels/PetriNets_inhibitor", None, None)]

Although the relation has been added, it cannot be guaranteed that this relation actually holds.
Indeed, the type mapping model is undefined, meaning that there is no way to verify this relation in itself.
Therefore, the *conformance_add* operation is only to be used to ensure that the necessary links are created and that it is possible to add a type mapping model or further specify it.

Whenever an operation is being made that opens the model (e.g., instantiation of new elements), a metamodel has to be additionally specified, such that the model can be opened in the correct context.
In the Python wrapper, this is abstracted to alleviate the user of constantly having to specify the metamodel every operations.
When an operation is executed that gives information on the metamodel (e.g., *model_add*, *transformation_execute*), this information is stored and used behind the scenes.
Nonetheless, it is possible that the user wants to switch the context manually.
This can be done with the *alter_context* operation::

    >>> alter_context("models/my_pn", "formalisms/PetriNets_inhibitor")

Note that this operation executes locally, as it only alleviates the user from having to specify this all the time.
As such, there is no error checking on the provided parameters.

When a model is used without prior creation (e.g., reusing a model defined by another user), the user must specify the context in advance, also using the *alter_context* operation.

Sometimes, it might be necessary to remove all context information, for which a dedicated operation is provided::

    >>> reset_context()

Note that for our Python wrapper, the Modelverse will automatically search for all defined metamodels if no context is set.

Tool Semantics
--------------

Another aspect of model conformance, and the third entry in the *model_types* operation, is the tool semantics to use.
The Modelverse considers not only syntactical differences at the M3 level, but also semantical differences.
For example, different tools might convey different semantics for the inheritance relation, such as in the case of multiple inheritance.
In the Modelverse, the conformance semantics is also explicitly modelled, and can therefore be altered.
When checking for conformance, it is possible to define multiple such conformance relations.
And thanks to the use of multi-conformance, it might very well be possible that a model conforms with different tool semantics as well.

New conformance relations can be defined as activities, which take a model, a type mapping, and a metamodel.
An example for (a subset of) AToMPM and MetaDepth conformance semantics is presented in the *examples/* folder of the Modelverse.
In this example, the same model is checked with both AToMPM and MetaDepth conformance semantics, which yields different results.

Although multiple conformance relations can be defined, all built-in Modelverse operations (e.g., *all_instances*) are based solely on the built-in Modelverse conformance relation for performance reasons.

Type Mapping Finding
--------------------

In several cases, the type mapping model can be automatically found by the Modelverse itself.
Indeed, if only a single such type mapping would make sense, it can greatly aid users if this is automatically found.
For now, this is only the case for several simple metamodels, such as *formalisms/Bottom* and *formalisms/TypeMapping*.
When automatic detection is possible, users can indeed just execute an *alter_context* operation, after which the model can be opened immediately, even without a type mapping being specified::

    >>> model_types("type_mappings/20")
    [("formalisms/TypeMapping", None, None)]
    >>> element_list_nice("type_mappings/20")
    [...]
    >>> model_types("type_mappings/20")
    [("formalisms/TypeMapping", "type_mappings/21", None)]

A type mapping will be automatically detected if the given model has an incomplete type mapping to the selected metamodel.
This can be the case if no type mapping is specified at all, or if the current type mapping is pruned for deleted elements in the metamodel (removing an element in the metamodel does not automatically remove the instances in the model), or if new elements are added at the model level without specifying a type.

Conformance Bottom
------------------

The Modelverse makes the Physical Type Model (PTM) available in the linguistic dimension in the form of the Conformance Bottom relation.
For each model, it is possible to open it using its defined metamodels, but also as an instance of the *formalisms/Bottom* model.
This metamodel has no special definition in the Modelverse, but it is relatively simple and does not constrain anything.
As such, it is guaranteed that each model in the Modelverse will conform to it.
Additionally, the model is very simple, meaning that it is possible to automatically deduce the type mapping::

    >>> model_types("models/my_pn")
    [("formalisms/PetriNets", "type_mappings/123", None)]
    >>> element_list_nice("models/my_pn")
    [{"__id": "__1", "__type": "Place", "tokens": 1, "name": "my_place"}]
    >>> alter_context("models/my_pn", "formalisms/Bottom")
    >>> element_list_nice("models/my_pn")
    [{"__id": "__1", "__type": "Node"},
     {"__id": "__234", "__type": "Node"},
     ...]

As the conformance bottom relation can be automatically deduced, this detection can also be triggered when executing activities.
Indeed, when an activity is executed that consumes a model conforming to *formalisms/Bottom*, any possible model can be passed to it, and the conversions will happen automatically and the conformance bottom relation will also be stored explicitly afterwards::

    >>> model_types("models/my_pn")
    [("formalisms/PetriNets", "type_mappings/123", None)]
    >>> transformation_execute_MT("models/count_nodes", {"model": "models/my_pn"}, {})
    >>> model_types("models/my_pn")
    [("formalisms/PetriNets", "type_mappings/123", None),
     ("formalisms/Bottom", "type_mappings/456", None)]

Note that the same is true for other metamodels to which conformance can be automatically deduced, such as the type mapping metamodel.

When changes are made to the model in the context of conformance bottom, no typing information is created for the other metamodels, as indeed there is no notion of typing upon creation.
As such, previously conforming models will no longer conform without also specifying the type of the newly created elements.
In the future, we intend to add completely automated type mapping completion, thereby automatically finding an applicable type for the new elements.
For now, the type mapping model has to be manually extended with typing information for the missing elements.
A model with an incomplete type mapping will not be opened by the Modelverse, as it cannot guarantee that all operations would work!
