la4j 显性特征值 - 在 java 8 中将命令式算法转换为函数式风格

la4j dominant eigenvalue - converting imperative algorithm to functional style in java 8

我正在研究矩阵特征值问题,使用幂法估计主特征值,其中 A 是 n x n 实矩阵。我正在使用 la4j 线性代数库。该算法使用以下步骤

  1. 归一化x0构造单位向量x1=x0/euclidean norm(x0)

对于 k=1 到 kmax

  1. 求x[k+1]=A[x[k]]
  2. 计算alpha[k+1]=转置(x[k])*x[k+1]
  3. 规范化x[k+1]并且不重命名x[k+1]=x[k+1]/euclidean norm(x[k+1])
  4. 终止条件:如果|alpha[k+1]-alpha[k]| <容忍,停止 否则,将 k 增加到 k+1 并转到步骤 2

这是我的class。我想使用 java 8 将 dominantEigenV 转换为功能样式。任何帮助将不胜感激。

import org.la4j.Matrix;
import org.la4j.Vector;
import org.la4j.Vectors;

public class EigenDecomp
{

public class EigenVO
{
    private double e_val;
    private Vector e_vec;

    public EigenVO(double e_val, Vector e_vec)
    {
        this.e_val=e_val;
        this.e_vec=e_vec;
    }

    public double getE_val()
    {
        return e_val;
    }

    public void setE_val(double e_val)
    {
        this.e_val = e_val;
    }

    public Vector getE_vec()
    {
        return e_vec;
    }

    public void setE_vec(Vector e_vec)
    {
        this.e_vec = e_vec;
    }
}

private Matrix A;
private Vector x0;
private double tolerance;
private double alpha0;
private int kMax;
private EigenVO result;

public EigenVO getResult()
{
    return result;
}

public EigenDecomp(Matrix A, Vector x0, double tolerance, int kMax)
{
    this.A = A;
    this.x0 = x0;
    this.tolerance = tolerance;
    this.kMax = kMax;
    this.result = new EigenVO(0.0, x0);
    this.alpha0 = Integer.MAX_VALUE;
}

public void dominantEigenV()
{
    for (int k=1; k<kMax; k++)
    {
        alpha0=result.getE_val();
        x0=result.getE_vec();
        result.setE_vec(A.multiply(result.getE_vec().toColumnMatrix()).getColumn(0));
        result.setE_val(((Vector)x0.toRowMatrix().multiply(result.getE_vec())).get(0));
        result.setE_vec(result.getE_vec().transform(Vectors.asDivFunction(result.getE_vec().euclideanNorm())));
        System.out.println("k:"+k + ",xk:" + this.x0 + ",xkp1:" + this.result.getE_vec() + ",alpha_k:"  + this.alpha0 + ",alphak_1:" + this.result.getE_val());
        if (Math.abs(result.getE_val() - alpha0) < tolerance)
        {
            break;
        }
    }
}

public static void main(String args[])
{
    Matrix A = Matrix
    .from2DArray(new double[][] 
    { 
        { -3.0,  -4.0, -4.0 },
        {  6.0,   9.0,  6.0 },
        { -9.0, -14.0, -8.0 }
    });
    Vector x0=Vector.fromArray(new double[] { 0.0, 1.0, 1.0 });
    double tolerance=0.0001;
    int kMax=50;
    EigenDecomp test=new EigenDecomp(A,x0, tolerance,kMax);
    test.dominantEigenV();
    System.out.println("eVal:" + test.getResult().getE_val());
    System.out.println("eVector:\n" + 
    test.getResult().getE_vec().toColumnMatrix().toString());
}
}

下面是使用 3x3 A 矩阵的主要方法的输出

