Skip to content

Commit 6aa731d

Browse files
authored
Merge branch 'pyscf:master' into libqcschema
2 parents 75307ed + 9f8fe30 commit 6aa731d

File tree

8 files changed

+542
-8
lines changed

8 files changed

+542
-8
lines changed

pyscf/dft/radi.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,14 +47,21 @@
4747
def murray(n, *args, **kwargs):
4848
raise RuntimeError('Not implemented')
4949

50-
# Gauss-Chebyshev of the first kind, and the transformed interval [0,\infty)
5150
def becke(n, charge, *args, **kwargs):
5251
'''Becke, JCP 88, 2547 (1988); DOI:10.1063/1.454033'''
5352
if charge == 1:
5453
rm = BRAGG_RADII[charge]
5554
else:
5655
rm = BRAGG_RADII[charge] * .5
57-
t, w = numpy.polynomial.chebyshev.chebgauss(n)
56+
57+
# Points and weights for Gauss-Chebyshev quadrature points of the second kind
58+
# The weights are adjusted to integrate a function on the interval [-1, 1] with uniform weighting
59+
# instead of weighted by sqrt(1 - t^2) = sin(i pi / (n+1)).
60+
i = numpy.arange(n) + 1
61+
t = numpy.cos(i * numpy.pi / (n + 1))
62+
w = numpy.pi / (n + 1) * numpy.sin(i * numpy.pi / (n + 1))
63+
64+
# Change of variables to map the domain to [0, inf)
5865
r = (1+t)/(1-t) * rm
5966
w *= 2/(1-t)**2 * rm
6067
return r, w

pyscf/dft/test/test_grids.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ def test_radi(self):
9191

9292
grid.radi_method = radi.becke
9393
grid.build(with_non0tab=False)
94-
self.assertAlmostEqual(lib.fp(grid.weights), 818061.875131255, 7)
94+
self.assertAlmostEqual(lib.fp(grid.weights), 780.7183109298, 9)
9595

9696
def test_prune(self):
9797
grid = gen_grid.Grids(h2o)

pyscf/lib/np_helper/np_helper.c

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
*/
1515

1616
#include <stdlib.h>
17+
#include <complex.h>
1718
#include "np_helper/np_helper.h"
1819

1920
void NPdset0(double *p, const size_t n)
@@ -47,3 +48,96 @@ void NPzcopy(double complex *out, const double complex *in, const size_t n)
4748
out[i] = in[i];
4849
}
4950
}
51+
52+
/*
53+
* These are mostly useful for first-touch array allocation on NUMA systems.
54+
* Use with numpy.empty.
55+
*/
56+
void NPomp_dset0(const size_t n, double *out)
57+
{
58+
#pragma omp parallel for schedule(static)
59+
for (size_t i = 0; i < n; i++) {
60+
out[i] = 0.0;
61+
}
62+
}
63+
64+
void NPomp_zset0(const size_t n, double complex *out)
65+
{
66+
#pragma omp parallel for schedule(static)
67+
for (size_t i = 0; i < n; i++) {
68+
out[i] = 0.0;
69+
}
70+
}
71+
72+
73+
/*
74+
* Copy a double precision matrix with multithreading.
75+
*/
76+
void NPomp_dcopy(const size_t m,
77+
const size_t n,
78+
const double *__restrict in, const size_t in_stride,
79+
double *__restrict out, const size_t out_stride)
80+
{
81+
#pragma omp parallel for schedule(static)
82+
for (size_t i = 0; i < m; i++) {
83+
#pragma omp simd
84+
for (size_t j = 0; j < n; j++) {
85+
out[i * out_stride + j] = in[i * in_stride + j];
86+
}
87+
}
88+
}
89+
90+
/*
91+
* Copy a complex double precision matrix with multithreading.
92+
*/
93+
void NPomp_zcopy(const size_t m,
94+
const size_t n,
95+
const double complex *__restrict in, const size_t in_stride,
96+
double complex *__restrict out, const size_t out_stride)
97+
{
98+
#pragma omp parallel for schedule(static)
99+
for (size_t i = 0; i < m; i++) {
100+
#pragma omp simd
101+
for (size_t j = 0; j < n; j++) {
102+
out[i * out_stride + j] = in[i * in_stride + j];
103+
}
104+
}
105+
}
106+
107+
/*
108+
* Elementwise multiplication of two double matrices.
109+
* B <- A \circ B
110+
*/
111+
void NPomp_dmul(const size_t m,
112+
const size_t n,
113+
const double *__restrict a, const size_t a_stride,
114+
double *__restrict b, const size_t b_stride,
115+
double *__restrict out, const size_t out_stride)
116+
{
117+
#pragma omp parallel for schedule(static)
118+
for (size_t i = 0; i < m; i++) {
119+
#pragma omp simd
120+
for (size_t j = 0; j < n; j++) {
121+
out[i * out_stride + j] = b[i * b_stride + j] * a[i * a_stride + j];
122+
}
123+
}
124+
}
125+
126+
/*
127+
* Elementwise multiplication of two complex double matrices.
128+
* B <- A \circ B
129+
*/
130+
void NPomp_zmul(const size_t m,
131+
const size_t n,
132+
const double complex *__restrict a, const size_t a_stride,
133+
double complex *__restrict b, const size_t b_stride,
134+
double complex *__restrict out, const size_t out_stride)
135+
{
136+
#pragma omp parallel for schedule(static)
137+
for (size_t i = 0; i < m; i++) {
138+
#pragma omp simd
139+
for (size_t j = 0; j < n; j++) {
140+
out[i * out_stride + j] = b[i * b_stride + j] * a[i * a_stride + j];
141+
}
142+
}
143+
}

