# How to efficiently populate a symmetric matrix?

I need to populate a matrix $$A_{kl}$$, where

$$k = (m-1)J+n$$ $$l = (p-1)J+q$$

And

$$m,p = 1, 2, ..., I$$ $$n,q = 1, 2, ..., J$$

Its components are (mnpq). For populate it, I'm using an expensive 4 Do loop

Do[
Do[
Do[
Do[
Print[m, n, p, q];
k = (m - 1) nC + n;
l = (p - 1) nC + q;
If[k <= l, A[[k, l]] = cf[Nfunc, xi, yi, wix, wiy, m, n, p, q],
0];
, {q, 1, J, 1}]
, {p, 1, I, 1}]
, {n, 1, J, 1}]
, {m, 1, I, 1}]


Knowing that $$A_{kl}$$ for a $$I=J=2$$, its components are (mnpq)

$$\begin{bmatrix} (1111) & (1112) & (1121) & (1122)\\ & (1212) & (1221) & (1222)\\ symm. & & (2121) & (2122)\\ & & & (2222)\\ \end{bmatrix}$$

Does anyone know a more efficient way to populate it? Maybe using a bult-in function?

UPDATE

cf = Compile[{{Nfunc, _Real, 2}, {xi, _Real, 1}, {yi, _Real,
1}, {wix, _Real, 1}, {wiy, _Real,
1}, {m, _Integer}, {n, _Integer}, {p, _Integer}, {q, _Integer}},
Module[{sum},
sum = 0.0;
For[i = 1, i <= Length@xi, i++,
For[j = 1, j <= Length@yi, j++,
sum =
sum + (8 \[Pi]^2)/
a^2 m p Cos[(m \[Pi] xi[[i]])/((1/
2) a)] Sin[(n \[Pi] yi[[j]])/((1/
2) b)] Cos[(p \[Pi] xi[[i]])/((1/
2) a)] Sin[(q \[Pi] yi[[j]])/((1/2) b)]*wix[[i]]*
wiy[[j]]*Nfunc[[i, j]];
]
];
sum],
CompilationTarget -> "C",
RuntimeAttributes -> {Listable},
Parallelization -> True,
RuntimeOptions -> "Speed"];

• At least you can just use only one Do. – Αλέξανδρος Ζεγγ May 5 '20 at 13:32
• What is the nature of cf? Is it Listable in any of its arguments? I.e. would cf[nFunc, xi, yi, wix, wiy, m, n, p, {q1, q2, q3}] correctly give a list of three values? – Marius Ladegård Meyer May 5 '20 at 13:35
• @ΑλέξανδροςΖεγγ, that is the exact point. I'm not getting create a algorithm to only one Do – Professor P. Cosmo Klunk May 5 '20 at 13:53
• @MariusLadegårdMeyer, m,n,p,q are integers. All others are lists. cf = Compile[{{Nfunc, _Real, 2}, {xi, _Real, 1}, {yi, _Real, 1}, {wix, _Real, 1}, {wiy, _Real, 1}, {m, _Integer}, {n, _Integer}, {p, _Integer}, {q, _Integer}}. – Professor P. Cosmo Klunk May 5 '20 at 13:55
• Yes, but you want to evaluate this function not for only one value of these integers, but for many different ones. If we put these many different ones in a list, and feed that list to cf, will it work? For instance, if I want to find Sin[x] for x = 1, 2, 3, ..., 100 I don't need to loop, I just do Sin[Range[100]]. I'm asking whether this applies to your function cf. – Marius Ladegård Meyer May 5 '20 at 13:58

Your $$m-1, n-1$$ and $$p-1, q-1$$ are the two digits of $$k-1$$ and $$l-1$$, respectively, in base $$J$$. This should be pretty fast:

dim = i*j;
A = ConstantArray[0, {dim, dim}];
Do[
A[[k, l]] = A[[l, k]] = cf[Nfunc, xi, yi, wix, wiy, Quotient[k - 1, j] + 1, Mod[k - 1, j] + 1, Quotient[l - 1, j] + 1, Mod[l - 1, j]+ 1]
, {k, 1, dim}, {l, k, dim}
]


If $$IJ$$ is huge and your cf is very very fast, then you can probably shave off a little bit more time by making an outer Do over k where you compute the quotient and mod once for that k, followed by an inner Do over l.

• I will update my question adding cf. Could you analyse it? Thank you for answering! – Professor P. Cosmo Klunk May 5 '20 at 14:29
• It doing i = 2; j = 2; dim = i*j; A = ConstantArray[0, {dim, dim}]; Do[A[[k, l]] = A[[l, k]] = {Quotient[k, j] + 1, Mod[k, j], Quotient[l, j] + 1, Mod[l, j]}, {k, 1, dim}, {l, k, dim}], we do not get the @bill s answer, as should be. – Professor P. Cosmo Klunk May 5 '20 at 14:58
• I have fixed the off-by-one issue. – Marius Ladegård Meyer May 5 '20 at 15:10
• Perfect, @Marius. Thank you very much! – Professor P. Cosmo Klunk May 5 '20 at 15:29

One way to create your data structure is to realize that your list of integers is closely related to all the Tuples taken in groups. For the case I=J=2, the permutations are of 1 and 2 taken 4 at a time:

mat = Partition[Tuples[{1, 2}, 4], 4];
mask = UpperTriangularize[ConstantArray[1, {4, 4}]];
outMat = mat mask + Transpose[mat mask] - mat IdentityMatrix[4]

outMat//MatrixForm


• Thank you answering. But I did not understand how I will access each component of the matrix A using your approach. – Professor P. Cosmo Klunk May 5 '20 at 14:28
• Use Part ([[ ]]). For example, outMat[[2, 3]] will give you the 2,3 element of the matrix. – bill s May 5 '20 at 15:07
• What a dumb! Sorry for this dumb question! Thank you very much! I accepted the @Marius's answer because it was more straight for me. But, without doubt, your approach is really great too! – Professor P. Cosmo Klunk May 5 '20 at 15:28
• I should also note that generating tuples from a length-$k$ list is isomorphic to getting base-$k$ digits, so: mat = Partition[IntegerDigits[Range[0, 15], 2, 4] + 1, 4] – J. M.'s torpor May 5 '20 at 16:39