Quick links: Tutorial - Examples - Files - Symbols.
Classes: Hierarchy - Index - List - Members.
Namespaces: Index - base - cs - display.
This page is available in English.

Manipulation du support

La classe cogitant::Support offre une représentation d'un support, tel qu'il est défini dans le modèle des graphes conceptuels emboîtés typés. Plus précisément, un support est formé d'un ensemble de types de concepts TC, un ensemble de types de relations TR, un ensemble de types d'emboîtements TE et un ensemble de marqueurs individuels I. Les ensembles de types sont partiellement ordonnés par une relation "sorte de". Enfin, la relation de conformité, $\tau$, associe à tout marqueur individuel un type de concept et la relation $\sigma$ associe à tout type de relation sa signature, c'est à dire le type de concept maximal de chacun de ses arguments.

Habituellement, lors de l'écriture d'une application utilisant la bibliothèque, on commence par instancier la classe cogitant::Environment. La plupart des opérations sont accessibles à travers cette classe. La méthode cogitant::Environment::support permet d'accéder au support associé à un environnement. Dans la suite de cette partie, nous détaillons l'utilisation des principaux attributs et méthodes de la classe cogitant::Support.

Ensembles de types et de marqueurs individuels

Les ensembles de types de concepts, types de relations, types d'emboîtements, et marqueurs individuels sont définis à partir de la classe cogitant::Set et sont accessibles au travers des méthodes cogitant::Support::conceptTypes, cogitant::Support::relationTypes, cogitant::Support::nestingTypes et cogitant::Support::individuals. Plus précisément, ces ensembles sont définis sous la forme de cogitant::Set de cogitant::ConceptType, cogitant::RelationType, cogitant::NestingType et cogitant::Individual. À chaque type et marqueur est donc associé un identificateur unique (cogitant::iSet) qui repère de façon unique chacune de ces entités. De cette façon, dans un graphe conceptuel, le type d'un sommet concept sera stocké sous la forme d'un iSet plutôt qu'une chaîne de caractère ou un pointeur.

Accès

Pour accéder à un élément de ces ensembles, il est évidemment possible d'utiliser la méthode cogitant::Set::iGetContent, mais des méthodes définies dans le support permettent d'accéder au contenu des ensembles plus facilement. Ces méthodes ont le même nom que les méthodes d'accès aux ensembles (cogitant::Support::conceptTypes(iSet), etc.), prennent comme paramètre un iSet et retournent l'élément correspondant à l'identificateur passé.

Exemple. Chargement d'un support et affichage des types de relations (appel à l'opérateur de sortie). Noter l'appel à la méthode cogitant::Support::relationTypes(iSet).

#include <iostream>
using namespace std;
using namespace cogitant;
int main(int, char* [])
{
e.readSupport("bcgct/bucolic/bucolic.bcs");
Support const * supp = e.support();
for (iSet i = supp->relationTypes()->iBegin(); i != supp->relationTypes()->iEnd(); supp->relationTypes()->iNext(i))
cout << *(supp->relationTypes(i)) << endl;
return 0;
}

