Take real or complex entries of eigensystem or separate them as a whole

I want to do the conditional assignment of variables as shown below:

{val, vec}={{-((1 + I)/Sqrt[2]), (1 + I)/Sqrt[2], -((1 - I)/Sqrt[2]), (1 - I)/
  Sqrt[2]}, {{(1 - I)/Sqrt[2], I, (1 + I)/Sqrt[2], 
   1}, {-((1 - I)/Sqrt[2]), I, -((1 + I)/Sqrt[2]), 1}, {(1 + I)/Sqrt[
   2], -I, (1 - I)/Sqrt[2], 
   1}, {-((1 + I)/Sqrt[2]), -I, -((1 - I)/Sqrt[2]), 1}}};
   
If [(Select[{val, vec}//Transpose, Element[#,Reals]&]//Length)>0,
{rval,rvec}=Select[{val, vec}//Transpose, Element[#,Reals]&]
] 

But I didn’t come up with a more concise method to achieve this goal. Any hints will be appreciated.

Regards,
Zhao

What are you trying to do? On brief inspection, it seems to me like Select[{val, vec} // Transpose, Element[#, Reals] &] cannot ever be true.

Please provide a simplified example that illustrates a specific question that you have.

I want to calculate the eigensystem of a real matrix, and then extract the real eigenvectors and eigenvalues, if such terms exist. However, I cannot know whether such a situation exists in advance, so it must be determined in real time based on the actual calculation results.

For example, see the following calculations:

In[7]:= gen1={{-1,2,-1},{-(3/2),3/2,-(1/2)},{-(1/2),3/2,-(3/2)}};
{val, vec}=Eigensystem[%,Cubics -> True]//ComplexExpand

gen2={
{0,0,0,-1},
{1,0,0,0},
{0,-1,0,0},
{0,0,-1,0}
};
{val, vec}=gen2//Eigensystem[#,Cubics -> True]&//ComplexExpand

Out[8]= {{-1, 
I, -I}, {{1/2, 1/2, 1}, {3/2 - I/2, 3/2 + I/2, 1}, {3/2 + I/2, 
3/2 - I/2, 1}}}

Out[10]= {{-((1 + I)/Sqrt[2]), (1 + I)/Sqrt[2], -((1 - I)/Sqrt[2]), (
1 - I)/Sqrt[
2]}, {{(1 - I)/Sqrt[2], I, (1 + I)/Sqrt[2], 1}, {-((1 - I)/Sqrt[2]),
I, -((1 + I)/Sqrt[2]), 1}, {(1 + I)/Sqrt[2], -I, (1 - I)/Sqrt[2], 
1}, {-((1 + I)/Sqrt[2]), -I, -((1 - I)/Sqrt[2]), 1}}}

As you can see, in the first example, there are real eigenvalues and eigenvectors, but in the second example, there is no such result.

What were you expecting this to give you?

{val, vec}//Transpose

What qualifies? If an element at the top level of {val, vec}//Transpose contains only real values at every level?

Can you just stop making me reverse engineer your code, please? Give me a sample input that you want to process, explain what processing you’re trying to do, and give me an expected result to test against.

Like, in one case, the first element of {val, vec} // Transpose is {-1, {1/2, 1/2, 1}}. Do you want that specific element to be selected or rejected?

In another case, the first element of {val, vec} // Transpose is {(-1 - I)/Sqrt[2], {(1 - I)/Sqrt[2], I, (1 + I)/Sqrt[2], 1}}. Do you want that specific element to be selected or rejected?

Maybe what you want is

Select[{val, vec} // Transpose, FreeQ[_Complex]]

Something like this might work

Cases[{val, vec} // Transpose // N, {_Real, {__Real}}]

See the following:

In[17]:= gen1={{-1,2,-1},{-(3/2),3/2,-(1/2)},{-(1/2),3/2,-(3/2)}};
{val1, vec1}=Eigensystem[%,Cubics -> True]//ComplexExpand;

Select[{val1, vec1}//Transpose, Element[#,Reals]&]
Select[{val1, vec1}, Element[#,Reals]&]

Out[19]= {{-1, {1/2, 1/2, 1}}}

Out[20]= {}

As you can see, without Transpose the real eigen value and eigen vector pairs can’t be selected out.

Because real eigen value corresponding to real eigen vector, and vice versa. So, I think this is enough.

I want to select all real eigenvalue/vector pairs, so in the first example, I should select it, but in the second example, I should not.

Conversion to numerical value may cause loss of precision, so I don’t want to do this.

Yes. This does the trick:

In[47]:= gen1={{-1,2,-1},{-(3/2),3/2,-(1/2)},{-(1/2),3/2,-(3/2)}};
{val1, vec1}=Eigensystem[%,Cubics -> True]//ComplexExpand

Select[{val1, vec1}//Transpose, Element[#,Reals]&]
Select[{val1, vec1} // Transpose, FreeQ[_Complex]]

Out[48]= {{-1, 
  I, -I}, {{1/2, 1/2, 1}, {3/2 - I/2, 3/2 + I/2, 1}, {3/2 + I/2, 
   3/2 - I/2, 1}}}

Out[49]= {{-1, {1/2, 1/2, 1}}}

Out[50]= {{-1, {1/2, 1/2, 1}}}

But my real goal is to simplify the logic of the If statement in the following code to complete the same assignment operation.

In[101]:= gen1={{-1,2,-1},{-(3/2),3/2,-(1/2)},{-(1/2),3/2,-(3/2)}};
{val1, vec1}=Eigensystem[%,Cubics -> True]//ComplexExpand;

If [(Select[{val1, vec1}//Transpose, FreeQ[_Complex]]//Length)>0,
{rval1,rvec1}=Select[{val1, vec1}//Transpose, FreeQ[_Complex]]//Transpose
]

Out[103]= {{-1}, {{1/2, 1/2, 1}}}

Okay, so we had two questions.

Question 1:

Given a structure like this, {a, {b, c, d}}, where a,b,c,d are all numbers, how can I determine that all of the numbers are non-complex? For example, given a list of such structures like this, {{2, {2, 2, 2}}, {I, {2, 2, 2}}, {2, {2, I, 2}}}, how could I use Select and some predicate to obtain the result {{2, {2, 2, 2}}}?

Answer 1:

Select[{{2, {2, 2, 2}}, {I, {2, 2, 2}}, {2, {2, I, 2}}}, FreeQ[_Complex]]

Question 2:

Given a condition like this, If[Length[complicatedList] > 0, myVariable = complicatedList], how can I avoid repeating complicatedList? complicatedList is actually an expression that filters and restructures a list and so is quite a large expression. I don’t want to repeat it because it makes the code hard to read and error prone.

Answer 2:

First off, I recommend that you don’t put the assignment inside the If. Burying assignments is a great way to cause confusion later. Instead, shoot for something like this:

myVariable = If[Length[complicatedList] > 0, complicatedList, Missing["no ducklings found"]]

This will make it much easier to trace and debug things later. You could use something else other than Missing, a default value for example. Just make sure that the assignment always happens. But this doesn’t get rid of the duplication. You could use With for that:

myVariable =
 With[
  {temp = complicatedList},
  If[Length[temp] > 0, temp, Missing["no ducklings found"]]]

If you prefer terse and don’t mind the hidden assignments, you could do this:

If[(myVariable = Length[complicatedList]) > 0, myVariable, myVariable = Missing["no ducklings found"]]

If for some reason the expression Length[complicatedList]) > 0 can fail to definitively produce either True or False, then you should use the 4-argument form of If.

Obviously I can execute the code and see what it produces. The point of the question was for you to explain to me in words the semantics of the situation. At the time I asked the question I was suspicious that you had unintentionally generated a structure that would always cause your Element check to fail. I later came to realize that your structure was as you desired but you misunderstood how Element worked.

The With version seems better, but the last method seem wrong:

In[437]:= {val, vec}={{-1, I, -I}, {{1/2, 1/2, 1}, {3/2 - I/2, 3/2 + I/2, 1}, {3/2 + I/2, 
   3/2 - I/2, 1}}};
myVariable =
 With[
  {temp = Select[{val, vec}//Transpose, FreeQ[_Complex]]},
  If[Length[temp] > 0, temp, Missing["no ducklings found"]]]
  
myVariable =If[(myVariable = Length[Select[{val, vec}//Transpose, FreeQ[_Complex]]]) > 0, myVariable, myVariable = Missing["no ducklings found"]]

Out[438]= {{-1, {1/2, 1/2, 1}}}

Out[439]= 1

Here is another approach:

a = {{1, 0, 0}, {0, 0, 1}, {0, -1, 0}};
b = {{1, 0, 0}, {0, 0, 1}, {0, 1, 0}};
c = {{0, 0, 0, -1}, {1, 0, 0, 0}, {0, -1, 0, 0}, {0, 0, -1, 0}};

getRealES[m_] := Select[Eigensystem[m] // Transpose, FreeQ[Complex]] /. {} -> "None real"

In[5]:= getRealES[a]
getRealES[b]
getRealES[c]

Out[5]= {{1, {1, 0, 0}}}
Out[6]= {{-1, {0, -1, 1}}, {1, {0, 1, 1}}, {1, {1, 0, 0}}}
Out[7]= "None real"

What’s the difference between the following two usages?

FreeQ[_Complex]
FreeQ[Complex]

In this case there is no difference. Two ways of saying the same thing.

A variant:

getRealES2[m_] := Select[Eigensystem[m] // Transpose, Element[#, Reals] &] /. {} :> Print["None real"]