Carsten
Carsten

Reputation: 2040

Generic Breeze Vector method

I am trying to implement a generic Scala method that processes Breeze vectors typed as Float or as Double (at least, less specificity a plus). Here is a simple example for Vector[Double]:

def vectorSum(vectors: Seq[Vector[Double]]): Vector[Double] = {
  vectors.reduce { (v1, v2) => v1 :+ v2 }
}

I am slightly new to Scala and Breeze, so my naive approach to make this generic is:

def vectorSumGeneric[T <: AnyVal](vectors: Seq[Vector[T]]): Vector[T] = {
  vectors.reduce { (v1, v2) => v1 :+ v2 }
}

However, this throws the following compile errors:

I've tried with some variations including T <% AnyVal and T <% Double, but they do not work either (as expected, probably). The Scala documentation to type bounds do not give me a clue about such a use case such as this. What is the correct way to solve this?

Upvotes: 2

Views: 435

Answers (1)

Till Rohrmann
Till Rohrmann

Reputation: 13346

The problem is that the type parameter T can be anything, but you have to make sure that your type T supports at least addition as an algebraic operation. If T is a semiring, then you can add two elements of type T. You can enforce T to be a semiring by specifying a context bound:

def vectorSum[T: Semiring](vectors: Seq[Vector[T]]): Vector[T] = {
  vectors.reduce(_ + _)
}

That way you enforce that for every instantiation of T you also have a Semiring[T] in your scope which defines the addition operation. Breeze already defines this structure for all primitive types which support addition.

If you want to support more algebraic operations such as division, then you should constrain your type variable to have a Field context bound.

def vectorDiv[T: Field](vectors: Seq[Vector[T]]): Vector[T] = {
  vectors.reduce(_ / _)
}

If you want to support general purpose element-wise binary operations on vectors:

def vectorBinaryOp[T](
    vectors: Seq[Vector[T]], op: (T, T) => T)(
    implicit canZipMapValues: CanZipMapValues[Vector[T], T, T, Vector[T]])
  : Vector[T] = {
  vectors.reduce{
    (left, right) => implicitly[CanZipMapValues[Vector[T], T, T, Vector[T]]].map(left, right, op)
  }
}

Then you can define arbitrary binary operations on vectors:

val vectors = Seq(DenseVector(1.0,2.0,3.0,4.0), DenseVector(2.0,3.0,4.0,5.0))
val result = VectorSum.vectorBinaryOp(vectors, (a: Double, b: Double) => (a / b))

Upvotes: 2

Related Questions