0

I am still new to Scala (still learning it). I have the following assignment, but am having problems running it due to the error "overloaded method value converged with alternatives:"

Full Error:

\KMeans.scala:101:8: overloaded method value converged with alternatives:
(eta: Double,oldMeans: scala.collection.parallel.ParSeq[kmeans.Point],newMeans: scala.collection.parallel.ParSeq[kmeans.Point])Boolean <and>
(eta: Double,oldMeans: scala.collection.Seq[kmeans.Point],newMeans: scala.collection.Seq[kmeans.Point])Boolean
cannot be applied to (Double)
if (!converged(eta)(means, newMeans))

Can you please help?

The full class is:

package kmeans

import scala.annotation.tailrec
import scala.collection.{Map, Seq, mutable}
import scala.collection.parallel.CollectionConverters._
import scala.collection.parallel.{ParMap, ParSeq}
import scala.util.Random
import org.scalameter._

class KMeans extends KMeansInterface {

  def generatePoints(k: Int, num: Int): Seq[Point] = {
    val randx = new Random(1)
    val randy = new Random(3)
    val randz = new Random(5)
    (0 until num)
      .map({ i =>
        val x = ((i + 1) % k) * 1.0 / k + randx.nextDouble() * 0.5
        val y = ((i + 5) % k) * 1.0 / k + randy.nextDouble() * 0.5
        val z = ((i + 7) % k) * 1.0 / k + randz.nextDouble() * 0.5
        new Point(x, y, z)
      }).to(mutable.ArrayBuffer)
  }

  def initializeMeans(k: Int, points: Seq[Point]): Seq[Point] = {
    val rand = new Random(7)
    (0 until k).map(_ => points(rand.nextInt(points.length))).to(mutable.ArrayBuffer)
  }

  def findClosest(p: Point, means: IterableOnce[Point]): Point = {
    val it = means.iterator
    assert(it.nonEmpty)
    var closest = it.next()
    var minDistance = p.squareDistance(closest)
    while (it.hasNext) {
      val point = it.next()
      val distance = p.squareDistance(point)
      if (distance < minDistance) {
        minDistance = distance
        closest = point
      }
    }
    closest
  }

  def classify(points: Seq[Point], means: Seq[Point]): Map[Point, Seq[Point]] = {
    means.map{(_, Seq())}.toMap ++ points.groupBy(findClosest(_, means))
  }

  def classify(points: ParSeq[Point], means: ParSeq[Point]): ParMap[Point, ParSeq[Point]] = {
    means.map{(_, ParSeq())}.toMap ++ points.groupBy(findClosest(_, means))
  }

  def findAverage(oldMean: Point, points: Seq[Point]): Point = if (points.isEmpty) oldMean else {
    var x = 0.0
    var y = 0.0
    var z = 0.0
    points.foreach { p =>
      x += p.x
      y += p.y
      z += p.z
    }
    new Point(x / points.length, y / points.length, z / points.length)
  }

  def findAverage(oldMean: Point, points: ParSeq[Point]): Point = if (points.isEmpty) oldMean else {
    var x = 0.0
    var y = 0.0
    var z = 0.0
    points.seq.foreach { p =>
      x += p.x
      y += p.y
      z += p.z
    }
    new Point(x / points.length, y / points.length, z / points.length)
  }

  def update(classified: Map[Point, Seq[Point]], oldMeans: Seq[Point]): Seq[Point] = {
    oldMeans.par.map(oldMean => findAverage(oldMean, classified(oldMean)))
  }

