The cogitant::Support class provides a support representation, as defined in the typed and nested conceptual graph model. More specifically, a support is made of a set of concept types TC, a set of relation types TR, a set of nesting types TE and a set of individual markers I. Sets of types are partially ordered by a "sort of" relation. Finally, the conformity relation, , assigns to every individual marker a concept type and the relation assigns to every relation type its signature, i.e. the type of maximal concept of each of its arguments.
Usually, when developing an application using the library, one starts of with instantiating the cogitant::Environment class. Most of operations are accessible through this class. The cogitant::Environment::support method provides an access to the support associated with an environment. In the remainder of this section we describe the use of main attributes and methods of the cogitant::Support class.
Sets of concept types, relations types, nesting types, and individual markers are defined from the cogitant::Set class and can be accessed through methods cogitant::Support::conceptTypes, cogitant::Support::relationTypes, cogitant::Support::nestingTypes and cogitant::Support::individuals. More specifically, these sets are defined in the form of cogitant::Set of cogitant::ConceptType, cogitant::RelationType, cogitant::NestingType and cogitant::Individual. To each type and marker is then associated a unique identifier (cogitant::iSet) that marks in a unique way each of these entities. In this way, in a conceptual graph, the type of a concept vertex will be stored in the form of an iSet instead of a string or a pointer.
To access an element of these sets, you can obviously use the cogitant::Set::iGetContent method, but methods defined in the support enable to access more easily to sets content. These methods have the same name as the methods for accessing sets (cogitant::Support::conceptTypes(iSet), etc.), take as a parameter an iSet and return the element corresponding to the passed identifier.
Example. Loading of a support and display of relation types (call the output operator). Note the call to the cogitant::Support::relationTypes(iSet) method.
The main methods which can be called on types and markers are defined in cogitant::SupportObject (which is a super class of the 4 classes of types and markers) and concern the access to the label (cogitant::SupportObject::label returns the string that represents the label) and the modification of this label (cogitant::SupportObject::setLabel which takes a string as a parameter and modifies the object label with the passed chain).
To add new types and markers, you should not call the iAdd method of Set, but use cogitant::Support::newConceptType, cogitant::Support::newRelationType, cogitant::Support::newNestingType and cogitant::Support::newIndividual methods. These methods take as a parameter a string representing the label of the object being added. For adding of a relation type, the method takes an additional parameter for setting the signature of the relation type (see below). And for adding individual markers, the method also accepts an additional parameter that enables to set the marker conformity (see below).
Finally, some methods enable to search the identifier of a type or of a marker from its label: cogitant::Support::findConceptType, cogitant::Support::findRelationType, cogitant::Support::findNestingType, cogitant::Support::findIndividual. All these methods take as a parameter a string and return the identifier of the object which has this string as a label. If such an object is not found, these methods return cogitant::ISET_NULL which is a constant of type cogitant::iSet which does not correspond to any valid identifier (as NULL
does not match any valid pointer).
All the support elements (concept types, relation types, nesting types, individual markers) can be provided with translations into different languages. Actually, each support element have, in addition to an independent label, the language (which is returned by cogitant::SupportObject::label and modified by cogitant::SupportObject::setLabel), other labels, each of these labels is associated with a language and corresponds to the object translation in the language in question. These labels are available through variants of label() or setLabel() which take as a parameter an identifier of the language cogitant::Language::Id. Language identifiers are managed by the cogitant::Language::Manager included in the support and accessible through cogitant::Support::languageManager. The language manager enables the link between a language identifier cogitant::Language::Id and a language name (as a string). Language names are used to store translations in file formats, and identifiers are used in memory for efficiency reasons.
Example. Loading of a support and label display in fr
of concept types. See also the support file loaded into this program.
In the conceptual graph model, sets of types are partially ordered by a "sort of" relation. In Cogitant, these partial orders are represented by instances of the cogitant::PartialOrder class. There are three of these objects that are associated with the support and that can be accessed by the cogitant::Support::conceptTypesOrder, cogitant::Support::relationTypesOrder and cogitant::Support::nestingTypesOrder methods. The cogitant::PartialOrder class is actually an abstract class that defines an interface for the partial orders representation. This class is concretized by cogitant::PartialOrder_Simple which is a straightforward implementation of an ordered set and cogitant::PartialOrder_SimpleMemo which provides the same implementation, but to which has been added a memorization in a table. Thus the latter class provides a more efficient implementation of the partial order as comparisons are stored and accessed in constant time, the disadvantage is of course the required memory size which is function of the square of the size of the set, and so which prevents the use of this class on large sets. Whatever the concrete class used, objects are used in the same way, i.e. using the methods defined in cogitant::PartialOrder.
When a type is created (with a call to cogitant::Support::newConceptType or to other comparable methodologies), this type is inserted in the corresponding partial order, and it is incomparable with all other types. For memory loading reasons, only type identifiers are stored in the data structures of the partial order, and all accesses, modifications, deletions are done from type identifiers (i.e. cogitant::iSet which locates occurences of cogitant::ConceptType in the set of concept types, etc.). Thus, to add to the partially ordered set the information an identifier type t1 is smaller than (is a kind of) an identifier type t2, you have to call the cogitant::PartialOrder::setImmediateLess(iSet, iSet) method, by passing it t1 and t2 as parameters. To compare two types, you can use cogitant::PartialOrder::isLessOrEqualThan, cogitant::PartialOrder::isGreaterOrEqualThan, cogitant::PartialOrder::isLessThan, cogitant::PartialOrder::isGreaterThan methods. All these methods take as parameters the two type identifiers and return a boolean. The other way to query a PartialOrder is to use the cogitant::PartialOrder::comparison method, which returns a cogitant::PartialOrder::Comparison, which is an enumerated type whose variables can take 4 values: EQUAL, LESS, GREATER, UNCOMPARABLE.
Example. Construction of a set of concept types composed of three types: Universal, Entity and Person. This set is ordered in the following manner: Person Entity Universal. The set is then queried and the various queries are sent to standard output.
Other methods are available on these classes and enable to browse types that are immediately below or immediately above a given type, to dump the set, to allocate a set size before filling it, dumping it, etc. Refer to the documentation for the cogitant::PartialOrder class.
The cogitant::Support::newIndividual method, to be called for creating a new individual marker, has two parameters: the first is a string corresponding to the label of the individual marker to be created, and the second, optional, is a cogitant::iSet marking the identifier of the concept type associated with the marker by the conformity relation. This parameter is optional, but must be defined in order to match the model. If it is not defined, no error will be detected on the concept vertices which use this marker (of whatever type) during an inspection (of conformity) of conceptual graphs (see cogitant::Environment::verifyConformity).
The cogitant::Support::conformity method queries the support in order to check whether a couple (identifier of concept type, identifier of individual marker) matches the relation of conformity. This method returns true
if and only if the individual marker can be used with the concept type. Obviously, if the conformity is not defined on the marker, this method always returns true
.
Finally, you can modify the concept type associated with a marker after the creation of cogitant::Individual, by calling on the latter the cogitant::Individual::setConformity method which takes a parameter, the identifier of the concept type. Obviously the call to this method can make graphs previously created (and previously correct) invalid because not respecting the conformity.
The cogitant::Support::newRelationType method takes a first parameter which is the label of the relation type to be added to the support and a second optional parameter which is a vector<iSet>
representing the signature of this type, i.e. identifiers of types of maximal concepts of arguments of this relation. If no vector is passed (or NULL
), the relation signature is not defined, and verifying signatures (cogitant::Environment::verifySignature) will not cause errors for this type. In order to match the conceptual graphs model, you have to define a signature for all relation types.
In order to modify the signature of a relation type already defined, you can use the cogitant::RelationType::setSignature method which takes as a parameter a vector<iSet>
and which sets the relation type signature to the concept type identifier vector passed as a parameter. The cogitant::RelationType::signature method returns the signature of the relation type in the form of a vector<iSet>
(and returns a vector
of size 0 if the signature is not defined). Finally the cogitant::RelationType::arity method returns an integer representing the relation type arity (0 if the signature is not defined).
Example. A set of concept types is created in the support of the environment. Two relation types are created: obj, of signature (Action, Entity) and agent, of signature (Action, Person). This set is then queried: the agent arity is displayed, as well as the obj signature (and more precisely the concept type labels in question).
The support optimization functions will only interest users wishing to improve the performance of the execution of an application using a support of a quite big size.
To use the optimization, the simplest way is to call the cogitant::Support::optimizeAuto method before loading a support. This method takes parameters enabling to choose what needs to be optimized. By default, without any parameter, all optimizations are enabled. These optimizations can be classified into two categories.
This optimization relates to the construction of a data structure enabling the search of types (of concepts, relations, nestings, as well as individual markers) by their label in logarithmic time rather than linear time.
The cogitant::Support::findConceptType method (and other findxxx methods) seeks the identifier of the concept type whose label corresponds to the string passed as a parameter. This research is simply done by a traversal of the set. Thus the execution time is linear in the size of the set of concept type. This can lead to quite long time processings when the set of concept types has a big size (over 400,000 for example). Even if one call to cogitant::Support::findConceptType is, in any case, fast, this method is often called, for example during graph loading, as concept type labels are stored in the files containing graphs (and the internal representation of graphs uses cogitant::iSet, conversion being done by cogitant::Support::findConceptType). So, for a graph loading, the method is called as many times as there are concept vertices.
If the method cogitant::Support::optimizeAuto is called before the support loading, the loading operation builds a data structure that enables a search of concept types into a logarithmic runtime in the set size (dichotomy). This data structure is obviously not built directly by the loading operation, but with a call to cogitant::Support::optimizeNowLabel which takes charge of optimizing one or more label sets. This method can also be called directly at the support building by a program (as opposed to the loading a BCGCT or CoGXML file), as soon as a set (of types, of concepts, etc.) has been filled.
It should be noted that these optimizations are producing structures that can be used to effectively search types or markers by their identifier. However, they do not help to search for types or markers by their CoGXML identifier (cogitant::SupportObject::identifier()). To build structures enabling a quick search by their identifier, you should call cogitant::Support::optimizeAutoIdentifiers(). Thus, the cogitant::Support::findConceptTypeIdentifier() methods and others will run faster. The CoGXML loading operation will take charge of calling this method.
This optimization concerns the construction of data structures enabling to more effectively interrogate partial orders of support.
This optimization is effective if the cogitant::PartialOrder_SimpleMemo class is used to represent partial orders, and if the cogitant::PartialOrder::optimize method is called. This class is precisely used by default, and this method is called by support loading operations provided that the correct settings have been passed to cogitant::Support::optimizeAuto. Actually, the cogitant::PartialOrder::optimize method performs no processing (ditto in the cogitant::PartialOrder_Simple class). Only its redefinition in cogitant::PartialOrder_SimpleMemo builds comparison memorization tables. If this method is not called on instances of the latter class, processings made by calls to comparison methods are the same as those of the cogitant::PartialOrder_Simple class. Thus, cogitant::PartialOrder_SimpleMemo can be used in all cases, even if the set is large, but in this case, one must be careful not to call the optimize method, or ensure the availability of enough RAM.
Caution: After optimization, the cogitant::PartialOrder instance should not be modified (such an attempt is detected in the modification methods and causes a thrown of a cogitant::ExceptionReadOnlyObject). The support consisting of three instances of cogitant::PartialOrder, you have to call the optimization method on these three objects. Instead of performing these three calls, it is easier to call the cogitant::Support::optimizeNowOrder method whose first three parameters are booleans which enable to select whether the cogitant::PartialOrder corresponding, respectively, to the concept types, relation types, nesting types must be optimized.