views:

344

answers:

3

I'm working on a framework for a EA (evolutionary alg) project in Scala. In this i have a trait that implements common EA-code and leaves problem spesific code like genotype convertion and fitness testing to classes that implement this trait. However, I don't want to fully implement the trait before it is actually run because of testing different population selection protocols/strategies. This gives the code

trait EAProblem{
// common code ...
   def fitness(ind:Individual):Double
   def selectionStrategy(p: Population): List[(Individual, Double)]
   def nextGeneration(p: Population): Population
}

/* Silly test problem */
abstract class OneMax(logPath: String) extends EAProblem {
  def phenotype(ind:Individual) = {
    ind.genotype
  }
  def fitness(ind: Individual): Double = {
    ind.genotype.size.toFloat / ind.genotype.capacity
  }
}

At runtime the protocol/strategy is choosen:

object EASelectionStrategyProtocolDemo {
  def main(args: Array[String]) {

    val problem_impl = List[EAProblem](
      // Full replacement
      new OneMax("sigma_strat_full-rep_prot_onemax.log.dat") {
        def selectionStrategy(p: Population): List[(Individual, Double)] =
          SelectionStrategies.sigmaScalingMatingSelection(p)
        def nextGeneration(p: Population): Population = SelectionProtocols.fullReplacement(p)
      },
      new OneMax("boltz_strat_full-rep_prot_onemax.log.dat") {
        def selectionStrategy(p: Population): List[(Individual, Double)] =
          SelectionStrategies.boltzmannSelection(p)
        def nextGeneration(p: Population): Population = SelectionProtocols.fullReplacement(p)
      })
    for(problem <- problem_impl)
       new Simulator(problem)
}

The SelectionStrategies/SelectionProtocols objects contains clusures with references to other code in EAProblem.

What I want now is some way to instantiate other abstract classes like OneMax (I have many of them) using reflection (or some other mechanism). Pseudocode:

val listOfClassNames = List("OneMax", "classA", "classB", ...)
for(className <- listOfClassNames){
    class_sigma = Class.forname(className)
    /*
    Implement class_class with this code and instantiate it
    def selectionStrategy(p: Population): List[(Individual, Double)] =
          SelectionStrategies.sigmaScalingMatingSelection(p)
    def nextGeneration(p: Population): Population = SelectionProtocols.fullReplacement(p)
    */
    class_boltz = Class.forname(className)
    /*
    Implement class_boltz with this code and instantiate it
    def selectionStrategy(p: Population): List[(Individual, Double)] =
          SelectionStrategies.boltzmannSelection(p)
    def nextGeneration(p: Population): Population = SelectionProtocols.fullReplacement(p)
    */
}
+1  A: 

I don't know if you can instantiate it by reflection while, at the same time, defining the abstract methods. Or, at least, not easily.

Why don't you make these methods into functions? The way I would go about it is something like this:

private var _selectionStrategy: Option[Population => List[(Individual, Double)]] = None
def selectionStrategy(p: Population) = _selectionStrategy.getOrElse(error("Uninitialized selection strategy!"))(p)
def setSelectionStrategy(f: Population => List[(Individual, Double)]) = if (_selectionStrategy.isEmpty)
  _selectionStrategy = f
else
  error("Selection strategy already initialized!")

// Same thing for nextGeneration

Of course, OneMax wouldn't be abstract then. Which, actually, is kind of the point. You then use reflection to create a new instance of OneMax, which is reasonably straight-forward, and use setSelectionStrategy and setNextGeneration to set the functions.

Daniel
A: 

If you really want to use reflection (with runtime code compilation) in this way, I think you're better off with a language like Python. But I don't think you really want to use reflection in this way.

I think your best bet is to have a second class rather than a trait contain the routines that perform fitness measurement. For example,

abstract class Selector {
  def fitness(ind: Individual): Double
  def name: String
}
class Throughput extends Selector {
  def fitness(ind: Individual) = ind.fractionCorrect/ind.computeTime
  def name = "Throughput"
}

You then can

val selectors = List(new Throughput, new ...)
val userInputMap = List.map( t => (t.name , t) ).toMap

and look up the right selector by name.

You then have OneMax (and the others) take the selector as a constructor argument, which you can provide from a string via the userInputMap.

Rex Kerr
+1  A: 
  1. you cannot instantiate abstract class
  2. you don't need abstract class

fitness, selectionStrategy, nextGeneration - are all "independent" variables. Thus tying them togather in one interface is going against the nature of the problem. Try this:

type Fitness = Individual => Double
type SelectionStrategy = Population = > List[(Individual, Double)]
type NextGeneration = Population => Population

case class EAProblem(
  fitness: Fitness, 
  selectionStrategy: SelectionStrategy, 
  nextGeneration: NextGeneration) { /* common code */ }

val fitnesses = List(OneMax, classA, classB, ...)
val strategies = List(
  SelectionStrategies.sigmaScalingMatingSelection, 
  SelectionStrategies.boltzmannSelection)

fitnesses.map ( fitness => 
  strategies.map ( strategy =>
    EAProblem(fitness, strategy, SelectionProtocols.fullReplacement)))

Edit: you can instantiate abstract class ... with CGLib or such

Alexey