All Downloads are FREE. Search and download functionalities are using the official Maven repository.

mgo.evolution.cmaes.CMAESArchive.scala Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) 2014 Romain Reuillon
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see .
 */

//package mgo.evolution.archive

//import mgo.evolution._
//import mgo.evolution.mutation.MinimumSigma
//import org.apache.commons.math3.linear.{ EigenDecomposition, MatrixUtils, Array2DRowRealMatrix, RealMatrix }
//import org.apache.commons.math3.util.{ MathUtils, FastMath, MathArrays }
//import monocle.syntax._
//
//import scala.util.Random

/*object CMAESArchive {

  case class FIFO[T](val content: List[T]) {
    def +=(t: T, size: Int): FIFO[T] = copy(content = t :: content.slice(0, size - 1))
  }

  case class Archive(
    xmean: RealMatrix,
    B: RealMatrix,
    D: RealMatrix,
    C: RealMatrix,
    BD: RealMatrix,
    diagC: RealMatrix,
    diagD: RealMatrix,
    sigma: Double,
    ps: RealMatrix,
    pc: RealMatrix,
    normps: Double,
    fitnessHistory: FIFO[Double],
    bestValue: Double,
    iterations: Long)

}

trait CMAESArchive <: Archive with Aggregation with GA with RandomValue with MinimumSigma { ar =>

  def initialSigma = 0.3
  def guess(implicit rng: Random) = Seq.fill[Double](genomeSize)(0.5)
  def negminresidualvariance = 0.66
  // where to make up for the variance loss
  def negalphaold = 0.5
  def mu(lambda: Int) = math.ceil(lambda / 2.0).toInt
  def activeCMAES = true

  type A = CMAESArchive.Archive
  def initialArchive(implicit rng: Random): A = {
    // initialize sigma

    // final double[][] sigmaArray = new double[guess.length][1];
    //for (int i = 0; i < guess.length; i++) {
    //  sigmaArray[i][0] = inputSigma[i];
    //}
    val sigmaArray = Array.fill[Double](genomeSize, 1)(initialSigma)
    val insigma = new Array2DRowRealMatrix(sigmaArray, false)
    val sigma = initialSigma // max(insigma); // overall standard deviation

    // initialize termination criteria
    /*stopTolUpX = 1e3 * max(insigma);
    stopTolX = 1e-11 * max(insigma);
    stopTolFun = 1e-12;
    stopTolHistFun = 1e-13;*/

    // initialize selection strategy parameters
    //mu = lambda / 2; // number of parents/points for recombination

    /*double sumw = 0;
    double sumwq = 0;
    for (int i = 0; i < mu; i++) {
      double w = weights.getEntry(i, 0);
      sumw += w;
      sumwq += w * w;
    }*/

    // variance-effectiveness of sum w_i x_i

    // initialize dynamic strategy parameters and constants
    //val cc = (4 + mueff / dimension) / (dimension + 4 + 2 * mueff / dimension)

    //damps = 1 + 2*max(0, sqrt((mueff-1)/(N+1))-1) + cs;
    //(1 + 2 * FastMath.max(0, FastMath.sqrt((mueff - 1) / (genomeSize + 1)) - 1)) *
    //  FastMath.max(0.3, 1 - genomeSize / (1e-6 + maxIterations)) + cs; // minor increment

    //val ccov1Sep = FastMath.min(1, ccov1 * (genomeSize + 1.5) / 3)
    //val ccovmuSep = FastMath.min(1 - ccov1, ccovmu * (genomeSize + 1.5) / 3)

    // intialize CMA internal values - updated each generation
    val xmean = MatrixUtils.createColumnRealMatrix(guess.toArray) // objective variables
    val diagD = insigma.scalarMultiply(1 / sigma)
    val diagC = square(diagD)
    val pc = zeros(genomeSize, 1) // evolution paths for C and sigma
    val ps = zeros(genomeSize, 1) // B defines the coordinate system
    val normps = ps.getFrobeniusNorm

    val B = eye(genomeSize, genomeSize)
    val D = ones(genomeSize, 1) // diagonal D defines the scaling
    val BD = times(B, repmat(diagD.transpose(), genomeSize, 1))
    val C = B.multiply(diag(square(D)).multiply(B.transpose())) // covariance
    CMAESArchive.Archive(
      xmean = xmean,
      B = B,
      D = D,
      C = C,
      BD = BD,
      diagC = diagC,
      diagD = diagD,
      sigma = sigma,
      ps = ps,
      pc = pc,
      normps = normps,
      fitnessHistory = CMAESArchive.FIFO(List(Double.PositiveInfinity)),
      bestValue = Double.PositiveInfinity,
      iterations = 0)
  }

  override def archive(a: A, oldIndividuals: Population[G, P, F], offspring: Population[G, P, F])(implicit rng: Random): A = {
    lazy val population = offspring

    lazy val lambda = population.size
    lazy val mu = ar.mu(lambda)

    lazy val logMu2 = FastMath.log(mu + 0.5)
    lazy val rawWeights = log(sequence(1, mu, 1)).scalarMultiply(-1).scalarAdd(logMu2)
    lazy val sumw = rawWeights.getColumn(0).sum
    lazy val sumwq = rawWeights.getColumn(0).map(x => x * x).sum

    lazy val weights = rawWeights.scalarMultiply(1 / sumw)
    lazy val mueff = sumw * sumw / sumwq
    lazy val cc = (4 + mueff / genomeSize) / (genomeSize + 4 + 2 * mueff / genomeSize)
    lazy val cs = (mueff + 2) / (genomeSize + mueff + 3.0)

    lazy val damps = (1 + 2 * FastMath.max(0, FastMath.sqrt((mueff - 1) / (genomeSize + 1)) - 1)) + cs // minor increment
    lazy val ccov1 = 2 / ((genomeSize + 1.3) * (genomeSize + 1.3) + mueff)
    lazy val ccovmu = FastMath.min(1 - ccov1, 2 * (mueff - 2 + 1 / mueff) / ((genomeSize + 2) * (genomeSize + 2) + mueff))
    lazy val chiN = FastMath.sqrt(genomeSize) * (1 - 1 / (4.0 * genomeSize) + 1 / (21.0 * genomeSize * genomeSize))

    /**
     * Update of the evolution paths ps and pc.
     *
     * @param zmean Weighted row matrix of the gaussian random numbers generating
     * the current offspring.
     * @param xold xmean matrix of the previous generation.
     * @return hsig flag indicating a small correction.
     */
    def updateEvolutionPaths(
      zmean: RealMatrix,
      xold: RealMatrix,
      xmean: RealMatrix,
      ps: RealMatrix,
      pc: RealMatrix,
      sigma: Double,
      B: RealMatrix,
      iterations: Double) = {
      val newPs = ps.scalarMultiply(1 - cs).add(B.multiply(zmean).scalarMultiply(FastMath.sqrt(cs * (2 - cs) * mueff)))
      val normps = newPs.getFrobeniusNorm
      val hsig = normps / FastMath.sqrt(1 - FastMath.pow(1 - cs, 2 * iterations)) / chiN < 1.4 + 2 / (genomeSize + 1.0);
      val pc1 = pc.scalarMultiply(1 - cc)
      val newPc =
        if (hsig) pc1.add(xmean.subtract(xold).scalarMultiply(FastMath.sqrt(cc * (2 - cc) * mueff) / sigma))
        else pc1
      (hsig, newPs, normps, newPc)
    }

    /**
     * Update of the covariance matrix C.
     *
     * @param hsig Flag indicating a small correction.
     * @param bestArx Fitness-sorted matrix of the argument vectors producing the
     * current offspring.
     * @param arz Unsorted matrix containing the gaussian random values of the
     * current offspring.
     * @param arindex Indices indicating the fitness-order of the current offspring.
     * @param xold xmean matrix of the previous generation.
     */
    def updateCovariance(
      hsig: Boolean,
      bestArx: RealMatrix,
      arzNeg: RealMatrix, // final int[] arindex,
      xold: RealMatrix,
      pc: RealMatrix,
      BD: RealMatrix,
      C: RealMatrix,
      sigma: Double) = {
      //val negccov = 0
      if (ccov1 + ccovmu > 0) {
        val arpos = bestArx.subtract(repmat(xold, 1, mu)).scalarMultiply(1 / sigma) // mu difference vectors
        val roneu = pc.multiply(pc.transpose()).scalarMultiply(ccov1) // rank one update
        // minor correction if hsig==false
        if (activeCMAES) {
          // Adapt covariance matrix C active CMA
          // keep at least 0.66 in all directions, small popsize are most
          // critical

          // prepare vectors, compute negative updating matrix Cneg
          //          final int[] arReverseIndex = reverse(arindex);
          //          RealMatrix arzneg = selectColumns(arz, MathArrays.copyOf(arReverseIndex, mu));
          //         RealMatrix arnorms = sqrt(sumRows(square(arzneg)));
          //          final int[] idxnorms = sortedIndices(arnorms.getRow(0));
          //          final RealMatrix arnormsSorted = selectColumns(arnorms, idxnorms);
          //          final int[] idxReverse = reverse(idxnorms);
          //          final RealMatrix arnormsReverse = selectColumns(arnorms, idxReverse);
          //          arnorms = divide(arnormsReverse, arnormsSorted);

          val arnormsRaw = sqrt(sumRows(square(arzNeg)))
          val idxnormsRaw = sortedIndices(arnormsRaw.getRow(0))
          val arnormsSorted = selectColumns(arnormsRaw, idxnormsRaw.toArray)
          val idxReverseRaw = reverse(idxnormsRaw.toArray)
          val arnormsReverse = selectColumns(arnormsRaw, idxReverseRaw)
          val arnorms = divide(arnormsReverse, arnormsSorted)

          // val arnormsSorted = arnormsRaw.getRow(1).sorted
          //val arnormsReverse = arnormsSorted.reverse
          //val arnorms = (arnormsReverse zip arnormsSorted).map{case (v1, v2) => v1 / v2} //divide(arnormsReverse, arnormsSorted)

          val idxnorms = sortedIndices(arnorms.getRow(0))
          val idxInv = inverse(idxnorms.toArray)
          val arnormsInv = selectColumns(arnorms, idxInv)

          // check and set learning rate negccov
          val negccovVal = (1 - ccovmu) * 0.25 * mueff / (FastMath.pow(genomeSize + 2, 1.5) + 2 * mueff)
          val negcovMax = (1 - negminresidualvariance) / square(arnormsInv).multiply(weights).getEntry(0, 0)

          val negccov = if (negccovVal > negcovMax) negcovMax else negccovVal

          //arzneg = times(arzneg, repmat(arnormsInv, dimension, 1));
          val artmp = BD.multiply(times(arzNeg, repmat(arnormsInv, genomeSize, 1)))
          val Cneg = artmp.multiply(diag(weights)).multiply(artmp.transpose())

          val oldFac = (if (hsig) 0 else ccov1 * cc * (2 - cc)) + (1 - ccov1 - ccovmu) + (negalphaold * negccov)
          val newC = C.scalarMultiply(oldFac)
            .add(roneu) // regard old matrix
            .add(arpos.scalarMultiply( // plus rank one update
              ccovmu + (1 - negalphaold) * negccov) // plus rank mu update
              .multiply(times(repmat(weights, 1, genomeSize),
                arpos.transpose)))
            .subtract(Cneg.scalarMultiply(negccov))

          (newC, negccov)
        } else {
          val oldFac = (if (hsig) 0 else ccov1 * cc * (2 - cc)) + (1 - ccov1 - ccovmu)
          val newC =
            C.scalarMultiply(oldFac)
              .add(roneu) // regard old matrix
              .add(arpos.scalarMultiply(ccovmu) // plus rank mu update
                .multiply(times(repmat(weights, 1, genomeSize),
                  arpos.transpose)))
          (newC, 0.0)
        }
      } else (C, 0.0)
    }

    /**
     * Update B and D from C.
     *
     * @param negccov Negative covariance factor.
     */
    def updateBD(negccov: Double, C: RealMatrix, iterations: Long, B: RealMatrix, D: RealMatrix, diagD: RealMatrix, BD: RealMatrix) = {
      if (ccov1 + ccovmu + negccov > 0 && (iterations % 1.0 / (ccov1 + ccovmu + negccov) / genomeSize / 10.0) < 1) {
        // to achieve O(N^2)
        val newC = triu(C, 0).add(triu(C, 1).transpose)
        // enforce symmetry to prevent complex numbers
        //(0 until newC.getRowDimension).foreach(l => println(newC.getRow(l).toSeq))
        val eig = new EigenDecomposition(newC)

        val newB = eig.getV // eigen decomposition, B==normalized eigenvectors
        val newD = eig.getD

        assert(!newB.getData.exists(_.exists(_.isNaN)))
        assert(!newD.getData.exists(_.exists(_.isNaN)))
        //val newDiagD = diag(newD)
        /*if (min(newDiagD) <= 0) {
          for (int i = 0; i < dimension; i++) {
            if (newDiagD.getEntry(i, 0) < 0) {
              newDiagD.setEntry(i, 0, 0);
            }
          }

          final double tfac = max(newDiagD) / 1e14;
          C = C.add(eye(dimension, dimension).scalarMultiply(tfac));
          newDiagD = newDiagD.add(ones(dimension, 1).scalarMultiply(tfac));
        }
        if (max(newDiagD) > 1e14 * min(newDiagD)) {
          final double tfac = max(newDiagD) / 1e14 - min(newDiagD);
          C = C.add(eye(dimension, dimension).scalarMultiply(tfac));
          newDiagD = newDiagD.add(ones(dimension, 1).scalarMultiply(tfac));
        }*/
        //(0 until newD.getRowDimension).foreach(l => println(newD.getRow(l).toSeq))

        val diagDSquare = diag(newD)
        for {
          r <- 0 until diagDSquare.getRowDimension
          c <- 0 until diagDSquare.getColumnDimension
          if diagDSquare.getEntry(r, c) < 0
        } diagDSquare.setEntry(r, c, 0)

        val newDiagD = sqrt(diagDSquare) // D contains standard deviations now

        assert(!newDiagD.getData.exists(_.exists(_.isNaN)))

        for {
          r <- 0 until newDiagD.getRowDimension
          c <- 0 until newDiagD.getColumnDimension
          if newDiagD.getEntry(r, c) < minimumSigma
        } newDiagD.setEntry(r, c, minimumSigma)

        val newBD = times(B, repmat(newDiagD.transpose, genomeSize, 1)) // O(n^2)

        (newC, newB, newD, newDiagD, newBD)
      } else (C, B, D, diagD, BD)
    }

    val fitness = population.map(i => aggregate(i.fitness))
    val sortedFiteness = fitness.sorted
    // Sort by fitness and compute weighted mean into xmean
    val arindex = fitness.zipWithIndex.sortBy { case (v, _) => v }.map(_._2).toArray //val arindex: Array[Int] = sortedIndices(fitness)

    // Calculate new xmean, this is selection and recombination
    val xold = a.xmean //val xold: RealMatrix = xmean

    val sortedOffspring = population.sortBy(i => aggregate(i.fitness))

    val bestGenomes = sortedOffspring.take(mu).map { i => i.genome &|-> values get }
    val bestArx: RealMatrix = new Array2DRowRealMatrix(bestGenomes.transpose.map(_.toArray).toArray, false) //selectColumns(arx, MathArrays.copyOf(arindex, mu))

    val sortedRandomValues = sortedOffspring.map { i => i.genome &|-> randomValues get }
    //val arz: RealMatrix = new Array2DRowRealMatrix(sigmas.map(_.toArray).toArray, false)
    val bestArz: RealMatrix = new Array2DRowRealMatrix(sortedRandomValues.take(mu).transpose.map(_.toArray).toArray, false) //selectColumns(arz, MathArrays.copyOf(arindex, mu));

    val xmean = bestArx.multiply(weights)
    val zmean = bestArz.multiply(weights)

    val (hsig, newPs, newNormps, newPc) =
      updateEvolutionPaths(
        zmean = zmean,
        xold = xold,
        xmean = xmean,
        ps = a.ps,
        pc = a.pc,
        sigma = a.sigma,
        B = a.B,
        iterations = a.iterations)

    //if (diagonalOnly <= 0) {
    val arzNeg = new Array2DRowRealMatrix(sortedRandomValues.reverse.take(mu).transpose.map(_.toArray).toArray, false)
    val (matC, negccov) = updateCovariance(hsig, bestArx, arzNeg, xold, newPc, a.BD, a.C, a.sigma)
    val (newC, newB, newD, newDiagD, newBD) = updateBD(negccov, matC, a.iterations, a.B, a.D, a.diagD, a.BD)
    val newDiagC = diag(newC)

    /*} else {
      updateCovarianceDiagonalOnly(hsig, bestArz);
    }*/
    // Adapt step size sigma - Eq. (5)
    val bestFitness = sortedFiteness.head
    val newBestValue = FastMath.min(bestFitness, a.bestValue)

    val nS1 = a.sigma * FastMath.exp(FastMath.min(1, (newNormps / chiN - 1) * cs / damps))

    val nS2 =
      if (newBestValue == sortedFiteness((0.1 + lambda / 4.0).toInt))
        nS1 * FastMath.exp(0.2 + cs / damps)
      else nS1

    val historyWorst = a.fitnessHistory.content.max
    val historyBest = a.fitnessHistory.content.min

    val newSigma =
      if (a.iterations > 1 && FastMath.max(historyWorst, bestFitness) - FastMath.min(historyBest, bestFitness) == 0)
        nS2 * FastMath.exp(0.2 + cs / damps)
      else nS2

    val historySize = 10 + (3 * 10 * genomeSize / lambda.toDouble).toInt
    val fitnessHistory = a.fitnessHistory += (bestFitness, historySize)

    CMAESArchive.Archive(
      xmean = xmean,
      B = newB,
      D = newD,
      C = newC,
      BD = newBD,
      diagC = newDiagC,
      diagD = newDiagD,
      sigma = newSigma,
      ps = newPs,
      pc = newPc,
      normps = newNormps,
      fitnessHistory = fitnessHistory,
      bestValue = newBestValue,
      iterations = a.iterations + 1)

    // store best in history
    //push(fitnessHistory,bestFitness)

    /*final double bestFitness = fitness[arindex[0]];
    final double worstFitness = fitness[arindex[arindex.length - 1]];
    if (bestValue > bestFitness) {
      bestValue = bestFitness;
      lastResult = optimum;
      optimum = new PointValuePair(fitfun.repair(bestArx.getColumn(0)),
        isMinimize ? bestFitness : -bestFitness);
      if (getConvergenceChecker() != null && lastResult != null &&
        getConvergenceChecker().converged(iterations, optimum, lastResult)) {
        break generationLoop;
      }
    }
    // handle termination criteria
    // Break, if fitness is good enough
    if (stopFitness != 0 && bestFitness < (isMinimize ? stopFitness : -stopFitness)) {
      break generationLoop;
    }
    final double[] sqrtDiagC = sqrt(diagC).getColumn(0);
    final double[] pcCol = pc.getColumn(0);
    for (int i = 0; i < dimension; i++) {
      if (sigma * FastMath.max(FastMath.abs(pcCol[i]), sqrtDiagC[i]) > stopTolX) {
        break;
      }
      if (i >= dimension - 1) {
        break generationLoop;
      }
    }
    for (int i = 0; i < dimension; i++) {
      if (sigma * sqrtDiagC[i] > stopTolUpX) {
        break generationLoop;
      }
    }
    final double historyBest = min(fitnessHistory);
    final double historyWorst = max(fitnessHistory);
    if (iterations > 2 &&
      FastMath.max(historyWorst, worstFitness) -
        FastMath.min(historyBest, bestFitness) < stopTolFun) {
      break generationLoop;
    }
    if (iterations > fitnessHistory.length &&
      historyWorst - historyBest < stopTolHistFun) {
      break generationLoop;
    }
    // condition number of the covariance matrix exceeds 1e14
    if (max(diagD) / min(diagD) > 1e7) {
      break generationLoop;
    }
    // user defined termination
    if (getConvergenceChecker() != null) {
      final PointValuePair current
        = new PointValuePair(bestArx.getColumn(0),
        isMinimize ? bestFitness : -bestFitness);
      if (lastResult != null &&
        getConvergenceChecker().converged(iterations, current, lastResult)) {
        break generationLoop;
      }
      lastResult = current;
    }
    // Adjust step size in case of equal function values (flat fitness)
    if (bestValue == fitness[arindex[(int)(0.1+lambda/4.)]]) {
      sigma *= FastMath.exp(0.2 + cs / damps);
    }
    if (iterations > 2 && FastMath.max(historyWorst, bestFitness) -
      FastMath.min(historyBest, bestFitness) == 0) {
      sigma *= FastMath.exp(0.2 + cs / damps);
    }
    // store best in history
    push(fitnessHistory,bestFitness);
    if (generateStatistics) {
      statisticsSigmaHistory.add(sigma);
      statisticsFitnessHistory.add(bestFitness);
      statisticsMeanHistory.add(xmean.transpose());
      statisticsDHistory.add(diagD.transpose().scalarMultiply(1E5));
    }*/
  }

  /**
   * @param m Input matrix.
   * @param cols Columns to select.
   * @return Matrix representing the selected columns.
   */
  private def selectColumns(m: RealMatrix, cols: Array[Int]) = {
    val d = Array.tabulate[Double](m.getRowDimension, cols.length) {
      (r, c) => m.getEntry(r, cols(c))
    }
    new Array2DRowRealMatrix(d, false)
  }

  /**
   * @param m Input matrix
   * @return Matrix representing the element-wise logarithm of m.
   */
  private def log(m: RealMatrix) = {
    val d = Array.tabulate[Double](m.getRowDimension, m.getColumnDimension) {
      (r, c) => FastMath.log(m.getEntry(r, c));
    }
    new Array2DRowRealMatrix(d, false)
  }

  /**
   * @param m Input matrix.
   * @return Matrix representing the element-wise square of m.
   */
  private def square(m: RealMatrix) = {
    val d = Array.tabulate[Double](m.getRowDimension, m.getColumnDimension) {
      (r, c) =>
        val e = m.getEntry(r, c)
        e * e
    }
    new Array2DRowRealMatrix(d, false)
  }

  /**
   * @param n Number of rows.
   * @param m Number of columns.
   * @return n-by-m matrix of zero values.
   */
  private def zeros(n: Int, m: Int) = new Array2DRowRealMatrix(n, m)

  /**
   * @param n Number of rows.
   * @param m Number of columns.
   * @return n-by-m matrix filled with 1.
   */
  private def ones(n: Int, m: Int) = {
    val d = Array.fill(n, m)(1.0)
    new Array2DRowRealMatrix(d, false)
  }

  /**
   * @param n Number of rows.
   * @param m Number of columns.
   * @return n-by-m matrix of 0 values out of diagonal, and 1 values on
   * the diagonal.
   */
  private def eye(n: Int, m: Int) = {
    val d = Array.ofDim[Double](n, m)
    for {
      r <- 0 until math.min(n, m)
    } d(r)(r) = 1
    new Array2DRowRealMatrix(d, false)
  }

  /**
   * @param m Input matrix 1.
   * @param n Input matrix 2.
   * @return the matrix where the elements of m and n are element-wise multiplied.
   */
  private def times(m: RealMatrix, n: RealMatrix) = {
    val d = Array.tabulate(m.getRowDimension, m.getColumnDimension) {
      (r, c) => m.getEntry(r, c) * n.getEntry(r, c)
    }
    new Array2DRowRealMatrix(d, false)
  }

  /**
   * @param mat Input matrix.
   * @param n Number of row replicates.
   * @param m Number of column replicates.
   * @return a matrix which replicates the input matrix in both directions.
   */
  private def repmat(mat: RealMatrix, n: Int, m: Int) = {
    val rd = mat.getRowDimension
    val cd = mat.getColumnDimension
    val d = Array.tabulate(n * rd, m * cd) {
      (r, c) => mat.getEntry(r % rd, c % cd)
    }
    new Array2DRowRealMatrix(d, false)
  }

  /**
   * @param m Input matrix.
   * @return the diagonal n-by-n matrix if m is a column matrix or the column
   * matrix representing the diagonal if m is a n-by-n matrix.
   */
  private def diag(m: RealMatrix) = {
    if (m.getColumnDimension == 1) {
      val d = Array.ofDim[Double](m.getRowDimension, m.getRowDimension)
      for (i <- 0 until m.getRowDimension) d(i)(i) = m.getEntry(i, 0)
      new Array2DRowRealMatrix(d, false)
    } else {
      val d = Array.tabulate[Double](m.getRowDimension, 1) {
        (i, _) => m.getEntry(i, i)
      }
      new Array2DRowRealMatrix(d, false)
    }
  }

  /**
   * @param m Input matrix.
   * @return Row matrix representing the sums of the rows.
   */
  private def sumRows(m: RealMatrix) = {
    val d =
      Array(
        Array.tabulate[Double](m.getColumnDimension) {
          c =>
            var sum = 0.0
            for (r <- 0 until m.getRowDimension) sum += m.getEntry(r, c)
            sum
        }
      )
    new Array2DRowRealMatrix(d, false)
  }

  /**
   * @param m Input matrix.
   * @return Matrix representing the element-wise square root of m.
   */
  private def sqrt(m: RealMatrix) = {
    val d = Array.tabulate[Double](m.getRowDimension, m.getColumnDimension) {
      (r, c) => FastMath.sqrt(m.getEntry(r, c))
    }
    new Array2DRowRealMatrix(d, false)
  }

  /**
   * @param m Input matrix 1.
   * @param n Input matrix 2.
   * @return Matrix where the elements of m and n are element-wise divided.
   */
  private def divide(m: RealMatrix, n: RealMatrix) = {
    val d = Array.tabulate[Double](m.getRowDimension, m.getColumnDimension) {
      (r, c) => m.getEntry(r, c) / n.getEntry(r, c)
    }
    new Array2DRowRealMatrix(d, false)
  }

  /**
   * Sorts fitness values.
   *
   * @param doubles Array of values to be sorted.
   * @return a sorted array of indices pointing into doubles.
   */
  private def sortedIndices(doubles: Array[Double]) = doubles.zipWithIndex.sortBy { case (v, _) => v }.unzip._2

  /**
   * @param indices Input index array.
   * @return the inverse of the mapping defined by indices.
   */
  private def inverse(indices: Array[Int]) = {
    val inverse = Array.ofDim[Int](indices.length)
    for (i <- 0 until indices.length) inverse(indices(i)) = i;
    inverse
  }

  /**
   * @param indices Input index array.
   * @return the indices in inverse order (last is first).
   */
  private def reverse(indices: Array[Int]) = indices.reverse

  /**
   * @param m Input matrix.
   * @param k Diagonal position.
   * @return Upper triangular part of matrix.
   */
  private def triu(m: RealMatrix, k: Int) = {
    val d = Array.tabulate[Double](m.getRowDimension, m.getColumnDimension) {
      (r, c) => if (r <= c - k) m.getEntry(r, c) else 0
    }
    new Array2DRowRealMatrix(d, false)
  }

  /**
   * @param start Start value.
   * @param end End value.
   * @param step Step size.
   * @return a sequence as column matrix.
   */
  private def sequence(start: Double, end: Double, step: Double) = {
    val size = ((end - start) / step + 1).toInt
    val d = Array.ofDim[Double](size, 1)
    var value = start
    for (r <- 0 until size) {
      d(r)(0) = value
      value += step
    }
    new Array2DRowRealMatrix(d, false)
  }
}*/




© 2015 - 2025 Weber Informatics LLC | Privacy Policy