Commit 4c729009 authored by Romain Reuillon's avatar Romain Reuillon
Browse files

[Core] enh: refactor sampling type class

parent aaac72f4
......@@ -17,7 +17,8 @@
package org.openmole.core.workflow.sampling
import org.openmole.core.context.{ Val, Variable }
import org.openmole.core.context.{ PrototypeSet, Val, Variable }
import org.openmole.core.expansion.{ FromContext, Validate }
/**
* An explicit sampling associates a prototype to an explicit set of values given through an iterable.
......@@ -27,9 +28,12 @@ import org.openmole.core.context.{ Val, Variable }
*/
object ExplicitSampling {
def apply[T](prototype: Val[T], data: Iterable[T]) = Sampling { _
data.map { v List(Variable(prototype, v)) }.iterator
} outputs (Seq(prototype))
implicit def isSampling[T]: IsSampling[ExplicitSampling[T]] = s
Sampling(
s.data.map { v List(Variable(s.prototype, v)) }.iterator,
Seq(s.prototype)
)
}
case class ExplicitSampling[T](prototype: Val[T], data: Iterable[T])
......@@ -6,18 +6,13 @@ import org.openmole.core.expansion.{ FromContext, Validate }
object FromContextSampling {
def apply(samples: FromContext.Parameters Iterator[Iterable[Variable[_]]]) = new FromContextSampling(samples, PrototypeSet.empty, Iterable.empty, Validate.success)
implicit def isSampling: IsSampling[FromContextSampling] = new IsSampling[FromContextSampling] {
override def validate(s: FromContextSampling): Validate = s.v
override def inputs(s: FromContextSampling): PrototypeSet = s.i
override def outputs(s: FromContextSampling): Iterable[Val[_]] = s.o
override def apply(s: FromContextSampling) = FromContext(s.samples)
def combine(s1: Iterator[Iterable[Variable[_]]], s2: Sampling) = FromContext { p
import p._
for (x s1; y s2().from(context ++ x)) yield x ++ y
}
}
implicit def isSampling: IsSampling[FromContextSampling] = s
Sampling(
FromContext(s.samples),
s.o,
s.i,
s.v
)
}
/**
......
......@@ -20,39 +20,13 @@ package org.openmole.core.workflow.sampling
import org.openmole.core.context._
import org.openmole.core.expansion._
object Sampling {
/**
* API constructor for FromContextSampling
* @param samples
* @return
*/
def apply(samples: FromContext.Parameters Iterator[Iterable[Variable[_]]]) = FromContextSampling(samples)
case class Sampling(
sampling: FromContext[Iterator[Iterable[Variable[_]]]],
outputs: Iterable[Val[_]],
inputs: PrototypeSet = PrototypeSet.empty,
validate: Validate = Validate.success)
trait IsSampling[-S] {
def apply(s: S): Sampling
}
trait Sampling {
def validate: Validate = Validate.success
/**
* Prototypes of the variables required by this sampling.
*
* @return the data
*/
def inputs: PrototypeSet = PrototypeSet.empty
/**
* Prototypes of the factors generated by this sampling.
*
* @return the factors prototypes
*/
def outputs: Iterable[Val[_]]
/**
* This method builds the explored plan in the given {@code context}.
*/
@throws(classOf[Throwable])
def apply(): FromContext[Iterator[Iterable[Variable[_]]]]
}
......@@ -17,7 +17,6 @@
package org.openmole.core.workflow
import org.openmole.core.expansion.{ FromContext, Validate }
/**
* Sampling aims at associating prototypes with values.
......@@ -39,34 +38,42 @@ package sampling {
implicit def fromContextIsDiscrete[T]: DiscreteFromContextDomain[FromContext[T], T] = domain Domain(domain.map(v Vector(v).iterator))
implicit def fromIsSampling[T](t: T)(implicit isSampling: IsSampling[T]) =
new Sampling {
override def validate = isSampling.validate(t)
override def inputs = isSampling.inputs(t)
override def outputs: Iterable[Val[_]] = isSampling.outputs(t)
override def apply(): FromContext[Iterator[Iterable[Variable[_]]]] = isSampling.apply(t)
}
implicit def fromIsSampling[T](t: T)(implicit isSampling: IsSampling[T]) = isSampling(t)
implicit def factorIsSampling[D, T](implicit domain: DiscreteFromContextDomain[D, T]) = new IsSampling[Factor[D, T]] {
def validate(f: Factor[D, T]): Validate = {
implicit def factorIsSampling[D, T](implicit domain: DiscreteFromContextDomain[D, T]): IsSampling[Factor[D, T]] = f => {
def inputs = {
val domainValue = domain(f.domain)
domainValue.domain.validate ++ domainValue.validation
domain(f.domain).inputs ++ domainValue.inputs
}
def inputs(f: Factor[D, T]) = {
def outputs = List(f.value)
def validate: Validate = {
val domainValue = domain(f.domain)
domain(f.domain).inputs ++ domainValue.inputs
domainValue.domain.validate ++ domainValue.validation
}
def outputs(f: Factor[D, T]) = List(f.value)
override def apply(f: Factor[D, T]): FromContext[Iterator[collection.Iterable[Variable[T]]]] =
domain(f.domain).domain.map { values values.map { v List(Variable(f.value, v)) } }
Sampling(
domain(f.domain).domain.map { values values.map { v List(Variable(f.value, v)) } },
outputs,
inputs,
validate,
)
}
type Sampling = sampling.Sampling
def EmptySampling() = Sampling { _ Iterator.empty }
object EmptySampling {
implicit def isSampling: IsSampling[EmptySampling] = s =>
Sampling(
FromContext.value(Iterator.empty),
Seq(),
Seq(),
Validate.success
)
}
case class EmptySampling()
}
}
......@@ -89,37 +96,4 @@ package object sampling {
*/
def Factor[D, T](p: Val[T], d: D) = In(p, d)
object IsSampling {
implicit def samplingIsSampling = new IsSampling[Sampling] {
override def validate(s: Sampling) = s.validate
override def inputs(s: Sampling): PrototypeSet = s.inputs
override def outputs(s: Sampling): Iterable[Val[_]] = s.outputs
override def apply(s: Sampling): FromContext[Iterator[Iterable[Variable[_]]]] = s()
}
}
trait IsSampling[-S] {
def validate(s: S): Validate
/**
* Prototypes of the variables required by this sampling.
*
* @return the data
*/
def inputs(s: S): PrototypeSet
/**
* Prototypes of the factors generated by this sampling.
*
* @return the factors prototypes
*/
def outputs(s: S): Iterable[Val[_]]
/**
* This method builds the explored plan in the given {@code context}.
*/
def apply(s: S): FromContext[Iterator[Iterable[Variable[_]]]]
}
}
\ No newline at end of file
......@@ -49,10 +49,10 @@ object ExplorationTask {
import p._
val variablesValues = {
val samplingValue = sampling(s).from(context).toVector
val samplingValue = sampling(s).sampling.from(context).toVector
val values =
TreeMap.empty[Val[_], Array[_]] ++ sampling.outputs(s).map { p
TreeMap.empty[Val[_], Array[_]] ++ sampling(s).outputs.map { p
p p.`type`.manifest.newArray(samplingValue.size)
}
......@@ -75,9 +75,9 @@ object ExplorationTask {
}
}: Context
} set (
inputs += (sampling.inputs(s).toSeq: _*),
exploredOutputs += (sampling.outputs(s).toSeq.map(_.toArray): _*)
) withValidate { sampling.validate(s) }
inputs += (sampling(s).inputs.toSeq: _*),
exploredOutputs += (sampling(s).outputs.toSeq.map(_.toArray): _*)
) withValidate { sampling(s).validate }
/**
* Given a [[MoleCapsule]], function to test if a given prototype is explored by it
......
......@@ -168,7 +168,7 @@ package object directsampling {
ExplorationTask(sampling)
}
def sampled = implicitly[IsSampling[S]].outputs(sampling).toSeq
def sampled = implicitly[IsSampling[S]].apply(sampling).outputs.toSeq
}
implicit class DirectSamplingHookDecorator[M](t: M)(implicit method: ExplorationMethod[M, DirectSampling.Method]) extends MethodHookDecorator[M, DirectSampling.Method](t) {
......
......@@ -29,28 +29,6 @@ object SensitivityMorris {
case class Method(inputs: Seq[ScalarOrSequenceOfDouble], outputs: Seq[Val[_]])
implicit def method: ExplorationMethod[SensitivityMorris, Method] = m {
implicit def defScope = m.scope
// the sampling for Morris is a One At a Time one,
// with respect to the user settings for repetitions, levels and inputs
val sampling = MorrisSampling(m.sample, m.level, m.inputs)
// the aggregation obviously is a Morris aggregation!
// it collects all the specific inputs added from the sampling
// to interpret the results
val aggregation = MorrisAggregation(m.inputs, m.outputs)
val w =
MapReduce(
evaluation = m.evaluation,
sampler = ExplorationTask(sampling),
aggregation = aggregation
)
DSLContainer(w, method = Method(m.inputs, m.outputs))
}
object MorrisHook {
def apply[F](method: Method, output: WritableOutput, format: F = CSVOutputFormat())(implicit name: sourcecode.Name, definitionScope: DefinitionScope, outputFormat: OutputFormat[F, Method]) =
......@@ -233,6 +211,14 @@ object SensitivityMorris {
object MorrisSampling extends JavaLogger {
implicit def isSampling: IsSampling[MorrisSampling] = s
Sampling(
s.apply(),
s.outputs,
s.inputs,
s.validate
)
def apply(
repetitions: FromContext[Int],
levels: FromContext[Int],
......@@ -248,8 +234,8 @@ object SensitivityMorris {
* The variable named like this contains the name of the factor which was changed
* in a given point.
*/
val varFactorName = Val[String]("factorname", namespace = namespace)
val varDelta = Val[Double]("delta", namespace = namespace)
val varFactorName: Val[String] = Val[String]("factorname", namespace = namespace)
val varDelta: Val[Double] = Val[Double]("delta", namespace = namespace)
/**
* For a given count of factors k, a number of levels p,
......@@ -326,10 +312,12 @@ object SensitivityMorris {
sealed class MorrisSampling(
val repetitions: FromContext[Int],
val levels: FromContext[Int],
val factors: Seq[ScalarOrSequenceOfDouble]) extends Sampling {
val factors: Seq[ScalarOrSequenceOfDouble]) {
override def inputs = factors.flatMap(_.inputs)
override def outputs = factors.map { _.prototype } ++ Seq(
def validate = repetitions.validate ++ levels.validate
def inputs = factors.flatMap(_.inputs)
def outputs = factors.map { _.prototype } ++ Seq(
MorrisSampling.varDelta,
MorrisSampling.varFactorName)
......@@ -362,7 +350,7 @@ object SensitivityMorris {
}
override def apply(): FromContext[Iterator[Iterable[Variable[_]]]] = FromContext { ctxt
def apply(): FromContext[Iterator[Iterable[Variable[_]]]] = FromContext { ctxt
import ctxt._
val r: Int = repetitions.from(context)
val p: Int = levels.from(context)
......@@ -383,6 +371,28 @@ object SensitivityMorris {
}
implicit def method: ExplorationMethod[SensitivityMorris, Method] = m {
implicit def defScope = m.scope
// the sampling for Morris is a One At a Time one,
// with respect to the user settings for repetitions, levels and inputs
val sampling = MorrisSampling(m.sample, m.level, m.inputs)
// the aggregation obviously is a Morris aggregation!
// it collects all the specific inputs added from the sampling
// to interpret the results
val aggregation = MorrisAggregation(m.inputs, m.outputs)
val w =
MapReduce(
evaluation = m.evaluation,
sampler = ExplorationTask(sampling),
aggregation = aggregation
)
DSLContainer(w, method = Method(m.inputs, m.outputs))
}
}
/**
......
......@@ -217,12 +217,13 @@ object SensitivitySaltelli {
def matrix = Seq(matrixName, matrixIndex)
def apply(samples: FromContext[Int], sobolSampling: FromContext[Boolean], factors: ScalarOrSequenceOfDouble*) =
Sampling { p
implicit def isSampling: IsSampling[SaltelliSampling] = saltelli => {
def apply = FromContext { p =>
import p._
val s = samples.from(context)
val vectorSize = factors.map(_.size(context)).sum
val isSobol = sobolSampling.from(context)
val s = saltelli.samples.from(context)
val vectorSize = saltelli.factors.map(_.size(context)).sum
val isSobol = saltelli.sobolSampling.from(context)
val (a, b) =
if (isSobol) {
......@@ -233,7 +234,7 @@ object SensitivitySaltelli {
val cIndices =
for {
f factors
f saltelli.factors
j (0 until f.size(context))
} yield (f, j, f.isScalar)
......@@ -242,7 +243,7 @@ object SensitivitySaltelli {
m: Namespace): List[Iterable[Variable[_]]] =
matrix.zipWithIndex.map {
case (l, index)
def line = ScalarOrSequenceOfDouble.unflatten(factors, l).from(context)
def line = ScalarOrSequenceOfDouble.unflatten(saltelli.factors, l).from(context)
Variable(SaltelliSampling.matrixName, m.toString) :: Variable(SaltelliSampling.matrixIndex, index) :: line
}.toList
......@@ -263,15 +264,24 @@ object SensitivitySaltelli {
}
(aVariables ++ bVariables ++ cVariables).iterator
} validate { samples.validate } inputs { factors.flatMap(_.inputs) } prototypes { factors.map(_.prototype) ++ matrix }
def apply(samples: FromContext[Int], factors: ScalarOrSequenceOfDouble*): FromContextSampling = SaltelliSampling(samples, true, factors: _*)
}
Sampling(
apply,
saltelli.factors.map(_.prototype) ++ matrix,
saltelli.factors.flatMap(_.inputs),
saltelli.samples.validate
)
}
def apply(samples: FromContext[Int], factors: ScalarOrSequenceOfDouble*): SaltelliSampling =
new SaltelliSampling(samples, true, factors: _*)
def buildC(
i: Int,
a: Array[Array[Double]],
b: Array[Array[Double]]
) =
i: Int,
a: Array[Array[Double]],
b: Array[Array[Double]]) =
a zip b map {
case (lineOfA, lineOfB) buildLineOfC(i, lineOfA, lineOfB)
}
......@@ -283,6 +293,8 @@ object SensitivitySaltelli {
}
case class SaltelliSampling(samples: FromContext[Int], sobolSampling: FromContext[Boolean], factors: ScalarOrSequenceOfDouble*)
}
......
......@@ -22,49 +22,63 @@ import org.openmole.core.expansion.{ FromContext, Validate }
import org.openmole.core.workflow.sampling._
object CompleteSampling {
implicit def isSampling: IsSampling[CompleteSampling] = s
Sampling(
s.apply(),
s.outputs,
s.inputs,
s.validate)
def apply(samplings: Sampling*) = new CompleteSampling(samplings: _*)
}
class CompleteSampling(val samplings: Sampling*) extends Sampling {
case class CompleteSampling(samplings: Sampling*) {
override def validate: Validate = samplings.flatMap(_.validate)
override def inputs = PrototypeSet.empty ++ samplings.flatMap { _.inputs }
override def outputs: Iterable[Val[_]] = samplings.flatMap { _.outputs }
def validate: Validate = samplings.flatMap(_.validate)
def inputs = PrototypeSet.empty ++ samplings.flatMap { _.inputs }
def outputs: Iterable[Val[_]] = samplings.flatMap { _.outputs }
override def apply() = FromContext { p
def apply() = FromContext { p
import p._
if (samplings.isEmpty) Iterator.empty
else
samplings.tail.foldLeft(samplings.head().from(context)) {
samplings.tail.foldLeft[Iterator[Iterable[Variable[_]]]](samplings.head.sampling.from(context)) {
(a, b) combine(a, b).from(context)
}
}
def combine(s1: Iterator[Iterable[Variable[_]]], s2: Sampling) = FromContext { p
import p._
for (x s1; y s2().from(context ++ x)) yield x ++ y
for (x s1; y s2.sampling.from(context ++ x)) yield x ++ y
}
}
object XSampling {
implicit def isSampling[S1, S2]: IsSampling[XSampling[S1, S2]] = new IsSampling[XSampling[S1, S2]] {
override def validate(s: XSampling[S1, S2]): Validate =
s.sampling1.validate(s.s1) ++ Validate { p
implicit def isSampling[S1, S2]: IsSampling[XSampling[S1, S2]] = s {
def validate =
s.sampling1(s.s1).validate ++ Validate { p
import p._
s.sampling2.validate(s.s2).apply(p.inputs ++ s.sampling1.outputs(s.s1))
s.sampling2(s.s2).validate.apply(p.inputs ++ s.sampling1(s.s1).outputs)
}
override def inputs(s: XSampling[S1, S2]): PrototypeSet = s.sampling1.inputs(s.s1) ++ s.sampling2.inputs(s.s2)
override def outputs(s: XSampling[S1, S2]): Iterable[Val[_]] = s.sampling1.outputs(s.s1) ++ s.sampling2.outputs(s.s2)
override def apply(s: XSampling[S1, S2]): FromContext[Iterator[Iterable[Variable[_]]]] = FromContext { p
def inputs = s.sampling1(s.s1).inputs ++ s.sampling2(s.s2).inputs
def outputs = s.sampling1(s.s1).outputs ++ s.sampling2(s.s2).outputs
def apply = FromContext { p
import p._
for {
x s.sampling1.apply(s.s1).apply(context)
y s.sampling2.apply(s.s2).apply(context ++ x)
x s.sampling1(s.s1).sampling.from(context)
y s.sampling2(s.s2).sampling.from(context ++ x)
} yield x ++ y
}
Sampling(
apply,
outputs,
inputs = inputs,
validate = validate
)
}
}
......
......@@ -21,21 +21,28 @@ import org.openmole.core.dsl.extension._
object ConcatenateSampling {
implicit def isSampling[S1, S2]: IsSampling[ConcatenateSampling[S1, S2]] = new IsSampling[ConcatenateSampling[S1, S2]] {
override def validate(s: ConcatenateSampling[S1, S2]): Validate = s.sampling1.validate(s.s1) ++ s.sampling2.validate(s.s2)
override def inputs(s: ConcatenateSampling[S1, S2]): PrototypeSet = s.sampling1.inputs(s.s1) ++ s.sampling2.inputs(s.s2)
override def outputs(s: ConcatenateSampling[S1, S2]): Iterable[Val[_]] = {
val p1 = s.sampling1.outputs(s.s1).toSet
val p2 = s.sampling2.outputs(s.s2).toSet
implicit def isSampling[S1, S2]: IsSampling[ConcatenateSampling[S1, S2]] = s {