我使用的是a SparseMatrix_Double和a DenseVector_Double中的SparseMultiply,使用的是Accelerate框架的Sparse Solvers,它随机崩溃:

enter image description here

有时,它有效.有时,它会崩溃EXC_BAD_ACCESS.有时,它会返回错误的结果.

我try 在方案设置的"诊断"选项卡上打开各种"address sanitizer"和/或各种内存判断选项,但它们没有报告任何问题.

这是怎么回事?

func matrixProductExperiment() {
    // Given sparse matrix A, and dense vector X, calculate product of dense vector Y, i.e., y = Ax:
    //
    //              A                X    =      Y
    //   ( 10.0  1.0      2.5 )  ( 2.20 ) = ( 32.025 )
    //   (  1.0 12.0 -0.3 1.1 )  ( 2.85 ) = ( 38.720 )
    //   (      -0.3  9.5     )  ( 2.79 ) = ( 25.650 )
    //   (  2.5  1.1      6.0 )  ( 2.87 ) = ( 25.855 )

    // We use a format known as Compressed Sparse Column (CSC) to store the data. Further,
    // as the matrix is symmetric, we only need to store half the data. The CSC format
    // stores the matrix as a series of column vectors where only the non-zero entries are
    // specified, stored as the pair of (row index, value), although in separate arrays:

    let rowCount: Int32     = 4
    let columnCount: Int32  = 4
    var matrixValues        = [ 10.0, 1.0, 2.5, 12.0, -0.3, 1.1, 9.5, 6.0 ]
    var rowIndices: [Int32] = [    0,   1,   3,    1,    2,   3,   2,   3 ]
    var columnStarts        = [    0,              3,              6,   7 ]

    // vector X

    var xValues = [ 2.20, 2.85, 2.79, 2.87 ]

    // In this library, this raw information is all wrapper into a flexible data type
    // that allows for more complex use cases in other situations.

    rowIndices.withUnsafeMutableBufferPointer { rowIndicesPointer in
        columnStarts.withUnsafeMutableBufferPointer { columnStartsPointer in
            matrixValues.withUnsafeMutableBufferPointer { valuesPointer in
                xValues.withUnsafeMutableBufferPointer { xPointer in
                    let a = SparseMatrix_Double(
                        // Structure of the matrix, without any values
                        structure: SparseMatrixStructure(
                            rowCount:     rowCount,
                            columnCount:  columnCount,
                            columnStarts: columnStartsPointer.baseAddress!,
                            rowIndices:   rowIndicesPointer.baseAddress!,
                            // Matrix meta-data
                            attributes: SparseAttributes_t(
                                transpose: false,
                                triangle: SparseLowerTriangle,
                                kind: SparseSymmetric,
                                _reserved: 0,
                                _allocatedBySparse: false
                            ),
                            blockSize: 1),
                        // Numerical values of the matrix
                        data: valuesPointer.baseAddress!
                    )

                    let x = DenseVector_Double(count: columnCount, data: xPointer.baseAddress!)

                    let y = [Double](unsafeUninitializedCapacity: Int(rowCount)) { resultBuffer, count in
                        let y = DenseVector_Double(count: rowCount, data: resultBuffer.baseAddress!)
                        SparseMultiply(a, x, y)
                        count = Int(rowCount)
                    }

                    print(y) // [32.025, 38.72, 25.65, 25.855] – Correct
                }
            }
        }
    }
}

推荐答案

问题是columnStarts.在所有实际的"列开始"之后,该数组必须包括矩阵值数组中的元素数量(以便它可以计算最后一列中有多少元素).请注意columnStarts数组末尾的额外值:

func matrixProductExperiment() {
    …

    let rowCount: Int32     = 4
    let columnCount: Int32  = 4
    var matrixValues        = [ 10.0, 1.0, 2.5, 12.0, -0.3, 1.1, 9.5, 6.0 ]
    var rowIndices: [Int32] = [    0,   1,   3,    1,    2,   3,   2,   3 ]
    var columnStarts        = [    0,              3,              6,   7,   8 ]
    assert(columnStarts.last == matrixValues.count, "\(String(describing: columnStarts.last)) ≠ \(matrixValues.count)")

    …
}

如果您使用SparseConvertFromCoordinate,如SparseMultiply documentation中所示,则您提供blockCount.但SparseMatrixStructure初始化器没有"块计数"参数,它从columnStarts中的最后一个值中提取这个参数(有点不明显,恕我直言).

参见Creating Sparse Matrices,上面写着:

此数组需要一个额外的最终条目,用于定义最终列的长度.

在我看来,"开始"数组必须以矩阵值的"计数"结束并不完全直观.令人尴尬的是,SparseMatrixStructure documentation没有指出这一点.但是,回想起来,我们可以明白为什么我们必须这样做.

那是我生命中的几个小时,我再也回不来了.这个解决方案对许多人来说可能是显而易见的,但对于那些在Accelerate中遇到稀疏矩阵随机崩溃的人来说,请仔细看看columnStartsarray.此值不具有有意义的验证和/或错误消息.如果省略计数,将收到间歇性/神秘错误.

这对我来说是一个愚蠢的错误,但追踪这个问题需要付出很多工作.希望这能为一些future 的读者节省几个小时的时间.

Swift相关问答推荐

SwiftUI:以程序方式滚动一个长文本

{Singleton类}类型的值没有成员$showegView'

在解码字符串时需要帮助.

如何将泛型函数存储到变量中?

在用户将输入保存到SWIFT中的核心数据后,如何创建指向我的应用程序S主页的链接?

如何在init()中使用setter/getters?

编写Swift字符串的属性包装时遇到问题

SWIFT计划计时器方法在时间间隔后未被调用

';NSInternal不一致异常';,原因:';可见导航栏Xcode 15.0 Crash请求布局

Swift图表';chartXVisibleDomain挂起或崩溃

如何删除 macOS 中的所有命令?

签署GoogleSignIn-GoogleSignIn需要开发团队

为什么更改属性后动画会加速? SwiftUI

使用自定义相机拍照 iOS 11.0 Swift 4. 更新错误

如何在 Swift 中将对象归零

Swift:如何从函数返回类类型

如何在 Swift 中将 NSURL 转换为字符串

在 Swiftui 中是否有一种简单的方法可以通过捏合来放大图像?

使用相机进行人脸检测

WKWebView 确实从本地文档文件夹加载资源