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

mgo.tools.metric.CrowdingDistance.scala Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) 2012 Romain Reuillon
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU 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.tools.metric

import cats.implicits._

/**
 * Crowding distance computation see Deb, K., Agrawal, S., Pratap, A. & Meyarivan, T.
 * A fast elitist non-dominated sorting genetic algorithm for multi-objective
 * optimization: NSGA-II. Lecture notes in computer science 1917, 849–858 (2000).
 */
object CrowdingDistance {

  /**
   * Compute the crowding distance
   *
   * @param data the set of point
   * @return the crowding distance of each point in the same order as the input
   * sequence
   */
  def apply(data: Vector[Vector[Double]], random: scala.util.Random): Vector[Double] = {
    def res =
      data.transpose.map { (d: Vector[Double]) =>
        val grouped: Map[Double, Seq[Int]] =
          d.zipWithIndex.groupBy { case (d, _) => d }.mapValues { _.map { case (_, i) => i } }.toMap

        val sortedDistances = grouped.keys.toVector.sorted

        type Crowding = (Double, Int)

        def groupCrowding(group: Seq[Int], c: Double): List[(Double, Int)] = {
          val randomIndex = random.nextInt(group.size)
          (c -> group(randomIndex)) :: group.patch(randomIndex, Seq.empty, 1).toList.map { t => 0.0 -> t }
        }

        val res: Vector[Crowding] =
          if (sortedDistances.size <= 2)
            sortedDistances.flatMap { d => groupCrowding(grouped(d), Double.PositiveInfinity) }
          else {
            def crowding(distances: List[Double], acc: List[Crowding]): List[Crowding] =
              distances match {
                case d1 :: d2 :: Nil =>
                  val g1 = groupCrowding(grouped(sortedDistances.head), Double.PositiveInfinity)
                  val g2 = groupCrowding(grouped(sortedDistances.last), Double.PositiveInfinity)
                  g1 ::: (g2 ::: acc).reverse
                case d1 :: d2 :: d3 :: _ =>
                  val gc = groupCrowding(grouped(d2), d3 - d1)
                  crowding(distances.tail, gc ::: acc)
                case _ => sys.error("Should never be empty")
              }

            crowding(sortedDistances.toList, List.empty).toVector
          }
        res.sortBy { case (_, index) => index }.map { case (c, _) => c }
      }

    res.transpose.map { _.sum }
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy