2

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:

  • diverging implicit expansion for type breeze.linalg.operators.OpAdd.Impl2[breeze.linalg.Vector[T],breeze.linalg.Vector[T],That] starting with method v_v_Idempotent_OpAdd in trait VectorOps
  • not enough arguments for method :+: (implicit op: breeze.linalg.operators.OpAdd.Impl2[breeze.linalg.Vector[T],breeze.linalg.Vector[T],That])That. Unspecified value parameter op.

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?

Carsten
  • 1,912
  • 1
  • 28
  • 55

1 Answers1

2

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))
Till Rohrmann
  • 13,148
  • 1
  • 25
  • 51
  • That works for the given example, thank you! I had no idea that Breeze provides a Semiring class. However, I would also like to implement similar methods with different operations, e.g. element-wise division. This is not supported by a Semiring, so it fails again. Is there an even more generic way to approach this? I suppose the same vector operations should be defined for all Vectors. – Carsten Aug 10 '15 at 13:11
  • Then you should constrain the type parameter to have a `Field` context bound. I updated the my answer correspondingly. – Till Rohrmann Aug 10 '15 at 13:48
  • If you want to have a general purpose binary operation function on `Vectors` you can use `CanZipMapValues`. I've added it to my answer. – Till Rohrmann Aug 10 '15 at 14:24
  • I was actually hoping that there would me a more straight-forward solution, but thanks for your solution! – Carsten Aug 10 '15 at 18:43
  • In the beginning Breeze's architecture with all these implicits seems to be cumbersome to work with, but it really gives you a lot of flexibility and allows you to easily extend the system with new operations. – Till Rohrmann Aug 11 '15 at 07:05