Les principales méthodes qui peuvent être appelées sur les types et marqueurs sont définies dans cogitant::SupportObject (qui est une super classe des 4 classes de types et marqueurs) et concernent l'accès à l'intitulé (cogitant::SupportObject::label retourne la chaîne qui représente l'intitulé) et la modification de cet intitulé (cogitant::SupportObject::setLabel qui prend comme paramètre une chaîne et modifie l'intitulé de l'objet avec la chaîne passée).

Ajout de types et marqueurs

Pour ajouter des types et marqueurs, il ne faut pas appeler la méthode iAdd de Set, mais utiliser les méthodes cogitant::Support::newConceptType, cogitant::Support::newRelationType, cogitant::Support::newNestingType et cogitant::Support::newIndividual. Ces méthodes prennent comme paramètre une chaîne de caractères représentant l'intitulé de l'objet à ajouter. Pour l'ajout d'un type de relation, la méthode prend un paramètre supplémentaire permettant de fixer la signature du type de relation (voir plus loin). Et pour l'ajout de marqueurs individuels, la méthode admet elle aussi un paramètre supplémentaire qui permet de fixer la conformité du marqueur (voir plus loin).

Enfin, des méthodes permettent de rechercher l'identificateur d'un type ou d'un marqueur à partir de son intitulé : cogitant::Support::findConceptType, cogitant::Support::findRelationType, cogitant::Support::findNestingType, cogitant::Support::findIndividual. Toutes ces méthodes prennent comme paramètre une chaîne de caractères et retournent l'identificateur de l'objet qui a cette chaîne pour intitulé. Si un tel objet n'est pas trouvé, ces méthodes retournent cogitant::ISET_NULL qui est une constante de type cogitant::iSet qui ne correspond à aucun identificateur valide (semblable à NULL ne correspond à aucun pointeur valide).

Traductions

Tous les éléments du support (types de concepts, types de relations, types d'emboîtements, marqueurs individuels) peuvent être munis de traductions dans différentes langues. En effet, chaque élément du support dispose, en plus d'une étiquette independante de la langue (qui est retournée par cogitant::SupportObject::label et modifiée par cogitant::SupportObject::setLabel), d'autres intitulés, chacun de ces intitulés est associé à une langue et correspond à la traduction de l'objet dans la langue en question. Ces intitulés sont accessibles à travers les variantes de label() ou setLabel() qui prennent comme paramètre un identificateur de langue cogitant::Language::Id. Les identificateurs de langues sont gérés par le cogitant::Language::Manager présent dans le support et accessible par cogitant::Support::languageManager. Le gestionnaire de langues permet de faire le lien entre un identificateur de langue cogitant::Language::Id et un nom de langue (sous la forme d'une chaîne de caractères). Les noms de langues sont utilisés pour stocker les traductions dans les formats de fichiers, et les identificateurs sont utilisés en mémoire pour des raisons d'efficacité.

Exemple. Chargement d'un support et affichage des intitulés en fr des types de concepts. Voir aussi le fichier support chargé dans ce programme.

#include <iostream>
using namespace std;
using namespace cogitant;
int main(int, char* [])
{
e.readSupport("bcgct/bucolic/bucolic.bcs");
Support const * supp = e.support();
for (iSet i = supp->conceptTypes()->iBegin(); i != supp->conceptTypes()->iEnd(); supp->conceptTypes()->iNext(i))
cout << supp->conceptTypes(i)->label(idfr) << endl;
return 0;
}

Ordres partiels sur les types

Dans le modèle des graphes conceptuels, les ensembles de types sont partiellement ordonnés par une relation "sorte de". Dans Cogitant, ces ordres partiels sont représentés par des instances de la classe cogitant::PartialOrder. Il y a trois de ces objets qui sont associés au support et qui sont accessibles par les méthodes cogitant::Support::conceptTypesOrder, cogitant::Support::relationTypesOrder et cogitant::Support::nestingTypesOrder. La classe cogitant::PartialOrder est en fait une classe abstraite qui définit une interface pour la représentation d'ordres partiels. Cette classe est concrétisée par cogitant::PartialOrder_Simple qui est un implantation au plus simple d'un ensemble ordonné et cogitant::PartialOrder_SimpleMemo qui fournit la même implantation, mais à laquelle a été rajoutée une mémorisation dans une table. Cette dernière classe fournit donc une implantation plus efficace de l'ordre partiel car les comparaisons sont stockées et sont accessibles en temps constant, l'inconvénient est bien sûr la taille mémoire nécessaire qui est fonction du carré de la taille de l'ensemble, et qui empêche donc d'utiliser cette classe sur de grands ensembles. Quelle que soit la classe concrète utilisée, les objets s'utilisent toutefois de la même façon, c'est à dire en utilisant les méthodes définies dans cogitant::PartialOrder.

Quand un type est créé (par un appel à cogitant::Support::newConceptType ou autres méthodes comparables), ce type est inséré dans l'ordre partiel correspondant, et il est incomparable avec tous les autres types. Pour des raisons d'occupation mémoire, seuls les identificateurs des types sont stockés dans les structures de données de l'ordre partiel, et tous les accès, modifications, suppressions se font à partir des identificateurs de types (c'est à dire des cogitant::iSet qui repèrent les cogitant::ConceptType dans l'ensemble des types de concepts, etc.). Ainsi, pour rajouter à l'ensemble partiellement ordonné l'information « un type d'identificateur t1 est plus petit que (est une sorte de) un type d'identificateur t2 », il faut appeler la méthode cogitant::PartialOrder::setImmediateLess(iSet,iSet) en lui passant comme paramètres t1 et t2. Pour comparer deux types, il est possible d'utiliser les méthodes cogitant::PartialOrder::isLessOrEqualThan, cogitant::PartialOrder::isGreaterOrEqualThan, cogitant::PartialOrder::isLessThan, cogitant::PartialOrder::isGreaterThan. Toutes ces méthodes prennent comme paramètres les deux identificateurs de types et retournent un booléen. L'autre façon d'interroger un PartialOrder consiste à utiliser la méthode cogitant::PartialOrder::comparison, qui retourne un cogitant::PartialOrder::Comparison, qui est un type énuméré dont les variables peuvent prendre 4 valeurs : EQUAL, LESS, GREATER, UNCOMPARABLE.

Exemple. Construction d'un ensemble de types de concepts formé de trois types : Universal, Entity et Person. Cet ensemble est ordonné de la façon suivante : Person $\leq$ Entity $\leq$ Universal. L'ensemble est ensuite interrogé et les diverses interrogations sont envoyées sur la sortie standard.

#include <iostream>
using namespace std;
using namespace cogitant;
int main(int, char* a[])
{
iSet universal = env.support()->newConceptType("Universal");
iSet entity = env.support()->newConceptType("Entity");
iSet person = env.support()->newConceptType("Person");
PartialOrder * tcorder = env.support()->conceptTypesOrder();
tcorder->setImmediateLess(universal, entity); // Entity est une sorte de Universal
tcorder->setImmediateLess(entity, person); // Person est une sorte de Entity
cout << tcorder->isLessOrEqualThan(person, universal) << endl; // true
cout << tcorder->isLessThan(universal, person) << endl; // false
cout << tcorder->isLessOrEqualThan(universal, universal) << endl; // true
cout << tcorder->isGreaterThan(universal, universal) << endl; // false
cout << tcorder->isGreaterThan(universal, person) << endl; // true
return 0;
}

D'autres méthodes sont disponibles sur ces classes et permettent de parcourir les types qui sont immédiatement inférieurs ou immédiatement supérieurs à un type donné, vider l'ensemble, réserver une taille d'ensemble avant de le remplir, le vider, etc. Se reporter à la documentation de la classe cogitant::PartialOrder.

Conformité

La méthode cogitant::Support::newIndividual, qui doit être appelée pour créer un nouveau marqueur individuel, a deux paramètres : le premier est une chaîne de caractères correspondant à l'intitulé du marqueur individuel à créer, et le deuxième, optionnel, est un cogitant::iSet repérant l'identificateur du type de concept associé au marqueur par la relation de conformité. Ce paramètre est optionnel, mais doit être défini pour respecter le modèle. S'il n'est pas défini, aucune erreur ne sera détectée sur les sommets concepts utilisant ce marqueur (quel que soit leur type) lors d'une vérification (de la conformité) de graphes conceptuels (cf. cogitant::Environment::verifyConformity).

La méthode cogitant::Support::conformity interroge le support pour déterminer si un couple (identificateur de type de concept, identificateur de marqueur individuel) respecte la relation de conformité. Cette méthode retourne true si et seulement si le marqueur individuel peut être utilisé avec le type de concept. Évidemment, si la conformité n'est pas définie sur le marqueur, cette méthode retourne toujours true.

Enfin, il est possible de modifier le type de concept associé à un marqueur après la création du cogitant::Individual, en appelant sur ce dernier la méthode cogitant::Individual::setConformity qui prend un paramètre, l'identificateur du type de concept. Évidemment l'appel à cette méthode peut rendre des graphes précédemment créés (et précédemment corrects) incorrects car ne respectant pas la conformité.

Signatures des relations

La méthode cogitant::Support::newRelationType prend un premier paramètre qui est l'intitulé du type de relation à ajouter au support et un second paramètre optionnel qui est un vector<iSet> représentant la signature de ce type, c'est à dire les identificateurs des types de concepts maximaux des arguments de cette relation. Si aucun vecteur n'est passé (ou NULL), la signature de la relation n'est pas définie, et la vérification des signatures (cogitant::Environment::verifySignature) ne provoquera jamais d'erreur pour ce type. Afin de respecter le modèle des graphes conceptuels, il est nécessaire de définir une signature pour tous les types de relations.

Afin de modifier la signature d'un type de relation déjà défini, il est possible d'utiliser la méthode cogitant::RelationType::setSignature qui prend comme paramètre un vector<iSet> et qui fixe la signature du type de la relation au vecteur d'identificateurs de types de concepts passé en paramètre. La méthode cogitant::RelationType::signature retourne la signature du type de relation sous la forme d'un vector<iSet> (et retourne un vector de taille 0 si la signature n'est pas définie). Enfin la méthode cogitant::RelationType::arity retourne un entier représentant l'arité du type de relation (0 si la signature n'est pas définie).

Exemple. Un ensemble de types de concepts est créé dans le support de l'environnement. Deux types de relations sont créés : obj, de signature (Action, Entity) et agent, qui a pour signature (Action, Person). Cet ensemble est ensuite interrogé : l'arité de agent est affichée, ainsi que la signature de obj (et plus précisément les intitulés des types de concepts en question).

#include <iostream>
#include <vector>
using namespace std;
using namespace cogitant;
int main(int, char* [])
{
// Init of all the concept types
iSet universal = env.support()->newConceptType("Universal");
iSet entity = env.support()->newConceptType("Entity");
iSet person = env.support()->newConceptType("Person");
iSet action = env.support()->newConceptType("Action");
PartialOrder * tcorder = env.support()->conceptTypesOrder();
tcorder->setImmediateLess(universal, entity);
tcorder->setImmediateLess(entity, person);
tcorder->setImmediateLess(universal, action);
// Init of all the relation types
vector<iSet> s;
s.push_back(action); s.push_back(entity);
iSet obj = env.support()->newRelationType("obj", &s);
s[0] = action; s[1] = person;
iSet agent = env.support()->newRelationType("agent", &s);
cout << env.support()->relationTypes(agent)->arity() << endl;
RelationType::Signature signature_obj = env.support()->relationTypes(obj)->signature();
for (RelationType::Signature::const_iterator i=signature_obj.begin(); i!=signature_obj.end(); i++)
cout << env.support()->conceptTypes((*i).front())->label() << " ";
cout << endl;
return 0;
}

Optimisation

Les fonctions d'optimisation du support n'interesseront que les utilisateurs désireux d'améliorer les performances d'exécution d'une application utilisant un support de taille assez grande.

Pour utiliser l'optimisation, le plus simple est d'appeler la méthode cogitant::Support::optimizeAuto avant de charger un support. Cette méthode prend des paramètres permettant de choisir ce qui doit être optimisé. Par défaut, sans aucun paramètre, toutes les optimisations sont activées. Ces optimisations peuvent être rangées en deux catégories.

Recherche d'objets par leur intitulé

Cette optimisation concerne la construction d'une structure de données permettant de rechercher des types (de concepts, de relations, d'emboîtements, ainsi que les marqueurs individuels) par leur intitulé en temps logarithmique plutôt qu'en temps linéaire.
La méthode cogitant::Support::findConceptType (et autres méthodes findxxx) cherche l'identificateur du type de concept dont l'intitulé correspond à la chaîne passée en paramètre. Cette recherche est simplement effectuée par un parcours de l'ensemble. Le temps d'exécution est donc linéaire en la taille de l'ensemble des types de concepts. Ceci peut conduire à des traitements relativement longs dans le cas où l'ensemble des types de concepts est de grande taille (plus de 400.000 par exemple). Même si un appel à cogitant::Support::findConceptType est, dans tous les cas, rapide, cette méthode est très souvent appelée, par exemple pendant le chargement de graphes, car les intitulés des types de concepts sont stockés dans les fichiers contenant les graphes (et la représentation interne des graphes utilise des cogitant::iSet, la conversion se faisant par cogitant::Support::findConceptType). Ainsi, pour le chargement d'un graphe, la méthode est appelée autant de fois qu'il y a de sommets concepts.
Si la méthode cogitant::Support::optimizeAuto a été appelée avant le chargement du support, l'opération de chargement construit une structure de données qui permet une recherche des types de concepts en un temps d'exécution logarithmique en la taille de l'ensemble (dichotomie). Cette structure de données n'est évidemment pas construite directement par l'opération de chargement, mais par un appel à cogitant::Support::optimizeNowLabel qui se charge d'optimiser un ou plusieurs ensemble d'étiquettes. Cette méthode peut d'ailleurs être appelée directement lors de la construction d'un support par programme (par opposition au chargement d'un fichier BCGCT ou CoGXML), dès qu'un ensemble (de types, de concepts, etc.) a été rempli.
Il est à noter que ces optimisations créent des structures qui permettent de rechercher efficacement des types ou marqueurs par leur intitulé. Par contre, elles ne sont d'aucun secours pour rechercher des types ou marqueurs par leur identifiant (cogitant::supportObject::identifier()) CoGXML. Pour contruire des structures permettant une recherche rapide par leur identifiant, il convient d'appeler cogitant::Support::optimizeAutoIdentifiers(). Ainsi, les méthodes cogitant::Support::findConceptTypeIdentifier() et autres s'exécuteront plus rapidement. L'opération de chargement CoGXML se charge d'appeler cette méthode.

Comparaison efficace de types

Cette optimisation concerne la construction de structures de données permettant d'interroger plus efficacement les ordres partiels du support.
Cette optimisation est effective si la classe cogitant::PartialOrder_SimpleMemo est utilisée pour représenter les ordres partiels, et si la méthode cogitant::PartialOrder::optimize est appelée. Cette classe est justement utilisée par défaut, et cette méthode est appelée par les opérations de chargement de support à condition que les bons paramètres aient été passés à cogitant::Support::optimizeAuto. En fait, la méthode cogitant::PartialOrder::optimize n'effectue aucun traitement (idem dans la classe cogitant::PartialOrder_Simple). Seule sa redéfinition dans cogitant::PartialOrder_SimpleMemo construit les tables de mémorisation des comparaisons. Si cette méthode n'est pas appelée sur les instances de cette dernière classe, les traitements effectués lors des appels aux méthodes de comparaison sont les mêmes que ceux de la classe cogitant::PartialOrder_Simple. Ainsi, cogitant::PartialOrder_SimpleMemo peut être utilisée dans tous les cas, même si l'ensemble est de grande taille, mais dans ce cas, il faut prendre garde à ne pas appeler la méthode optimize, ou s'assurer de disposer de suffisament de mémoire vive.
Attention : Après optimisation, l'instance de cogitant::PartialOrder ne doit pas être modifiée (une telle tentative est détectée dans les méthodes de modification et provoque la levée d'une cogitant::ExceptionReadOnlyObject). Le support étant composé de trois instances de cogitant::PartialOrder, il est nécessaire d'appeler la méthode d'optimisation sur ces trois objets. Plutôt que d'effectuer ces trois appels, il est plus simple d'appeler la méthode cogitant::Support::optimizeNowOrder dont les trois premiers paramètres sont des booléens qui permettent de choisir si le cogitant::PartialOrder correspondant respectivement aux types de concepts, types de relations, types d'emboîtements doit être optimisé.