<commit_before>package com.eharmony.aloha.models.exploration

import com.eharmony.aloha.ModelSerializationTestHelper
import com.eharmony.aloha.factory.ModelFactory
import com.eharmony.aloha.id.ModelId
import com.eharmony.aloha.models.{AnySemanticsWithoutFunctionCreation, CloserTesterModel, ConstantModel}
import com.eharmony.aloha.score.conversions.ScoreConverter.Implicits._
import com.eharmony.aloha.semantics.func.GenFunc0
import org.junit.Assert._
import org.junit.Test
import spray.json._
import spray.json.DefaultJsonProtocol._

import scala.collection.{immutable => sci}

/**
  * Created by jmorra on 2/26/16.
  */
class EpsilonGreedyModelTest extends ModelSerializationTestHelper {
  private[this] val reader = EpsilonGreedyModel.Parser.modelJsonReader[Any, String](ModelFactory(ConstantModel.parser), Option(AnySemanticsWithoutFunctionCreation))
  private[this] val delta = 0.00001f
  implicit val audit = true

  @Test def testSerialization() {
    val constantPolicy = ConstantModel(Right(1), ModelId(2, "abc"))
    val m = EpsilonGreedyModel(ModelId(3, "def"), constantPolicy, 0.1f, null, sci.IndexedSeq(1, 2, 3))
    val m1 = serializeDeserializeRoundTrip(m)
    assertEquals(m, m1)

    val m2 = EpsilonGreedyModel(ModelId(3, "def"), constantPolicy, 0.1f, null, sci.IndexedSeq("1", "2", "3"))
    val m3 = serializeDeserializeRoundTrip(m2)
    assertEquals(m2, m3)
  }

  @Test def testClosed() {
    val sub = new CloserTesterModel[Int]()
    EpsilonGreedyModel(ModelId.empty, sub, 0.1f, GenFunc0("", (s: String) => 1l), sci.IndexedSeq(1, 2)).close()
    assertTrue(sub.isClosed)
  }

  @Test def random() {
    val epsilon = 0.9f
    val m = makeModel(1, epsilon, 1)
    val s = m.getScore(null)
    assertEquals(s._2.get.getScore.getProbability, epsilon / 3, delta)
    assertEquals(s._1.right.get, "b")
  }

  @Test def policy() {
    val epsilon = 0.1f
    val m = makeModel(1, epsilon, 0)
    val s = m.getScore(null)
    assertEquals(s._2.get.getScore.getProbability, 1 - epsilon + epsilon / 3, delta)
    assertEquals(s._1.right.get, "a")
  }

  def makeModel(policyValue: Int, epsilon: Float, salt: Long): EpsilonGreedyModel[Any, String] = {
    val js =
      s"""
        |{
        | "modelType": "EpsilonGreedyExploration",
        | "modelId": {"id": 0, "name": ""},
        | "epsilon": $epsilon,
        | "salt": "$salt",
        | "defaultPolicy": {
        |   "modelType": "Constant",
        |   "modelId": {"id": 1, "name": ""},
        |   "value": $policyValue
        | },
        | "classLabels": ["a", "b", "c"]
        |}
      """.stripMargin.parseJson
    reader.read(js)
  }
}<commit_msg>Updated unit test to reflect what's happening inside MWT so that it can detect if MWT's algorithm changes.<commit_after>package com.eharmony.aloha.models.exploration

import com.eharmony.aloha.ModelSerializationTestHelper
import com.eharmony.aloha.factory.ModelFactory
import com.eharmony.aloha.id.ModelId
import com.eharmony.aloha.models.{AnySemanticsWithoutFunctionCreation, CloserTesterModel, ConstantModel}
import com.eharmony.aloha.score.conversions.ScoreConverter.Implicits._
import com.eharmony.aloha.semantics.func.GenFunc0
import com.mwt.utilities.PRG
import org.junit.Assert._
import org.junit.Test
import spray.json._
import spray.json.DefaultJsonProtocol._

import scala.collection.{immutable => sci}

/**
  * Created by jmorra on 2/26/16.
  */
class EpsilonGreedyModelTest extends ModelSerializationTestHelper {
  private[this] val reader = EpsilonGreedyModel.Parser.modelJsonReader[Any, String](ModelFactory(ConstantModel.parser), Option(AnySemanticsWithoutFunctionCreation))
  private[this] val delta = 0.00001f
  implicit val audit = true

  @Test def testSerialization() {
    val constantPolicy = ConstantModel(Right(1), ModelId(2, "abc"))
    val m = EpsilonGreedyModel(ModelId(3, "def"), constantPolicy, 0.1f, null, sci.IndexedSeq(1, 2, 3))
    val m1 = serializeDeserializeRoundTrip(m)
    assertEquals(m, m1)

    val m2 = EpsilonGreedyModel(ModelId(3, "def"), constantPolicy, 0.1f, null, sci.IndexedSeq("1", "2", "3"))
    val m3 = serializeDeserializeRoundTrip(m2)
    assertEquals(m2, m3)
  }

  @Test def testClosed() {
    val sub = new CloserTesterModel[Int]()
    EpsilonGreedyModel(ModelId.empty, sub, 0.1f, GenFunc0("", (s: String) => 1l), sci.IndexedSeq(1, 2)).close()
    assertTrue(sub.isClosed)
  }

  @Test def random() {
    val seed = 1L

    val epsilon = 1f
    val m = makeModel(1, epsilon, seed)

    // This is because we want to mimic the process inside MWT's epsilon greedy explorer
    // so that if something changes, we can become aware.
    val random = new PRG(seed)
    random.uniformUnitInterval()
    val action = random.uniformInt(1, m.classLabels.size)

    val s = m.getScore(null)
    assertEquals(epsilon / m.classLabels.size, s._2.get.getScore.getProbability, delta)
    assertEquals(m.classLabels(action - 1), s._1.right.get)
    assertEquals("b", s._1.right.get)
  }

  @Test def policy() {
    val epsilon = 0f
    val m = makeModel(1, epsilon, 0)
    val s = m.getScore(null)
    assertEquals(1 - epsilon + epsilon / m.classLabels.size, s._2.get.getScore.getProbability, delta)
    assertEquals("a", s._1.right.get)
  }

  def makeModel(policyValue: Int, epsilon: Float, salt: Long): EpsilonGreedyModel[Any, String] = {
    val js =
      s"""
        |{
        | "modelType": "EpsilonGreedyExploration",
        | "modelId": {"id": 0, "name": ""},
        | "epsilon": $epsilon,
        | "salt": "$salt",
        | "defaultPolicy": {
        |   "modelType": "Constant",
        |   "modelId": {"id": 1, "name": ""},
        |   "value": $policyValue
        | },
        | "classLabels": ["a", "b", "c"]
        |}
      """.stripMargin.parseJson
    reader.read(js)
  }
}
{'fraction_non_alphanumeric': 0.13805646977308159, 'fraction_numerical': 0.019227438073791788, 'mean_word_length': 3.652699435938759, 'pattern_counts': {'":': 26, '<': 3, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4235050', 'n_tokens_mistral': 2062, 'n_tokens_neox': 1949, 'n_words': 425}