* Created array indexer for "upper diagonal" indexing mode.
Apparently I was able to create the non-iterative version of UDdec (1-based indices).
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
"""
|
||||
Symmetric array layout (lower diagonal, Fortran column-major ordering):
|
||||
|
||||
i >= j
|
||||
|
||||
----> j
|
||||
| (1,1)
|
||||
@@ -11,7 +11,7 @@ i | (2,1) (2,2)
|
||||
: : :
|
||||
(N,1) (N,2) (N,3) ... (N,N)
|
||||
|
||||
In linear form:
|
||||
The linear index goes like this:
|
||||
|
||||
1
|
||||
2 N+1
|
||||
@@ -71,3 +71,323 @@ def copy_over_array(N, arr_L):
|
||||
print "%5d %5d %5d %5d %5d" % (ii,jj,iskip,jskip,ldiag_index+1)
|
||||
rslt[i,j] = arr_L[ldiag_index]
|
||||
return rslt
|
||||
|
||||
|
||||
# These are the reference implementation:
|
||||
|
||||
def LD(i,j,N):
|
||||
"""python equivalent of gafqmc_LD on nwchem-gafqmc integral
|
||||
dumper module.
|
||||
Translates a lower-diagonal index (ii >= jj) to linear index
|
||||
0, 1, 2, 3, ...
|
||||
This follows python convention; thus 0 <= i < N, and so also j.
|
||||
|
||||
"""
|
||||
# iskip is row traversal, jskip is column traversal.
|
||||
# (iskip+jskip) is the final array index.
|
||||
if i >= j:
|
||||
ii = i
|
||||
jj = j
|
||||
else:
|
||||
ii = j
|
||||
jj = i
|
||||
|
||||
iskip = ii - jj # + 1
|
||||
#jskip = (jj-1)*N - (jj-2)*(jj-1)/2 # for 1-based
|
||||
jskip = (jj)*N - (jj-1)*(jj)//2 # for 0-based
|
||||
return iskip + jskip
|
||||
|
||||
|
||||
def LDdec(ij, N):
|
||||
"""Back-translates linear index 0, 1, 2, 3, ... to a lower-diagonal
|
||||
index pair (ii >= jj).
|
||||
This is not optimal, but it avoids storing an auxiliary array
|
||||
that is easily computable. Plus, this function is supposed to
|
||||
be called rarely.
|
||||
"""
|
||||
jskip = 0
|
||||
for j in xrange(N):
|
||||
if jskip + (N - j) > ij:
|
||||
jj = j
|
||||
ii = ij - jskip + j
|
||||
return (ii,jj)
|
||||
jskip += N - j
|
||||
|
||||
raise ValueError, "LDdec(ij=%d,N=%d): invalid index ij" % (ij,N)
|
||||
|
||||
# end reference implementation
|
||||
|
||||
def test_LD_enc_dec(N):
|
||||
"""Simple test to check LD encoding and decoding correctness.
|
||||
For python-style indexing (0 <= i < N, similarly for j)."""
|
||||
for i in xrange(N):
|
||||
for j in xrange(N):
|
||||
ij = LD(i,j,N)
|
||||
(ii,jj) = LDdec(ij,N)
|
||||
print "%3d %3d | %6d | %3d %3d" % (i,j, ij, ii,jj)
|
||||
|
||||
|
||||
def test_LD_enc_dec_diagonal(N):
|
||||
"""Simple test to check LD encoding and decoding correctness.
|
||||
For python-style indexing (0 <= i < N, similarly for j).
|
||||
For diagonal element only
|
||||
"""
|
||||
from numpy import sqrt
|
||||
LDsize = N * (N+1) / 2
|
||||
for i in xrange(N):
|
||||
j = i
|
||||
ij = LD(i,j,N)
|
||||
(ii,jj) = LDdec(ij,N)
|
||||
jj2 = int( sqrt(((LDsize) - ij) * 2) )
|
||||
print "%3d %3d | %6d ( %6d %6d ) | %3d %3d // %3d %3d" % (
|
||||
i,j,
|
||||
ij,
|
||||
ij-LDsize, -2*(ij-LDsize),
|
||||
ii,jj,
|
||||
-1, jj2)
|
||||
# ^^ distance from end of array
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
"""
|
||||
Faster LDdec is possible.
|
||||
|
||||
Consider:
|
||||
jskip = (jj)*N - (jj-1)*(jj)/2 # for 0-based
|
||||
= jj*(2*N - (jj-1)) / 2
|
||||
|
||||
|
||||
|
||||
"""
|
||||
|
||||
def Hack2_LD_enc_dec(N):
|
||||
"""Simple test to check LD encoding and decoding correctness.
|
||||
For python-style indexing (0 <= i < N, similarly for j)."""
|
||||
from numpy import sqrt
|
||||
LDsize = N * (N+1) / 2
|
||||
for j in xrange(N):
|
||||
for i in xrange(j,N):
|
||||
ij = LD(i,j,N)
|
||||
(ii,jj) = LDdec(ij,N)
|
||||
jj2 = ( sqrt(((LDsize) - ij) * 2) )
|
||||
#print "%3d %3d | %6d | %3d %3d" % (i,j, ij, ii,jj)
|
||||
print "%3d %3d | %6d %6d | %3d %3d // %8.4f" % (
|
||||
i,j,
|
||||
ij, LDsize-ij,
|
||||
ii,jj,
|
||||
jj2)
|
||||
|
||||
|
||||
|
||||
###############################################################################
|
||||
|
||||
|
||||
"""
|
||||
UPPER DIAGONAL IMPLEMENTATION
|
||||
|
||||
j >= i
|
||||
Should be easier (?)
|
||||
Symmetric array layout (lower diagonal, Fortran column-major ordering):
|
||||
|
||||
|
||||
----> j
|
||||
| (1,1) (1,2) (1,3) ... (1,N)
|
||||
i | (2,2) (2,3) ... (2,N)
|
||||
v (3,3) ... (3,N)
|
||||
: :
|
||||
(N,N)
|
||||
|
||||
The linear index goes like this:
|
||||
1 2 4 ... N*(N+1)/2-4
|
||||
3 5 ... N*(N+1)/2-3
|
||||
6 ... N*(N+1)/2-2
|
||||
: N*(N+1)/2-1
|
||||
N*(N+1)/2
|
||||
|
||||
# For 1-based indices:
|
||||
|
||||
iskip = i - j (easy)
|
||||
jskip = j * (j+1) / 2
|
||||
|
||||
In the large j limit, jskip is approximately 0.5*j**2 .
|
||||
This means that the j can be 'guessed' by:
|
||||
j_guess = int( sqrt(ij * 2) )
|
||||
= int( sqrt(j * (j + 1) + (i - j)*2) )
|
||||
= int( sqrt(j**2 + 2*i - j) )
|
||||
|
||||
Remember that i <= j, so j_guess ranges from (inclusive endpoints):
|
||||
|
||||
j_guess_min = int( sqrt(j**2 - j + 2) )
|
||||
j_guess_max = int( sqrt(j**2 + j) )
|
||||
|
||||
Note: since
|
||||
|
||||
sqrt((j-1)**2) = sqrt(j**2 - 2j + 1)
|
||||
sqrt((j+1)**2) = sqrt(j**2 + 2j + 1)
|
||||
|
||||
this means that, for all j > 0,
|
||||
j_guess_min >= j-1
|
||||
j_guess_max = j
|
||||
|
||||
So only those two j values matter.
|
||||
Once we get the j, we can get the i value easily.
|
||||
|
||||
# For 0-based indices:
|
||||
|
||||
iskip = i - j (easy)
|
||||
jskip = (j+1) * (j+2) / 2 - 1
|
||||
"""
|
||||
|
||||
def UD(i,j,N):
|
||||
"""Translates a lower-diagonal index (ii <= jj) to linear index
|
||||
0, 1, 2, 3, ...
|
||||
This follows python convention; thus 0 <= i < N, and so also j.
|
||||
|
||||
"""
|
||||
# iskip is row traversal, jskip is column traversal.
|
||||
# (iskip+jskip) is the final array index.
|
||||
if i <= j:
|
||||
ii = i
|
||||
jj = j
|
||||
else:
|
||||
ii = j
|
||||
jj = i
|
||||
|
||||
iskip = ii - jj # + 1
|
||||
jskip = (j+1) * (j+2) // 2 - 1 # for 0-based
|
||||
return iskip + jskip
|
||||
|
||||
def UDdec(ij, N):
|
||||
"""Back-translates linear index 0, 1, 2, 3, ... to a lower-diagonal
|
||||
index pair (ii <= jj).
|
||||
This is not optimal, but it avoids storing an auxiliary array
|
||||
that is easily computable. Plus, this function is supposed to
|
||||
be called rarely.
|
||||
Derived from the 1-based version (UDdec1) below.
|
||||
"""
|
||||
|
||||
jskip = 0
|
||||
for j in xrange(1,N+1):
|
||||
jskip = j * (j+1) // 2
|
||||
if ij < jskip:
|
||||
i = ij - jskip + j
|
||||
return (i,j-1)
|
||||
|
||||
|
||||
def UD1(i,j,N):
|
||||
"""The 1-based version of UD
|
||||
"""
|
||||
# iskip is row traversal, jskip is column traversal.
|
||||
# (iskip+jskip) is the final array index.
|
||||
if i <= j:
|
||||
ii = i
|
||||
jj = j
|
||||
else:
|
||||
ii = j
|
||||
jj = i
|
||||
|
||||
iskip = ii - jj
|
||||
jskip = (j) * (j+1) // 2 # for 1-based
|
||||
return iskip + jskip
|
||||
|
||||
def UDdec1(ij, N):
|
||||
"""Back-translates linear index 1, 2, 3, ... to a lower-diagonal
|
||||
index pair (ii <= jj).
|
||||
This is not optimal, but it avoids storing an auxiliary array
|
||||
that is easily computable. Plus, this function is supposed to
|
||||
be called rarely.
|
||||
"""
|
||||
jskip = 0
|
||||
for j in xrange(1,N+1):
|
||||
jskip = j * (j+1) // 2
|
||||
if ij < jskip + 1:
|
||||
"""
|
||||
ij = iskip + jskip
|
||||
-> iskip = ij - jskip = i - j
|
||||
-> i = ij - jskip + j
|
||||
"""
|
||||
i = ij - jskip + j
|
||||
return (i,j)
|
||||
|
||||
raise ValueError, "UDdec1(ij=%d,N=%d): invalid index ij" % (ij,N)
|
||||
|
||||
|
||||
def UDdec1_v2(ij, N):
|
||||
"""Back-translates linear index 1, 2, 3, ... to a lower-diagonal
|
||||
index pair (ii <= jj).
|
||||
Version 2, avoiding loop at a cost of doing sqrt() call.
|
||||
"""
|
||||
from numpy import sqrt
|
||||
j = int(sqrt(ij*2+1))
|
||||
jskip = j * (j+1) // 2
|
||||
if ij < jskip + 1:
|
||||
pass # correct already
|
||||
else:
|
||||
j = j + 1
|
||||
jskip = j * (j+1) // 2
|
||||
i = ij - jskip + j
|
||||
return (i,j)
|
||||
|
||||
def UDdec1_v3(ij, N):
|
||||
"""Back-translates linear index 1, 2, 3, ... to a lower-diagonal
|
||||
index pair (ii <= jj).
|
||||
Version 3, avoiding loop at a cost of doing sqrt() call.
|
||||
"""
|
||||
from numpy import sqrt
|
||||
j = int(sqrt(ij*2)) # +1 not needed?
|
||||
jskip = j * (j+1) // 2
|
||||
if ij < jskip + 1:
|
||||
pass # correct already
|
||||
else:
|
||||
j = j + 1
|
||||
jskip = j * (j+1) // 2
|
||||
i = ij - jskip + j
|
||||
return (i,j)
|
||||
|
||||
|
||||
def test_UD_enc_dec(N):
|
||||
"""Simple test to check UD encoding and decoding correctness.
|
||||
For python-style indexing (0 <= i < N, similarly for j)."""
|
||||
# Success for N = 5, 8
|
||||
for j in xrange(N):
|
||||
for i in xrange(0,j+1):
|
||||
ij = UD(i,j,N)
|
||||
(ii,jj) = UDdec(ij,N)
|
||||
# print "%3d %3d | %6d | %3d %3d" % (i,j, ij, ii,jj)
|
||||
print "%3d %3d | %6d | %3d %3d >> %1s" % (i,j, ij, ii,jj, ("v" if i==ii and j==jj else "X"))
|
||||
|
||||
def test_UD_enc_dec1(N):
|
||||
"""Simple test to check UD encoding and decoding correctness.
|
||||
For python-style indexing (0 <= i < N, similarly for j)."""
|
||||
for j in xrange(1,N+1):
|
||||
for i in xrange(1,j+1):
|
||||
ij = UD1(i,j,N)
|
||||
(ii,jj) = UDdec1(ij,N)
|
||||
print "%3d %3d | %6d | %3d %3d >> %1s" % (i,j, ij, ii,jj, ("v" if i==ii and j==jj else "X"))
|
||||
|
||||
def hack1_UD_enc_dec1(N):
|
||||
"""Simple test to check UD encoding and decoding correctness.
|
||||
For python-style indexing (0 <= i < N, similarly for j)."""
|
||||
from numpy import sqrt
|
||||
ok = True
|
||||
for j in xrange(1,N+1):
|
||||
for i in xrange(1,j+1):
|
||||
ij = UD1(i,j,N)
|
||||
(ii,jj) = UDdec1(ij,N)
|
||||
#iii = i
|
||||
#jjj = int(sqrt(ij*2+1))
|
||||
#(iii,jjj) = UDdec1_v2(ij,N) # -- tested OK empirically till N=1000
|
||||
(iii,jjj) = UDdec1_v3(ij,N) # also tested OK empirically till N=1000
|
||||
if not (i==iii and j==jjj):
|
||||
ok=False
|
||||
print "%3d %3d | %6d %6d | %3d %3d : %3d %3d >> %1s" % (
|
||||
i,j,
|
||||
ij, ij*2,
|
||||
ii,jj,
|
||||
iii, jjj,
|
||||
("v" if i==iii and j==jjj else "X"))
|
||||
print ("ok" if ok else "NOT OK")
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user