k:1,xk:0.000 1.000 1.000,xkp1:-0.288 0.540 -0.791,alpha_k:0.0,alphak_1:-7.0 
k:2,xk:-0.288 0.540 -0.791,xkp1:0.662 -0.573 0.484,alpha_k:-7.0,alphak_1:-2.49288486416559
k:3,xk:0.662 -0.573 0.484,xkp1:-0.547 0.577  -0.607,alpha_k:-2.49288486416559,alphak_1:-2.936497651061076
k:4,xk:-0.547 0.577 -0.607,xkp1:0.587 -0.577  0.567,alpha_k:-2.936497651061076,alphak_1:-2.992843189162536
k:5,xk:0.587 -0.577 0.567,xkp1:-0.574 0.577  -0.581,alpha_k:-2.992843189162536,alphak_1:-2.9992035320913533
k:6,xk:-0.574 0.577 -0.581,xkp1:0.578 -0.577  0.576,alpha_k:-2.9992035320913533,alphak_1:-2.999911487899692
k:7,xk:0.578 -0.577 0.576,xkp1:-0.577 0.577  -0.578,alpha_k:-2.999911487899692,alphak_1:-2.999990165128744
eVal:-2.999990165128744
eVector:
-0.577
0.577
-0.578

首先让我把你的命令式方法再放一遍,这样我们就可以比较两者

public void dominantEigenV()
{
  for (int k=1; k<kMax; k++)
  {
    alpha0=result.getE_val();
    x0=result.getE_vec();
    result.setE_vec(A.multiply(result.getE_vec().toColumnMatrix()).getColumn(0));
    result.setE_val(((Vector)x0.toRowMatrix().multiply(result.getE_vec())).get(0));
    result.setE_vec(result.getE_vec().transform(Vectors.asDivFunction(result.getE_vec().euclideanNorm())));
    System.out.println("k:"+k + ",xk:" + this.x0 + ",xkp1:" + this.result.getE_vec() + ",alpha_k:"  + this.alpha0 + ",alphak_1:" + this.result.getE_val());
    if (Math.abs(result.getE_val() - alpha0) < tolerance)
    {
        break;
    }
  }
}

然后,您只需要创建一个Consumer方法并添加一个辅助方法来打印每次迭代中的值

public Consumer<EigenDecomp> dominantEigenV = 
  ei->IntStream.range(1, kMax)
  .peek(k->
    ((Consumer<EigenDecomp>)e->ei.alpha0=ei.result.getE_val())
      .andThen(e->x0=ei.result.getE_vec())
      .andThen(e->ei.result.setE_vec(A.multiply(ei.result.getE_vec().toColumnMatrix()).getColumn(0)))
      .andThen(e->ei.result.setE_val(((Vector)ei.x0.toRowMatrix().multiply(ei.result.getE_vec())).get(0)))
      .andThen(e->ei.result.setE_vec(ei.result.getE_vec().transform(Vectors.asDivFunction(ei.result.getE_vec().euclideanNorm()))))
      .andThen(e->System.out.println(e.toString(k)))
      .accept(ei)
     ).allMatch(k->Math.abs(ei.result.getE_val()-ei.alpha0) > ei.tolerance);

public String toString(int k)
{
    return "k:"+k + ",xk:" + this.x0 + ",xkp1:" + this.result.getE_vec() + ",alpha_k:"  + this.alpha0 + ",alphak_1:" + this.result.getE_val();
}

让我解释一下主要部分:

for (int k=1; k<kMax; k++) 

替换为

IntStream.range(1, kMax)

每次k迭代后使用peek操作执行一个consumer block

.peek(k->
)

对于每个操作,使用消费者链,为了在 peek 中启用它,您可以在消费者中进行第一个赋值,然后在第二个和 Then() 上进行另一个表示为 lambda 的赋值......等等,直到所有作业已完成

(Consumer<EigenDecomp>)e->ei.alpha0=ei.result.getE_val()).andThen(

链结束后,pass accept传过来的参数ei

.accept(ei)

对于停止条件,使用 allMatch 并添加一个谓词,以便在满足条件时算法可以在 k 达到 kMax 之前停止

.allMatch(k->Math.abs(ei.result.getE_val()-ei.alpha0) > ei.tolerance)