Search code examples
scalamatrixvectorscala-breeze

Using the kronecker product on complex matrices with scalaNLP breeze


I had a piece of code:

def this(vectors: List[DenseVector[Double]]) {
    this(vectors.length)
    var resultVector = vectors.head
    for (vector <- vectors) {
        resultVector = kron(resultVector.toDenseMatrix, vector.toDenseMatrix).toDenseVector
    }
    _vector = resultVector
}

It worked just the way I wanted it to work. The problem is that I needed complex values in stead of doubles. After importing breeze.math.Complex, I changed the code to:

def this(vectors: List[DenseVector[Complex]]) {
    this(vectors.length)
    var resultVector = vectors.head
    for (vector <- vectors) {
       resultVector = kron(resultVector.toDenseMatrix, vector.toDenseMatrix).toDenseVector
    }
    _vector = resultVector
}

This however results into the errors:

Error:(42, 26) could not find implicit value for parameter impl: breeze.linalg.kron.Impl2[breeze.linalg.DenseMatrix[breeze.math.Complex],breeze.linalg.DenseMatrix[breeze.math.Complex],VR]
      resultVector = kron(resultVector.toDenseMatrix, vector.toDenseMatrix).toDenseVector
                         ^

Error:(42, 26) not enough arguments for method apply: (implicit impl: breeze.linalg.kron.Impl2[breeze.linalg.DenseMatrix[breeze.math.Complex],breeze.linalg.DenseMatrix[breeze.math.Complex],VR])VR in trait UFunc.
Unspecified value parameter impl.
      resultVector = kron(resultVector.toDenseMatrix, vector.toDenseMatrix).toDenseVector
                         ^

Is this a bug or am I forgetting to do something?


Solution

  • I found the problem in the following way:

    1. I first rewrote the function to use less matrix conversions
    2. As there was a problem with the implicit impl variable of kron, I also rewrote the function call to explicitly state which variable to use to use

    .

    def this(vectors: List[DenseVector[Complex]]) {
        this(vectors.length)
        var resultMatrix = vectors.head.toDenseMatrix
        for (i <- 1 until vectors.length) {
             resultMatrix = kron(resultMatrix, vectors(i).toDenseMatrix)(kron.kronDM_M[Complex, Complex, DenseMatrix[Complex], Complex])
        }
        _vector = resultMatrix.toDenseVector
    }
    

    This showed me that there was no ScalarMulOp for V2, M, DenseMatrix[RV] where M is a Matrix[V1], V1 and V2 are the input types and RV is the output type of the ScalarMulOp

    Digging through the source code of breeze I found in DenseMatrixOps that there only was an implicit ScalarMulOp for the above types if V1, V2 and RV are of type Int, Long, Float and Double. By copying the function and making it specific for Complex numbers, I was able to get the kronecker product to work. Now I could also remove the explicit use of (kron.kronDM_M[Complex, Complex, DenseMatrix[Complex], Complex]). The ScalarMulOp function in question is:

    implicit def s_dm_op_Complex_OpMulScalar(implicit op: OpMulScalar.Impl2[Complex, Complex, Complex]):
      OpMulScalar.Impl2[Complex, DenseMatrix[Complex], DenseMatrix[Complex]] =
    
        new OpMulScalar.Impl2[Complex, DenseMatrix[Complex], DenseMatrix[Complex]] {
          def apply(b: Complex, a: DenseMatrix[Complex]): DenseMatrix[Complex] = {
            val res: DenseMatrix[Complex] = DenseMatrix.zeros[Complex](a.rows, a.cols)
            val resd: Array[Complex] = res.data
            val ad: Array[Complex] = a.data
            var c = 0
    
            var off = 0
            while (c < a.cols) {
              var r = 0
              while (r < a.rows) {
                resd(off) = op(b, ad(a.linearIndex(r, c)))
                r += 1
                off += 1
              }
              c += 1
            }
    
            res
          }
    
          implicitly[BinaryRegistry[Complex, Matrix[Complex], OpMulScalar.type, Matrix[Complex]]].register(this)
        }