8. Inheritance¶
Inheritance is a key aspect of the O3PRM language. O3PRM offers four different inheritance mechanisms, all with a specific task. Type inheritance allows to create specialization among random variables’ domains. Coupled with type casting, it can be used to model complex problems. Class and interface inheritances offer a more traditional inheritance feature. However its implementation in the O3PRM language adds a lot of expressiveness to Probabilistic Relational Models. Finally, interface implementation is how we implemented multiple inheritance.
8.1. Type Inheritance¶
Subtypes are used to model a is a
relation between types. They are declared
using the extends
keyword. You can only subtype categorical types.
type t_state labels (OK, NOK);
type t_degraded extends t_state ( OK: OK, DYSFONCTION: NOK, DEGRADED: NOK);
Here we declared the type t_degraded
as a subtype of t_state
. The
mapping notation used inside parentheses indicates how to interpret each
of t_degraded
outcomes as a random variable of type t_state
.
8.2. Interface Inheritance¶
An interface can extend another one, using the keyword extends
. By doing so,
the sub interface inherits all of its super interface attributes and
references.
interface SomeIface {}
interface SuperIface {
SomeIface myRef;
t_state state;
}
interface SubIface extends SuperIface {
// No need to declare myRef and state:
// They are inherited from SuperIface.
}
8.2.1. Reference Overloading¶
When you declare a sub interface, you can overload inherited reference slots. To do so, the new reference slot type must be a sub class or sub interface of the reference slot type in the super interface.
interface SomeIface {}
interface SomeOtherIface extends SomeIface {
boolean state;
}
interface SuperIface {
SomeIface myRef;
t_state state;
}
interface SubIface extends SuperIface {
// myRef is overloaded with the sub type SomeOtherIface
SomeOtherIface myRef;
}
8.2.2. Attribute Overloading¶
As for reference overloading, you can overload inherited attributes with a subtype of the attribute types in the super interface.
interface SuperIface {
SomeIface myRef;
t_state state;
}
interface SubIface extends SuperIface {
// state is overloaded with t_state subtype t_degraded
t_degraded state;
}
8.3. Class Inheritance¶
Class inheritance works the same way as inheritance for interfaces with the additional possibility to overload an inherited attribute’s CPT.
8.3.1. Attribute CPT Overloading¶
To overload an inherited attribute’s CPT, you simply need to declare an attribute with a compatible type.
class SuperClass {
boolean state { [ 0.5, 0.5 ] };
}
class SubClass {
boolean state { [ 0.2, 0.8 ] };
}
8.4. Multiple Inheritance¶
Classes can implement interfaces using the keyword implements
. When a
class implements an interface, it must declare all of the interface’s
attributes and reference slots. If the class
implements several interfaces, then it must declare all the attributes and
reference slots of all its interfaces.
interface MyIface {
boolean state;
}
interface MyOhterIface {
MyIface aIface;
boolean working;
}
class MyClass implements MyIface, MyOtherIface {
MyIface aIface;
boolean state {[0.2, 0.8]};
boolean working dependson state {
[0.3, 0.6,
0.7, 0.4]
};
}
Note that, if a class implements a set of interfaces, then all of its subclasses also implement the same set of interfaces.
8.5. Casting and cast descendants¶
Casting and cast descendants are how the O3PRM language handles attribute type overloading and probabilistic dependencies. Attributes types and CPTs are tightly coupled: the size of a CPT is the product of the domain sizes of its attribute’s type and its parents types. The following example will help us illustrate why we need casting and casting descendants:
type t_state labels(OK, NOK);
type t_degraded extends t_state(OK: OK, degraded: NOK, NOK: NOK);
interface Pump {
t_state state;
}
class WaterTank {
Pump myPump;
boolean overflow dependson myPump.state {
// OK | NOK => myPump.state
[ 0.99, 0.25, // overflow == false
0.01, 0.75] // overflow == true
};
}
// Centrifugal Water Pump
class CWPump implements Pump {
t_degraded state {
[ 0.80, // OK
0.19, // degraded
0.01] // NOK
};
}
system MyPumpSystem {
WaterTank tank;
CWPump pump;
tank.myPump = pump;
}
In this example, we model a water tank overflow problem. We have an interface
describing pumps, a class representing a water tank and an implementation
of interface Pump
for a centrifugal water pump.
If you look at class WaterTank
you will notice that its attribute
overflow
depends on Pump
attribute state
, which is of type
t_state
.
However, in system MyPumpSystem
, the reference myPump
of the
instance tank
of Class WaterTank
is assigned to an instance of
class CWPump
. Since we overloaded the Pump.state
type by
t_state
subtype t_degraded
, the CPT definition of attribute
WaterTank.overflow
should be incompatible.
This is not the case here because a cast descendant of attribute CWPump.state
is automatically added to the class CWPump
:
t_state state dependons (t_degraded)state {
// OK, degraded, NOK => (t_degraded)state
[ 1.0, 0.0, 0.0, // OK
0.0, 1.0, 1.0] // NOK
};
This cast descendant is of the expected type and preserves
WaterTank.overflow
CPT’s compatibility.
Attributes added automatically are called cast descendants and can be accessed using the casting notion:
<parent> ::= [ "(" <path> ")"] <path>