Skip to content

Commit

Permalink
Intermediate results of any and all iterations are now collected in t…
Browse files Browse the repository at this point in the history
…he context under a synthesized fact.
  • Loading branch information
jhkuperus committed Nov 4, 2016
1 parent 20d1d44 commit 7408fa7
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 11 deletions.
32 changes: 22 additions & 10 deletions engine-core/src/main/scala/org/scalarules/engine/FactEngine.scala
@@ -1,6 +1,7 @@
package org.scalarules.engine

import org.scalarules.derivations._
import org.scalarules.facts.{Fact, ListFact, SynthesizedFact}

import scala.util.{Failure, Success, Try}

Expand Down Expand Up @@ -54,27 +55,35 @@ object FactEngine {
def runNormalDerivations(c: Context, derivations: List[Derivation]): Context = {
def evaluator(c: Context, d: Derivation): Context = {
if (!c.contains(d.output) && d.condition(c)) {
val operation = d match {
d match {
case der: SubRunDerivation => {
val options: Seq[Option[Any]] = runSubCalculations(c, der.subRunData)
Some(options.flatten)
val subRunResults: Seq[(Option[Any], Context)] = runSubCalculations(c, der.subRunData)

val (results, contexts) = subRunResults.unzip

c + (d.output -> results.flatten) + (synthesizeIterationResultsFact(d.output) -> contexts)
}
case der: DefaultDerivation => der.operation(c)
case der: DefaultDerivation =>
val result = der.operation(c)
if (result.isEmpty) c else c + (d.output -> result.get)
}

if (operation.isEmpty) c else c + (d.output -> operation.get)
} else {
c
}
}

def runSubCalculations[B](c: Context, subRunData: SubRunData[Any, B]): Seq[Option[Any]] = {
subRunData.inputList.toEval(c).get.map(input => subRunData.yieldValue(runDerivations(c ++ subRunData.contextAdditions(input), subRunData.derivations, evaluator)))
def runSubCalculations[B](c: Context, subRunData: SubRunData[Any, B]): Seq[(Option[Any], Context)] = {
subRunData.inputList.toEval(c).get.map(input => {
val subRunContext: Context = runDerivations(c ++ subRunData.contextAdditions(input), subRunData.derivations, evaluator)
(subRunData.yieldValue(subRunContext), subRunContext -- c.keys)
})
}

runDerivations(c, derivations, evaluatorExceptionWrapper(evaluator))
}

private def synthesizeIterationResultsFact(fact: Fact[Any]) =
SynthesizedFact[List[Context]](fact, "iteration_results", s"Synthesized fact to contain intermediate results of an iterated evalution over ${fact.name}")

/**
* Runs the provided Derivations and yields the resulting Context as well as a List of all Steps computed.
Expand All @@ -92,8 +101,11 @@ object FactEngine {
d match {
case der: SubRunDerivation => {
val (results, subSteps): (List[Context], List[List[Step]]) = runSubCalculations(c, der.subRunData, d).unzip
val options = results.map(der.subRunData.yieldValue)
processStep(d, c, subSteps.flatten ::: steps, Some(options.flatten))
val iterationResultOptions = results.map(der.subRunData.yieldValue)

val newContext = c + (d.output -> iterationResultOptions.flatten) + (synthesizeIterationResultsFact(d.output) -> results)
val newStep = Step(c, d, "Completed iterated evaluation", newContext)
(newContext, newStep :: subSteps.flatten ::: steps)
}
case der: DefaultDerivation => processStep(d, c, steps, der.operation(c))
}
Expand Down
6 changes: 6 additions & 0 deletions engine-core/src/main/scala/org/scalarules/facts/facts.scala
Expand Up @@ -31,3 +31,9 @@ case object OriginFact extends Fact[Nothing] {

def valueType: String = "Nothing"
}

case class SynthesizedFact[+A](factOriginalFact: Fact[Any], synthesizedPostfix: String, description: String = "", valueType: String = "") extends Fact[A] {
def name: String = factOriginalFact.name + "_" + synthesizedPostfix

def toEval: Evaluation[A] = new SingularFactEvaluation[A](this)
}
Expand Up @@ -6,6 +6,7 @@ import org.scalatest.prop.GeneratorDrivenPropertyChecks
import org.scalatest.{FlatSpec, Matchers}
import org.scalarules.derivations._
import org.scalarules.facts.Fact
import org.scalarules.utils.PrettyPrinter

class FactEngineTestComputeInputs extends FlatSpec with Matchers {

Expand Down Expand Up @@ -134,7 +135,8 @@ class FactEngineTestRunSubRunDefaultDerivations extends FlatSpec with Matchers w
it should "calculate values correctly when all input values are available" in {
val result = FactEngine.runNormalDerivations(context, List(FactEngineTestValues.derivationSubRun))

result should have size (context.size + 1)
// We're expecting the actual output as well as the synthesized fact containing all iteration results
result should have size (context.size + 2)
result(SubRunOutput) should equal (inputs.map(i => i + 10))
}

Expand Down

0 comments on commit 7408fa7

Please sign in to comment.