Commit 72379236 authored by Romain Reuillon's avatar Romain Reuillon
Browse files

[Plugin] enh: define aggregate for exact evolutions as well

parent afdaf062
......@@ -410,7 +410,7 @@ object GAIntegration {
genome.zipWithIndex.map { case (g, i) Genome.toArrayVariable(g, variables.map(_(i).value)) }.toVector
}
def objectivesOfPopulationToVariables[I](objectives: Seq[Objective[_]], phenotypeValues: Vector[Vector[Double]]): Vector[Variable[_]] =
def objectivesOfPopulationToVariables[I](objectives: Seq[Objective], phenotypeValues: Vector[Vector[Double]]): Vector[Variable[_]] =
Objectives.resultPrototypes(objectives).toVector.zipWithIndex.map {
case (objective, i)
Variable(
......
......@@ -32,8 +32,8 @@ object MetadataGeneration {
}
}
def objectiveData(o: Objective[_]) =
EvolutionMetadata.Objective(o.as.getOrElse(o.prototype.name), o.delta, o.negative, o.noisy)
def objectiveData(o: Objective) =
EvolutionMetadata.Objective(o.as.getOrElse(Objective.prototype(o).name), o.delta, o.negative, o.noisy)
def fromString(s: String): EvolutionMetadata =
decode[EvolutionMetadata](s) match {
......
......@@ -113,7 +113,7 @@ object NSGA2 {
mu: Int,
genome: Genome,
phenotypeContent: PhenotypeContent,
objectives: Seq[Objective[_]],
objectives: Seq[Objective],
operatorExploration: Double,
reject: Option[Condition])
......@@ -206,7 +206,7 @@ object NSGA2 {
operatorExploration: Double,
genome: Genome,
phenotypeContent: PhenotypeContent,
objectives: Seq[Objective[_]],
objectives: Seq[Objective],
historySize: Int,
cloneProbability: Double,
reject: Option[Condition]
......
......@@ -103,7 +103,7 @@ object NSGA3 {
references: ReferencePoints,
genome: Genome,
phenotypeContent: PhenotypeContent,
objectives: Seq[Objective[_]],
objectives: Seq[Objective],
operatorExploration: Double,
reject: Option[Condition])
......@@ -190,7 +190,7 @@ object NSGA3 {
operatorExploration: Double,
genome: Genome,
phenotypeContent: PhenotypeContent,
objectives: Seq[Objective[_]],
objectives: Seq[Objective],
historySize: Int,
cloneProbability: Double,
reject: Option[Condition]
......
......@@ -321,7 +321,7 @@ object NichedNSGA2 {
niche: FromContext[Niche[CDGenome.DeterministicIndividual.Individual[Phenotype], Vector[Int]]],
genome: Genome,
phenotypeContent: PhenotypeContent,
objectives: Seq[Objective[_]],
objectives: Seq[Objective],
operatorExploration: Double,
reject: Option[Condition])
......@@ -425,7 +425,7 @@ object NichedNSGA2 {
operatorExploration: Double,
genome: Genome,
phenotypeContent: PhenotypeContent,
objectives: Seq[Objective[_]],
objectives: Seq[Objective],
historySize: Int,
cloneProbability: Double,
reject: Option[Condition])
......
......@@ -25,7 +25,7 @@ object OSE {
limit: Vector[Double],
genome: Genome,
phenotypeContent: PhenotypeContent,
objectives: Seq[Objective[_]],
objectives: Seq[Objective],
operatorExploration: Double,
reject: Option[Condition])
......@@ -114,7 +114,7 @@ object OSE {
limit: Vector[Double],
genome: Genome,
phenotypeContent: PhenotypeContent,
objectives: Seq[Objective[_]],
objectives: Seq[Objective],
historySize: Int,
cloneProbability: Double,
operatorExploration: Double,
......@@ -285,7 +285,7 @@ object OSE {
def toObjectives(f: Seq[FitnessPattern]) = f.map(_.objective)
}
case class FitnessPattern(objective: Objective[_], limit: Double)
case class FitnessPattern(objective: Objective, limit: Double)
def apply(
origin: Seq[OriginAxe],
......
......@@ -12,56 +12,87 @@ import scala.reflect.ClassTag
object Objective {
object ToObjective {
implicit def valIsToExact[T](implicit td: ToDouble[T]): ToObjective[Val[T]] = v Objective[T](v, td.apply _, negative = false, delta = None, as = None)
implicit def valIsToExact[T](implicit td: ToDouble[T]): ToObjective[Val[T]] = v Objective(_ ComputeValue(v, td.apply _), negative = false, delta = None, as = None)
implicit def negativeToExact[T](implicit exact: ToObjective[T]): ToObjective[Negative[T]] = t exact.apply(t.value).copy(negative = true)
implicit def deltaIsToExact[T, V](implicit exact: ToObjective[T], td: ToDouble[V]): ToObjective[Delta[T, V]] = t exact.apply(t.value).copy(delta = Some(td.apply(t.delta)))
implicit def asIsToExact[T](implicit exact: ToObjective[T]): ToObjective[As[T, String]] = t exact.apply(t.value).copy(as = Some(t.as))
implicit def asValIsToExact[T, P](implicit exact: ToObjective[T]): ToObjective[As[T, Val[P]]] = t exact.apply(t.value).copy(as = Some(t.as.name))
implicit def aggregateStringIsNoisy[T: ClassTag]: ToObjective[Aggregate[Val[T], String]] =
implicit def aggregateStringIsObjective[T: ClassTag]: ToObjective[Aggregate[Val[T], String]] =
(t: Aggregate[Val[T], String]) {
val fromContext: FromContext[Double] = t.aggregate
def aggregate = FromContext { p
import p._
(v: Array[T]) fromContext.from(Context(t.value.toArray -> v))
}
Objective(t.value.array, aggregate, negative = false, delta = None, as = None, validate = fromContext.validate, noisy = true)
def value(noisy: Boolean) =
if (!noisy) {
def aggregate = FromContext { p
import p._
(v: T) fromContext.from(Context(t.value -> v))
}
ComputeValue(t.value, aggregate, aggregateString = true)
}
else {
def aggregate = FromContext { p
import p._
(v: Array[T]) fromContext.from(Context(t.value.toArray -> v))
}
ComputeValue(t.value.array, aggregate, aggregateString = true)
}
Objective(
value,
negative = false,
delta = None,
as = None,
validate = fromContext.validate)
}
implicit def aggregateArrayIsToNoisy[T: ClassTag]: ToObjective[Aggregate[Val[T], Array[T] Double]] = a Objective(a.value.array, a.aggregate, negative = false, delta = None, as = None, noisy = true)
implicit def aggregateSeqIsToNoisy[T: ClassTag]: ToObjective[Aggregate[Val[T], Seq[T] Double]] = a Objective(a.value.array, (v: Array[T]) a.aggregate(v.toVector), negative = false, delta = None, as = None, noisy = true)
implicit def aggregateVectorIsToNoisy[T: ClassTag]: ToObjective[Aggregate[Val[T], Vector[T] Double]] = a Objective(a.value.array, (v: Array[T]) a.aggregate(v.toVector), negative = false, delta = None, as = None, noisy = true)
}
implicit def aggregateIsToObjective[T: ClassTag]: ToObjective[Aggregate[Val[T], T Double]] = a {
Objective(_ ComputeValue(a.value, a.aggregate), negative = false, delta = None, as = None)
}
implicit def aggregateArrayIsToNoisy[T: ClassTag]: ToObjective[Aggregate[Val[T], Array[T] Double]] = a {
Objective(_ ComputeValue(a.value.array, a.aggregate), negative = false, delta = None, as = None, noisy = true)
}
implicit def aggregateSeqIsToNoisy[T: ClassTag]: ToObjective[Aggregate[Val[T], Seq[T] Double]] = a Objective(_ ComputeValue(a.value.array, (v: Array[T]) a.aggregate(v.toVector)), negative = false, delta = None, as = None, noisy = true)
implicit def aggregateVectorIsToNoisy[T: ClassTag]: ToObjective[Aggregate[Val[T], Vector[T] Double]] = a Objective(_ ComputeValue(a.value.array, (v: Array[T]) a.aggregate(v.toVector)), negative = false, delta = None, as = None, noisy = true)
}
trait ToObjective[T] {
def apply(t: T): Objective[_]
def apply(t: T): Objective
}
implicit def toObjective[T: ToObjective](t: T): Objective[_] = implicitly[ToObjective[T]].apply(t)
implicit def toObjective[T: ToObjective](t: T): Objective = implicitly[ToObjective[T]].apply(t)
def toExact(o: Objective[_]) =
def toExact(o: Objective) =
o.noisy match {
case false o
case true throw new UserBadDataError(s"Objective $o cannot be aggregated it should be exact.")
case true throw new UserBadDataError(s"Objective $o aggregation has been defined for a stochastic fitness function.")
}
def toNoisy[T](o: Objective[T]) = {
def toNoisy(o: Objective) = {
o.noisy match {
case true o
case false
def medianAggregation(e: Objective[T]) = FromContext { p
import p._
import org.openmole.tool.statistics._
(p: Array[T]) p.map(e.toDouble.from(context)).median
if (!o.computeValue.aggregateString) {
def medianAggregation[T](v: ComputeValue[T]) = {
def agg = FromContext { p
import p._
import org.openmole.tool.statistics._
(p: Array[T]) p.map(v.toDouble.from(context)).median
}
ComputeValue(v.prototype.array, agg)
}
Objective(_ medianAggregation(o.computeValue), o.negative, o.delta, o.as, noisy = true)
}
Objective(o.prototype.array, medianAggregation(o), o.negative, o.delta, o.as, noisy = true)
else o.copy(noisy = true)
}
}
def toFitnessFunction(phenotypeContent: PhenotypeContent, objectives: Seq[Objective[_]]) = FromContext { p
def toFitnessFunction(phenotypeContent: PhenotypeContent, objectives: Seq[Objective]) = FromContext { p
import p._
(phenotype: Phenotype) {
val context = Phenotype.toContext(phenotypeContent, phenotype)
......@@ -69,7 +100,7 @@ object Objective {
}
}
def aggregate(phenotypeContent: PhenotypeContent, objectives: Seq[Objective[_]]) = FromContext { p
def aggregate(phenotypeContent: PhenotypeContent, objectives: Seq[Objective]) = FromContext { p
import p._
(v: Vector[Phenotype])
......@@ -77,31 +108,41 @@ object Objective {
objectives.toVector.map { _.value.from(context ++ aggregatedContext.values) }
}
def prototype(o: Objective[_]) = if (!o.noisy) o.prototype else o.prototype.unsecureFromArray
def prototype(o: Objective) = if (!o.noisy) o.prototype else o.prototype.unsecureFromArray
}
case class ComputeValue[P](
prototype: Val[P],
toDouble: FromContext[P Double],
aggregateString: Boolean = false) {
case class Objective[P](
prototype: Val[P],
toDouble: FromContext[P Double],
negative: Boolean,
delta: Option[Double],
as: Option[String],
noisy: Boolean = false,
validate: Validate = Validate.success) {
def apply(delta: Option[Double], negative: Boolean) = FromContext { p
import p._
val value = toDouble.from(context).apply(context(prototype))
private def value = FromContext { p
import p._
val value = toDouble.from(context).apply(context(prototype))
def deltaValue =
delta match {
case Some(delta) math.abs(value - delta)
case None value
}
def deltaValue =
delta match {
case Some(delta) math.abs(value - delta)
case None value
}
if (!negative) deltaValue else -deltaValue
}
if (!negative) deltaValue else -deltaValue
}
}
case class Objective(
v: Boolean Objective.ComputeValue[_],
negative: Boolean,
delta: Option[Double],
as: Option[String],
noisy: Boolean = false,
validate: Validate = Validate.success) {
lazy val computeValue = v(noisy)
private def value = computeValue(delta, negative)
private def prototype = computeValue.prototype
}
......@@ -112,10 +153,10 @@ object Objectives {
def toNoisy(o: Objectives) = o.map(o Objective.toNoisy(o))
def resultPrototypes(o: Objectives) = {
def resultPrototype(o: Objective[_]) =
def resultPrototype(o: Objective) =
(o.delta, o.as) match {
case (_, Some(s)) Objective.prototype(o).withName(s)
case (Some(_), None) Objective.prototype(o).withNamespace(o.prototype.namespace.names ++ Seq("delta"))
case (Some(_), None) Objective.prototype(o).withNamespace(Objective.prototype(o).namespace.names ++ Seq("delta"))
case _ Objective.prototype(o)
}
......
......@@ -211,7 +211,7 @@ object PSE {
pattern: Vector[Double] Vector[Int],
genome: Genome,
phenotypeContent: PhenotypeContent,
objectives: Seq[Objective[_]],
objectives: Seq[Objective],
operatorExploration: Double,
reject: Option[Condition],
grid: Seq[PatternAxe]
......@@ -317,7 +317,7 @@ object PSE {
pattern: Vector[Double] Vector[Int],
genome: Genome,
phenotypeContent: PhenotypeContent,
objectives: Seq[Objective[_]],
objectives: Seq[Objective],
historySize: Int,
cloneProbability: Double,
operatorExploration: Double,
......@@ -441,7 +441,7 @@ object PSE {
}
case class PatternAxe(p: Objective[_], scale: Vector[Double])
case class PatternAxe(p: Objective, scale: Vector[Double])
def apply(
genome: Genome,
......
......@@ -170,7 +170,7 @@ object Profile {
niche: Seq[ProfileElement],
genome: Genome,
phenotypeContent: PhenotypeContent,
objectives: Seq[Objective[_]],
objectives: Seq[Objective],
operatorExploration: Double,
reject: Option[Condition])
......@@ -293,7 +293,7 @@ object Profile {
operatorExploration: Double,
genome: Genome,
phenotypeContent: PhenotypeContent,
objectives: Seq[Objective[_]],
objectives: Seq[Objective],
historySize: Int,
cloneProbability: Double,
reject: Option[Condition])
......
......@@ -31,7 +31,7 @@ import squants.time.Time
package object evolution {
type Objectives = Seq[Objective[_]]
type Objectives = Seq[Objective]
type Genome = Seq[Genome.GenomeBound]
implicit def intToCounterTerminationConverter(n: Long) = EvolutionWorkflow.AfterEvaluated(n)
......
......@@ -365,6 +365,23 @@ class WorkflowSpec extends FlatSpec with Matchers {
val a = Val[Double]
val b = Val[Double]
def f(v: Double) = v / 2
val nsga = NSGA2Evolution(
evaluation = EmptyTask() set (inputs += a, outputs += (a, b)),
objective = Seq(b aggregate f _ as "aggF", a aggregate "a / 2"),
genome = Seq(a in (0.0, 1.0)),
termination = 100
)
Validation(nsga).isEmpty should equal(true)
}
"Aggregation" should "be possible in stochastic NSGA" in {
val a = Val[Double]
val b = Val[Double]
def f(v: Vector[Double]) = v.head
val nsga = NSGA2Evolution(
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment