2

How can I find the smallest positive real number in a complex vector of size N by 1 in Eigen3? For example, in this case I'd like to find the value 3.64038.

#include <Eigen/Dense>
#include <iostream>
using namespace std;
using namespace Eigen;

int main()
{
    MatrixXd m(4, 4);
    m << 1, 0, 1, 1,
        0, 2, 0, 1,
        0, 2, 1, 0,
        2, 1, 2, 1;
    cout << m << endl;
    cout << m.eigenvalues() << endl;
    return 0;
}

Output

1 0 1 1
0 2 0 1
0 2 1 0
2 1 2 1
        (3.64038,0)
      (-0.444745,0)
 (0.902183,1.01932)
(0.902183,-1.01932)

Vector elements that have an imaginary part not equal to 0 should be excluded.

I wrote the following function, but was wondering if there is an approach using Eigen's methods.

double findPositiveRealMin(VectorXcd v)
{
    VectorXd v_imag = v.imag();
    VectorXd v_real = v.real();
    for (int i = 0; i < v.rows(); i++)
    {
        if (v_imag[i] != 0 | v_real[i] <= 0)
            v_real[i] = 1.0e16;
    }
    return v_real.minCoeff();
}
rinkert
  • 6,593
  • 2
  • 12
  • 31
  • For a complex vector `v`, you can fetch the parts of the number via `.real()` / `.imag()`, so: `v.real().maxCoeff()` would give you the max. real coefficient. See the documentation: https://eigen.tuxfamily.org/dox/group__QuickRefPage.html https://eigen.tuxfamily.org/dox/group__TutorialMatrixArithmetic.html https://eigen.tuxfamily.org/dox/group__CoeffwiseMathFunctions.html – Cedric Jun 14 '21 at 11:24
  • "the smallest positive real value" both `0.902183`s not wanted because they have non-zero imaginary parts? – Caleth Jun 14 '21 at 11:29
  • @Caleth yes indeed. – rinkert Jun 14 '21 at 11:33
  • @Cedric thanks, but I require the lowest number larger than zero, with an imaginary part of zero. – rinkert Jun 14 '21 at 11:35
  • @rinkert: right, my mistake. I misinterpreted your example. I suppose you could write a custom visitor as https://stackoverflow.com/questions/50027494/eigen-indices-of-dense-matrix-meeting-condition, but I am not aware of an in-built method. Maybe someone else has a better suggestion. – Cedric Jun 14 '21 at 11:47
  • 1
    Actually, slightly contrived, but this seems to work: `(v.imag().array() == 0 && v.real().array() > 0).select(v.real(), 1e16).minCoeff()` – Cedric Jun 14 '21 at 12:02
  • 1
    @Cedric that works thanks! you can turn that into an answer if you want – rinkert Jun 14 '21 at 12:18

2 Answers2

2

One option is to create a logical array and then call Eigen::select on it. Inspired by https://forum.kde.org/viewtopic.php?f=74&t=91378

In this case:

Eigen::VectorXcd v = m.eigenvalues();

// minimum positive real value with zero imaginary part
Eigen::Array<bool,Eigen::Dynamic,1> cond1 = (v.imag().array() == 0);
Eigen::Array<bool,Eigen::Dynamic,1> cond2 = (v.real().array() > 0);
double some_big_value = 1e16;

std::cout << (cond1 && cond2).select(v.real(), some_big_value).minCoeff() << std::endl;

... or, as a one-liner:

std::cout << (v.imag().array() == 0 && v.real().array() > 0).select(v.real(), 1e16).minCoeff() << std::endl;
Cedric
  • 278
  • 1
  • 9
1

This one-liner uses the ternary operator in combination with Eigen's unaryExpr() method.

std::cout << m.eigenvalues().unaryExpr([](auto a){return a.imag() != 0 || a.real() < 0 ? 1.e16 : a;}).real().minCoeff() << std::endl;
RHertel
  • 23,412
  • 5
  • 38
  • 64