Math.NET能够解决任何矩阵吗?

6
我将使用Math.NET来解决以下方程组:
系数矩阵A:
var matrixA = DenseMatrix.OfArray(new[,] {
    { 20000, 0, 0, -20000, 0, 0, 0, 0, 0 },
    { 0, 666.66666666666663, 2000, 0, -666.66666666666663, 2000, 0, 0, 0 }, 
    { 0, 2000,  8000,  0,  -2000, 4000, 0, 0, 0 },
    { -20000, 0, 0, 20666.66666666666663, 0, 2000, -666.66666666666663, 0, 2000 },
    { 0, -666.66666666666663, -2000, 0, 20666.66666666666663, -2000, 0, -20000, 0 },
    { 0, 2000, 4000, 2000, -2000, 16000, -2000, 0, 4000 },
    { 0, 0, 0, -666.66666666666663, 0, -2000, 666.66666666666663, 0, -2000 },
    { 0, 0, 0, 0, -20000, 0, 0, 20000, 0 },
    { 0, 0, 0, 2000, 0, 4000, -2000, 0, 7999.9999999999991 }});

结果向量 b:

double[] loadVector = { 0, 0, 0, 5, 0, 0, 0, 0, 0 };
var vectorB = MathNet.Numerics.LinearAlgebra.Vector<double>.Build.Dense(loadVector);

我从一个有限元分析示例问题中引用了以下数字,因此,根据该示例,我期望的答案是:
[0.01316, 0, 0.0009199, 0.01316, -0.00009355, -0.00188, 0, 0, 0]

