All Downloads are FREE. Search and download functionalities are using the official Maven repository.

markovNamegen.Model.scala Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2020 Braian De Roo
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package markovNamegen

import braianideroo.random.SeedRandom
import braianideroo.random.value._
import zio.ZIO._
import zio.{ Ref, ZIO }

private[markovNamegen] class Model private (
  val order: Int,
  probabilities: Ref[Map[String, RandomVIO[Nothing, Option[String]]]],
  val smoothing: SmoothF[Any, String],
  letters: IndexedSeq[(String, Double)]
) {

  def generate(context: String): ZIO[SeedRandom, Nothing, Option[String]] =
    for {
      maybeChain <- probabilities.get >>= (x => succeed(x.get(context)))
      res <- maybeChain match {
              case Some(ref) => ref
              case None      => ZIO.none
            }
    } yield res

  def retrain(data: IndexedSeq[String]): ZIO[Any, Nothing, Map[String, RandomVIO[Nothing, Option[String]]]] =
    train(data) *> probabilities.get

  private def train(data: IndexedSeq[String]) =
    for {
      observations <- Ref.make[Map[String, Ref[Probabilities[Any, String]]]](Map())
      _ <- foreach_(data) { element =>
            val parts = getParts(element, order)
            foreach_(parts)(part =>
              for {
                obs <- observations.get
                ref <- obs.get(part._1) match {
                        case Some(value) => succeed(value)
                        case None        => addLetters(part._1, observations)
                      }
                _ <- addPart(part, ref)
              } yield ()
            )
          }
      // smooth the probabilities
      obs <- observations.get
      _ <- foreach_(obs) { x =>
            for {
              aux <- x._2.get
              _   <- aux.smooth(smoothing)
            } yield ()
          }
      x = obs.map(x => (x._1, x._2.get >>= (y => RandomValue.fromMap(y))))
      _ <- probabilities.set(x)
    } yield ()

  private def addLetters(element: String, observations: Ref[Map[String, Ref[Probabilities[Any, String]]]]) =
    for {
      lettersProb <- ZIO.foreach(letters)(x => Probability.make[Any](x._2).map(y => (x._1, y)))
      refVec      <- Ref.make[Probabilities[Any, String]](Map.from(lettersProb))
      _           <- observations.update(_ + (element -> refVec))
    } yield refVec

  private def addPart(part: (String, String), obs: Ref[Probabilities[Any, String]]): ZIO[Any, Nothing, Unit] =
    for {
      probabilities <- obs.get
      probability   = probabilities(part._2)
      _             <- probability.addModifier("model", Modifier[Any](ZIO.some(1)))
    } yield ()

  def getParts(string: String, order: Int): Vector[(String, String)] = {
    @scala.annotation.tailrec
    def inner(string: String, order: Int, res: Vector[(String, String)]): Vector[(String, String)] =
      if (string.length <= order) res
      else {
        val aux = string.substring(0, order)
        inner(string.tail, order, res :+ (aux, string(order).toString))
      }
    inner(("#" * order) + string + "#", order, Vector())
  }

}

object Model {
  def make(
    smoothingF: SmoothF[Any, String],
    order: Int,
    letters: IndexedSeq[String],
    probabilities: Ref[Map[String, RandomVIO[Nothing, Option[String]]]]
  ): Model =
    new Model(order, probabilities, smoothingF, letters.map(letter => (letter, 0.0)))

  def make(
    smoothingF: SmoothF[Any, String],
    order: Int,
    letters: IndexedSeq[String],
    probabilities: Map[String, RandomVIO[Nothing, Option[String]]]
  ): ZIO[Any, Nothing, Model] =
    Ref
      .make[Map[String, RandomVIO[Nothing, Option[String]]]](probabilities)
      .map(ref => make(smoothingF, order, letters, ref))

  def make(smoothingF: SmoothF[Any, String], order: Int, letters: IndexedSeq[String]): ZIO[Any, Nothing, Model] =
    make(smoothingF, order, letters, Map())
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy