<commit_before>package cdgp

import fuel.core.StatePop
import fuel.func._
import fuel.util.{CallCounter, CallEvery, Collector, Options, TRandom}
import swim.eval.{EpsLexicaseSelection, LexicaseSelection01}
import swim.tree._

import scala.collection.mutable


/**
  * This file contains some ready to use genetic programming algorithms using
  * Counterexample-Driven Genetic Programming (CDGP) in various configurations.
  * You may use them as examples to construct your own variations.
  */




/**
  * Trait for common aspects of CDGP algorithms defined in this file.
  * @tparam S Class representing solutions
  * @tparam E Class representing evaluations
  */
trait CDGPAlgorithm[S <: Op, E <: Fitness] {
  def cdgpState: State
  def bsf: BestSoFar[S, E]

  /**
    * Current state of the population. Should be updated after every iteration.
    */
  var pop: Option[StatePop[(S, E)]] = None

  /**
    * Number of generations until the algorithm ended.
    */
  var numGenerations = 0

  /**
    * Saves the provided state of the population and returns it, so that it can
    * be used further in the pipe.
    */
  def updateAfterIteration(s: StatePop[(S, E)]): StatePop[(S, E)] = {
    pop = Some(s)
    numGenerations += 1
    s
  }

  def reportStats(s: StatePop[(S, E)])
                 (implicit opt: Options, coll: Collector): StatePop[(S, E)] = {
    if (bsf.bestSoFar.isDefined) {
      val (bestOfRun, e) = bsf.bestSoFar.get
      e.saveInColl(coll)
      val solutionOrigSmtlib = SMTLIBFormatter.opToString(bestOfRun)
      val solutionSimpSmtlib = cdgpState.simplifySolution(solutionOrigSmtlib).getOrElse(solutionOrigSmtlib).
        replace("?)", ")").replace("? ", " ") // clean trailing '?' in double outputs from SMT solver
      val solutionSimpOp = SMTLIBFormatter.smtlibToOp(solutionSimpSmtlib)

      coll.set("result.bestOrig", bestOfRun)
      coll.set("result.bestOrig.smtlib", solutionOrigSmtlib)
      coll.set("result.bestOrig.size", bestOfRun.size)
      coll.set("result.bestOrig.height", bestOfRun.height)

      coll.set("result.best", solutionSimpOp)
      coll.set("result.best.smtlib", solutionSimpSmtlib)
      coll.set("result.best.size", solutionSimpOp.size)
      coll.set("result.best.height", solutionSimpOp.height)

      coll.set("result.totalGenerations", numGenerations)
    }
    cdgpState.reportData()
    s
  }
}


/**
 * Implements a termination condition based on no progress on the validation set.
 * If the number of calls exceeds the maxWithoutImprovement parameter, a signal to
 * terminate evolution is send. However, if the solution was not changed in the meantime,
 * termination signal will be issued only in case new different solution will be worse.
 */
class ValidationSetTermination[E](trainingSet: Seq[(Map[String, Any], Option[Any])],
                                  validationSet: Seq[(Map[String, Any], Option[Any])],
                                  evaluatorComplete: EvaluatorCompleteTests[Double],
                                  maxWithoutImprovement: Int)
                                 (implicit coll: Collector)
  extends Function1[BestSoFar[Op, E], Boolean] {
  val logTrainSet: mutable.ArrayBuffer[Double] = mutable.ArrayBuffer[Double]()
  val logValidSet: mutable.ArrayBuffer[Double] = mutable.ArrayBuffer[Double]()
  var bsfValid: Option[(Op, Double)] = None  // contains the best solution found at some time in evolution and evaluation on the validation set
  var iterNotImproved: Int = 0
  override def apply(bsf: BestSoFar[Op, E]): Boolean = {
    if (bsf.bestSoFar.isEmpty)
      throw new Exception("Trying to evaluate empty bestOfRun on the validation set.")
    else {
      val (bOp, _) = bsf.bestSoFar.get
      val errorT = error(bOp, trainingSet)  // because e.g. in Lexicase fitness is a sequence
      val errorV = error(bOp, validationSet)  // because e.g. in Lexicase fitness is a sequence
      // print(s"Errors: $errorT (train)   $errorV (valid)")
      logTrainSet.append(errorT)
      logValidSet.append(errorV)
      coll.set("cdgp.errorT", errorT)
      coll.set("cdgp.errorV", errorV)
      if (bsfValid.isEmpty) {
        bsfValid = Some((bOp, errorV))
        iterNotImproved = 0
        false
      }
      else {
        val (bvOp, bvErrorV) = bsfValid.get
        if (errorV < bvErrorV) {  // if solution is better on the validation set
          bsfValid = Some((bOp, errorV))
          iterNotImproved = 0
          false
        }
        else {
          iterNotImproved += 1
          if (!bOp.equals(bvOp) && iterNotImproved > maxWithoutImprovement) true else false
        }
      }
    }<commit_msg>Minor<commit_after>package cdgp

import fuel.core.StatePop
import fuel.func._
import fuel.util.{CallCounter, CallEvery, Collector, Options, TRandom}
import swim.eval.{EpsLexicaseSelection, LexicaseSelection01}
import swim.tree._

import scala.collection.mutable


/**
  * This file contains some ready to use genetic programming algorithms using
  * Counterexample-Driven Genetic Programming (CDGP) in various configurations.
  * You may use them as examples to construct your own variations.
  */




/**
  * Trait for common aspects of CDGP algorithms defined in this file.
  * @tparam S Class representing solutions
  * @tparam E Class representing evaluations
  */
trait CDGPAlgorithm[S <: Op, E <: Fitness] {
  def cdgpState: State
  def bsf: BestSoFar[S, E]

  /**
    * Current state of the population. Should be updated after every iteration.
    */
  var pop: Option[StatePop[(S, E)]] = None

  /**
    * Number of generations until the algorithm ended.
    */
  var numGenerations = 0

  /**
    * Saves the provided state of the population and returns it, so that it can
    * be used further in the pipe.
    */
  def updateAfterIteration(s: StatePop[(S, E)]): StatePop[(S, E)] = {
    pop = Some(s)
    numGenerations += 1
    s
  }

  def reportStats(s: StatePop[(S, E)])
                 (implicit opt: Options, coll: Collector): StatePop[(S, E)] = {
    if (bsf.bestSoFar.isDefined) {
      val (bestOfRun, e) = bsf.bestSoFar.get
      e.saveInColl(coll)
      val solutionOrigSmtlib = SMTLIBFormatter.opToString(bestOfRun)
      val solutionSimpSmtlib = cdgpState.simplifySolution(solutionOrigSmtlib).getOrElse(solutionOrigSmtlib).
        replace("?)", ")").replace("? ", " ") // clean trailing '?' in double outputs from SMT solver
      val solutionSimpOp = SMTLIBFormatter.smtlibToOp(solutionSimpSmtlib)

      coll.set("result.bestOrig", bestOfRun)
      coll.set("result.bestOrig.smtlib", solutionOrigSmtlib)
      coll.set("result.bestOrig.size", bestOfRun.size)
      coll.set("result.bestOrig.height", bestOfRun.height)

      coll.set("result.best", solutionSimpOp)
      coll.set("result.best.smtlib", solutionSimpSmtlib)
      coll.set("result.best.size", solutionSimpOp.size)
      coll.set("result.best.height", solutionSimpOp.height)

      coll.set("result.totalGenerations", numGenerations)
    }
    cdgpState.reportData()
    s
  }
}


/**
 * Implements a termination condition based on no progress on the validation set.
 * If the number of calls exceeds the maxWithoutImprovement parameter, a signal to
 * terminate evolution is send. However, if the solution was not changed in the meantime,
 * termination signal will be issued only in case new different solution will be worse.
 */
class ValidationSetTermination[E](trainingSet: Seq[(Map[String, Any], Option[Any])],
                                  validationSet: Seq[(Map[String, Any], Option[Any])],
                                  evaluatorComplete: EvaluatorCompleteTests[Double],
                                  maxWithoutImprovement: Int)
                                 (implicit coll: Collector)
  extends Function1[BestSoFar[Op, E], Boolean] {
  val logTrainSet: mutable.ArrayBuffer[Double] = mutable.ArrayBuffer[Double]()
  val logValidSet: mutable.ArrayBuffer[Double] = mutable.ArrayBuffer[Double]()
  var bsfValid: Option[(Op, Double)] = None  // contains the best solution found at some time in evolution and evaluation on the validation set
  var iterNotImproved: Int = 0
  override def apply(bsf: BestSoFar[Op, E]): Boolean = {
    if (bsf.bestSoFar.isEmpty)
      throw new Exception("Trying to evaluate empty bestOfRun on the validation set.")
    else {
      val (bOp, _) = bsf.bestSoFar.get
      val errorT = error(bOp, trainingSet)  // because e.g. in Lexicase fitness is a sequence
      val errorV = error(bOp, validationSet)  // because e.g. in Lexicase fitness is a sequence
      // println(s"Errors: $errorT (train)   $errorV (valid)")
      logTrainSet.append(errorT)
      logValidSet.append(errorV)
      coll.set("cdgp.errorT", errorT)
      coll.set("cdgp.errorV", errorV)
      if (bsfValid.isEmpty) {
        bsfValid = Some((bOp, errorV))
        iterNotImproved = 0
        false
      }
      else {
        val (bvOp, bvErrorV) = bsfValid.get
        if (errorV < bvErrorV) {  // if solution is better on the validation set
          bsfValid = Some((bOp, errorV))
          iterNotImproved = 0
          false
        }
        else {
          iterNotImproved += 1
          if (!bOp.equals(bvOp) && iterNotImproved > maxWithoutImprovement) true else false
        }
      }
    }
{'fraction_non_alphanumeric': 0.10406750324534833, 'fraction_numerical': 0.0019472090004327132, 'mean_word_length': 2.935717326521924, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4726789', 'n_tokens_mistral': 2885, 'n_tokens_neox': 2785, 'n_words': 796}