然而,我在使用Math.NET和一款我找到的在线矩阵计算器时,大多数情况下会得到解为零(通过迭代求解器)、NaN或极其不准确的大数字(通过直接求解器)。
在Math.NET中,我尝试了将我的矩阵插入提供的示例中,包括:
迭代示例:
namespace Examples.LinearAlgebra.IterativeSolversExamples
{
/// <summary>
/// Composite matrix solver
/// </summary>
public class CompositeSolverExample : IExample
{
    public void Run()
    {
        // Format matrix output to console
        var formatProvider = (CultureInfo)CultureInfo.InvariantCulture.Clone();
        formatProvider.TextInfo.ListSeparator = " ";

        // Solve next system of linear equations (Ax=b):
        // 5*x + 2*y - 4*z = -7
        // 3*x - 7*y + 6*z = 38
        // 4*x + 1*y + 5*z = 43

        // Create matrix "A" with coefficients
        var matrixA = DenseMatrix.OfArray(new[,] { { 20000, 0, 0, -20000, 0, 0, 0, 0, 0 }, { 0, 666.66666666666663, 2000, 0, -666.66666666666663, 2000, 0, 0, 0 }, 
                                                { 0, 2000,  8000,  0,  -2000, 4000, 0, 0, 0 }, { -20000, 0, 0, 20666.66666666666663, 0, 2000, -666.66666666666663, 0, 2000 },
                                                {0, -666.66666666666663, -2000, 0, 20666.66666666666663, -2000, 0, -20000, 0 }, { 0, 2000, 4000, 2000, -2000, 16000, -2000, 0, 4000 },
                                                { 0, 0, 0, -666.66666666666663, 0, -2000, 666.66666666666663, 0, -2000 }, { 0, 0, 0, 0, -20000, 0, 0, 20000, 0 },
                                                 {0, 0, 0, 2000, 0, 4000, -2000, 0, 7999.9999999999991 }});


        Console.WriteLine(@"Matrix 'A' with coefficients");
        Console.WriteLine(matrixA.ToString("#0.00\t", formatProvider));
        Console.WriteLine();

        // Create vector "b" with the constant terms.
        double[] loadVector = {0,0,0,5,0,0,0,0,0};
        var vectorB = MathNet.Numerics.LinearAlgebra.Vector<double>.Build.Dense(loadVector);
        Console.WriteLine(@"Vector 'b' with the constant terms");
        Console.WriteLine(vectorB.ToString("#0.00\t", formatProvider));
        Console.WriteLine();

        // Create stop criteria to monitor an iterative calculation. There are next available stop criteria:
        // - DivergenceStopCriterion: monitors an iterative calculation for signs of divergence;
        // - FailureStopCriterion: monitors residuals for NaN's;
        // - IterationCountStopCriterion: monitors the numbers of iteration steps;
        // - ResidualStopCriterion: monitors residuals if calculation is considered converged;

        // Stop calculation if 1000 iterations reached during calculation
        var iterationCountStopCriterion = new IterationCountStopCriterion<double>(500000);

        // Stop calculation if residuals are below 1E-10 --> the calculation is considered converged
        var residualStopCriterion = new ResidualStopCriterion<double>(1e-10);

        // Create monitor with defined stop criteria
        var monitor = new Iterator<double>(iterationCountStopCriterion, residualStopCriterion);

        // Load all suitable solvers from current assembly. Below in this example, there is user-defined solver
        // "class UserBiCgStab : IIterativeSolverSetup<double>" which uses regular BiCgStab solver. But user may create any other solver
        // and solver setup classes which implement IIterativeSolverSetup<T> and pass assembly to next function:
        var solver = new CompositeSolver(SolverSetup<double>.LoadFromAssembly(Assembly.GetExecutingAssembly()));

        // 1. Solve the matrix equation
        var resultX = matrixA.SolveIterative(vectorB, solver, monitor);
        Console.WriteLine(@"1. Solve the matrix equation");
        Console.WriteLine();

        // 2. Check solver status of the iterations.
        // Solver has property IterationResult which contains the status of the iteration once the calculation is finished.
        // Possible values are:
        // - CalculationCancelled: calculation was cancelled by the user;
        // - CalculationConverged: calculation has converged to the desired convergence levels;
        // - CalculationDiverged: calculation diverged;
        // - CalculationFailure: calculation has failed for some reason;
        // - CalculationIndetermined: calculation is indetermined, not started or stopped;
        // - CalculationRunning: calculation is running and no results are yet known;
        // - CalculationStoppedWithoutConvergence: calculation has been stopped due to reaching the stopping limits, but that convergence was not achieved;
        Console.WriteLine(@"2. Solver status of the iterations");
        Console.WriteLine(monitor.Status);
        Console.WriteLine();

        // 3. Solution result vector of the matrix equation
        Console.WriteLine(@"3. Solution result vector of the matrix equation");
        Console.WriteLine(resultX.ToString("#0.00\t", formatProvider));
        Console.WriteLine();

        // 4. Verify result. Multiply coefficient matrix "A" by result vector "x"
        var reconstructVecorB = matrixA*resultX;
        Console.WriteLine(@"4. Multiply coefficient matrix 'A' by result vector 'x'");
        Console.WriteLine(reconstructVecorB.ToString("#0.00\t", formatProvider));
        Console.WriteLine();
        Console.Read();
    }
}
}

直接示例:

namespace Examples.LinearAlgebraExamples
{
/// <summary>
/// Direct solvers (using matrix decompositions)
/// </summary>
/// <seealso cref="http://en.wikipedia.org/wiki/Numerical_analysis#Direct_and_iterative_methods"/>
public class DirectSolvers : IExample
{
    /// <summary>
    /// Gets the name of this example
    /// </summary>
    public string Name
    {
        get
        {
            return "Direct solvers";
        }
    }

    /// <summary>
    /// Gets the description of this example
    /// </summary>
    public string Description
    {
        get
        {
            return "Solve linear equations using matrix decompositions";
        }
    }

    /// <summary>
    /// Run example
    /// </summary>
    public void Run()
    {
        // Format matrix output to console
        var formatProvider = (CultureInfo) CultureInfo.InvariantCulture.Clone();
        formatProvider.TextInfo.ListSeparator = " ";

        // Solve next system of linear equations (Ax=b):
        // 5*x + 2*y - 4*z = -7
        // 3*x - 7*y + 6*z = 38
        // 4*x + 1*y + 5*z = 43

         matrixA = DenseMatrix.OfArray(new[,] { { 20000, 0, 0, -20000, 0, 0, 0, 0, 0 }, { 0, 666.66666666666663, 2000, 0, -666.66666666666663, 2000, 0, 0, 0 }, 
                                                { 0, 2000,  8000,  0,  -2000, 4000, 0, 0, 0 }, { -20000, 0, 0, 20666.66666666666663, 0, 2000, -666.66666666666663, 0, 2000 },
                                                {0, -666.66666666666663, -2000, 0, 20666.66666666666663, -2000, 0, -20000, 0 }, { 0, 2000, 4000, 2000, -2000, 16000, -2000, 0, 4000 },
                                                { 0, 0, 0, -666.66666666666663, 0, -2000, 666.66666666666663, 0, -2000 }, { 0, 0, 0, 0, -20000, 0, 0, 20000, 0 },
                                                 {0, 0, 0, 2000, 0, 4000, -2000, 0, 7999.9999999999991 }});

        Console.WriteLine(@"Matrix 'A' with coefficients");
        Console.WriteLine(matrixA.ToString("#0.00\t", formatProvider));
        Console.WriteLine();

        // Create vector "b" with the constant terms.
        double[] loadVector = { 0, 0, 0, 5000, 0, 0, 0, 0, 0 };
        var vectorB = MathNet.Numerics.LinearAlgebra.Vector<double>.Build.Dense(loadVector);
        Console.WriteLine(@"Vector 'b' with the constant terms");
        Console.WriteLine(vectorB.ToString("#0.00\t", formatProvider));
        Console.WriteLine();

        // 1. Solve linear equations using LU decomposition
        var resultX = matrixA.LU().Solve(vectorB);
        Console.WriteLine(@"1. Solution using LU decomposition");
        Console.WriteLine(resultX.ToString("#0.00\t", formatProvider));
        Console.WriteLine();

        // 2. Solve linear equations using QR decomposition
        resultX = matrixA.QR().Solve(vectorB);
        Console.WriteLine(@"2. Solution using QR decomposition");
        Console.WriteLine(resultX.ToString("#0.00\t", formatProvider));
        Console.WriteLine();

        // 3. Solve linear equations using SVD decomposition
        matrixA.Svd().Solve(vectorB, resultX);
        Console.WriteLine(@"3. Solution using SVD decomposition");
        Console.WriteLine(resultX.ToString("#0.00\t", formatProvider));
        Console.WriteLine();

        // 4. Solve linear equations using Gram-Shmidt decomposition
        matrixA.GramSchmidt().Solve(vectorB, resultX);
        Console.WriteLine(@"4. Solution using Gram-Shmidt decomposition");
        Console.WriteLine(resultX.ToString("#0.00\t", formatProvider));
        Console.WriteLine();

        // 5. Verify result. Multiply coefficient matrix "A" by result vector "x"
        var reconstructVecorB = matrixA*resultX;
        Console.WriteLine(@"5. Multiply coefficient matrix 'A' by result vector 'x'");
        Console.WriteLine(reconstructVecorB.ToString("#0.00\t", formatProvider));
        Console.WriteLine();

        // To use Cholesky or Eigenvalue decomposition coefficient matrix must be 
        // symmetric (for Evd and Cholesky) and positive definite (for Cholesky)
        // Multipy matrix "A" by its transpose - the result will be symmetric and positive definite matrix
        var newMatrixA = matrixA.TransposeAndMultiply(matrixA);
        Console.WriteLine(@"Symmetric positive definite matrix");
        Console.WriteLine(newMatrixA.ToString("#0.00\t", formatProvider));
        Console.WriteLine();

        // 6. Solve linear equations using Cholesky decomposition
        newMatrixA.Cholesky().Solve(vectorB, resultX);
        Console.WriteLine(@"6. Solution using Cholesky decomposition");
        Console.WriteLine(resultX.ToString("#0.00\t", formatProvider));
        Console.WriteLine();

        // 7. Solve linear equations using eigen value decomposition
        newMatrixA.Evd().Solve(vectorB, resultX);
        Console.WriteLine(@"7. Solution using eigen value decomposition");
        Console.WriteLine(resultX.ToString("#0.00\t", formatProvider));
        Console.WriteLine();

        // 8. Verify result. Multiply new coefficient matrix "A" by result vector "x"
        reconstructVecorB = newMatrixA*resultX;
        Console.WriteLine(@"8. Multiply new coefficient matrix 'A' by result vector 'x'");
        Console.WriteLine(reconstructVecorB.ToString("#0.00\t", formatProvider));
        Console.WriteLine();
        Console.Read();
    }
}
}

例子中的数字可能是错误的,但在继续之前我需要确定自己是否正确地使用了Math.NET。我是否按照这些解算器的示例使用它们?还有其他任何我可以尝试而这些示例没有涵盖的东西吗?
有关“有限元分析示例问题(第8页,示例1)”请参阅此处。似乎他们在某个地方搞错了单位,因此为了使我的矩阵与他们的匹配,我们必须使用以下输入。
Member  A (mm^2)    E (N/mm^2)  I (mm^4)    L (mm)
AB     600000000    0.0002      60000000      6
BC     600000000    0.0002      60000000      6

需要注意的是,他们已经消除了一些在计算过程中应该自然消失的行和列。然而,在我使用的矩阵中这些行和列仍然存在。


我用 x = inv(A)*b 得到的答案是 x={-757.5757,0,0,-757.5757,0,0,-757.5757,0,0},而且 A 的条件非常糟糕。请先检查您的数学计算。 - John Alexiou
3个回答

5
数学.NET可以解决任何矩阵吗?
不可以。具体而言,它不能解决无解的方程组,其他求解器也是如此。
在这种情况下,您的矩阵A是奇异的,即它没有逆矩阵。这意味着您的方程组或者没有解(不一致),或者有无限多个解(请参见介绍数值方法第6.5节中的例子)。奇异矩阵的行列式为零。您可以使用mathnet中的Determinant方法来查看这一点。
Console.WriteLine("Determinant {0}", matrixA.Determinant());

这可以提供
Determinant 0

A的奇异条件是其行(或列)的线性组合为零。例如,在这里,第2、5和8行的总和为零。这些不是唯一加起来为零的行。(您稍后将看到另一个示例。实际上,有三种不同的方法可以做到这一点,从技术上讲,这个9x9矩阵的“秩”为6而不是9。)
请记住,当您尝试解决Ax=b时,您只需解决一组同时方程。在二维中,您可能会遇到如下系统:
A = [1 1   b = [1 
     2 2],      2]

解决这个问题等价于找到x0x1,使得

  x0 +   x1 = 1
2*x0 + 2*x1 = 2

这里有无数解决方案,满足 x1 = 1 - x0,即在直线 x0 + x1 = 1 上。或者说

A = [1 1   b = [1 
     1 1],      2]

这相当于

  x0 +  x1 = 1
  x0 +  x1 = 2

显然没有解决方案,因为我们可以从第二个方程式中减去第一个方程式来得到0 = 1

在您的情况下,第1、4和7个方程式为:

 20000*x0 -20000               *x3                                          = 0
-20000*x0 +20666.66666666666663*x3 +2000*x5 -666.66666666666663*x6 +2000*x8 = 5
            -666.66666666666663*x3 -2000*x5 +666.66666666666663*x6 -2000*x8 = 0

将它们相加得到0=5,因此您的系统没有解决方案。
最好在像Matlab或R这样的交互环境中探索矩阵。由于Python在Visual Studio中可用,并且通过numpy提供类似于Matlab的环境,因此我已经用一些Python代码演示了上述内容。我建议使用Visual Studio的Python工具,我已成功地在Visual Studio 2012和2013中使用过。
# numpy is a Matlab-like environment for linear algebra in Python
import numpy as np

# matrix A
A = np.matrix ([
    [ 20000, 0, 0, -20000, 0, 0, 0, 0, 0 ],
    [ 0, 666.66666666666663, 2000, 0, -666.66666666666663, 2000, 0, 0, 0 ], 
    [ 0, 2000,  8000,  0,  -2000, 4000, 0, 0, 0 ],
    [ -20000, 0, 0, 20666.66666666666663, 0, 2000, -666.66666666666663, 0, 2000 ],
    [ 0, -666.66666666666663, -2000, 0, 20666.66666666666663, -2000, 0, -20000, 0 ],
    [ 0, 2000, 4000, 2000, -2000, 16000, -2000, 0, 4000 ],
    [ 0, 0, 0, -666.66666666666663, 0, -2000, 666.66666666666663, 0, -2000 ],
    [ 0, 0, 0, 0, -20000, 0, 0, 20000, 0 ],
    [ 0, 0, 0, 2000, 0, 4000, -2000, 0, 7999.9999999999991 ]])

