Reputation: 2040
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?
Upvotes: 2
Views: 435
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