Skip to main content

matrix - Can any one help me make my program work faster?



In order to construct the matrix h1+h2, I have used three Table commands. I am using fxn[n1a_, n1b_, n2a_, n2b_] as a function in a loop. I need my program to be a lot faster than what it is now, since this is only a part of my main program. Could any one tell me how I can make it work faster? In other words, what are the points I should pay attention to in Mathematica, in case of timing?


Description:


In the original program I am using an iteration method in which I need to construct the matrix h1+h2, calculate the new eigenvectors in each step, reconstruct the matrix h1+h2, until I observe convergence in eigenenergies.


avec, is a guessed vector, and I am using it as an initial vector to construct the matrix h1+h2.


Later on, I need to calculate the eigenvectors of this matrix, choose the vectors related to the minimum eigenvalues and put them instead of the previous avec.


h1 is the kinetic term of my matrix and h2 is the exchange potential (the formula which I have used came from the math I did for Hartree-Fock approximation).



fxn is a function I constructed based on the pre-calculated integrals in my code (so that I do not need to calculate the integrals in each iteration).


The problem now is the calculation for h2, which consumes most of the time. I assume the problem is the way I am using the Table commands. But I couldn't think of a more efficient way.


  avec = {{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 1, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0}, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
nvec = {1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6};
svec = {-1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1};
kdfxn[i_, j_] := If[i == j, 1, 0]

ne = 3;
n\[Mu] = 12;

\[Delta] = -50;
\[Beta] = 1;

fxn[n1a_, n1b_, n2a_, n2b_] := N[ Which[
n1a == n1b && n2a == n2b,
1/6 (1 - 3/(n1a^2 \[Pi]^2) - 3/(n2a^2 \[Pi]^2)),
n1a == n1b && n2a != n2b,
(4 (1 + (-1)^(n2a + n2b)) n2a n2b)/((n2a^2 - n2b^2)^2 \[Pi]^2),
n1a != n1b && n2a == n2b,
(4 (1 + (-1)^(n1a + n1b)) n1a n1b)/((n1a^2 - n1b^2)^2 \[Pi]^2),

True, -((
32 (-1 + (-1)^(n1a + n1b)) (-1 + (-1)^(
n2a + n2b)) n1a n1b n2a n2b)/((n1a^2 - n1b^2)^2 (n2a^2 -
n2b^2)^2 \[Pi]^4))]]

hmat = Table[
n0 = nvec[[n\[Mu]0]];
n1 = nvec[[n\[Mu]1]];
h1 = (n0^2 \[Pi]^2 + \[Beta] svec[[n\[Mu]0]]) kdfxn[n\[Mu]0,
n\[Mu]1] ;

h2 = Total[Table[
n2 = nvec[[n\[Mu]2]];
n3 = nvec[[n\[Mu]3]];
Table[
temp1 =
fxn[n1, n0, n3, n2] avec[[j, n\[Mu]3]] avec[[j, n\[Mu]2]] ;
temp2 =
fxn[n1, n3, n0, n2] avec[[j, n\[Mu]3]] avec[[j, n\[Mu]2]];
temp = \[Delta] (temp1 - temp2), {j, 1, ne}],
{n\[Mu]2, 1, n\[Mu]},

{n\[Mu]3, 1, n\[Mu]}], Infinity];
h1 + h2
, {n\[Mu]0, 1, n\[Mu]}, {n\[Mu]1, 1, n\[Mu]}] // Timing



I want to use the code @Henrik Schumacher gave me to find my new avecs (which are coming from the eigenvectors of hcmat) to recalculate hcmat and iterate. The problem is I can do it manually by substituting new avec instead of the previous one, but as I start using table it doesn't work. I don't know why it's acting like that.


Table[chmat = 
With[{ccfxn = cfxn},
Compile[{{nm, _Integer}, {ne, _Integer}, {d, _Real}, {b, \
_Real}, {avec, _Real, 2}, {nvec, _Real, 1}, {svec, _Real, 1}},

Block[{n0, n1, n2, n3, h1, h2, tmp, tmp2},
Table[n0 = Compile`GetElement[nvec, nm0];
n1 = Compile`GetElement[nvec, nm1];
tmp = 0.;
Do[n2 = Compile`GetElement[nvec, nm2];
n3 = Compile`GetElement[nvec, nm3];
tmp2 = (ccfxn[n1, n0, n3, n2] - ccfxn[n1, n3, n0, n2]);

Do[tmp +=
tmp2 Compile`GetElement[avec, j, nm3] Compile`GetElement[

avec, j, nm2], {j, 1, ne}], {nm2, 1, nm}, {nm3, 1,
nm}];
d tmp +
If[nm0 ==
nm1, (n0^2 Pi^2 + b Compile`GetElement[svec, nm0]),
0.], {nm0, 1, nm}, {nm1, 1, nm}]],
CompilationTarget -> "C",
CompilationOptions -> {"InlineCompiledFunctions" -> True},
RuntimeOptions -> "Speed"]];


hmat2 = chmat[n\[Mu], ne, \[Delta], \[Beta], avec, nvec, svec]; //
AbsoluteTiming // First
hmat = hmat2

eout = Eigensystem[hmat];
evals = eout[[1]];
evecs = eout[[2]];
sortedevals = Sort[evals];
bvec = Chop[Table[bpos = Position[evals, sortedevals[[i]]][[1, 1]];
evecs[[bpos]], {i, 1, ne}]];

Table[If[Sign[bvec[[i, 1]]] == Sign[avec[[i, 1]]], avec = ( bvec) ,
avec = (Sign[avec[[i, 1]]] bvec) ], {i, 1, ne}];
normalizedavec = Table[avec[[i]]/norm[avec, i], {i, 1, ne}];
avec = normalizedavec;
en = Total[Take[sortedevals, ne]], {j, 1, 5}]

In the rest of the code, I am finding the first ne(3) minimum eigenenergies and finding the the related eigenvectors, to construct bvec. by doing some modification on bvec and normalizing it ,I could get the new avec I want to use instead of the previous one. Now my problem is doing the iteration. I would appreciate it if you could help me with this.


I want to calculate this integrals


 sol = Assuming[
A \[Element] Reals && 0 <= s1 <= 1 && 1 <= n1a <= 10 &&

1 <= n2a <= 10 && 1 <= n1b <= 10 && 1 <= n2b <= 10,
Expand@FullSimplify[
Integrate[
4 A Exp[-B (s2 - s1)] Sin[n1a \[Pi] s1] Sin[n1b \[Pi] s1] Sin[
n2a \[Pi] s2] Sin[n2b \[Pi] s2], {s2, s1, 1}]]];

sol1 = Assuming[
A \[Element] Reals && 0 <= s2 <= 1 && 1 <= n1a <= 10 &&
1 <= n2a <= 10 && 1 <= n1b <= 10 && 1 <= n2b <= 10,
Expand@FullSimplify[

Integrate[
4 A Exp[-B (s1 - s2)] Sin[n1a \[Pi] s1] Sin[n1b \[Pi] s1] Sin[
n2a \[Pi] s2] Sin[n2b \[Pi] s2], {s1, s2, 1}]]];

(int[n1a_, n2a_, n1b_, n2b_] =
Integrate[sol, {s1, 0, 1},
Assumptions ->
B \[Element] Reals && A \[Element] Reals && 1 <= n1a <= 10 &&
1 <= n2a <= 10 && 1 <= n1b <= 10 && 1 <= n2b <= 10] +
Integrate[sol1, {s2, 0, 1},

Assumptions ->
B \[Element] Reals && A \[Element] Reals && 1 <= n1a <= 10 &&
1 <= n2a <= 10 && 1 <= n1b <= 10 && 1 <= n2b <= 10])

and replace n1a in the int function with n1a+0.0001 . Then I need to use the result of int after the replacement, as the material for code :code=result in:


        "cfxn = Block[{n1a, n1b, n2a, n2b}, 
With[{code = "result"},
Compile[{{n1a, _Integer}, {n1b, _Integer}, {n2a, _Integer}, {n2b, \
_Integer}}, code, CompilationTarget -> "C"]]];"


but I am getting these errors:



CompiledFunction::cfse: Compiled expression CompiledFunction[{10,11.,5464},<<7>>,LibraryFunction[/Users/delaramnematollahi/Library/Mathematica/ApplicationData/CCompilerDriver/BuildFolder/Delarams-MacBook-Pro-3755/compiledFunction3.dylib,compiledFunction3,{<<1>>},{Real,2}]] should be a machine-size real number.



which I do not understand why.


Could you please help me with this?



Answer



Compile can be your friend. Fortunately, your code is almost perfectly prodedural and thus can be compiled with only few changes.


avec = {{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
nvec = {1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6};

svec = {-1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1};
ne = 3;
nμ = 12;
δ = -50;
β = 1;

cfxn = Block[{n1a, n1b, n2a, n2b},
With[{code = Which[
n1a == n1b && n2a == n2b,
Evaluate[N[1/6 (1 - 3/(n1a^2 π^2) - 3/(n2a^2 π^2))]],


n1a == n1b && n2a != n2b,
Evaluate[N[(4 (1 + (-1)^(n2a + n2b)) n2a n2b)/((n2a^2 - n2b^2)^2 π^2)]],

n1a != n1b && n2a == n2b,
Evaluate[N[(4 (1 + (-1)^(n1a + n1b)) n1a n1b)/((n1a^2 - n1b^2)^2 π^2)]],

True,
Evaluate[N[-((32 (-1 + (-1)^(n1a + n1b)) (-1 + (-1)^(n2a + n2b)) n1a n1b n2a n2b)/((n1a^2 - n1b^2)^2 (n2a^2 - n2b^2)^2 π^4))]]
]},

Compile[{{n1a, _Integer}, {n1b, _Integer}, {n2a, _Integer}, {n2b, _Integer}},
code,
CompilationTarget -> "C"
]
]];

Here I tried to enforce that cfxn uses floating point numbers internally (therefore the Evaluate) such that less Integers have to be type cast to Reals.


chmat = With[{ccfxn = cfxn},
Compile[{{nm, _Integer}, {ne, _Integer}, {d, _Real}, {b, _Real}, {avec, _Real, 2}, {nvec, _Real, 1}, {svec, _Real, 1}},
Block[{n0, n1, n2, n3, h1, h2, tmp, tmp2},

Table[
n0 = nvec[[nm0]];
n1 = nvec[[nm1]];
tmp = 0.;
Do[
n2 = nvec[[nm2]];
n3 = nvec[[nm3]];
tmp2 = (ccfxn[n1, n0, n3, n2] - ccfxn[n1, n3, n0, n2]);
Do[tmp += tmp2 avec[[j, nm3]] avec[[j, nm2]], {j, 1, ne}],
{nm2, 1, nm}, {nm3, 1, nm}];

d tmp + If[nm0 == nm1, N[(n0^2 Pi^2 + b svec[[nm0]])], 0.],
{nm0, 1, nm}, {nm1, 1, nm}]
],
CompilationTarget -> "C",
CompilationOptions -> {"InlineCompiledFunctions" -> True}
]];

Edit: My former implementation of chmat did not produce the correct result. I removed it and also optimized away the Total: Summing up the results in a fixed Real register has the advantage that no auxiliary arrays have to be allocated and that no indexing into such arrays takes place within Total.


You can compute hmat with


hmat2= chmat[nμ, ne, δ, β, avec, nvec, svec];//AbsoluteTiming//First

hmat==hmat2

(* 0.003411 *)
(* True *)

On my machine, this results in a 600-fold speed-up.


Addendum:


This need not to be the end of the story. By deactivating some security checks and replacing Part with CompileGetElement` I even get a speed-up factor of over 2500 (compared to the original code). This is how it looks:


chmat = With[{ccfxn = cfxn}, 
Compile[{{nm, _Integer}, {ne, _Integer}, {d, _Real}, {b, _Real}, {avec, _Real, 2}, {nvec, _Real, 1}, {svec, _Real, 1}},

Block[{n0, n1, n2, n3, h1, h2, tmp, tmp2},
Table[
n0 = Compile`GetElement[nvec, nm0];
n1 = Compile`GetElement[nvec, nm1];
tmp = 0.;
Do[
n2 = Compile`GetElement[nvec, nm2];
n3 = Compile`GetElement[nvec, nm3];
tmp2 = (ccfxn[n1, n0, n3, n2] - ccfxn[n1, n3, n0, n2]);
Do[

tmp += tmp2 Compile`GetElement[avec, j, nm3] Compile`GetElement[avec, j, nm2],
{j, 1, ne}],
{nm2, 1, nm}, {nm3, 1, nm}];
d tmp + If[nm0 == nm1, (n0^2 Pi^2 + b Compile`GetElement[svec, nm0]), 0.],
{nm0, 1, nm}, {nm1, 1, nm}]
],
CompilationTarget -> "C",
CompilationOptions -> {"InlineCompiledFunctions" -> True},
RuntimeOptions -> "Speed"
]];

Comments

Popular posts from this blog

plotting - Filling between two spheres in SphericalPlot3D

Manipulate[ SphericalPlot3D[{1, 2 - n}, {θ, 0, Pi}, {ϕ, 0, 1.5 Pi}, Mesh -> None, PlotPoints -> 15, PlotRange -> {-2.2, 2.2}], {n, 0, 1}] I cant' seem to be able to make a filling between two spheres. I've already tried the obvious Filling -> {1 -> {2}} but Mathematica doesn't seem to like that option. Is there any easy way around this or ... Answer There is no built-in filling in SphericalPlot3D . One option is to use ParametricPlot3D to draw the surfaces between the two shells: Manipulate[ Show[SphericalPlot3D[{1, 2 - n}, {θ, 0, Pi}, {ϕ, 0, 1.5 Pi}, PlotPoints -> 15, PlotRange -> {-2.2, 2.2}], ParametricPlot3D[{ r {Sin[t] Cos[1.5 Pi], Sin[t] Sin[1.5 Pi], Cos[t]}, r {Sin[t] Cos[0 Pi], Sin[t] Sin[0 Pi], Cos[t]}}, {r, 1, 2 - n}, {t, 0, Pi}, PlotStyle -> Yellow, Mesh -> {2, 15}]], {n, 0, 1}]

plotting - Plot 4D data with color as 4th dimension

I have a list of 4D data (x position, y position, amplitude, wavelength). I want to plot x, y, and amplitude on a 3D plot and have the color of the points correspond to the wavelength. I have seen many examples using functions to define color but my wavelength cannot be expressed by an analytic function. Is there a simple way to do this? Answer Here a another possible way to visualize 4D data: data = Flatten[Table[{x, y, x^2 + y^2, Sin[x - y]}, {x, -Pi, Pi,Pi/10}, {y,-Pi,Pi, Pi/10}], 1]; You can use the function Point along with VertexColors . Now the points are places using the first three elements and the color is determined by the fourth. In this case I used Hue, but you can use whatever you prefer. Graphics3D[ Point[data[[All, 1 ;; 3]], VertexColors -> Hue /@ data[[All, 4]]], Axes -> True, BoxRatios -> {1, 1, 1/GoldenRatio}]

plotting - Mathematica: 3D plot based on combined 2D graphs

I have several sigmoidal fits to 3 different datasets, with mean fit predictions plus the 95% confidence limits (not symmetrical around the mean) and the actual data. I would now like to show these different 2D plots projected in 3D as in but then using proper perspective. In the link here they give some solutions to combine the plots using isometric perspective, but I would like to use proper 3 point perspective. Any thoughts? Also any way to show the mean points per time point for each series plus or minus the standard error on the mean would be cool too, either using points+vertical bars, or using spheres plus tubes. Below are some test data and the fit function I am using. Note that I am working on a logit(proportion) scale and that the final vertical scale is Log10(percentage). (* some test data *) data = Table[Null, {i, 4}]; data[[1]] = {{1, -5.8}, {2, -5.4}, {3, -0.8}, {4, -0.2}, {5, 4.6}, {1, -6.4}, {2, -5.6}, {3, -0.7}, {4, 0.04}, {5, 1.0}, {1, -6.8}, {2, -4.7}, {3, -1.