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

com.cyrusinnovation.computation.CompoundComputation.scala Maven / Gradle / Ivy

package com.cyrusinnovation.computation
/*
 * Copyright 2014 Cyrus Innovation, LLC. Licensed under Apache license 2.0.
 */

import scala.collection.{MapLike, LinearSeqOptimized}

/** A single computation created from a series of computations. The sequence of computations
 * aborts if the result of an inner computation (usually, an AbortingComputation) indicates
 * that it should not continue.
 *
 * This type of computation will always propagate exceptions up the stack.
 *
 * @constructor     Instantiate a SequentialComputation from a list of computations.
 * @param steps     A list of computations that make up the sequence.
 *
 */
class SequentialComputation(val steps: List[Computation]) extends Computation {
  def resultKey = steps.last.resultKey

  def compute(domain: Domain): Domain = {
    steps.foldLeft(domain) {
      (domainSoFar: Domain, step: Computation) => {
        val newDomain = step.compute(domainSoFar)
        if (newDomain.continue) newDomain else return Domain(newDomain.facts, true)
      }
    }
  }
}

/** A computation that applies another computation serially to a sequence of values (i.e., a Scala
 * LinearSeqOptimized). The value sequence is passed to the computation as an entry in the domain
 * of facts. The result is a separate sequence of values returned as an entry in the output domain.
 * The series of computations can be terminated early, and only applied to part of the sequence of
 * values, if the inner computation (usually, an AbortingComputation) indicates that it should
 * not continue.
 *
 * For each value in the sequence to be iterated over, the iterative computation creates a distinct
 * domain of facts. This domain is the original domain with the addition of a key-value pair containing
 * the single value. (The key is the key required by the inner computation to identify its
 * input data.)
 *
 * This type of computation will always propagate exceptions up the stack.
 *
 * @constructor           Instantiate an IterativeComputation from another computation, indicating the
 *                        keys in the data map required to carry out the computation and the key that
 *                        will designate the results.
 * @param inner           The computation that will be applied to each element in the value sequence.
 * @param inputMapping    A tuple whose first element is the key that designates the sequence of
 *                        values in the domain, and whose second element designates the key
 *                        required to pass a single value to the inner computation.
 * @param resultKey       The key that will designate the result of the computation in the outgoing
 *                        domain of facts. The result takes the form of a list of the results of
 *                        each inner computation).
 *
 */
class IterativeComputation[+A, +SeqType <: LinearSeqOptimized[A, SeqType]](val inner: Computation,
                           inputMapping: (Symbol, Symbol),
                           val resultKey: Symbol) extends Computation {

  def compute(domain: Domain): Domain = {
    val input: Any = domain.facts.get(inputMapping._1).get
    val reversedResultSequence = computeResultSequence(input, domain)
    val resultSequence = reversedResultSequence.reverse
    Domain.combine(Map(resultKey -> resultSequence), domain)
  }

  private def computeResultSequence(input: Any, originalDomain: Domain): List[Any] = {
    val inputSequence = input.asInstanceOf[LinearSeqOptimized[A, SeqType]]
    
    inputSequence.foldLeft(List[Any]()) {
      (resultsSoFar: List[Any], value: Any) => {
        val domainWithSingleValueAdded = Domain.combine(Map(inputMapping._2 -> value), originalDomain)
        val innerResults = inner.compute(domainWithSingleValueAdded)
        innerResults.facts.get(inner.resultKey) match {
          case Some(result) => {
            val theResults = result :: resultsSoFar
            if(innerResults.continue) theResults else return theResults
          }
          case None => resultsSoFar
        }
      }
    }
  }
}

/** A computation that applies another computation to a each value of a map (i.e., a Scala MapLike).
 * This map  is passed to the computation as an entry in the domain of facts. The result is a
 * separate map of values returned as an entry in the output domain, whose keys are the keys of
 * the original map, and whose values are the results of the inner computation applied to the
 * corresponding values. The series of computations can be terminated early, and only applied
 * to part of the map, if the inner computation (usually, an AbortingComputation) indicates that
 * it should not continue.
 *
 * For each key-value pair in the map, the MappingComputation creates a distinct domain of facts.
 * This domain is the original domain with the addition of an entry containing the value from the
 * key-value pair. (The entry's key is the key required by the inner computation to identify its
 * input data.)
 *
 * This type of computation will always propagate exceptions up the stack.
 *
 * @constructor           Instantiate a MappingComputation from another computation, indicating the
 *                        keys in the domain required to carry out the computation and the key that
 *                        will designate the results.
 * @param inner           The computation that will be applied to each value in the map.
 * @param inputMapping    A tuple whose first element is the key that designates in the domain the
 *                        value map to be iterated over, and whose second element designates the key
 *                        required to pass a single value to the inner computation.
 * @param resultKey       The key that will designate the result of the computation in the outgoing
 *                        domain of facts. The result takes the form of a Map of the results of each
 *                        inner computation, mapped from the keys of the original value map.
 */
class MappingComputation[A, +B, +MapType <: MapLike[A, B, MapType] with Map[A, B]](val inner: Computation,
                           inputMapping: (Symbol, Symbol),
                           val resultKey: Symbol) extends Computation {

  def compute(domain: Domain): Domain = {
    val input: Any = domain.facts.get(inputMapping._1).get
    val resultMap = computeResultSequence(input, domain)
    Domain.combine(Map(resultKey -> resultMap), domain)
  }

  private def computeResultSequence(input: Any, originalDomain: Domain): Map[A, Any] = {
    val inputSequence = input.asInstanceOf[MapLike[A, B, MapType]]

    inputSequence.foldLeft(Map[A, Any]()) {
      (resultsSoFar: Map[A, Any], keyValueTuple: (A, B)) => {
        val domainWithSingleValueAdded = Domain.combine(Map(inputMapping._2 -> keyValueTuple._2), originalDomain)

        val innerResults = inner.compute(domainWithSingleValueAdded)
        innerResults.facts.get(inner.resultKey) match {
          case Some(result) => {
            val theResults = resultsSoFar + (keyValueTuple._1 -> result)
            if(innerResults.continue) theResults else return theResults
          }
          case None => resultsSoFar
        }
      }
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy