通用 Breeze 向量方法
Generic Breeze Vector method
我正在尝试实现一个通用的 Scala 方法,该方法处理 Breeze 类型为 Float 或 Double 的向量(至少,特异性较低者优先)。
这是 Vector[Double] 的一个简单示例:
def vectorSum(vectors: Seq[Vector[Double]]): Vector[Double] = {
vectors.reduce { (v1, v2) => v1 :+ v2 }
}
我对 Scala 和 Breeze 有点陌生,所以我天真的方法是:
def vectorSumGeneric[T <: AnyVal](vectors: Seq[Vector[T]]): Vector[T] = {
vectors.reduce { (v1, v2) => v1 :+ v2 }
}
但是,这会引发以下编译错误:
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.
我尝试了一些变体,包括 T <% AnyVal
和 T <% Double
,但它们也不起作用(可能是预期的那样)。类型边界的 Scala 文档没有给我关于此类用例的线索。
解决这个问题的正确方法是什么?
问题是类型参数 T
可以是任何东西,但你必须确保你的类型 T
至少支持作为代数运算的加法。如果 T
是一个半环,那么你可以添加两个 T
类型的元素。您可以通过指定上下文绑定来强制 T
成为半环:
def vectorSum[T: Semiring](vectors: Seq[Vector[T]]): Vector[T] = {
vectors.reduce(_ + _)
}
通过这种方式,您可以为 T
的每个实例化强制执行此操作,您的范围内还有一个 Semiring[T]
,它定义了加法运算。 Breeze 已经为所有支持加法的原始类型定义了这个结构。
如果你想支持更多的代数运算,比如除法,那么你应该限制你的类型变量有一个 Field
上下文绑定。
def vectorDiv[T: Field](vectors: Seq[Vector[T]]): Vector[T] = {
vectors.reduce(_ / _)
}
如果你想支持通用的向量元素二元运算:
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)
}
}
然后你可以在向量上定义任意二元运算:
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))
我正在尝试实现一个通用的 Scala 方法,该方法处理 Breeze 类型为 Float 或 Double 的向量(至少,特异性较低者优先)。 这是 Vector[Double] 的一个简单示例:
def vectorSum(vectors: Seq[Vector[Double]]): Vector[Double] = {
vectors.reduce { (v1, v2) => v1 :+ v2 }
}
我对 Scala 和 Breeze 有点陌生,所以我天真的方法是:
def vectorSumGeneric[T <: AnyVal](vectors: Seq[Vector[T]]): Vector[T] = {
vectors.reduce { (v1, v2) => v1 :+ v2 }
}
但是,这会引发以下编译错误:
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.
我尝试了一些变体,包括 T <% AnyVal
和 T <% Double
,但它们也不起作用(可能是预期的那样)。类型边界的 Scala 文档没有给我关于此类用例的线索。
解决这个问题的正确方法是什么?
问题是类型参数 T
可以是任何东西,但你必须确保你的类型 T
至少支持作为代数运算的加法。如果 T
是一个半环,那么你可以添加两个 T
类型的元素。您可以通过指定上下文绑定来强制 T
成为半环:
def vectorSum[T: Semiring](vectors: Seq[Vector[T]]): Vector[T] = {
vectors.reduce(_ + _)
}
通过这种方式,您可以为 T
的每个实例化强制执行此操作,您的范围内还有一个 Semiring[T]
,它定义了加法运算。 Breeze 已经为所有支持加法的原始类型定义了这个结构。
如果你想支持更多的代数运算,比如除法,那么你应该限制你的类型变量有一个 Field
上下文绑定。
def vectorDiv[T: Field](vectors: Seq[Vector[T]]): Vector[T] = {
vectors.reduce(_ / _)
}
如果你想支持通用的向量元素二元运算:
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)
}
}
然后你可以在向量上定义任意二元运算:
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))