This is an automated email from the ASF dual-hosted git repository.

sergeykamov pushed a commit to branch NLPCRAFT-70_NEW
in repository https://gitbox.apache.org/repos/asf/incubator-nlpcraft.git


The following commit(s) were added to refs/heads/NLPCRAFT-70_NEW by this push:
     new 47e7600  WIP.
47e7600 is described below

commit 47e76004797def80f5acb1a330575cbff396d721
Author: Sergey Kamov <[email protected]>
AuthorDate: Wed Jul 21 12:46:09 2021 +0300

    WIP.
---
 .../ctxword/NCContextWordCategoriesEnricher.scala  |  84 ++++++----
 .../nlpcraft/model/ctxword/lightswitch_model2.yaml |  10 +-
 .../ctxword/NCLightSwitchScalaModel2Spec.scala     | 180 +++++++++++----------
 3 files changed, 150 insertions(+), 124 deletions(-)

diff --git 
a/nlpcraft/src/main/scala/org/apache/nlpcraft/server/nlp/enrichers/ctxword/NCContextWordCategoriesEnricher.scala
 
b/nlpcraft/src/main/scala/org/apache/nlpcraft/server/nlp/enrichers/ctxword/NCContextWordCategoriesEnricher.scala
index ef7ac9e..499a64d 100644
--- 
a/nlpcraft/src/main/scala/org/apache/nlpcraft/server/nlp/enrichers/ctxword/NCContextWordCategoriesEnricher.scala
+++ 
b/nlpcraft/src/main/scala/org/apache/nlpcraft/server/nlp/enrichers/ctxword/NCContextWordCategoriesEnricher.scala
@@ -26,7 +26,7 @@ import org.apache.nlpcraft.common.{NCE, NCService}
 import org.apache.nlpcraft.server.mdo.NCCtxWordCategoriesConfigMdo
 import org.apache.nlpcraft.server.nlp.core.{NCNlpParser, NCNlpServerManager, 
NCNlpWord}
 import org.apache.nlpcraft.server.nlp.enrichers.NCServerEnricher
-import org.apache.nlpcraft.server.sugsyn.{NCSuggestSynonymManager, 
NCSuggestionRequest, NCWordSuggestion}
+import org.apache.nlpcraft.server.sugsyn.{NCSuggestSynonymManager, 
NCSuggestionRequest => Request, NCWordSuggestion => Suggestion}
 import org.jibx.schema.codegen.extend.DefaultNameConverter
 
 import java.text.DecimalFormat