  def update(classified: ParMap[Point, ParSeq[Point]], oldMeans: ParSeq[Point]): ParSeq[Point] = {
    oldMeans.par.map(oldMean => findAverage(oldMean, classified(oldMean)))
  }

def converged(eta: Double, oldMeans: Seq[Point], newMeans: Seq[Point]): Boolean = {
  (oldMeans zip newMeans)
    .forall(entry => entry._1.squareDistance(entry._2) <= eta)
}

def converged(eta: Double, oldMeans: ParSeq[Point], newMeans: ParSeq[Point]): Boolean = {
  (oldMeans zip newMeans)
    .forall(entry => entry._1.squareDistance(entry._2) <= eta)
}

@tailrec
final def kMeans(points: Seq[Point], means: Seq[Point], eta: Double): Seq[Point] = {
  val meansClassification = classify(points, means)
  val newMeans = update(meansClassification, means)

  if (!converged(eta)(means, newMeans))
    kMeans(points, newMeans, eta)
  else
    newMeans
}

@tailrec
final def kMeans(points: ParSeq[Point], means: ParSeq[Point], eta: Double): ParSeq[Point] = {
  val meansClassification = classify(points, means)
  val newMeans = update(meansClassification, means)

  if (!converged(eta)(means, newMeans))
    kMeans(points, newMeans, eta)
  else
    newMeans
}
}

/** Describes one point in three-dimensional space.
*
*  Note: deliberately uses reference equality.
*/
class Point(val x: Double, val y: Double, val z: Double) {
private def square(v: Double): Double = v * v
def squareDistance(that: Point): Double = {
  square(that.x - x)  + square(that.y - y) + square(that.z - z)
}
private def round(v: Double): Double = (v * 100).toInt / 100.0
override def toString = s"(${round(x)}, ${round(y)}, ${round(z)})"
}


object KMeansRunner {

val standardConfig = config(
  Key.exec.minWarmupRuns -> 20,
  Key.exec.maxWarmupRuns -> 40,
  Key.exec.benchRuns -> 25,
  Key.verbose -> true
) withWarmer(new Warmer.Default)

def main(args: Array[String]): Unit = {
  val kMeans = new KMeans()

  val numPoints = 500000
  val eta = 0.01
  val k = 32
  val points = kMeans.generatePoints(k, numPoints)
  val means = kMeans.initializeMeans(k, points)

  val seqtime = standardConfig measure {
    kMeans.kMeans(points, means, eta)
  }

  val parPoints = points.par
  val parMeans = means.par

  val partime = standardConfig measure {
    kMeans.kMeans(parPoints, parMeans, eta)
  }

  // Additional `println` to avoid bad interaction with JLine output
  println()
  println()
  println()
  println()
  println(s"sequential time: $seqtime")
  println(s"parallel time: $partime")
  println(s"speedup: ${seqtime.value / partime.value}")
  println()
  println()
  println()
}

// Workaround Dotty's handling of the existential type KeyValue
implicit def keyValueCoerce[T](kv: (Key[T], T)): KeyValue = {
  kv.asInstanceOf[KeyValue]
}
}

The interface:

package kmeans

import scala.collection.{Map, Seq}
import scala.collection.parallel.{ParMap, ParSeq}

/**
 * The interface used by the grading infrastructure. Do not change signatures
 * or your submission will fail with a NoSuchMethodError.
 */
trait KMeansInterface {
  def classify(points: Seq[Point], means: Seq[Point]): Map[Point, Seq[Point]]
  def classify(points: ParSeq[Point], means: ParSeq[Point]): ParMap[Point, ParSeq[Point]]
  def update(classified: Map[Point, Seq[Point]], oldMeans: Seq[Point]): Seq[Point]
  def update(classified: ParMap[Point, ParSeq[Point]], oldMeans: ParSeq[Point]): ParSeq[Point]
  def converged(eta: Double, oldMeans: Seq[Point], newMeans: Seq[Point]): Boolean
  def converged(eta: Double, oldMeans: ParSeq[Point], newMeans: ParSeq[Point]): Boolean
  def kMeans(points: Seq[Point], means: Seq[Point], eta: Double): Seq[Point]
  def kMeans(points: ParSeq[Point], means: ParSeq[Point], eta: Double): ParSeq[Point]
}
pme
  • 14,156
  • 3
  • 52
  • 95

1 Answers1

0

The method should be called as converged(eta, means, newMeans) not converged(eta)(means, newMeans). If you look, both def converged are defined with a single parameter list (with 3 parameters), not with two.

The most relevant part of this error is not the part you quoted but

cannot be applied to (Double)

Alexey Romanov
  • 167,066
  • 35
  • 309
  • 487