/****************************************************************************
 *   This file is part of the aGrUM/pyAgrum library.                        *
 *                                                                          *
 *   Copyright (c) 2005-2025 by                                             *
 *       - Pierre-Henri WUILLEMIN(_at_LIP6)                                 *
 *       - Christophe GONZALES(_at_AMU)                                     *
 *                                                                          *
 *   The aGrUM/pyAgrum library is free software; you can redistribute it    *
 *   and/or modify it under the terms of either :                           *
 *                                                                          *
 *    - the GNU Lesser General Public License as published by               *
 *      the Free Software Foundation, either version 3 of the License,      *
 *      or (at your option) any later version,                              *
 *    - the MIT license (MIT),                                              *
 *    - or both in dual license, as here.                                   *
 *                                                                          *
 *   (see https://agrum.gitlab.io/articles/dual-licenses-lgplv3mit.html)    *
 *                                                                          *
 *   This aGrUM/pyAgrum library is distributed in the hope that it will be  *
 *   useful, but WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,          *
 *   INCLUDING BUT NOT LIMITED TO THE WARRANTIES MERCHANTABILITY or FITNESS *
 *   FOR A PARTICULAR PURPOSE  AND NONINFRINGEMENT. IN NO EVENT SHALL THE   *
 *   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
 *   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,        *
 *   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR  *
 *   OTHER DEALINGS IN THE SOFTWARE.                                        *
 *                                                                          *
 *   See LICENCES for more details.                                         *
 *                                                                          *
 *   SPDX-FileCopyrightText: Copyright 2005-2025                            *
 *       - Pierre-Henri WUILLEMIN(_at_LIP6)                                 *
 *       - Christophe GONZALES(_at_AMU)                                     *
 *   SPDX-License-Identifier: LGPL-3.0-or-later OR MIT                      *
 *                                                                          *
 *   Contact  : info_at_agrum_dot_org                                       *
 *   homepage : http://agrum.gitlab.io                                      *
 *   gitlab   : https://gitlab.com/agrumery/agrum                           *
 *                                                                          *
 ****************************************************************************/


/*!
 * \page using_prm How to use Probabilistic Relational Models
 *
 * # Type inheritance #
 *
 * Type inheritance is not part of the classical PRM formalism, thus we present
 * here insights on how it works and how aGrUM manages such concepts.
 *
 * Creating subtypes is not a difficult task and the constructor's
 * gum::prm::Type::Type(const Type&, const std::vector\<Idx\>&, const
 * DiscreteVariable&) documentation should give you enough information.
 *
 * Things become more tricky when adding an Attribute (or a ClassElement
 * representing a random variable) which Type is a subtype. In this case it is
 * necessary to add what we call "casting descendants", i.e. deterministic
 * nodes which can be used to cast the random variable in the desired super
 * type.
 *
 * Suppose we have 3 Type t_1, t_2 and t_3 with t_2 being a descendant of t_1
 * and t_3 of t_2. If we add an Attribute named foo of Type t_3 to a Class C,
 * then 3 Attribute are added to C, each named foo, with types t_1, t_2 and
 * t_3. However only the Attribute of Type t_3 is associated to the name foo in
 * C's name map. To retrieve the other Attribute you must use safe type names.
 *
 * The syntax of a safe type name is "\<type\>name" they guaranty the Type of
 * the Attribute you asked for. In our example even if there is 3 Attribute
 * named foo, only the one with Type t_3 can be retrieved using foo. The others
 * are only accessible using safe type names: \<t_1\>foo and \<t_2\>foo. If you
 * really want to be sure what you are doing, you can always use safe type
 * names for Attributes since \<t_3\>foo is also added to C's name map.
 *
 * The last point is how we add casting descendants in a Class. As we said
 * casting descendants are deterministic nodes, they simple cast a random
 * variable of one Type to another. Each Attribute which Type is a subtype has
 * a child of it's Type super type added. We do that to any Attribute which
 * type is a subtype (this include Attributes added as casting descendants).
 * In our example adding foo will also add the arcs: \<t_3\>foo -\> \<t_2\>foo
 * and \<t_2\>foo -\> \<t_1\>foo.
 *
 * # ReferenceSlots and SlotChains #
 *
 * ReferenceSlot are the equivalent of reference in the PRM formalism. They
 * represent a relation between two Class which can be instantiated as a
 * one-to-one or a one-to-many relation. SlotChains are the equivalent of slot
 * chain (or reference chain) in the PRM formalism. Both SlotChains and
 * ReferenceSlot are tightly related: a SlotChain is a sequence of ReferenceSlot
 * ending by an Attribute or Aggregate.
 *
 * Reference in a PRM follows the "pull" paradigm: a ClassElement in a Class
 * requiring information defined in a different Class uses SlotChains to
 * reference such ClassElement.  This implies that parent-to-child dependencies
 * are not known from the parent since it is its children whom pull the
 * information toward them. This can be problematic while inferring in a PRM.
 * To deal with this lack of information we use the notion of inverse reference
 * and introduce a new one: inverse slotchain.
 *
 * Inverse reference are implemented as ReferenceSlot representing the inverse
 * relation defined by a ReferenceSlot. When adding a ReferenceSlot named ref
 * with slot type B to Class A, the Class A automatically adds a ReferenceSlot
 * named \<A\>ref with slot type A to B. We call ref the declared ReferenceSlot
 * and \<A\>ref the inverse ReferenceSlot. Such inverse ReferenceSlot are
 * created each time a declared ReferenceSlot is added, even if it is inherited
 * or implemented. Such inverse ReferenceSlot can then be used to know which
 * Instance are related with one another.
 *
 * If an inverse ReferenceSlot is added to a Class or Interface then all
 * sub-Class, sub-Interface and implementation receive the same inverse
 * ReferenceSlot. However the referenced inverse of a declared ReferenceSlot
 * will always be the inverse ReferenceSlot of the root Class in the
 * inheritance tree.
 *
 * Inverse SlotChain represent the reverse path defined by a SlotChain: if a
 * SlotChain rho = ref_1.ref_2.foo then its inverse is inv_rho =
 * \<B\>ref_2.\<A\>ref_1.bar where bar is the ClassElement depending on foo, A
 * the domain of ref_1 and B the domain of ref_2. Inverse SlotChain are not
 * added when a declared SlotChain is added but when a child is added to
 * declared SlotChain. Thus there is as many inverse SlotChain of a declared
 * SlotChain as the number of his children.
 *
 * The name of a SlotChain is its reference chain ended by the ClassElement it
 * points to. Since for each ClassElementContainer ClassElement name are
 * unique, the uniqueness of SlotChain name is guaranteed.
 *
 * Inverse ReferenceSlot and SlotChain are always multiple and when adding a
 * ReferenceSlot to an Interface no inverse ReferenceSlot are added since
 * implementing Class will take care of it.
 *
 * # Exceptions semantics #
 *
 * Exceptions in PRM have specific significations:
 *
 *    - gum::NotFound: raised if an identifier (usually a string or a
 *    gum::NodeId) does not match any gum::prm::ClassElement in a given
 *    gum::prm::ClassElementContainer or gum::prm::Instance.
 *
 *    - gum::PRMTypeError: raised if an gum::prm::PRMObject is not a valid type
 *    when using it in some context.  For example assigning an
 *    gum::prm::Instance to gum::prm::ReferenceSlot where the
 *    gum::prm::Instance type is not a subtype of the gum::prm::ReferenceSlot
 *    slot type will raise a gum::PRMTypeError exception.
 *
 *    - gum::WrongClassElement: raised if a gum::prm::ClassElement is used
 *    where it should not be.  For example adding an gum::prm::Attribute as a
 *    parent of a gum::prm::ReferenceSlot in a gum::prm::Class will raise a
 *    gum::WrongClassElement.
 *
 *    - gum::OutOfBounds: raised if a bound is not respected.  For example
 *    if more than one gum::prm::Instance is assigned to a
 *    gum::prm::ReferenceSlot which is not an array a gum::OutOfBounds will
 *    be raised.
 */
