-
Notifications
You must be signed in to change notification settings - Fork 0
PromotingMaskingRating
So far we have been dealing with the promoting morphing strategy. Morpheus however offers other strategies, which employ other approaches to choosing the winning alternative. We are going to learn two new strategies in this lesson:
- Masking strategy is used to reduce a morph model's list of alternatives by means of the so-called fragment mask
- Rating strategy assigns a rating to one or more alternatives. Only those alternatives with the highest rating are used to select the winning alternative.
The rating strategy has the highest authority, then the masking one, while the least important is the promoting one.
Before presenting the masking and rating strategies, we need to get more information on how the promoting strategy works.
Let us consider the following simple morph model
(Offline or Online) with (Raw or Pretty)
This model can be depicted as a tree shown in the following diagram.
The model has two dimensions that correspond to the two disjunctors (or
) in the model. Each disjunctor contains two alternative fragments, thus the number of all alternatives defined by this model is 4. A very important aspect is the order of the alternatives in the list since the first alternative in the list is by default taken as the winner. According to the rules described here the list of the alternatives generated by the model will be sorted in the following way:
{0, 2}
{1, 2}
{0, 3}
{1, 3}
Definition: Two fragments are independent if they belong to two different disjunctors (i.e. dimensions).
The goal of the promoting strategy is to 'promote' one or more independent fragments. It means to transform the morph model tree in such a way that the promoted fragments are present in the first alternative in the list generated from the tree, i.e. the default winning alternative.
We will use the notation (contactCoord, printerCoord)
to denote the fragments to be promoted. Then (0, 0)
is the default promotion leading to choosing the {Offline, Raw}
alternative.
In order to promote fragments (1, 0)
, the promoting strategy must transform the tree as follows.
The new tree yields this list of alternatives.
{1, 2}
{0, 2}
{1, 3}
{0, 3}
We can continue by promoting (1, 1)
. The corresponding tree will then be this.
And the corresponding list of alternative is:
{1, 3}
{0, 3}
{1, 2}
{0, 2}
And finally the remaining combination is (0, 1)
, which gives this tree:
with these alternatives:
{0, 3}
{1, 3}
{0, 2}
{1, 2}
Masking allows us to suppress alternatives, which do not match the so-called fragment mask. The fragment mask specifies which fragments may be present in the winning alternative. By turning some fragments off we can effectively reduce the set of the alternatives. By default, all fragments are turned on.
In order to make the explanation clearer, let us introduce the following notation, which represents an alternative along with a bit vector indicating fragments contained in the alternative.
{fragments}[OfflineBit, OnlineBit, RawBit, PrettyBit]
The four alternatives from our model then look like this:
{0, 2}[1, 0, 1, 0]
{1, 2}[0, 1, 1, 0]
{0, 3}[1, 0, 0, 1]
{1, 3}[0, 1, 0, 1]
Now, the fragment mask for our model is a vector of four bits. A bit fN
set to 1 indicates that the corresponding fragment may be present in the winning alternative.
mask = (f0 f1 f2 f3)
We can use this mask to reduce the original list of alternatives A
by clearing some bits in the mask. The sublist S ⊂ A
consists of all alternatives whose bit vector a
masked with the fragment mask (bitwise AND) gives a
. It can be formulated more precisely by the following formula.
∀ a ∈ S; a & mask = a
For a better manipulation with the fragment bits, let us construct the following matrix corresponding to the previous list of alternatives.
|1 0 1 0|
|0 1 1 0|
F = |1 0 0 1|
|0 1 0 1|
When no fragment is explicitly specified, the mask is (1 1 1 1)
. Masking matrix F
gives the same matrix. Thus there is no reduction of the original list of alternatives.
|1 0 1 0|
|0 1 1 0|
F & mask = |1 0 0 1|
|0 1 0 1|
If we wish to constrain the list of alternatives only to those not containing the Pretty
fragment, the mask will be (1 1 1 0)
. Masking matrix F
then will yield the following matrix.
|1 0 1 0|
|0 1 1 0|
F & mask = |1 0 0 0|
|0 1 0 0|
Here, only the first two rows remain same after the mask is applied. The first alternative in the sublist, which preserves the order of the original list, is the winner.
{0, 2}[1, 0, 1, 0]
{1, 2}[0, 1, 1, 0]
If we clear an additional fragment to the mask, let us say Online
, the mask will be (1 0 1 0)
. Then the masked matrix F
will be
|1 0 1 0|
|0 0 1 0|
F & mask = |1 0 0 0|
|0 0 0 0|
where only the first row remains unchanged. This single alternative becomes the winner.
{0, 2}[1, 0, 1, 0]
The rating strategy allows us to rate individual alternatives. Then the winner alternative is selected only from those alternatives having the highest rating. For a better understanding let us use the following notation, which depicts the rating of an alternative.
{fragments}:Rating
By default the alternatives have no rating, i.e. their rating is 0. The four alternatives from our model then look like this:
{0, 2}:0
{1, 2}:0
{0, 3}:0
{1, 3}:0
We can explicitly assign new ratings to a selected sub-set of the original alternatives. Let's say we will rate alternatives 1 and 4 with rating 1. Then the sub-list will look like this
{0, 2}:1
{1, 3}:1
since only these two alternatives have the highest rating. The winner is the first alternative in this sub-list.
All strategies can be composed in any order. In the following example we will see the effect of all strategies on the choosing of the winning alternative. We will use the following text decorations to depict the winner, underlining a mask-matching alternative and striking-out a suppressed alternative with a low rating.
The default list of alternatives is this:
{0, 2}:0
{1, 2}:0
{0, 3}:0
{1, 3}:0
Let us first promote the fragment 1.
{1, 2}:0
{0, 2}:0
{1, 3}:0
{0, 3}:0
Then we will unmask (clear) fragment 2.
{1, 2}:0
{0, 2}:0
{1, 3}:0
{0, 3}:0
And next we will rate alternatives {1, 2} and {0, 3} by 1.
{1, 2}:1
{0, 2}:0
{1, 3}:0
{0, 3}:1
And finally we will mask (turn on) fragment 2 again and unmask fragment 3 (clear).
{1, 2}:1
{0, 2}:0
{1, 3}:0
{0, 3}:1
The following use-case shows how the three strategies can be composed in a real code.
Let us first create a morph kernel in two steps. First we have to create the model.
val contactModel = parse[(OfflineContact or OnlineContact) with
(ContactRawPrinter or ContactPrettyPrinter) with
(StandardOutputChannel or MemoryOutputChannel)](true)
Then we construct a stack of three promoting strategies, each controlling one dimension. We do not employ the other two strategies yet. Notice the LastRatingStrategy
strategy used as the root (bottom) strategy. It is actually a strategy returning the alternatives (i.e. the list of alternatives, the tree, the fragment mask and the ratings) of the morph, on which method remorph
is being called. When calling remorph
repeatedly, it always uses the output alternatives from the last invocation as the input alternatives in the next invocation.
var channelCoord: Int = 0
val channelStr = promote[StandardOutputChannel or MemoryOutputChannel](new LastRatingStrategy[contactModel.Model](), channelCoord)
var printerCoord: Int = 0
val printerStr = promote[ContactRawPrinter or ContactPrettyPrinter](channelStr, printerCoord)
var statusCoord: Int = 0
val contactStr = promote[OfflineContact or OnlineContact](printerStr, statusCoord)
Now we can instantiate the kernel.
val contactData = ContactData("Pepa", "Novák", male = true, email="[email protected]", Locale.CANADA)
implicit val offlineContactFrag = single[OfflineContact, Contact](contactData)
implicit val onlineContactFrag = single[OnlineContact, Contact](contactData)
val contactKernel = singleton(contactModel, contactStr)
In order to see the list of the structure of individual winner alternatives, we will prepare this helper method printing alternatives from the whole combinatorial space (2x2x2).
def printAllAlts(): Unit = {
for (i <- 0 to 1; j <- 0 to 1; k <- 0 to 1) {
channelCoord = i
printerCoord = j
statusCoord = k
contactKernel.~.remorph
println(contactKernel.~.myAlternative)
}
}
Printing the list of alternatives yields this output.
(OfflineContact, ContactRawPrinter, StandardOutputChannel)
(OnlineContact, ContactRawPrinter, StandardOutputChannel)
(OfflineContact, ContactPrettyPrinter, StandardOutputChannel)
(OnlineContact, ContactPrettyPrinter, StandardOutputChannel)
(OfflineContact, ContactRawPrinter, MemoryOutputChannel)
(OnlineContact, ContactRawPrinter, MemoryOutputChannel)
(OfflineContact, ContactPrettyPrinter, MemoryOutputChannel)
(OnlineContact, ContactPrettyPrinter, MemoryOutputChannel)
Now we are going to employ the masking strategy, which will suppress all alternative containing StandardOutputChannel
.
var banStdOut = 1
val maskStrategy = unmask[\?[StandardOutputChannel]](contactStr, banStdOut)
contactKernel.~.remorph(maskStrategy)
printAllAlts()
Remember that \?[StandardOutputChannel]
is equivalent to Unit or StandardOutputChannel
. Setting banStdOut
to 0 will select {Unit}
or {}
empty alternative, thus no fragment gets unmasked. On the contrary, the fragment in the other (not selected) alternative {StandardOutputChannel}
is turned on, i.e. it does not change its status, since by default all fragments are on. Since no fragment was cleared, all alternatives are allowed.
On the other hand, when banStdOut
is set to 1 then the fragment in the {StandardOutputChannel}
alternative is unmasked (cleared). In such a case the only allowed alternatives are those not containing the StandardOutputChannel
fragment.
Printing all combinations proves that despite promoting alternatives with StandardOutputChannel
too, they are overridden by the alternatives not containing StandardOutputChannel
.
(OfflineContact, ContactRawPrinter, MemoryOutputChannel)
(OnlineContact, ContactRawPrinter, MemoryOutputChannel)
(OfflineContact, ContactPrettyPrinter, MemoryOutputChannel)
(OnlineContact, ContactPrettyPrinter, MemoryOutputChannel)
(OfflineContact, ContactRawPrinter, MemoryOutputChannel)
(OnlineContact, ContactRawPrinter, MemoryOutputChannel)
(OfflineContact, ContactPrettyPrinter, MemoryOutputChannel)
(OnlineContact, ContactPrettyPrinter, MemoryOutputChannel)
Next, we will employ the rating strategy, which assigns rating 1 to the alternatives containing ContactPrettyPrinter
.
var fixPretty = Set((0, 1))
val ratingStrategy = rate[ContactPrettyPrinter](contactKernel.~.strategy, fixPretty)
contactKernel.~.remorph(ratingStrategy)
printAllAlts()
The rate
macro has similar signature as promote
and mask
, however the last argument contains a function returning a set of pairs, where each pair consists of the number of the alternative in the sub-model specified in the type arguments, and the rating.
The result of the printing of the whole space will be homogenous now.
(OfflineContact, ContactPrettyPrinter, MemoryOutputChannel)
(OnlineContact, ContactPrettyPrinter, MemoryOutputChannel)
(OfflineContact, ContactPrettyPrinter, MemoryOutputChannel)
(OnlineContact, ContactPrettyPrinter, MemoryOutputChannel)
(OfflineContact, ContactPrettyPrinter, MemoryOutputChannel)
(OnlineContact, ContactPrettyPrinter, MemoryOutputChannel)
(OfflineContact, ContactPrettyPrinter, MemoryOutputChannel)
(OnlineContact, ContactPrettyPrinter, MemoryOutputChannel)
And now we are going to unapply the effect of the new strategies. First, let us unapply the fragment mask by
banStdOut = 0
contactKernel.~.remorph
printAllAlts()
Setting the banStdOut
to 0 turns StandardOutputChannel
fragment making all fragments allowed. Then the strategy promoting StandardOutputChannel
and the strategy rating higher the ContactPrettyPrinter
prevail:
(OfflineContact, ContactPrettyPrinter, StandardOutputChannel)
(OnlineContact, ContactPrettyPrinter, StandardOutputChannel)
(OfflineContact, ContactPrettyPrinter, StandardOutputChannel)
(OnlineContact, ContactPrettyPrinter, StandardOutputChannel)
(OfflineContact, ContactPrettyPrinter, MemoryOutputChannel)
(OnlineContact, ContactPrettyPrinter, MemoryOutputChannel)
(OfflineContact, ContactPrettyPrinter, MemoryOutputChannel)
(OnlineContact, ContactPrettyPrinter, MemoryOutputChannel)
And finally, we will unapply the rating strategy by rating the alternatives with ContactPrettyPrinter
by 0.
fixPretty = Set((0, 0))
contactKernel.~.remorph
printAllAlts()
(OfflineContact, ContactRawPrinter, StandardOutputChannel)
(OnlineContact, ContactRawPrinter, StandardOutputChannel)
(OfflineContact, ContactPrettyPrinter, StandardOutputChannel)
(OnlineContact, ContactPrettyPrinter, StandardOutputChannel)
(OfflineContact, ContactRawPrinter, MemoryOutputChannel)
(OnlineContact, ContactRawPrinter, MemoryOutputChannel)
(OfflineContact, ContactPrettyPrinter, MemoryOutputChannel)
(OnlineContact, ContactPrettyPrinter, MemoryOutputChannel)
####Morpheus Tutorial####
- Modelling simple entity
- Adding some behavior
- Abstracting fragment
- Reusing fragment
- Using fragment in Java
- Multidimensional morphs
- Morphing strategies
- Mutable morphs
- Delegating and sharing
- Dimension wrappers
- Fragment wrappers
- Using a morph as a fragment
- Kernel references
- Kernel references use cases
- Promoting, masking and rating