@@ -48,9 +48,9 @@ object NCContextWordCategoriesEnricher extends 
NCServerEnricher {
     private final val CONVERTER = new DefaultNameConverter
     private final val FMT = new DecimalFormat("#0.00000")
 
-    private case class Reason(word: String, suggestionConfidence: Double, 
corpusConfidence: Double) {
+    private case class Reason(word: String, suggConf: Double, valOrCorpConf: 
Double) {
         override def toString: String =
-            s"Word: $word, confidences: 
suggestion=${FMT.format(suggestionConfidence)}, 
corpus=${FMT.format(corpusConfidence)}"
+            s"Word: $word, confidences: suggestion=${FMT.format(suggConf)}, 
value or corpus=${FMT.format(valOrCorpConf)}"
     }
 
     private case class Confidence(value: Double, reason: Option[Reason] = 
None) {
@@ -64,6 +64,7 @@ object NCContextWordCategoriesEnricher extends 
NCServerEnricher {
         override def toString: String = s"Element [id=$elementId, 
confidence=$confidence]]"
     }
 
+    // Maps: Key is word, values are all element IDs.
     private case class ValuesHolder(normal: Map[String, Set[String]], stems: 
Map[String, Set[String]]) {
         private def map2Str(m: Map[String, Set[String]]): String =
             m.toSeq.flatMap { case (v, elems) =>
@@ -73,6 +74,7 @@ object NCContextWordCategoriesEnricher extends 
NCServerEnricher {
         override def toString: String = s"Values [normal=${map2Str(normal)}, 
stems=${map2Str(stems)}]"
     }
 
+    // Maps: Key is elementID, values are all values synonyms for this element.
     private case class ElementData(normals: Map[String, Double], stems: 
Map[String, Double], lemmas: Map[String, Double]) {
         def get(norm: String, stem: String, lemma: String): Option[Double] =
             normals.get(norm) match {
@@ -116,7 +118,7 @@ object NCContextWordCategoriesEnricher extends 
NCServerEnricher {
           */
         def calculate(suggConf: Double, corpusConf: Double): Double =
             // Corpus data is more important. Empirical factors configured.
-            calcWeightedGeoMean(Map(suggConf -> 1, corpusConf -> 3))
+            calcWeightedGeoMean(Map(suggConf -> 1, corpusConf -> 2))
 
         /**
           * Calculates weighted geometrical mean value.
@@ -180,7 +182,7 @@ object NCContextWordCategoriesEnricher extends 
NCServerEnricher {
       * @param elemSingleVals
       * @return
       */
-    private def mkRequests(corpusNlpSeq: Seq[Seq[NCNlpWord]], elemSingleVals: 
Set[String]): Iterable[NCSuggestionRequest] =
+    private def mkRequests(corpusNlpSeq: Seq[Seq[NCNlpWord]], elemSingleVals: 
Set[String]): Iterable[Request] =
         corpusNlpSeq.
             flatMap {
                 corpusNlp =>
@@ -203,7 +205,7 @@ object NCContextWordCategoriesEnricher extends 
NCServerEnricher {
                         // We can compare them with synonyms values (suppose 
that model synonyms value defined as lemma)
                         getIndexes(corpusNlp.map(p => norm(p.lemma)), 
elemSingleValsNorm)
 
-                    def mkRequest(idx: Int, syn: String): NCSuggestionRequest 
= {
+                    def mkRequest(idx: Int, syn: String): Request = {
                         var newSen = substitute(corpusWords, syn, idx)
 
                         val nlpWordsNew = parser.parse(newSen.mkString(" "))
@@ -218,7 +220,7 @@ object NCContextWordCategoriesEnricher extends 
NCServerEnricher {
                         else if (NOUNS_POS_PLURALS.contains(pos) && 
NOUNS_POS_SINGULAR.contains(posNew))
                             newSen = substitute(corpusWords, 
CONVERTER.pluralize(syn), idx)
 
-                        NCSuggestionRequest(newSen, idx)
+                        Request(newSen, idx)
                     }
 
                     for (idx <- idxs; syn <- elemSingleVals)
@@ -236,14 +238,16 @@ object NCContextWordCategoriesEnricher extends 
NCServerEnricher {
       *
       * @param cfg
       * @param key
+      * @param vh
+      * @param parent
       * @return
       */
-    private def getCorpusData(cfg: NCCtxWordCategoriesConfigMdo, key: 
ModelProbeKey, parent: Span = null):
+    private def getCorpusData(cfg: NCCtxWordCategoriesConfigMdo, key: 
ModelProbeKey, vh: ValuesHolder, parent: Span = null):
         Map[/** Element ID */String, ElementData] =
         elemsCorpuses.synchronized { elemsCorpuses.get(key) } match {
             case Some(cache) => cache
             case None =>
-                val res = askSamples(cfg, parent)
+                val res = askSamples(cfg, vh, parent)
 
                 elemsCorpuses.synchronized { elemsCorpuses += key -> res }
 
@@ -296,20 +300,21 @@ object NCContextWordCategoriesEnricher extends 
NCServerEnricher {
       * @param sugg
       * @return
       */
-    private def getLemma(req: NCSuggestionRequest, sugg: NCWordSuggestion): 
String =
+    private def getLemma(req: Request, sugg: Suggestion): String =
         parser.parse(substitute(req.words, sugg.word, req.index).mkString(" 
"))(req.index).lemma
 
     /**
       *
       * @param cfg
-      * @return
+      * @param vh
+      * @param parent
       */
     @throws[NCE]
-    private def askSamples(cfg: NCCtxWordCategoriesConfigMdo, parent: Span = 
null):
+    private def askSamples(cfg: NCCtxWordCategoriesConfigMdo, vh: 
ValuesHolder, parent: Span = null):
         Map[/** Element ID */String, ElementData] = {
         val corpusNlp = cfg.corpus.toSeq.map(s => parser.parse(s))
 
-        val recs: Map[String, Seq[NCSuggestionRequest]] =
+        val recs: Map[String, Seq[Request]] =
             (
                 for (
                     (elemId, elemSingleVals) <- cfg.singleValues.toSeq;
@@ -321,7 +326,7 @@ object NCContextWordCategoriesEnricher extends 
NCServerEnricher {
                 map { case (elemId, m) => elemId -> m.map(_._2) }
 
         if (recs.nonEmpty) {
-            val respsSeq: Seq[(NCSuggestionRequest, Seq[NCWordSuggestion])] =
+            val respsSeq: Seq[(Request, Seq[Suggestion])] =
                 
syncExec(NCSuggestSynonymManager.suggestWords(recs.flatMap(_._2).toSeq, parent 
= parent)).
                     toSeq.sortBy(p => (p._1.words.mkString, p._1.index))
 
@@ -338,7 +343,7 @@ object NCContextWordCategoriesEnricher extends 
NCServerEnricher {
 
             val req2Elem = recs.flatMap { case (elemId, recs) => recs.map(p => 
p -> elemId) }
 
-            def mkMap(convert: (NCSuggestionRequest, NCWordSuggestion) => 
String):
+            def mkMap(convert: (Request, Suggestion) => String):
                 Map[/** Element ID */ String, /** Word key */ Map[String, /** 
Confidences */ Seq[Double]]] = {
                 val seq: Seq[(String, Map[String, Double])] =
                     respsSeq.
@@ -388,9 +393,17 @@ object NCContextWordCategoriesEnricher extends 
NCServerEnricher {
                 ).
                     toMap.
                     map { case (elemId, (normals, stems, lemmas)) =>
-                        val normalsAll = normals
-                        val stemsAll = stems -- normals.keySet
-                        val lemmasAll = lemmas -- normals.keySet -- 
stems.keySet
+                        // Skips suggestions, which already exists as values 
for element.
+                        def dropValues[T](words: Map[String, Seq[Double]], 
vals: Map[String, Set[String]]):
+                            Map[String, Seq[Double]] =
+                            words.filter { case (word, _) => vals.get(word) 
match {
+                                case Some(elemIds) => !elemIds.contains(elemId)
+                                case None => true
+                            }}
+
+                        val normalsAll = dropValues(normals, vh.normal)
+                        val stemsAll = dropValues(stems -- normalsAll.keySet, 
vh.stems)
+                        val lemmasAll = lemmas -- normals.keySet -- 
stemsAll.keySet
 
                         def mkDebugElementCell(normsSize: Int, stemsSize: Int, 
lemmasSize: Int): String =
                             s"Element: $elemId [normals=$normsSize, 
stems=$stemsSize, lemmas=$lemmasSize]"
@@ -468,16 +481,16 @@ object NCContextWordCategoriesEnricher extends 
NCServerEnricher {
                         val key = ModelProbeKey(cfg.probeId, cfg.modelId)
 
                         // 1. Values. Direct.
-                        val vd = getValuesData(cfg, key)
+                        val vh = getValuesData(cfg, key)
 
-                        val (vNorms, vStems) = (vd.normal, vd.stems)
+                        val (vNorms, vStems) = (vh.normal, vh.stems)
 
                         if (DEBUG_MODE)
                             logger.info(
                                 s"Model loaded [" +
                                 s"key=$key, elements: " +
                                 s"${cfg.elements.mkString(", ")}, " +
-                                s"values data=$vd]"
+                                s"values data=$vh]"
                             )
 
                         def get(m: Map[String, Set[String]], key: String): 
Set[String] = m.getOrElse(key, Set.empty)
@@ -489,7 +502,7 @@ object NCContextWordCategoriesEnricher extends 
NCServerEnricher {
                             add(n, elemId, Confidence(INCL_MAX_CONFIDENCE))
 
                         // 2. Via corpus.
-                        val corpusData = getCorpusData(cfg, key, parent)
+                        val corpusData = getCorpusData(cfg, key, vh, parent)
 
                         for (
                             nounTok <- nouns;
@@ -501,9 +514,9 @@ object NCContextWordCategoriesEnricher extends 
NCServerEnricher {
 
                         // 3. Ask for sentence (via co-references)
                         val idxs = ns.tokens.flatMap(p => if 
(p.pos.startsWith("N")) Some(p.index) else None).toSeq
-                        val reqs = idxs.map(idx => 
NCSuggestionRequest(ns.tokens.map(_.origText).toSeq, idx))
+                        val reqs = idxs.map(idx => 
Request(ns.tokens.map(_.origText).toSeq, idx))
 
-                        val resps: Map[NCWordSuggestion, NCSuggestionRequest] =
+                        val resps: Map[Suggestion, Request] =
                             
syncExec(NCSuggestSynonymManager.suggestWords(reqs, parent = parent)).
                                 flatMap { case (req, suggs) => suggs.map(_ -> 
req) }
 
@@ -532,6 +545,19 @@ object NCContextWordCategoriesEnricher extends 
NCServerEnricher {
 
                         val missed = if (DEBUG_MODE) 
mutable.HashMap.empty[Key, ArrayBuffer[Confidence]] else null
 
+                        def calcConf(elemId: String, data: ElementData, req: 
Request, s: Suggestion): Option[Double] = {
+                            val suggNorm = norm(s.word)
+                            val suggStem = stem(s.word)
+
+                            if (
+                                vh.normal.getOrElse(suggNorm, 
Set.empty).contains(elemId) ||
+                                vh.stems.getOrElse(suggStem, 
Set.empty).contains(elemId)
+                            )
+                                Some(1.0)
+                            else
+                                data.get(norm = suggNorm, stem = suggStem, 
lemma = getLemma(req, s))
+                        }
+
                         for (
                             // Token index (tokIdx) should be correct because 
request created from original words,
                             // separated by space, and Suggestion Manager uses 
space tokenizer.
@@ -539,12 +565,12 @@ object NCContextWordCategoriesEnricher extends 
NCServerEnricher {
                             suggConf = normalizeConf(sugg.score);
                             (elemId, elemData) <- corpusData;
                             elemConf = cfg.elements(elemId);
-                            corpConfOpt = elemData.get(norm(sugg.word), 
stem(sugg.word), getLemma(req, sugg))
-                            if corpConfOpt.isDefined;
-                            corpConf = corpConfOpt.get;
-                            normConf = ConfMath.calculate(suggConf, corpConf)
+                            valOrCorpConfOpt = calcConf(elemId, elemData, req, 
sugg)
+                            if valOrCorpConfOpt.isDefined;
+                            valOrCorpConf = valOrCorpConfOpt.get;
+                            normConf = ConfMath.calculate(suggConf, 
valOrCorpConf)
                         ) {
-                            def mkConf(): Confidence = Confidence(normConf, 
Some(Reason(sugg.word, suggConf, corpConf)))
+                            def mkConf(): Confidence = Confidence(normConf, 
Some(Reason(sugg.word, suggConf, valOrCorpConf)))
                             def getToken: NCNlpSentenceToken = 
ns.tokens(req.index)
 
                             if (normConf >= elemConf)
diff --git 
a/nlpcraft/src/test/resources/org/apache/nlpcraft/model/ctxword/lightswitch_model2.yaml
 
b/nlpcraft/src/test/resources/org/apache/nlpcraft/model/ctxword/lightswitch_model2.yaml
index f15442c..3b4e07b 100644
--- 
a/nlpcraft/src/test/resources/org/apache/nlpcraft/model/ctxword/lightswitch_model2.yaml
+++ 
b/nlpcraft/src/test/resources/org/apache/nlpcraft/model/ctxword/lightswitch_model2.yaml
@@ -28,22 +28,18 @@ enabledBuiltInTokens: [] # This example doesn't use any 
built-in tokens.
 permutateSynonyms: true
 abstractTokens:
   - "ls:part:place"
-  - "ls:part:floor"
+  - "ls:part:placeFloor"
   - "ls:part:placeType"
   - "ls:part:light"
 sparse: true
 elements:
   - id: "ls:part:place"
     description: "Abstract element. Used for top level element `ls:loc`"
-    # TODO: Value is not so big because this category is vague enough.
-    categoryConfidence: 0.4
+    # TODO: Value set for examples set.
+    categoryConfidence: 0.65
     values:
       - name: "room"
-      - name: "closet"
-      - name: "kitchen"
       - name: "bedroom"
-      - name: "washroom"
-      - name: "garage"
 
   # For simplifying example, concrete floor type can be recognized by these 
synonyms words.
   - id: "ls:part:placeFloor"
diff --git 
a/nlpcraft/src/test/scala/org/apache/nlpcraft/model/ctxword/NCLightSwitchScalaModel2Spec.scala
 
b/nlpcraft/src/test/scala/org/apache/nlpcraft/model/ctxword/NCLightSwitchScalaModel2Spec.scala
index 3948a73..57b076c 100644
--- 
a/nlpcraft/src/test/scala/org/apache/nlpcraft/model/ctxword/NCLightSwitchScalaModel2Spec.scala
+++ 
b/nlpcraft/src/test/scala/org/apache/nlpcraft/model/ctxword/NCLightSwitchScalaModel2Spec.scala
@@ -22,7 +22,6 @@ import com.fasterxml.jackson.module.scala.DefaultScalaModule
 import org.apache.nlpcraft.model.tools.test.NCTestAutoModelValidator
 import org.apache.nlpcraft.model.{NCIntentRef, NCIntentSample, NCIntentTerm, 
NCModelFileAdapter, NCResult, NCToken}
 import org.apache.nlpcraft.{NCTestContext, NCTestEnvironment}
-import org.junit.jupiter.api.Assertions.{assertEquals, assertTrue}
 import org.junit.jupiter.api.{Assertions, Test}
 
 import scala.jdk.CollectionConverters.ListHasAsScala
@@ -37,7 +36,8 @@ case class NCContextWordSpecModel3Data(
     action: String,
     place: String,
     placeType: Option[String] = None,
-    placeFloor: Option[String] = None
+    placeFloor: Option[String] = None,
+    placeConfidence: java.lang.Double = 0
 )
 
 import org.apache.nlpcraft.model.ctxword.NCContextWordSpecModel3Data._
@@ -58,18 +58,25 @@ class NCLightSwitchScalaModel2 extends 
NCModelFileAdapter("org/apache/nlpcraft/m
         "Kill the illumination now second floor kid closet!"
     ))
     def onMatch(@NCIntentTerm("act") actTok: NCToken, @NCIntentTerm("loc") 
locTok: NCToken): NCResult = {
-        def getPart(id: String): Option[String] = 
locTok.getPartTokens.asScala.find(_.getId == id) match {
+        def getPart(id: String): NCToken =
+            locTok.getPartTokens.asScala.find(_.getId == id).
+            getOrElse(throw new AssertionError(s"Token not found: $id"))
+        def getPartTextOpt(id: String): Option[String] = 
locTok.getPartTokens.asScala.find(_.getId == id) match {
             case Some(t) => Some(t.getOriginalText.toLowerCase)
             case None => None
         }
 
+        val place = getPart("ls:part:place")
+        val conf: Double = place.meta("ls:part:place:confidence")
+
         NCResult.json(
             MAPPER.writeValueAsString(
                 NCContextWordSpecModel3Data(
                     action = if (actTok.getId == "ls:on") "on" else "off",
-                    place = getPart("ls:part:place").get,
-                    placeType = getPart("ls:part:placeType"),
-                    placeFloor = getPart("ls:part:placeFloor")
+                    place = place.getOriginalText.toLowerCase,
+                    placeConfidence = conf,
+                    placeType = getPartTextOpt("ls:part:placeType"),
+                    placeFloor = getPartTextOpt("ls:part:placeFloor")
                 )
             )
         )
@@ -95,94 +102,91 @@ class NCLightSwitchScalaModel2SpecSamples {
 class NCLightSwitchScalaModel2Spec extends NCTestContext {
     import org.apache.nlpcraft.model.ctxword.{NCContextWordSpecModel3Data => R}
 
-    private def test0(txt: String, expected: NCContextWordSpecModel3Data): 
Unit = {
-        val res = getClient.ask(txt)
+    private def check(testsData: (String, NCContextWordSpecModel3Data)*): Unit 
= {
+        val errs = collection.mutable.HashMap.empty[String, String]
+        val okMsgs = collection.mutable.ArrayBuffer.empty[String]
 
-        assertTrue(res.isOk, s"Checked: $txt")
-        assertTrue(res.getResult.isPresent, s"Checked: $txt")
+        testsData.foreach { case (txt, expected) =>
+            def addError(msg: String): Unit = errs += txt -> msg
 
-        val actual = MAPPER.readValue(res.getResult.get(), 
classOf[NCContextWordSpecModel3Data])
+            val res = getClient.ask(txt)
 
-        assertEquals(expected, actual, s"Expected: $expected, actual: $actual")
-    }
+            if (!res.isOk)
+                addError(res.getResultError.get())
+            else {
+                val actual = MAPPER.readValue(res.getResult.get(), classOf[R])
 
-    @Test
-    def testSamplesDetailed(): Unit = {
-        test0(
-            "Turn the lights off in the room.",
-            R(action = "off", place = "room")
-        )
-        test0(
-            "Set the lights on in in the room.",
-            R(action = "on", place = "room")
-        )
-        test0(
-            "Lights up in the kitchen.",
-            R(action = "on", place = "kitchen")
-        )
-        test0(
-            "Please, put the light out in the upstairs bedroom.",
-            R(action = "off", place = "bedroom", placeFloor = Some("upstairs"))
-        )
-        test0(
-            "Turn the lights off in the guest bedroom.",
-            R(action = "off", place = "bedroom", placeType = Some("guest"))
-        )
-        test0(
-            "No lights in the first floor guest washroom, please.",
-            R(action = "off", place = "washroom", placeType = Some("guest"), 
placeFloor = Some("first floor"))
-        )
-        test0(
-            "Light up the garage, please!",
-            R(action = "on", place = "garage")
-        )
-        test0(
-            "Kill the illumination now second floor kid closet!",
-            R(action = "off", place = "closet",  placeType = Some("kid"), 
placeFloor = Some("second floor"))
-        )
+                def getMainData(d: NCContextWordSpecModel3Data): String =
+                    s"Main [action=${d.action}, place=${d.place}, 
placeType=${d.placeType}, placeFloor=${d.placeFloor}]"
+
+                val actualData = getMainData(actual)
+                val expData = getMainData(expected)
+
+                if (expData != actualData)
+                    addError(s"Expected: $expData, actual: $actualData")
+                else
+                    okMsgs += s"`$txt` processed ok with detected place 
`${actual.place}` and confidence `${actual.placeConfidence}`."
+            }
+        }
+
+        println(s"Test passed: ${okMsgs.size}")
+        println(s"Test errors: ${errs.size}")
+
+        okMsgs.foreach(println)
+
+        if (errs.nonEmpty)
+            throw new AssertionError(errs.mkString("\n"))
     }
 
+    /**
+      * `ls:part:place` has 2 values: room and bedroom.
+      * Samples contains also: kitchen, washroom, garage, closet. These words 
detected with some confidence < 1.
+      */
     @Test
-    def testSynonymsSameCategory(): Unit = {
-        // Word `loft` is not defined as place.
-        test0(
-            "Turn the lights off in the loft.",
-            R(action = "off", place = "loft")
-        )
-        // Word `loft` is not defined as place.
-        test0(
-            "Set the lights on in in the loft.",
-            R(action = "on", place = "loft")
-        )
-        // Word `office` is not defined as place.
-        test0(
-            "Lights up in the office.",
-            R(action = "on", place = "office")
-        )
-        // Word `library` is not defined as place.
-        test0(
-            "Please, put the light out in the upstairs library.",
-            R(action = "off", place = "library", placeFloor = Some("upstairs"))
-        )
-        // Word `office` is not defined as place.
-        test0(
-            "Turn the lights off in the guest office.",
-            R(action = "off", place = "office", placeType = Some("guest"))
-        )
-        // Word `chamber` is not defined as place.
-        test0(
-            "No lights in the first floor guest chamber, please.",
-            R(action = "off", place = "chamber", placeType = Some("guest"), 
placeFloor = Some("first floor"))
-        )
-        // Word `office` is not defined as place.
-        test0(
-            "Light up the office, please!",
-            R(action = "on", place = "office")
-        )
-        // Word `chamber` is not defined as place.
-        test0(
-            "Kill the illumination now second floor kid chamber!",
-            R(action = "off", place = "chamber",  placeType = Some("kid"), 
placeFloor = Some("second floor"))
+    def testSamplesDetailed(): Unit =
+        check(
+            "Turn the lights off in the room." ->
+                R(action = "off", place = "room"),
+            "Set the lights on in in the room." ->
+                R(action = "on", place = "room"),
+            "Lights up in the kitchen." ->
+                R(action = "on", place = "kitchen"),
+            "Please, put the light out in the upstairs bedroom." ->
+                R(action = "off", place = "bedroom", placeFloor = 
Some("upstairs")),
+            "Turn the lights off in the guest bedroom." ->
+                R(action = "off", place = "bedroom", placeType = 
Some("guest")),
+            "No lights in the first floor guest washroom, please." ->
+                R(action = "off", place = "washroom", placeType = 
Some("guest"), placeFloor = Some("first floor")),
+            "Light up the garage, please!" ->
+                R(action = "on", place = "garage"),
+            "Kill the illumination now second floor kid closet!" ->
+                R(action = "off", place = "closet",  placeType = Some("kid"), 
placeFloor = Some("second floor"))
+        )
+
+    /**
+      * `ls:part:place` has 2 values: room and bedroom.
+      * Samples contains also: loft, hallway, library, chamber, office.
+      * Note, that These words are not provided as in samples.
+      * These words detected with some confidence < 1.
+      */
+    @Test
+    def testSynonymsSameCategory(): Unit =
+        check(
+            "Turn the lights off in the loft." ->
+                R(action = "off", place = "loft"),
+            "Set the lights on in in the loft." ->
+                R(action = "on", place = "loft"),
+            "Lights up in the hallway." ->
+                R(action = "on", place = "hallway"),
+            "Please, put the light out in the upstairs library." ->
+                R(action = "off", place = "library", placeFloor = 
Some("upstairs")),
+            "Turn the lights off in the guest office." ->
+                R(action = "off", place = "office", placeType = Some("guest")),
+            "No lights in the first floor guest chamber, please." ->
+                R(action = "off", place = "chamber", placeType = 
Some("guest"), placeFloor = Some("first floor")),
+            "Light up the office, please!" ->
+                R(action = "on", place = "office"),
+            "Kill the illumination now second floor kid chamber!" ->
+                R(action = "off", place = "chamber",  placeType = Some("kid"), 
placeFloor = Some("second floor"))
         )
-    }
 }
\ No newline at end of file

Reply via email to