# vector b
b = np.array([0, 0, 0, 5, 0, 0, 0, 0, 0])
b.shape = (9,1)

# attempt to solve Ax=b
np.linalg.solve(A,b)

以下代码将会抛出错误信息:LinAlgError: Singular matrix。你可以通过展示第二、五、八行的和等于零,来证明A是奇异矩阵。

A[1,]+A[4,]+A[7,]

注意行号是从0开始计数的。
为了证明第1个、第4个和第7个方程式导致0=5,需要通过将列向量b追加到A形成增广矩阵,然后将相应的(从0开始计数的)行加在一起来演示。
Aaug = np.append(A,b,1)

Aaug[0,] + Aaug[3,] + Aaug[6,]

即使您的矩阵不是奇异的,您仍然可能面临数值不稳定的问题:这种情况被称为病态问题。检查矩阵的条件数来确定如何解决这个问题(维基百科np.linalg.cond(A)matrixA.ConditionNumber())。


6
这个回答非常耐心地表达了“你应该学习线性代数和可能需要一些数值分析”的意思,同时也教授了这方面的知识。太棒了! - ellisbben
为什么你会使用Python和NumPy来回答一个明显标记为“.net”、“mathnet”的问题呢? - jth41
@jth41 感谢您的评论。原因是这个问题比任何一种编程语言都更普遍--它不能在任何一种编程语言中解决。我在C#方面的经验比Python更丰富,但Python/numpy是探索这个问题最简单的方法。今天我一直在思考,我想添加一个或两个mathnet特定的点。特别是我对计算行列式(奇异矩阵为0)和理想情况下的条件数很感兴趣。但昨晚我花了一段时间来解决数学问题,时间不够了--请在周末之后再回来查看。 - TooTone

4
你问题里的后两句话是你问题的根源:
同时请注意,他们消除了一些在计算过程中自然应该消失的行和列。我使用的矩阵中仍然存在这些行和列。
在你的例子中,某些连接在特定方向上被固定(称为边界条件)。有时,在进行有限元分析时,如果你不根据这些边界条件从你的刚度矩阵和负载矩阵中删除相应的行和列,你最终会得到一个无法解决的系统,这就是这里的情况。
请尝试再次使用DirectSolver,并使用以下内容:
var matrixA = DenseMatrix.OfArray(new[,] { {20000,  0,  -20000, 0,  0}, {0, 8000    ,0, -2000   ,4000},
                                                   {-20000, 0,  20666.667   ,0, 2000}, {0,  -2000   ,0, 20666.67,   -2000},
                                                    {0, 4000    ,2000   ,-2000, 16000}});

并且

double[] loadVector = { 0, 0, 5, 0, 0 };
var vectorB = MathNet.Numerics.LinearAlgebra.Vector<double>.Build.Dense(loadVector);

回答你的问题,是的你使用方法正确,但你解决的系统错误。修正你的输入,你应该得到你想要的输出。
我也应该指出,我建议使用直接求解器示例,因为似乎你正在寻找一个精确的解决方案。迭代求解器通过仅近似一个解来节省计算时间。

0

不,它不能解决奇异矩阵。但是其他任何代码也无法解决此问题,因为这里没有解决方案。

对于您的特定情况,矩阵A是奇异的。大小为9×9,但秩为6。MATLAB报告条件为1.9e17。因此,请在期望合理答案之前检查您的刚度矩阵组成。也许您需要规范化您的矩阵,即提取E I系数以将数字从1e5降至接近1,这更易于计算。

顺便说一下

如果您不喜欢Math.NET,或者您想验证代码使用纯c#。请阅读MSDN Magazine article by James McCaffrey并使用列出的代码。

var A = new [,] { ... };
var b = new [] { ... };
var x = LU.SustemSolve(A,b);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接