pyscf/lib/np_helper/np_helper.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,24 @@ void NPzset0(double complex *p, const size_t n);
7070
void NPdcopy(double *out, const double *in, const size_t n);
7171
void NPzcopy(double complex *out, const double complex *in, const size_t n);
7272

73+
void NPomp_dset0(const size_t n, double *out);
74+
void NPomp_zset0(const size_t n, double complex *out);
75+
76+
void NPomp_dcopy(const size_t m, const size_t n,
77+
const double *in, const size_t in_stride,
78+
double *out, const size_t out_stride);
79+
void NPomp_zcopy(const size_t m, const size_t n,
80+
const double complex *in, const size_t in_stride,
81+
double complex *out, const size_t out_stride);
82+
void NPomp_dmul(const size_t m, const size_t n,
83+
const double *a, const size_t a_stride,
84+
double *b, const size_t b_stride,
85+
double *out, const size_t out_stride);
86+
void NPomp_zmul(const size_t m, const size_t n,
87+
const double complex *a, const size_t a_stride,
88+
double complex *b, const size_t b_stride,
89+
double complex *out, const size_t out_stride);
90+
7391
void NPdgemm(const char trans_a, const char trans_b,
7492
const int m, const int n, const int k,
7593
const int lda, const int ldb, const int ldc,

pyscf/lib/numpy_helper.py

Lines changed: 126 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -552,12 +552,10 @@ def inplace_transpose_scale(a, alpha=1.0):
552552
alpha : float, optional
553553
scaling factor, by default 1.0
554554
"""
555-
assert a.ndim == 2
555+
lda, order, _ = leading_dimension_order(a)
556556
assert a.shape[0] == a.shape[1]
557557
n = a.shape[0]
558-
astrides = [s // a.itemsize for s in a.strides]
559-
lda = max(astrides)
560-
assert min(astrides) == 1
558+
assert order in ('C', 'F')
561559
if a.dtype == numpy.double:
562560
_np_helper.NPomp_d_itranspose_scale(
563561
ctypes.c_int(n), ctypes.c_double(alpha),
@@ -974,6 +972,37 @@ def frompointer(pointer, count, dtype=float):
974972
a = numpy.ndarray(count, dtype=numpy.int8, buffer=buf)
975973
return a.view(dtype)
976974

975+
def leading_dimension_order(a):
976+
"""Return the leading dimension and the order of a matrix.
977+
978+
Parameters
979+
----------
980+
a : ndarray
981+
2D array.
982+
983+
Returns
984+
-------
985+
lda : int
986+
Leading dimension of the array -- the stride between rows or columns.
987+
order : str
988+
'F' for col major, 'C' for row major, 'G' for neither.
989+
a_cshape : tuple
990+
If a is row major, a.shape; if a is col major, a.T.shape; otherwise None.
991+
"""
992+
assert a.ndim == 2
993+
astrides = [s//a.itemsize for s in a.strides]
994+
lda = max(astrides)
995+
if astrides[0] == 1:
996+
order = 'F'
997+
a_cshape = a.T.shape
998+
elif astrides[1] == 1:
999+
order = 'C'
1000+
a_cshape = a.shape
1001+
else:
1002+
order = 'G'
1003+
a_cshape = None
1004+
return lda, order, a_cshape
1005+
9771006
norm = numpy.linalg.norm
9781007

9791008
def cond(x, p=None):
@@ -1177,6 +1206,99 @@ def expm(a):
11771206
y, buf = buf, y
11781207
return y
11791208

1209+
def omatcopy(a, out=None):
1210+
"""Copies a matrix.
1211+
1212+
Parameters
1213+
----------
1214+
a : ndarray
1215+
Matrix to be copied. The order of the matrix is preserved.
1216+
a can be either row or column major.
1217+
out : ndarray, optional
1218+
Matrix to be overwritten. A new one is allocated if not provided.
1219+
1220+
Returns
1221+
-------
1222+
out : ndarray
1223+
Copy of a with the same order.
1224+
"""
1225+
lda, _, a_cshape = leading_dimension_order(a)
1226+
if out is None:
1227+
out = numpy.empty_like(a)
1228+
ld_out, _, out_cshape = leading_dimension_order(out)
1229+
assert out_cshape == a_cshape and a_cshape is not None
1230+
if a.dtype == numpy.double:
1231+
fn = _np_helper.NPomp_dcopy
1232+
elif a.dtype == numpy.complex128:
1233+
fn = _np_helper.NPomp_zcopy
1234+
else:
1235+
raise NotImplementedError
1236+
fn(ctypes.c_size_t(a_cshape[0]),
1237+
ctypes.c_size_t(a_cshape[1]),
1238+
a.ctypes.data_as(ctypes.c_void_p),
1239+
ctypes.c_size_t(lda),
1240+
out.ctypes.data_as(ctypes.c_void_p),
1241+
ctypes.c_size_t(ld_out))
1242+
return out
1243+
1244+
def zeros(shape, dtype=numpy.double, order='C'):
1245+
"""Allocate and zero an array in parallel. Useful for multi-socket systems
1246+
due to the first touch policy. On most systems np.zeros does not count
1247+
as first touch. Arrays returned by this function will (ideally) have
1248+
pages backing them that are distributed across the sockets.
1249+
"""
1250+
dtype = numpy.dtype(dtype)
1251+
if dtype == numpy.double:
1252+
out = numpy.empty(shape, dtype=dtype, order=order)
1253+
_np_helper.NPomp_dset0(ctypes.c_size_t(out.size),
1254+
out.ctypes.data_as(ctypes.c_void_p))
1255+
elif dtype == numpy.complex128:
1256+
out = numpy.empty(shape, dtype=dtype, order=order)
1257+
_np_helper.NPomp_zset0(ctypes.c_size_t(out.size),
1258+
out.ctypes.data_as(ctypes.c_void_p))
1259+
else: # fallback
1260+
out = numpy.zeros(shape, dtype=dtype, order=order)
1261+
return out
1262+
1263+
def entrywise_mul(a, b, out=None):
1264+
"""Entrywise multiplication of two matrices.
1265+
1266+
Parameters
1267+
----------
1268+
a : ndarray
1269+
b : ndarray
1270+
out : ndarray, optional
1271+
Output matrix. A new one is allocated if not provided.
1272+
1273+
Returns
1274+
-------
1275+
ndarray
1276+
a * b
1277+
"""
1278+
assert a.ndim == 2 and b.ndim == 2
1279+
assert a.shape == b.shape and a.dtype == b.dtype
1280+
lda, _, a_cshape = leading_dimension_order(a)
1281+
ldb, _, b_cshape = leading_dimension_order(b)
1282+
if out is None:
1283+
out = numpy.empty_like(b)
1284+
ld_out, _, out_cshape = leading_dimension_order(out)
1285+
assert a_cshape == b_cshape and b_cshape == out_cshape and a_cshape is not None
1286+
if a.dtype == numpy.double:
1287+
fn = _np_helper.NPomp_dmul
1288+
elif a.dtype == numpy.complex128:
1289+
fn = _np_helper.NPomp_zmul
1290+
else:
1291+
return numpy.multiply(a, b, out=out)
1292+
fn(ctypes.c_size_t(a_cshape[0]),
1293+
ctypes.c_size_t(a_cshape[1]),
1294+
a.ctypes.data_as(ctypes.c_void_p),
1295+
ctypes.c_size_t(lda),
1296+
b.ctypes.data_as(ctypes.c_void_p),
1297+
ctypes.c_size_t(ldb),
1298+
out.ctypes.data_as(ctypes.c_void_p),
1299+
ctypes.c_size_t(ld_out))
1300+
return out
1301+
11801302
def ndarray_pointer_2d(array):
11811303
'''Return an array that contains the addresses of the first element in each
11821304
row of the input 2d array.

pyscf/lib/test/test_numpy_helper.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,42 @@ def test_ndarray_pointer_2d(self):
254254
addr = lib.ndarray_pointer_2d(a)
255255
self.assertTrue(all(addr == a.ctypes.data + numpy.array([0, 24, 48])))
256256

257+
def test_omatcopy(self):
258+
a = numpy.random.random((5,5))
259+
b = numpy.empty_like(a)
260+
lib.omatcopy(a, out=b)
261+
self.assertTrue(numpy.all(a == b))
262+
a = numpy.random.random((403,410)).T
263+
b = numpy.empty_like(a)
264+
lib.omatcopy(a, out=b)
265+
self.assertTrue(numpy.all(a == b))
266+
267+
def test_zeros(self):
268+
a = lib.zeros((100,100), dtype=numpy.double)
269+
self.assertTrue(numpy.all(a == 0))
270+
self.assertTrue(a.dtype == numpy.double)
271+
a = lib.zeros((100,100), dtype=numpy.complex128)
272+
self.assertTrue(numpy.all(a == 0))
273+
self.assertTrue(a.dtype == numpy.complex128)
274+
a = lib.zeros((100,100), dtype=numpy.int32)
275+
self.assertTrue(numpy.all(a == 0))
276+
self.assertTrue(a.dtype == numpy.int32)
277+
278+
def test_entrywise_mul(self):
279+
a = numpy.random.random((101,100))
280+
b = numpy.random.random((101,100))
281+
prod = lib.entrywise_mul(a, b)
282+
self.assertTrue(numpy.allclose(prod, a * b))
283+
a = numpy.random.random((101,100))
284+
b = numpy.random.random((101,100))
285+
a = a + a*1j
286+
b = b + b*1j
287+
prod = lib.entrywise_mul(a, b)
288+
self.assertTrue(numpy.allclose(prod, a * b))
289+
# inplace test
290+
lib.entrywise_mul(a, b, out=b)
291+
self.assertTrue(numpy.allclose(prod, b))
292+
257293
if __name__ == "__main__":
258294
print("Full Tests for numpy_helper")
259295
unittest.main()

pyscf/scf/dispersion.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050

5151
# These xc functionals are not supported yet
5252
_black_list = {
53-
'wb97x-d', 'wb97x-d3',
53+
'wb97x-d', 'wb97x-d3', 'wb97x_d', 'wb97x_d3',
5454
'wb97m-d3bj2b', 'wb97m-d3bjatm',
5555
'b97m-d3bj2b', 'b97m-d3bjatm',
5656
}

0 commit comments

Comments
 (0)