From 86427dfa88b0abe784dd025e882b80e92e53a60c Mon Sep 17 00:00:00 2001 From: Fionn Malone Date: Tue, 20 Jun 2023 19:36:08 +0000 Subject: [PATCH 01/19] Add k-point THC code. --- .../pbc/thc/utils/__init__.py | 12 + .../pbc/thc/utils/gvec_logic.py | 184 +++ .../pbc/thc/utils/gvec_logic_test.py | 207 +++ .../resource_estimates/pbc/thc/utils/isdf.py | 1115 +++++++++++++++++ .../pbc/thc/utils/isdf_test.py | 657 ++++++++++ .../pbc/thc/utils/kmeans.py | 159 +++ .../pbc/thc/utils/kmeans_test.py | 62 + .../pbc/thc/utils/thc_jax.py | 1049 ++++++++++++++++ .../pbc/thc/utils/thc_jax_test.py | 352 ++++++ 9 files changed, 3797 insertions(+) create mode 100644 src/openfermion/resource_estimates/pbc/thc/utils/__init__.py create mode 100644 src/openfermion/resource_estimates/pbc/thc/utils/gvec_logic.py create mode 100644 src/openfermion/resource_estimates/pbc/thc/utils/gvec_logic_test.py create mode 100644 src/openfermion/resource_estimates/pbc/thc/utils/isdf.py create mode 100644 src/openfermion/resource_estimates/pbc/thc/utils/isdf_test.py create mode 100644 src/openfermion/resource_estimates/pbc/thc/utils/kmeans.py create mode 100644 src/openfermion/resource_estimates/pbc/thc/utils/kmeans_test.py create mode 100644 src/openfermion/resource_estimates/pbc/thc/utils/thc_jax.py create mode 100644 src/openfermion/resource_estimates/pbc/thc/utils/thc_jax_test.py diff --git a/src/openfermion/resource_estimates/pbc/thc/utils/__init__.py b/src/openfermion/resource_estimates/pbc/thc/utils/__init__.py new file mode 100644 index 000000000..d0d41a5f5 --- /dev/null +++ b/src/openfermion/resource_estimates/pbc/thc/utils/__init__.py @@ -0,0 +1,12 @@ +# coverage: ignore +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/src/openfermion/resource_estimates/pbc/thc/utils/gvec_logic.py b/src/openfermion/resource_estimates/pbc/thc/utils/gvec_logic.py new file mode 100644 index 000000000..64dd1a41b --- /dev/null +++ b/src/openfermion/resource_estimates/pbc/thc/utils/gvec_logic.py @@ -0,0 +1,184 @@ +# coverage: ignore +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import itertools +import numpy as np +from pyscf.lib.numpy_helper import cartesian_prod + + +def get_miller_indices(kmesh): + """Calculate the miller indices on a gamma centered non-1stBZ Monkorhst-Pack + mesh + + Args: + kmesh: 1-D iteratble with the number of k-points in the x,y,z direction + [Nk_x, NK_y, NK_z] where NK_x/y/z are positive integers + + Returns: + np.array 2D that is prod([Nk_x, NK_y, NK_z]) + + """ + if kmesh[0] < 1: + raise TypeError("Bad miller index dimension in x") + if kmesh[1] < 1: + raise TypeError("Bad miller index dimension in y") + if kmesh[2] < 1: + raise TypeError("Bad miller index dimension in z") + + ks_int_each_axis = [] + for n in kmesh: + ks_int_each_axis.append(np.arange(n, dtype=float)) + int_scaled_kpts = cartesian_prod(ks_int_each_axis) + return int_scaled_kpts + + +def get_delta_kp_kq_Q(int_scaled_kpts): + """Generate kp - kq - Q = S for kp, kq, and Q. The difference of the three + integers is stored as a four tensor D_{kp, kq, Q} = S. where the dimension + of D is (nkpts, nkpts, nkpts, 3). The last dimension stores the x,y,z + components of S. + + Args: + int_scaled_kpts: array of kpts represented as miller indices + [[nkx, nky, nkz], ...] + + Returns: + np.array nkpts x nkpts that corresponds to + + """ + delta_k1_k2_Q_int = (int_scaled_kpts[:, None, None, :] - + int_scaled_kpts[None, :, None, :] - + int_scaled_kpts[None, None, :, :]) + return delta_k1_k2_Q_int + + +def build_transfer_map(kmesh, scaled_kpts): + """Define mapping momentum_transfer_map[Q][k1] = k2 that satisfies + k1 - k2 + G = Q. + where k1, k2, Q are all tuples of integers [[0, Nkx-1], [0, Nky-1], + [0, Nkz-1]] + and G is [{0, Nkx}, {0, Nky}, {0, Nkz}]. + + This is computed from `get_delta_kp_kq_Q` which computes k1 - k2 -Q = S. + Thus k1 - k2 = Q + S which shows that S is [{0, Nkx}, {0, Nky}, {0, Nkz}]. + + Thus to compute map[Q, k1] = k2 + + Args: + kmesh: kmesh [nkx, nky, nkz] the number of kpoints in each direction + scaled_kpts: miller index representation + [[0, nkx-1], [0, nky-1], [0, nkz-1]] of all the kpoints + + Returns: + transfer map satisfying k1 - k2 + G = Q in matrix form map[Q, k1] = k2 + + """ + nkpts = len(scaled_kpts) + delta_k1_k2_Q_int = get_delta_kp_kq_Q(scaled_kpts) + transfer_map = np.zeros((nkpts, nkpts), dtype=np.int32) + for kpidx, kqidx, qidx in itertools.product(range(nkpts), repeat=3): + # explicitly build my transfer matrix + if np.allclose( + [ + np.rint(delta_k1_k2_Q_int[kpidx, kqidx, qidx][0]) % kmesh[0], + np.rint(delta_k1_k2_Q_int[kpidx, kqidx, qidx][1]) % kmesh[1], + np.rint(delta_k1_k2_Q_int[kpidx, kqidx, qidx][2]) % kmesh[2], + ], + 0, + ): + transfer_map[qidx, kpidx] = kqidx + return transfer_map + + +def build_conjugate_map(kmesh, scaled_kpts): + """build mapping such that map[k1] = -k1 + + Args: + kmesh: kpoint mesh + scaled_kpts: integer k-points + + Returns: + kconj_map: conjugate k-point mapping + + """ + nkpts = len(scaled_kpts) + kpoint_dict = dict( + zip( + [tuple(map(int, scaled_kpts[x])) for x in range(nkpts)], + range(nkpts), + )) + kconj_map = np.zeros((nkpts), dtype=int) + for kidx in range(nkpts): + negative_k_scaled = -scaled_kpts[kidx] + fb_negative_k_scaled = tuple(( + int(negative_k_scaled[0]) % kmesh[0], + int(negative_k_scaled[1]) % kmesh[1], + int(negative_k_scaled[2]) % kmesh[2], + )) + kconj_map[kidx] = kpoint_dict[fb_negative_k_scaled] + return kconj_map + + +def build_G_vectors(kmesh): + """Build all 8 Gvectors + + Args: + kmesh: returns tuple: G_dict a dictionary mapping miller index to + appropriate + G_vector index. The actual cell Gvector can be recovered with + np.einsum("x,wx->w", (n1, n2, n3), cell.reciprocal_vectors() + + Returns: + G_dict a dictionary mapping miller index to appropriate + G_vector index. The actual cell Gvector can be recovered with + np.einsum("x,wx->w", (n1, n2, n3), cell.reciprocal_vectors() + + """ + G_dict = {} + indx = 0 + for n1, n2, n3 in itertools.product([0, -1], repeat=3): + G_dict[(n1 * kmesh[0], n2 * kmesh[1], n3 * kmesh[2])] = indx + indx += 1 + return G_dict + + +def build_gpq_mapping(kmesh, int_scaled_kpts): + """build map for kp - kq = Q + G where G is + [{0, -Nkx}, {0, -Nky}, {0, -Nkz}]. G will be 0 or Nkz because kp - kq + takes on values between [-Nka + 1, Nka - 1] in each component. + + Args: + kmesh: number of k-points along each direction [Nkx, Nky, Nkz]. + int_scaled_kpts: scaled_kpts. Each kpoint is a tuple of 3 integers + where each integer is between [0, Nka-1]. + + Returns: + array mapping where first two indices are the index of kp and kq + and the last dimension holds the gval that is + [{0, Nkx}, {0, Nky}, {0, Nkz}]. + + """ + momentum_map = build_transfer_map(kmesh, int_scaled_kpts) + nkpts = len(int_scaled_kpts) + Gpq_mapping = np.zeros((nkpts, nkpts, 3), dtype=np.int32) + for iq in range(nkpts): + for ikp in range(nkpts): + ikq = momentum_map[iq, ikp] + q_minus_g = int_scaled_kpts[ikp] - int_scaled_kpts[ikq] + g_val = ( + 0 if q_minus_g[0] >= 0 else -kmesh[0], + 0 if q_minus_g[1] >= 0 else -kmesh[1], + 0 if q_minus_g[2] >= 0 else -kmesh[2], + ) + Gpq_mapping[ikp, ikq, :] = np.array(g_val) + + return Gpq_mapping diff --git a/src/openfermion/resource_estimates/pbc/thc/utils/gvec_logic_test.py b/src/openfermion/resource_estimates/pbc/thc/utils/gvec_logic_test.py new file mode 100644 index 000000000..97ba1b4e8 --- /dev/null +++ b/src/openfermion/resource_estimates/pbc/thc/utils/gvec_logic_test.py @@ -0,0 +1,207 @@ +# coverage: ignore +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import itertools +import numpy as np +from openfermion.resource_estimates.pbc.thc.utils.gvec_logic import ( + get_miller_indices, + get_delta_kp_kq_Q, + build_transfer_map, + build_G_vectors, + build_gpq_mapping, + build_conjugate_map, +) + + +def test_get_miller_indices(): + kmesh = [3, 1, 1] + int_scaled_kpts = get_miller_indices(kmesh) + assert np.allclose(int_scaled_kpts[:, 0], np.arange(3)) + assert np.allclose(int_scaled_kpts[:, 1], 0) + assert np.allclose(int_scaled_kpts[:, 2], 0) + + kmesh = [3, 2, 1] + int_scaled_kpts = get_miller_indices(kmesh) + assert np.allclose(int_scaled_kpts[:, 0], [0, 0, 1, 1, 2, 2]) + assert np.allclose(int_scaled_kpts[:, 1], [0, 1, 0, 1, 0, 1]) + + +def test_get_delta_k1_k2_Q(): + kmesh = [3, 2, 1] + nkpts = np.prod(kmesh) + scaled_kpts = get_miller_indices(kmesh) + delta_k1_k2_Q_int = get_delta_kp_kq_Q(scaled_kpts) + assert delta_k1_k2_Q_int.shape == (nkpts, nkpts, nkpts, 3) + for kpidx, kqidx, qidx in itertools.product(range(nkpts), repeat=3): + assert np.allclose( + scaled_kpts[kpidx] - scaled_kpts[kqidx] - scaled_kpts[qidx], + delta_k1_k2_Q_int[kpidx, kqidx, qidx], + ) + + +def test_transfer_map(): + kmesh = [3, 1, 1] + nkpts = np.prod(kmesh) + scaled_kpts = get_miller_indices(kmesh) + transfer_map = build_transfer_map(kmesh, scaled_kpts=scaled_kpts) + for kpidx, kqidx in itertools.product(range(nkpts), repeat=2): + q_scaled_kpt = scaled_kpts[kpidx] - scaled_kpts[kqidx] + q_scaled_kpt_aliased = np.array([ + q_scaled_kpt[0] % kmesh[0], + q_scaled_kpt[1] % kmesh[1], + q_scaled_kpt[2] % kmesh[2], + ]) + qidx = np.where((scaled_kpts[:, 0] == q_scaled_kpt_aliased[0]) & + (scaled_kpts[:, 1] == q_scaled_kpt_aliased[1]) & + (scaled_kpts[:, 2] == q_scaled_kpt_aliased[2]))[0] + assert transfer_map[qidx, kpidx] == kqidx + + true_transfer_map = np.array([[0, 1, 2], [2, 0, 1], [1, 2, 0]]) + assert np.allclose(transfer_map, true_transfer_map) + kmesh = [4, 1, 1] + nkpts = np.prod(kmesh) + scaled_kpts = get_miller_indices(kmesh) + transfer_map = build_transfer_map(kmesh, scaled_kpts=scaled_kpts) + for kpidx, kqidx in itertools.product(range(nkpts), repeat=2): + q_scaled_kpt = scaled_kpts[kpidx] - scaled_kpts[kqidx] + q_scaled_kpt_aliased = np.array([ + q_scaled_kpt[0] % kmesh[0], + q_scaled_kpt[1] % kmesh[1], + q_scaled_kpt[2] % kmesh[2], + ]) + qidx = np.where((scaled_kpts[:, 0] == q_scaled_kpt_aliased[0]) & + (scaled_kpts[:, 1] == q_scaled_kpt_aliased[1]) & + (scaled_kpts[:, 2] == q_scaled_kpt_aliased[2]))[0] + assert transfer_map[qidx, kpidx] == kqidx + + true_transfer_map = np.array([[0, 1, 2, 3], [3, 0, 1, 2], [2, 3, 0, 1], + [1, 2, 3, 0]]) + assert np.allclose(transfer_map, true_transfer_map) + + kmesh = [3, 2, 1] + nkpts = np.prod(kmesh) + scaled_kpts = get_miller_indices(kmesh) + transfer_map = build_transfer_map(kmesh, scaled_kpts=scaled_kpts) + for kpidx, kqidx in itertools.product(range(nkpts), repeat=2): + q_scaled_kpt = scaled_kpts[kpidx] - scaled_kpts[kqidx] + q_scaled_kpt_aliased = np.array([ + q_scaled_kpt[0] % kmesh[0], + q_scaled_kpt[1] % kmesh[1], + q_scaled_kpt[2] % kmesh[2], + ]) + qidx = np.where((scaled_kpts[:, 0] == q_scaled_kpt_aliased[0]) & + (scaled_kpts[:, 1] == q_scaled_kpt_aliased[1]) & + (scaled_kpts[:, 2] == q_scaled_kpt_aliased[2]))[0] + assert transfer_map[qidx, kpidx] == kqidx + + true_transfer_map = np.array([ + [0, 1, 2, 3, 4, 5], + [1, 0, 3, 2, 5, 4], + [4, 5, 0, 1, 2, 3], + [5, 4, 1, 0, 3, 2], + [2, 3, 4, 5, 0, 1], + [3, 2, 5, 4, 1, 0], + ]) + assert np.allclose(transfer_map, true_transfer_map) + + +def test_build_Gvectors(): + kmesh = [3, 2, 1] + g_dict = build_G_vectors(kmesh) + indx = 0 + for n1, n2, n3 in itertools.product([0, -1], repeat=3): + assert np.isclose(g_dict[(n1 * kmesh[0], n2 * kmesh[1], n3 * kmesh[2])], + indx) + indx += 1 + + +def test_gpq_mapping(): + kmesh = [3, 2, 1] + nkpts = np.prod(kmesh) + scaled_kpts = get_miller_indices(kmesh) + transfer_map = build_transfer_map(kmesh, scaled_kpts=scaled_kpts) + gpq_map = build_gpq_mapping(kmesh, scaled_kpts) + for iq in range(nkpts): + for ikp in range(nkpts): + ikq = transfer_map[iq, ikp] + q_plus_g = scaled_kpts[ikp] - scaled_kpts[ikq] + g_val = gpq_map[ikp, ikq] + q_val = q_plus_g - np.array(g_val) + assert np.allclose(q_val, scaled_kpts[iq]) + + assert all(g_val <= 0) + assert g_val[0] in [0, -kmesh[0]] + assert g_val[1] in [0, -kmesh[1]] + assert g_val[2] in [0, -kmesh[2]] + + +def test_build_conjugate_map(): + kmesh = [4, 3, 3] + nkpts = np.prod(kmesh) + scaled_kpts = get_miller_indices(kmesh) + kconj_map = build_conjugate_map(kmesh, scaled_kpts=scaled_kpts) + for kidx in range(nkpts): + neg_kscaled_idx = kconj_map[kidx] + gval = scaled_kpts[neg_kscaled_idx] + scaled_kpts[kidx] + # -Q + Q = 0 + G + assert gval[0] in [0, kmesh[0]] + assert gval[1] in [0, kmesh[1]] + assert gval[2] in [0, kmesh[2]] + + +def test_compliment_g(): + # setup + kmesh = [4, 1, 1] + nkpts = np.prod(kmesh) + scaled_kpts = get_miller_indices(kmesh) + g_dict = build_G_vectors(kmesh) + gpq_map = build_gpq_mapping(kmesh, scaled_kpts) + transfer_map = build_transfer_map(kmesh, scaled_kpts=scaled_kpts) + + # confirm that our transfer map function is working + for qidx, kpidx, ksidx in itertools.product(range(nkpts), repeat=3): + kqidx = transfer_map[qidx, kpidx] + kridx = transfer_map[qidx, ksidx] + + conserved_k = (scaled_kpts[kpidx] - scaled_kpts[kqidx] + + scaled_kpts[kridx] - scaled_kpts[ksidx]) + assert conserved_k[0] % kmesh[0] == 0 + assert conserved_k[1] % kmesh[1] == 0 + assert conserved_k[2] % kmesh[2] == 0 + + # generate set of k for every (q and g) + q_idx_g_idx = {} + for qidx in range(nkpts): + for gvals, gidx in g_dict.items(): + q_idx_g_idx[(qidx, gidx)] = [] + # print("Traget gval ", gvals) + kidx_satisfying_q_g = [] + for kidx in range(nkpts): + kmq_idx = transfer_map[qidx, kidx] + test_gval = gpq_map[kidx, kmq_idx] + # print(test_gval, test_gval == gvals) + if all(test_gval == gvals): + kidx_satisfying_q_g.append(kidx) + q_idx_g_idx[(qidx, gidx)] = kidx_satisfying_q_g + + # count how many total k-values and how many per q values + total_k = 0 + for qidx in range(nkpts): + sub_val = 0 + for g1idx, g2idx in itertools.product(range(8), repeat=2): + total_k += len(q_idx_g_idx[(qidx, g1idx)]) * len( + q_idx_g_idx[(qidx, g2idx)]) + sub_val += len(q_idx_g_idx[(qidx, g1idx)]) * len( + q_idx_g_idx[(qidx, g2idx)]) + assert np.isclose(sub_val, nkpts**2) + + assert np.isclose(total_k, nkpts**3) \ No newline at end of file diff --git a/src/openfermion/resource_estimates/pbc/thc/utils/isdf.py b/src/openfermion/resource_estimates/pbc/thc/utils/isdf.py new file mode 100644 index 000000000..ee4e13b9c --- /dev/null +++ b/src/openfermion/resource_estimates/pbc/thc/utils/isdf.py @@ -0,0 +1,1115 @@ +# coverage: ignore +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Module for performing ISDF THC factorization of k-point dependent integrals. + +The ISDF implementation currently provides a THC-like factorization of the two +electron integrals which should converge to the FFTDF representation of the ERIs +in the limit of large THC rank. This differs from the assumption of using RSGDF +throughout the rest of the resource estimation scripts. However, we typically +are only interested in ISDF as an initial guess for the THC factors which are +then subsequently reoptimized to regularize lambda. The assumption here is +that FFTDF / ISDF is a good enough approximation to the RSGDF ERIs and thus +serves as a good initial guess. +""" + +import itertools +from dataclasses import dataclass +from typing import Tuple, Union + +import numpy as np +import numpy.typing as npt +import scipy.linalg + +from pyscf.pbc import df, gto, scf, tools +from pyscf.pbc.dft import numint +from pyscf.pbc.dft.gen_grid import UniformGrids +from pyscf.pbc.lib.kpts_helper import conj_mapping, get_kconserv, unique + +from openfermion.resource_estimates.pbc.thc.utils.kmeans import KMeansCVT +from openfermion.resource_estimates.pbc.utils.hamiltonian_utils import ( + build_momentum_transfer_mapping,) + + +def check_isdf_solution( + orbitals: npt.NDArray, + interp_orbitals: npt.NDArray, + xi: npt.NDArray, +) -> float: + r"""Check accuracy of isdf least squares solution. + + Very costly and should only be used for testing purposes. + + Args: + orbitals: Orbitals on full real space grid. [num_grd, num_orb] + interp_orbitals: interpolating orbitals (those orbitals evaluated on + interpolating points.) [num_interp, num_orb] + xi: interpolating vectors. [num_grid, num_interp] + Returns + error: |phi_{ij}(r) - \sum_m xi_m(r) phi_{ij}(r_m) + orbitals: npt.NDArray: + interp_orbitals: npt.NDArray: + xi: npt.NDArray: + """ + + lhs = np.einsum("Ri,Rj->Rij", orbitals.conj(), orbitals, optimize=True) + rhs = np.einsum("mi,mj->mij", + interp_orbitals.conj(), + interp_orbitals, + optimize=True) + lhs_check = np.einsum("Rm,mij->Rij", xi, rhs, optimize=True) + return np.linalg.norm(lhs - lhs_check) + + +def solve_isdf(orbitals: npt.NDArray, + interp_indx: npt.NDArray) -> Tuple[npt.NDArray, npt.NDArray]: + """Solve for interpolating vectors given interpolating points and orbitals. + + Used for supercell and k-point so factor out as function. + + Args: + orbitals: orbitals on a grid of shape [num_grid_points, num_orbitals] + interp_indx: array indexing interpolating points (subset of grid + points to use selected by K-Means algorithm. shape is + [num_interp_points]. + orbitals: npt.NDArray: + interp_indx: npt.NDArray: + + Returns: + tuple: (Interpolang vectors, interpolating orbitals) (xi_mu(r), + phi_i(r_mu)). Note xi_mu(r) is called Theta[R, mu] in keeping with + original ISDF notation. + + """ + interp_orbitals = orbitals[interp_indx] + # Form pseudo-densities + # P[R, r_mu] = \sum_{i} phi_{R,i}^* phi_{mu,i} + pseudo_density = np.einsum("Ri,mi->Rm", + orbitals.conj(), + interp_orbitals, + optimize=True) + # [Z C^]_{J, mu} = (sum_i phi_{i, J}^* phi_{i, mu}) (sum_j phi_{j, J} + # phi_{i, mu}) + ZC_dag = np.einsum("Rm,Rm->Rm", + pseudo_density, + pseudo_density.conj(), + optimize=True) + # Just down sample from ZC_dag + CC_dag = ZC_dag[interp_indx].copy() + # Solve ZC_dag = Theta CC_dag + # Theta = ZC_dag @ CC_dag_inv + # Solve ZC_dag = Theta CC_dag + # -> ZC_dag^T = CC_dag^T Theta^T + # rcond = None uses MACH_EPS * max(M,N) for least squares convergence. + Theta_dag, _, _, _ = np.linalg.lstsq(CC_dag.conj().T, + ZC_dag.conj().T, + rcond=None) + return Theta_dag.conj().T, interp_orbitals + + +def supercell_isdf( + mydf: df.FFTDF, + interp_indx: npt.NDArray, + orbitals: npt.NDArray, + grid_points: npt.NDArray, + kpoint=np.zeros(3), +) -> Tuple[npt.NDArray, npt.NDArray, npt.NDArray]: + r""" + Build ISDF-THC tensors. + + Given the orbitals evaluated on a (dense) real space grid, and a set of + interpolating points (indexed by interp_indx) determine the interpolating + orbitals (chi), central tensor (zeta), and interpolating vectors Theta (also + called xi). + + Args: + mydf: instance of pyscf.pbc.df.FFTDF object. + interp_indx: array indexing interpolating points determined through + K-Means CVT procedure. Dimension [num_interp_points] + orbitals: orbitals on a grid of shape [num_grid_points, num_orbitals] + grid_points: Real space grid. Dimension [num_grid_points, num_dim], + num_dim is 1, 2 or 3 for 1D, 2D, 3D. + + Returns: + tuple: (chi, zeta, Theta): orbitals on interpolating + points, zeta (central tensor), and matrix of interpolating vectors Theta + of dimension [num_grid_points, num_interp_points] (also called + xi_mu(r)), where num_grid_points is the number of real space grid points + and num_interp_points is the number of interpolating points. + + """ + + cell = mydf.cell + num_grid_points = len(grid_points) + + Theta, chi = solve_isdf(orbitals, interp_indx) + + # FFT Theta[R, mu] -> Theta[mu, G] + # Transpose as fft expects contiguous. + Theta_G = tools.fft(Theta.T, mydf.mesh) + coulG = tools.get_coulG(cell, k=kpoint, mesh=mydf.mesh) + weighted_coulG = coulG * cell.vol / num_grid_points**2.0 + + # zeta_{mu,nu} = \sum_G 4pi/(omega * G^2) zeta_{mu,G} * (zeta_G*){nu, G} + Theta_G_tilde = np.einsum("iG,G->iG", Theta_G, weighted_coulG) + zeta = (Theta_G_tilde) @ Theta_G.conj().T + return chi, zeta, Theta + + +def build_kpoint_zeta( + df_inst: df.FFTDF, + Q: int, + delta_G: npt.NDArray, + delta_G_prime: npt.NDArray, + grid_points: npt.NDArray, + xi_mu: npt.NDArray, +) -> npt.NDArray: + """Build k-point THC zeta (central tensor) for given Q, delta_G, + delta_G_prime. + + Args: + mydf: instance of pyscf.pbc.df.FFTDF object. + Q: Momentum transfer (in 1BZ). + delta_G: Reciprocal lattice vector satisfying Q - (Q-k) = delta_G + delta_G_prime: Reciprocal lattice vector satisfying Q - (Q-k') = delta_G + grid_points: Real space grid. Dimension [num_grid_points, num_dim], + num_dim is 1, 2 or 3 for 1D, 2D, 3D. + xi_mu: array containing interpolating vectors determined during ISDF + procedure + + Returns: + zeta: central tensor of dimension [num_interp_points, num_interp_points] + + """ + cell = df_inst.cell + num_grid_points = grid_points.shape[0] + # delta_G - delta_G_prime because we have Gpq and Gsr and Gsr = -Grs, phase + # = Delta G = Gpq + Grs + phase_factor = np.exp(-1j * (np.einsum( + "x,Rx->R", delta_G - delta_G_prime, grid_points, optimize=True))) + # Minus sign again due to we use Q = kp - kq, but we should have + # V(G + k_q - k_p) + coulG = tools.get_coulG(cell, k=-(Q + delta_G), mesh=df_inst.mesh) + weighted_coulG = coulG * cell.vol / num_grid_points + xi_muG = tools.fft(xi_mu.T, df_inst.mesh) + xi_muG *= weighted_coulG + vR = tools.ifft(xi_muG, df_inst.mesh) + zeta = np.einsum("R,Rn,mR->mn", phase_factor, xi_mu, vR, optimize=True) + return zeta + + +def build_kpoint_zeta_single_tranlsation( + df_inst: df.FFTDF, + q: int, + delta_G: npt.NDArray, + grid_points: npt.NDArray, + xi_mu: npt.NDArray, +) -> npt.NDArray: + """Build k-point THC zeta (central tensor) for given Q, delta_G, + delta_G_prime. + + Args: + mydf: instance of pyscf.pbc.df.FFTDF object. + q: Momentum transfer kp-kq. + delta_G: Reciprocal lattice vector satisfying Q - (Q-k) = delta_G + grid_points: Real space grid. Dimension [num_grid_points, num_dim], + num_dim is 1, 2 or 3 for 1D, 2D, 3D. + xi_mu: array containing interpolating vectors determined during ISDF + procedure + + Returns: + zeta: central tensor of dimension [num_interp_points, num_interp_points] + + """ + cell = df_inst.cell + num_grid_points = grid_points.shape[0] + # delta_G - delta_G_prime because we have Gpq and Gsr and Gsr = -Grs, phase + # = Delta G = Gpq + Grs + phase_factor = np.exp( + -1j * (np.einsum("x,Rx->R", delta_G, grid_points, optimize=True))) + # Minus sign again due to we use Q = kp - kq, but we should have + # V(G + k_q - k_p) + coulG = tools.get_coulG(cell, k=-q, mesh=df_inst.mesh) + weighted_coulG = coulG * cell.vol / num_grid_points + xi_muG = tools.fft(xi_mu.T, df_inst.mesh) + xi_muG *= weighted_coulG + vR = tools.ifft(xi_muG, df_inst.mesh) + zeta = np.einsum("R,Rn,mR->mn", phase_factor, xi_mu, vR, optimize=True) + return zeta + + +def build_G_vectors(cell: gto.Cell) -> npt.NDArray: + """Build all 27 Gvectors + + Args: + cell: pyscf.pbc.gto.Cell object. + cell: gto.Cell: + + Returns: + tuple: G_dict a dictionary mapping miller index to appropriate + G_vector index and G_vectors array of 27 G_vectors shape [27, 3]. + + """ + G_dict = {} + G_vectors = np.zeros((27, 3), dtype=np.float64) + lattice_vectors = cell.lattice_vectors() + indx = 0 + for n1, n2, n3 in itertools.product(range(-1, 2), repeat=3): + G_dict[(n1, n2, n3)] = indx + G_vectors[indx] = np.einsum("n,ng->g", (n1, n2, n3), + cell.reciprocal_vectors()) + miller_indx = np.rint( + np.einsum("nx,x->n", lattice_vectors, G_vectors[indx]) / + (2 * np.pi)) + assert (miller_indx == (n1, n2, n3)).all() + indx += 1 + return G_dict, G_vectors + + +def find_unique_G_vectors(G_vectors: npt.NDArray, G_mapping: npt.NDArray + ) -> Tuple[npt.NDArray, npt.NDArray]: + """Find all unique G-vectors and build mapping to original set. + + Args: + G_vectors: array of 27 G-vectors. + G_mapping: array of 27 G-vectors. + + Returns: + unique_G_index in range [0,...,num_unique_Gs[iq]], and delta_Gs are the + unique G-vectors of size [num_qpoints, num_unique_Gs[iq]]. + + """ + unique_mapping = np.zeros_like(G_mapping) + num_qpoints = G_mapping.shape[0] + delta_Gs = np.zeros((num_qpoints,), dtype=object) + for iq in range(num_qpoints): + unique_G = np.unique(G_mapping[iq]) + delta_Gs[iq] = G_vectors[unique_G] + # Build map to unique index + unique_mapping[iq] = [ + ix for el in G_mapping[iq] for ix in np.where(unique_G == el)[0] + ] + + return unique_mapping, delta_Gs + + +def build_G_vector_mappings_double_translation( + cell: gto.Cell, + kpts: npt.NDArray, + momentum_map: npt.NDArray, +) -> Tuple[npt.NDArray, npt.NDArray, npt.NDArray, npt.NDArray]: + """Build G-vector mappings that map k-point differences to 1BZ. + + Args: + cell: pyscf.pbc.gto.Cell object. + kpts: array of kpoints. + momentum_map: momentum mapping to satisfy Q = (k_p - k_q) mod G. + momentum_map[iq, ikp] = ikq. + + Returns: + tuple: (G_vectors, Gpq_mapping, Gpq_mapping_unique, delta_Gs), G_vectors + is a list of all 27 + G-vectors and Gpq_mapping[iq, kp] = indx_Gpq, where Gpq = kpts[ikp] - + kpts[ikq] - kpts[iq], i.e. returns index to G-vector (consistent with + G_vectors) satisfying this condition. Gpq_mapping_unique provides + mapping to unique G_vector index. Delta_gs provides compressed lists of + unique G vectors. + + """ + G_dict, G_vectors = build_G_vectors(cell) + lattice_vectors = cell.lattice_vectors() + num_kpts = len(kpts) + Gpq_mapping = np.zeros((num_kpts, num_kpts), dtype=np.int32) + num_kpts = len(kpts) + for iq in range(num_kpts): + for ikp in range(num_kpts): + ikq = momentum_map[iq, ikp] + delta_Gpq = (kpts[ikp] - kpts[ikq]) - kpts[iq] + miller_indx = np.rint( + np.einsum("wx,x->w", lattice_vectors, delta_Gpq) / (2 * np.pi)) + Gpq_mapping[iq, ikp] = G_dict[tuple(miller_indx)] + Gpq_mapping_unique, delta_Gs = find_unique_G_vectors(G_vectors, Gpq_mapping) + return G_vectors, Gpq_mapping, Gpq_mapping_unique, delta_Gs + + +def get_miller(lattice_vectors: npt.NDArray, G: npt.NDArray) -> npt.NDArray: + """Convert G to miller indx. + + Args: + lattice_vectors: Array of lattice vectors. + G: Reciprocal lattice vector. + + Returns: + miller_index: 3D array of miller indices. + + """ + miller_indx = np.rint( + np.einsum("wx,x->w", lattice_vectors, G) / (2 * np.pi)).astype(np.int32) + return miller_indx + + +def build_minus_Q_G_mapping(cell: gto.Cell, kpts: npt.NDArray, + momentum_map: npt.NDArray) -> npt.NDArray: + """Build mapping for G that satisfied (-Q) + G + (Q + Gpq) = 0 (*) + and kp - kq = Q + Gpq. + + Args: + cell: pyscf.pbc.gto.Cell object. + kpts: array of kpoints. + momentum_map: momentum mapping to satisfy Q = (k_p - k_q) mod G. + momentum_map[iq, ikp] = ikq. + + Returns: + tuple: (minus_Q_mapping, minus_Q_mapping_unique), + minus_Q_mapping[indx_minus_Q, k] yields index for G satisfying (*) + above, where indx_minus_Q is given by indx_minus_Q = minus_k[Q], and + minus_k = conj_mapping(cell, kpts). minus_Q_mapping_unique indexes the + appropriate G vector given by delta_Gs[indx_minus_Q][indx] = G, where + indx = minus_Q_mapping_unique[indx_minus_Q, k], and deltaGs is built by + build_G_vector_mappings_double_translation. + + """ + ( + G_vecs, + G_map, + _, + delta_Gs, + ) = build_G_vector_mappings_double_translation(cell, kpts, momentum_map) + G_dict, _ = build_G_vectors(cell) + num_kpts = len(kpts) + lattice_vectors = cell.lattice_vectors() + minus_k_map = conj_mapping(cell, kpts) + minus_Q_mapping = np.zeros((num_kpts, num_kpts), dtype=np.int32) + minus_Q_mapping_unique = np.zeros((num_kpts, num_kpts), dtype=np.int32) + for iq in range(num_kpts): + minus_iq = minus_k_map[iq] + for ik in range(num_kpts): + Gpq = G_vecs[G_map[iq, ik]] + # Complementary Gpq (G in (*) in docstring) + Gpq_comp = -(kpts[minus_iq] + kpts[iq] + Gpq) + # find index in original set of 27 + iGpq_comp = G_dict[tuple(get_miller(lattice_vectors, Gpq_comp))] + minus_Q_mapping[minus_iq, ik] = iGpq_comp + indx_delta_Gs = np.array([ + G_dict[tuple(get_miller(lattice_vectors, G))] + for G in delta_Gs[minus_iq] + ]) + minus_Q_mapping_unique[minus_iq] = [ + ix for el in minus_Q_mapping[minus_iq] + for ix in np.where(el == indx_delta_Gs)[0] + ] + + return minus_Q_mapping, minus_Q_mapping_unique + + +def build_G_vector_mappings_single_translation( + cell: gto.Cell, + kpts: npt.NDArray, + kpts_pq: npt.NDArray, +) -> Tuple[npt.NDArray, npt.NDArray, npt.NDArray, npt.NDArray]: + """Build G-vector mappings that map k-point differences to 1BZ. + + Args: + cell: pyscf.pbc.gto.Cell object. + kpts: array of kpoints. + kpts_pq: Unique list of kp - kq indices of shape [num_unique_pq, 2]. + + Returns: + tuple: (G_vectors, Gpqr_mapping, Gpqr_mapping_unique, delta_Gs), G_vectors + is a list of all 27 + G-vectors and Gpqr_mapping[iq, kr] = indx_Gpqr, where Gpqr = kpts[ikp] - + kpts[ikq] + kpts[ikr] - kpts[iks], i.e. returns index to G_vectors + (consistent with G_vectors) satisfying this condition. + Gpqr_mapping_unique provides + mapping to unique G_vector index. Delta_gs provides compressed lists of + unique G vectors. + + """ + G_dict, G_vectors = build_G_vectors(cell) + lattice_vectors = cell.lattice_vectors() + num_kpts = len(kpts) + Gpqr_mapping = np.zeros((len(kpts_pq), num_kpts), dtype=np.int32) + kconserv = get_kconserv(cell, kpts) + for iq, (ikp, ikq) in enumerate(kpts_pq): + q = kpts[ikp] - kpts[ikq] + for ikr in range(num_kpts): + iks = kconserv[ikp, ikq, ikr] + delta_Gpqr = q + kpts[ikr] - kpts[iks] + # delta_Gpq += kpts[0] + miller_indx = np.rint( + np.einsum("wx,x->w", lattice_vectors, delta_Gpqr) / (2 * np.pi)) + Gpqr_mapping[iq, ikr] = G_dict[tuple(miller_indx)] + + Gpqr_mapping_unique, delta_Gs = find_unique_G_vectors( + G_vectors, Gpqr_mapping) + return G_vectors, Gpqr_mapping, Gpqr_mapping_unique, delta_Gs + + +def inverse_G_map_double_translation( + cell: gto.Cell, + kpts: npt.NDArray, + momentum_map: npt.NDArray, +) -> npt.NDArray: + """For given Q and G figure out all k which satisfy Q - k + G = 0 + + Args: + cell: pyscf.pbc.gto.Cell object. + kpts: array of kpoints. + momentum_map: momentum mapping to satisfy Q = (k_p - k_q) mod G. + momentum_map[iq, ikp] = ikq. + + Returns: + inverse_map: ragged numpy array. inverse_map[iq, iG] returns array + of size in range(0, num_kpts) and lists all k-point indices that + satisfy G_pq[iq, ik] = iG, i.e. an array of all ik. + + """ + G_dict, G_vectors = build_G_vectors(cell) + lattice_vectors = cell.lattice_vectors() + num_kpts = len(kpts) + Gpq_mapping = np.zeros((num_kpts, num_kpts), dtype=np.int32) + num_kpts = len(kpts) + for iq in range(num_kpts): + for ikp in range(num_kpts): + ikq = momentum_map[iq, ikp] + delta_Gpq = (kpts[ikp] - kpts[ikq]) - kpts[iq] + miller_indx = np.rint( + np.einsum("wx,x->w", lattice_vectors, delta_Gpq) / (2 * np.pi)) + Gpq_mapping[iq, ikp] = G_dict[tuple(miller_indx)] + + inverse_map = np.zeros( + ( + num_kpts, + 27, + ), + dtype=object, + ) + for iq in range(num_kpts): + for iG in range(len(G_vectors)): + inverse_map[iq, iG] = np.array( + [ik for ik in range(num_kpts) if Gpq_mapping[iq, ik] == iG]) + + return inverse_map + + +def build_eri_isdf_double_translation( + chi: npt.NDArray, + zeta: npt.NDArray, + q_indx: int, + kpts_indx: list, + G_mapping: npt.NDArray, +) -> npt.NDArray: + """Build (pkp qkq | rkr sks) from k-point ISDF factors. + + Args: + chi: array of interpolating orbitals of shape + [num_kpts, num_mo, num_interp_points] + zeta: central tensor of dimension + [num_kpts, num_G, num_G, num_interp_points, num_interp_points]. + q_indx: Index of momentum transfer. + kpts_indx: List of kpt indices corresponding to [kp, kq, kr, ks] + G_mapping: array to map kpts to G vectors [q_indx, kp] = G_pq + + Returns: + eri: (pkp qkq | rkr sks) + + """ + ikp, ikq, ikr, iks = kpts_indx + Gpq = G_mapping[q_indx, ikp] + Gsr = G_mapping[q_indx, iks] + eri = np.einsum( + "pm,qm,mn,rn,sn->pqrs", + chi[ikp].conj(), + chi[ikq], + zeta[q_indx][Gpq, Gsr], + chi[ikr].conj(), + chi[iks], + optimize=True, + ) + return eri + + +def build_eri_isdf_single_translation( + chi: npt.NDArray, + zeta: npt.NDArray, + q_indx: int, + kpts_indx: list, + G_mapping: npt.NDArray, +) -> npt.NDArray: + """Build (pkp qkq | rkr sks) from k-point ISDF factors. + + Args: + chi: array of interpolating orbitals of shape + [num_kpts, num_mo, num_interp_points] + zeta: central tensor of dimension + [num_kpts, num_G, num_G, num_interp_points, num_interp_points]. + q_indx: Index of momentum transfer. + kpts_indx: List of kpt indices corresponding to [kp, kq, kr, ks] + G_mapping: array to map kpts to G vectors [q_indx, kp] = G_pq + + Returns: + eri: (pkp qkq | rkr sks) + + """ + ikp, ikq, ikr, iks = kpts_indx + delta_G_indx = G_mapping[q_indx, ikr] + eri = np.einsum( + "pm,qm,mn,rn,sn->pqrs", + chi[ikp].conj(), + chi[ikq], + zeta[q_indx][delta_G_indx], + chi[ikr].conj(), + chi[iks], + optimize=True, + ) + return eri + + +def kpoint_isdf_double_translation( + df_inst: df.FFTDF, + interp_indx: npt.NDArray, + kpts: npt.NDArray, + orbitals: npt.NDArray, + grid_points: npt.NDArray, + only_unique_G: bool = True, +) -> Tuple[npt.NDArray, npt.NDArray, npt.NDArray, npt.NDArray]: + r""" + Build kpoint ISDF-THC tensors. + + Given the orbitals evaluated on a (dense) real space grid, and a set of + interpolating points (indexed by interp_indx) determine the interpolating + orbitals (chi), central tensor (zeta), and interpolating vectors Theta (also + called xi). + + For the double translation case we build zeta[Q, G, G'] for all possible G + and G' that satisfy Q - (Q-k) = G. If only_unique_G is True we only build + the unique G's which satisfiy this expression rather than all 27^2. + + Args: + df_inst: instance of pyscf.pbc.df.FFTDF object. + interp_indx: array indexing interpolating points determined through + K-Means CVT procedure. Dimension [num_interp_points] + kpts: Array of k-points. + orbitals: orbitals on a grid of shape [num_grid_points, + num_orbitals], note num_orbitals = N_k * m, where m is the number of + orbitals in the unit cell and N_k is the number of k-points. + grid_points: Real space grid. Dimension [num_grid_points, num_dim], + num_dim is 1, 2 or 3 for 1D, 2D, 3D. + only_unique_G: Only build central tensor for unique Gs which satisfy + momentum conservation condition. + + Returns: + chi: orbitals on interpolating points + zeta: THC central tensor. Dimension is + [num_kpts, 27, 27, num_interp_points, num_interp_points] + if only_unique_G is False otherwise it is of shape [num_kpts, + num_unique[Q], num_unique[Q], 27, num_interp_points, num_interp_points]. + Theta: Matrix of interpolating vectors of dimension [num_grid_points, + num_interp_points] (also called xi_mu(r)), where num_grid_points is the + number of real space grid points and num_interp_points is the number of + interpolating points. Zeta (the + G_mapping: G_mapping maps k-points to the appropriate delta_G index, i.e. + G_mapping[iq, ik] = i_delta_G. the index will map to the appropriate + index in the reduced set of G vectors. + """ + num_grid_points = len(grid_points) + assert orbitals.shape[0] == num_grid_points + xi, chi = solve_isdf(orbitals, interp_indx) + momentum_map = build_momentum_transfer_mapping(df_inst.cell, kpts) + num_kpts = len(kpts) + num_interp_points = xi.shape[1] + assert xi.shape == (num_grid_points, num_interp_points) + ( + G_vectors, + G_mapping, + G_mapping_unique, + delta_Gs_unique, + ) = build_G_vector_mappings_double_translation(df_inst.cell, kpts, + momentum_map) + if only_unique_G: + G_mapping = G_mapping_unique + delta_Gs = delta_Gs_unique + else: + delta_Gs = [G_vectors] * num_kpts + G_mapping = G_mapping + zeta = np.zeros((num_kpts,), dtype=object) + for iq in range(num_kpts): + num_G = len(delta_Gs[iq]) + out_array = np.zeros( + (num_G, num_G, num_interp_points, num_interp_points), + dtype=np.complex128, + ) + for iG, delta_G in enumerate(delta_Gs[iq]): + for iG_prime, delta_G_prime in enumerate(delta_Gs[iq]): + zeta_indx = build_kpoint_zeta(df_inst, kpts[iq], delta_G, + delta_G_prime, grid_points, xi) + out_array[iG, iG_prime] = zeta_indx + zeta[iq] = out_array + return chi, zeta, xi, G_mapping + + +def kpoint_isdf_single_translation( + df_inst: df.FFTDF, + interp_indx: npt.NDArray, + kpts: npt.NDArray, + orbitals: npt.NDArray, + grid_points: npt.NDArray, +) -> Tuple[npt.NDArray, npt.NDArray, npt.NDArray, npt.NDArray]: + r""" + Build kpoint ISDF-THC tensors. + + Given the orbitals evaluated on a (dense) real space grid, and a set of + interpolating points (indexed by interp_indx) determine the interpolating + orbitals (chi), central tensor (zeta), and interpolating vectors Theta (also + called xi). + + For the double translation case we build zeta[Q, G, G'] for all possible G + and G' that satisfy Q - (Q-k) = G. If only_unique_G is True we only build + the unique G's which satisfiy this expression rather than all 27^2. + + Args: + df_inst: instance of pyscf.pbc.df.FFTDF object. + interp_indx: array indexing interpolating points determined through + K-Means CVT procedure. Dimension [num_interp_points] + kpts: Array of k-points. + orbitals: orbitals on a grid of shape [num_grid_points, + num_orbitals], note num_orbitals = N_k * m, where m is the number of + orbitals in the unit cell and N_k is the number of k-points. + grid_points: Real space grid. Dimension [num_grid_points, num_dim], + num_dim is 1, 2 or 3 for 1D, 2D, 3D. + + Returns: + chi: orbitals on interpolating points + zeta: THC central tensor. Dimension is + [num_kpts, 27, 27, num_interp_points, num_interp_points] + if only_unique_G is False otherwise it is of shape [num_kpts, + num_unique[Q], num_unique[Q], 27, num_interp_points, num_interp_points]. + Theta: Matrix of interpolating vectors of dimension [num_grid_points, + num_interp_points] (also called xi_mu(r)), where num_grid_points is the + number of real space grid points and num_interp_points is the number of + interpolating points. Zeta (the + G_mapping: G_mapping maps k-points to the appropriate delta_G index, i.e. + G_mapping[iq, ik] = i_delta_G. the index will map to the appropriate + index in the reduced set of G vectors. + """ + num_grid_points = len(grid_points) + assert orbitals.shape[0] == num_grid_points + xi, chi = solve_isdf(orbitals, interp_indx) + num_kpts = len(kpts) + num_interp_points = xi.shape[1] + assert xi.shape == (num_grid_points, num_interp_points) + kpts_pq = np.array([(kp, kpts[ikq]) + for ikp, kp in enumerate(kpts) + for ikq in range(num_kpts)]) + kpts_pq_indx = np.array([ + (ikp, ikq) for ikp, kp in enumerate(kpts) for ikq in range(num_kpts) + ]) + transfers = kpts_pq[:, 0] - kpts_pq[:, 1] + unique_q, unique_indx, _ = unique(transfers) + _, _, G_map_unique, delta_Gs = build_G_vector_mappings_single_translation( + df_inst.cell, kpts, kpts_pq_indx[unique_indx]) + num_q_vectors = len(unique_q) + zeta = np.zeros((num_q_vectors,), dtype=object) + for iq in range(len(unique_q)): + num_G = len(delta_Gs[iq]) + out_array = np.zeros((num_G, num_interp_points, num_interp_points), + dtype=np.complex128) + for iG, delta_G in enumerate(delta_Gs[iq]): + zeta_indx = build_kpoint_zeta_single_tranlsation( + df_inst, unique_q[iq], delta_G, grid_points, xi) + out_array[iG] = zeta_indx + zeta[iq] = out_array + return chi, zeta, xi, G_map_unique + + +def build_isdf_orbital_inputs(mf_inst: scf.RHF) -> npt.NDArray: + """Build orbital product inputs from mean field object + + Args: + mf_inst: pyscf pbc mean-field object. + mf_inst: scf.RHF: + + Returns: + cell_periodic_mo: cell periodic part of Bloch orbital on real space grid. + shape: [num_grid_points, num_kpts*num_mo] + + """ + cell = mf_inst.cell + kpts = mf_inst.kpts + num_kpts = len(kpts) + grid_points = cell.gen_uniform_grids(mf_inst.with_df.mesh) + num_mo = mf_inst.mo_coeff[0].shape[-1] # assuming the same for each k-point + num_grid_points = grid_points.shape[0] + bloch_orbitals_ao = np.array( + numint.eval_ao_kpts(cell, grid_points, kpts=kpts)) + bloch_orbitals_mo = np.einsum("kRp,kpi->kRi", + bloch_orbitals_ao, + mf_inst.mo_coeff, + optimize=True) + exp_minus_ikr = np.exp(-1j * np.einsum("kx,Rx->kR", kpts, grid_points)) + cell_periodic_mo = np.einsum("kR,kRi->kRi", exp_minus_ikr, + bloch_orbitals_mo) + # go from kRi->Rki + # AO ISDF + cell_periodic_mo = cell_periodic_mo.transpose((1, 0, 2)).reshape( + (num_grid_points, num_kpts * num_mo)) + return cell_periodic_mo + + +def density_guess( + density: npt.NDArray, + grid_inst: UniformGrids, + grid_points: npt.NDArray, + num_interp_points: int, +) -> npt.NDArray: + """Select initial centroids based on electronic density. + + Args: + density: Density on real space grid. + grid_inst: pyscf UniformGrids object. + grid_points: Real space grid points. + num_interp_points: Number of interpolating points. + + Returns: + grid_points: Grid points sampled using density as a weighting function. + + """ + norm_factor = np.einsum("R,R->", density, grid_inst.weights).real + prob_dist = (density.real * grid_inst.weights) / norm_factor + indx = np.random.choice( + len(grid_points), + num_interp_points, + replace=False, + p=prob_dist, + ) + return grid_points[indx] + + +def interp_indx_from_qrcp(Z: npt.NDArray, + num_interp_pts: npt.NDArray, + return_diagonal: bool = False): + """Find interpolating points via QRCP + + Z^T P = Q R, where R has diagonal elements that are in descending order of + magnitude. Interpolating points are then chosen by the columns of the + permuted Z^T. + + Args: + orbitals_on_grid: cell-periodic part of bloch orbitals on real space grid + of shape (num_grid_points, num_kpts*num_mo) + num_interp_pts: integer corresponding to number of interpolating points + to select from full real space grid. + Z: npt.NDArray: + return_diagonal: bool: (Default value = False) + + Returns: + interp_indx: Index of interpolating points in full real space grid. + + """ + + Q, R, P = scipy.linalg.qr(Z.T, pivoting=True) + signs_R = np.diag(np.sign(R.diagonal())) + # Force diagonal of R to be positive which isn't strictly enforced in QR + # factorization. + R = np.dot(signs_R, R) + Q = np.dot(Q, signs_R) + interp_indx = P[:num_interp_pts] + if return_diagonal: + return interp_indx, R.diagonal() + else: + return interp_indx + + +def setup_isdf(mf_inst: scf.RHF, verbose: bool = False + ) -> Tuple[npt.NDArray, npt.NDArray, npt.NDArray]: + """Setup common data for ISDF solution. + + Args: + mf_inst: pyscf pbc mean-field object. + verbose: Whether to print some information. + mf_inst: scf.RHF: + verbose: bool: (Default value = False) + + Returns: + grid_points: Real space grid points. + cell_periodic_mo: Cell periodic part of MOs on a grid. + [num_grid, num_kpts * num_orb] + bloch_orbitals_mo: MOs on a grid. [num_grid, num_kpts, num_orb] + + """ + assert isinstance(mf_inst.with_df, df.FFTDF), "mf object must use FFTDF" + cell = mf_inst.cell + kpts = mf_inst.kpts + grid_points = cell.gen_uniform_grids(mf_inst.with_df.mesh) + num_grid_points = grid_points.shape[0] + if verbose: + print("Real space grid shape: ({}, {})".format(grid_points.shape[0], + grid_points.shape[1])) + print("Number of grid points: {}".format(num_grid_points)) + bloch_orbitals_ao = np.array( + numint.eval_ao_kpts(cell, grid_points, kpts=kpts)) + bloch_orbitals_mo = np.einsum("kRp,kpi->kRi", + bloch_orbitals_ao, + mf_inst.mo_coeff, + optimize=True) + num_mo = mf_inst.mo_coeff[0].shape[-1] # assuming the same for each k-point + num_kpts = len(kpts) + # Cell periodic part + # u = e^{-ik.r} phi(r) + exp_minus_ikr = np.exp(-1j * np.einsum("kx,Rx->kR", kpts, grid_points)) + cell_periodic_mo = np.einsum("kR,kRi->kRi", exp_minus_ikr, + bloch_orbitals_mo) + # go from kRi->Rki + # AO ISDF + cell_periodic_mo = cell_periodic_mo.transpose((1, 0, 2)).reshape( + (num_grid_points, num_kpts * num_mo)) + return grid_points, cell_periodic_mo, bloch_orbitals_mo + + +@dataclass +class KPointTHC: + """Light class to hold THC tensors. + + Atributes: + chi: THC leaf tensor. shape = [num_kpts, num_mo, num_thc] + zeta: THC central tensor. + shape = [num_kpts, num_G, num_G, num_thc, num_thc] + G_mapping: For a given Q and k index this array gives the G index for + zeta. + xi: ISDF interpolating vectors. May be none if this class holds + reoptimized THC decomposition. + + Examples: + The following pseudocode shows how you can build an ERI matrix block + given elements of this class. + + >>> kthc = solve_kmeans_kpisdf(...) + >>> ikp, ikq, ikr, iks = kpts_indx + >>> Gpq = kthc.G_mapping[q_indx, ikp] + >>> Gsr = kthc.G_mapping[q_indx, iks] + >>> eri = np.einsum( + "pm,qm,mn,rn,sn->pqrs", + kthc.chi[ikp].conj(), + kthc.chi[ikq], + kthc.zeta[q_indx][Gpq, Gsr], + kthc.chi[ikr].conj(), + kthc.chi[iks], + optimize=True, + ) + + """ + + chi: npt.NDArray[np.complex128] + zeta: npt.NDArray + G_mapping: npt.NDArray + xi: Union[npt.NDArray[np.complex128], None] + + @property + def num_interp_points(self) -> int: + """Number of interpolating points (THC dimension)""" + return chi.shape[-1] + + @property + def num_thc_factors(self) -> int: + """Number of interpolating points (THC dimension)""" + return chi.shape[-1] + + +def solve_kmeans_kpisdf( + mf_inst: scf.RHF, + num_interp_points: int, + max_kmeans_iteration: int = 500, + single_translation: bool = False, + use_density_guess: bool = True, + kmeans_weighting_function: str = "density", + verbose: bool = True, +) -> KPointTHC: + r"""Solve for k-point THC factors using k-means CVT ISDF procedure. + + Args: + mf_inst: pyscf pbc mean-field object. + num_interp_points: Number of interpolating points (THC rank M). + max_kmeans_iteration: Max iteration for k-means CVT algorithm. + single_translation: Build THC factors assuming single translation of kp + - kq. If true we build zeta[Q, G], else zeta[Q, G, G']. + use_density_guess: Select initial grid points according to electron + density? Default True. + kmeans_weighting_function: Weighting function to use in k-means CVT. + One of ["density", "orbital_density", "sum_squares"]. + verbose: Whether to print some information. + + Returns: + solution: THC factors held in KPointTHC object. + """ + if verbose: + print(f" Number of interpolating points: {num_interp_points}") + print(f" Max K-Means iteration: {max_kmeans_iteration}") + print(f" Use density initial guess: {density_guess}") + print(f" K-Means weighting function: {kmeans_weighting_function}") + print(f" Single Translation: {single_translation}") + # Build real space grid, and orbitals on real space grid + grid_points, cell_periodic_mo, bloch_orbitals_mo = setup_isdf(mf_inst) + grid_inst = UniformGrids(mf_inst.cell) + # Find interpolating points using K-Means CVT algorithm + kmeans = KMeansCVT(grid_points, max_iteration=max_kmeans_iteration) + nocc = mf_inst.cell.nelec[0] # assuming same for each k-point + density = np.einsum( + "kRi,kRi->R", + bloch_orbitals_mo[:, :, :nocc].conj(), + bloch_orbitals_mo[:, :, :nocc], + optimize=True, + ) + if use_density_guess: + initial_centroids = density_guess(density, grid_inst, grid_points, + num_interp_points) + else: + initial_centroids = None + weighting_function = None + # Build weighting function for CVT algorithm. + if kmeans_weighting_function == "density": + weighting_function = density + elif kmeans_weighting_function == "orbital_density": + # w(r) = sum_{ij} |phi_i(r)| |phi_j(r)| + tmp = np.einsum( + "kRi,pRj->Rkipj", + abs(bloch_orbitals_mo), + abs(bloch_orbitals_mo), + optimize=True, + ) + weighting_function = np.einsum("Rkipj->R", tmp) + elif kmeans_weighting_function == "sum_squares": + # w(r) = sum_{i} |phi_{ki}(r)| + weighting_function = np.einsum( + "kRi,kRi->R", + bloch_orbitals_mo.conj(), + bloch_orbitals_mo, + optimize=True, + ) + weighting_function = weighting_function + else: + raise ValueError( + f"Unknown value for weighting function {kmeans_weighting_function}") + interp_indx = kmeans.find_interpolating_points( + num_interp_points, + weighting_function.real, + verbose=verbose, + centroids=initial_centroids, + ) + solution = solve_for_thc_factors( + mf_inst, + interp_indx, + cell_periodic_mo, + grid_points, + single_translation=single_translation, + verbose=verbose, + ) + return solution + + +def solve_qrcp_isdf( + mf_inst: scf.RHF, + num_interp_points: int, + single_translation: bool = True, + verbose: bool = True, +) -> KPointTHC: + r"""Solve for k-point THC factors using QRCP ISDF procedure. + + Args: + mf_inst: pyscf pbc mean-field object. + num_interp_points: Number of interpolating points (THC rank M). + single_translation: Build THC factors assuming single translation of kp + single_translation: Build THC factors assuming single translation of kp + - kq. If true we build zeta[Q, G], else zeta[Q, G, G']. + verbose: Whether to print some information. + + Returns: + solution: THC factors held in KPointTHC object. + """ + # Build real space grid, and orbitals on real space grid + grid_points, cell_periodic_mo, _ = setup_isdf(mf_inst) + # Find interpolating points using K-Means CVT algorithm + # Z_{R, (ki)(k'j)} = u_{ki}(r)* u_{k'j}(r) + num_grid_points = len(grid_points) + num_orbs = cell_periodic_mo.shape[1] + Z = np.einsum("Ri,Rj->Rij", + cell_periodic_mo.conj(), + cell_periodic_mo, + optimize=True).reshape((num_grid_points, num_orbs**2)) + interp_indx = interp_indx_from_qrcp(Z, num_interp_points) + # Solve for THC factors. + solution = solve_for_thc_factors( + mf_inst, + interp_indx, + cell_periodic_mo, + grid_points, + single_translation=single_translation, + verbose=verbose, + ) + return solution + + +def solve_for_thc_factors( + mf_inst, + interp_points_index, + cell_periodic_mo, + grid_points, + single_translation=True, + verbose=True, +) -> KPointTHC: + r"""Solve for k-point THC factors using interpolating points as input. + + Args: + mf_inst: pyscf pbc mean-field object. + interp_points_index: Indices of interpolating points found from k-means + CVT or QRCP. + cell_periodic_mo: cell periodic part of Bloch orbital on real space grid. + shape: [num_grid_points, num_kpts*num_mo] + kmeans_weighting_function: Weighting function to use in k-means CVT. + One of ["density", "orbital_density", "sum_squares"]. + grid_points: Real space grid. Dimension [num_grid_points, num_dim], + num_dim is 1, 2 or 3 for 1D, 2D, 3D. + single_translation: Build THC factors assuming single translation of kp + - kq. If true we build zeta[Q, G], else zeta[Q, G, G']. + (Default value = True) + verbose: Whether to print some information. (Default value = True) + + Returns: + solution: THC factors held in KPointTHC object. + """ + assert isinstance(mf_inst.with_df, df.FFTDF), "mf object must use FFTDF" + if single_translation: + chi, zeta, xi, G_mapping = kpoint_isdf_single_translation( + mf_inst.with_df, + interp_points_index, + mf_inst.kpts, + cell_periodic_mo, + grid_points, + ) + else: + chi, zeta, xi, G_mapping = kpoint_isdf_double_translation( + mf_inst.with_df, + interp_points_index, + mf_inst.kpts, + cell_periodic_mo, + grid_points, + ) + num_interp_points = len(interp_points_index) + num_kpts = len(mf_inst.kpts) + num_mo = mf_inst.mo_coeff[0].shape[-1] # assuming the same for each k-point + assert chi.shape == (num_interp_points, num_kpts * num_mo) + # go from Rki -> kiR + chi = chi.reshape((num_interp_points, num_kpts, num_mo)) + chi = chi.transpose((1, 2, 0)) + + solution = KPointTHC(chi=chi, zeta=zeta, xi=xi, G_mapping=G_mapping) + + return solution diff --git a/src/openfermion/resource_estimates/pbc/thc/utils/isdf_test.py b/src/openfermion/resource_estimates/pbc/thc/utils/isdf_test.py new file mode 100644 index 000000000..a3679bd06 --- /dev/null +++ b/src/openfermion/resource_estimates/pbc/thc/utils/isdf_test.py @@ -0,0 +1,657 @@ +# coverage: ignore +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from ase.build import bulk +import numpy as np + +from pyscf.pbc import gto, scf +from pyscf.pbc.dft import numint +from pyscf.pbc.tools import pyscf_ase +from pyscf.pbc.lib.kpts_helper import unique, get_kconserv, member + +from openfermion.resource_estimates.pbc.thc.utils.kmeans import KMeansCVT +from openfermion.resource_estimates.pbc.thc.utils.isdf import ( + inverse_G_map_double_translation, + build_kpoint_zeta, + get_miller, + build_minus_Q_G_mapping, + build_G_vectors, + build_G_vector_mappings_single_translation, + build_G_vector_mappings_double_translation, + build_eri_isdf_double_translation, + build_eri_isdf_single_translation, + solve_kmeans_kpisdf, + solve_qrcp_isdf, + supercell_isdf, +) +from openfermion.resource_estimates.pbc.utils.hamiltonian_utils import ( + build_momentum_transfer_mapping,) + + +def test_supercell_isdf_gamma(): + cell = gto.Cell() + cell.atom = """ + C 0.000000000000 0.000000000000 0.000000000000 + C 1.685068664391 1.685068664391 1.685068664391 + """ + cell.basis = "gth-szv" + cell.pseudo = "gth-hf-rev" + cell.a = """ + 0.000000000, 3.370137329, 3.370137329 + 3.370137329, 0.000000000, 3.370137329 + 3.370137329, 3.370137329, 0.000000000""" + cell.unit = "B" + cell.verbose = 0 + cell.mesh = [11] * 3 + cell.build(parse_arg=False) + # kpts = cell.make_kpts(kmesh, scaled_center=[0.2, 0.3, 0.5]) + + mf = scf.RHF(cell) + mf.kernel() + + from pyscf.pbc.dft import gen_grid + + grid_inst = gen_grid.UniformGrids(cell) + grid_points = cell.gen_uniform_grids(mf.with_df.mesh) + orbitals = numint.eval_ao(cell, grid_points) + orbitals_mo = np.einsum("Rp,pi->Ri", orbitals, mf.mo_coeff, optimize=True) + num_mo = mf.mo_coeff.shape[1] + num_interp_points = np.prod(cell.mesh) + interp_indx = np.arange(np.prod(cell.mesh)) + + chi, zeta, Theta = supercell_isdf(mf.with_df, + interp_indx, + orbitals=orbitals_mo, + grid_points=grid_points) + assert Theta.shape == (len(grid_points), num_interp_points) + # Check overlap + # Evaluate overlap from orbitals. Do it integral way to ensure + # discretization error is the same from using coarse FFT grid for testing + # speed + ovlp_ao = np.einsum( + "mp,mq,m->pq", + orbitals.conj(), + orbitals, + grid_inst.weights, + optimize=True, + ) + ovlp_mo = np.einsum("pi,pq,qj->ij", + mf.mo_coeff.conj(), + ovlp_ao, + mf.mo_coeff, + optimize=True) + ovlp_mu = np.einsum("Rm,R->m", Theta, grid_inst.weights, optimize=True) + orbitals_mo_interp = orbitals_mo[interp_indx] + ovlp_isdf = np.einsum( + "mi,mj,m->ij", + orbitals_mo_interp.conj(), + orbitals_mo_interp, + ovlp_mu, + optimize=True, + ) + assert np.allclose(ovlp_mo, ovlp_isdf) + # Check ERIs. + eri_ref = mf.with_df.ao2mo(mf.mo_coeff) + from pyscf import ao2mo + + eri_ref = ao2mo.restore(1, eri_ref, num_mo) + # THC eris + Lijn = np.einsum("mi,mj,mn->ijn", chi.conj(), chi, zeta, optimize=True) + eri_thc = np.einsum("ijn,nk,nl->ijkl", Lijn, chi.conj(), chi, optimize=True) + assert np.allclose(eri_thc, eri_ref) + + +def test_supercell_isdf_complex(): + cell = gto.Cell() + cell.atom = """ + C 0.000000000000 0.000000000000 0.000000000000 + C 1.685068664391 1.685068664391 1.685068664391 + """ + cell.basis = "gth-szv" + cell.pseudo = "gth-hf-rev" + cell.a = """ + 0.000000000, 3.370137329, 3.370137329 + 3.370137329, 0.000000000, 3.370137329 + 3.370137329, 3.370137329, 0.000000000""" + cell.unit = "B" + cell.mesh = [11] * 3 + cell.verbose = 0 + cell.build(parse_arg=False) + # kpts = cell.make_kpts(kmesh, scaled_center=[0.2, 0.3, 0.5]) + + mf = scf.RHF(cell, kpt=np.array([0.1, -0.001, 0.022])) + mf.kernel() + assert np.max(np.abs(mf.mo_coeff.imag)) > 1e-2 + + from pyscf.pbc.dft import gen_grid + + grid_inst = gen_grid.UniformGrids(cell) + grid_points = cell.gen_uniform_grids(mf.with_df.mesh) + orbitals = numint.eval_ao(cell, grid_points, kpt=mf.kpt) + orbitals_mo = np.einsum("Rp,pi->Ri", orbitals, mf.mo_coeff, optimize=True) + num_mo = mf.mo_coeff.shape[1] + num_interp_points = 10 * num_mo + nocc = cell.nelec[0] + density = np.einsum( + "Ri,Ri->R", + orbitals_mo[:, :nocc].conj(), + orbitals_mo[:, :nocc], + optimize=True, + ) + kmeans = KMeansCVT(grid_points, max_iteration=500) + interp_indx = kmeans.find_interpolating_points( + num_interp_points, + density.real, + verbose=False, + ) + + chi, zeta, Theta = supercell_isdf(mf.with_df, + interp_indx, + orbitals=orbitals_mo, + grid_points=grid_points) + assert Theta.shape == (len(grid_points), num_interp_points) + # Check overlap + # Evaluate overlap from orbitals. Do it integral way to ensure + # discretization error is the same from using coarse FFT grid for testing + # speed + ovlp_ao = np.einsum( + "mp,mq,m->pq", + orbitals.conj(), + orbitals, + grid_inst.weights, + optimize=True, + ) + ovlp_mo = np.einsum("pi,pq,qj->ij", + mf.mo_coeff.conj(), + ovlp_ao, + mf.mo_coeff, + optimize=True) + ovlp_mu = np.einsum("Rm,R->m", Theta, grid_inst.weights, optimize=True) + orbitals_mo_interp = orbitals_mo[interp_indx] + ovlp_isdf = np.einsum( + "mi,mj,m->ij", + orbitals_mo_interp.conj(), + orbitals_mo_interp, + ovlp_mu, + optimize=True, + ) + assert np.allclose(ovlp_mo, ovlp_isdf) + # Check ERIs. + eri_ref = mf.with_df.ao2mo(mf.mo_coeff, kpts=mf.kpt.reshape((1, -1))) + # Check there is a complex component + assert np.max(np.abs(eri_ref.imag)) > 1e-3 + # for complex integrals ao2mo will yield num_mo^4 elements. + eri_ref = eri_ref.reshape((num_mo,) * 4) + # THC eris + Lijn = np.einsum("mi,mj,mn->ijn", chi.conj(), chi, zeta, optimize=True) + eri_thc = np.einsum("ijn,nk,nl->ijkl", Lijn, chi.conj(), chi, optimize=True) + assert np.allclose(eri_thc, eri_ref) + + +def test_G_vector_mapping_double_translation(): + ase_atom = bulk("AlN", "wurtzite", a=3.11, c=4.98) + cell = gto.Cell() + cell.exp_to_discard = 0.1 + cell.atom = pyscf_ase.ase_atoms_to_pyscf(ase_atom) + cell.a = ase_atom.cell[:].copy() + cell.basis = "gth-szv" + cell.pseudo = "gth-hf-rev" + cell.verbose = 0 + cell.build(parse_arg=False) + + nk = 3 + kmesh = [nk, nk, nk] + kpts = cell.make_kpts(kmesh) + + momentum_map = build_momentum_transfer_mapping(cell, kpts) + ( + G_vecs, + G_map, + G_unique, + _, + ) = build_G_vector_mappings_double_translation(cell, kpts, momentum_map) + num_kpts = len(kpts) + for iq in range(num_kpts): + for ikp in range(num_kpts): + ikq = momentum_map[iq, ikp] + q = kpts[ikp] - kpts[ikq] + G_shift = G_vecs[G_map[iq, ikp]] + assert np.allclose(q, kpts[iq] + G_shift) + for iq in range(num_kpts): + unique_G = np.unique(G_map[iq]) + for i, G in enumerate(G_map[iq]): + assert unique_G[G_unique[iq][i]] == G + + inv_G_map = inverse_G_map_double_translation(cell, kpts, momentum_map) + for iq in range(num_kpts): + for ik in range(num_kpts): + ix_G_qk = G_map[iq, ik] + assert ik in inv_G_map[iq, ix_G_qk] + + +def test_G_vector_mapping_single_translation(): + cell = gto.Cell() + cell.atom = """ + C 0.000000000000 0.000000000000 0.000000000000 + C 1.685068664391 1.685068664391 1.685068664391 + """ + cell.basis = "gth-szv" + cell.pseudo = "gth-hf-rev" + cell.a = """ + 0.000000000, 3.370137329, 3.370137329 + 3.370137329, 0.000000000, 3.370137329 + 3.370137329, 3.370137329, 0.000000000""" + cell.unit = "B" + cell.verbose = 0 + cell.build(parse_arg=False) + + nk = 3 + kmesh = [nk, nk, nk] + kpts = cell.make_kpts(kmesh) + num_kpts = len(kpts) + + kpts_pq = np.array([(kp, kpts[ikq]) + for ikp, kp in enumerate(kpts) + for ikq in range(num_kpts)]) + + kpts_pq_indx = np.array([ + (ikp, ikq) for ikp, kp in enumerate(kpts) for ikq in range(num_kpts) + ]) + transfers = kpts_pq[:, 0] - kpts_pq[:, 1] + assert len(transfers) == (nk**3)**2 + _, unique_indx, _ = unique(transfers) + ( + _, + _, + G_unique, + delta_Gs, + ) = build_G_vector_mappings_single_translation(cell, kpts, + kpts_pq_indx[unique_indx]) + kconserv = get_kconserv(cell, kpts) + for ikp in range(num_kpts): + for ikq in range(num_kpts): + for ikr in range(num_kpts): + iks = kconserv[ikp, ikq, ikr] + delta_G_expected = kpts[ikp] - kpts[ikq] + kpts[ikr] - kpts[iks] + q = kpts[ikp] - kpts[ikq] + qindx = member(q, transfers[unique_indx])[0] + # print(q, len(transfers[unique_indx]), len(transfers)) + dG_indx = G_unique[qindx, ikr] + # print(qindx, dG_indx, delta_Gs[qindx].shape) + # print(qindx) + # print(len(delta_Gs[qindx]), dG_indx) + delta_G = delta_Gs[qindx][dG_indx] + assert np.allclose(delta_G_expected, delta_G) + + +def test_kpoint_isdf_double_translation(): + cell = gto.Cell() + cell.atom = """ + C 0.000000000000 0.000000000000 0.000000000000 + C 1.685068664391 1.685068664391 1.685068664391 + """ + cell.basis = "gth-szv" + cell.pseudo = "gth-hf-rev" + cell.a = """ + 0.000000000, 3.370137329, 3.370137329 + 3.370137329, 0.000000000, 3.370137329 + 3.370137329, 3.370137329, 0.000000000""" + cell.unit = "B" + cell.verbose = 0 + cell.mesh = [11] * 3 # set to coarse value to just check ERIS numerically. + cell.build(parse_arg=False) + + kmesh = [1, 2, 1] + kpts = cell.make_kpts(kmesh) + mf = scf.KRHF(cell, kpts) + mf.kernel() + + momentum_map = build_momentum_transfer_mapping(cell, kpts) + num_mo = mf.mo_coeff[0].shape[-1] + num_thc = np.prod(cell.mesh) + kpt_thc = solve_kmeans_kpisdf( + mf, + num_thc, + use_density_guess=True, + verbose=False, + single_translation=False, + ) + num_kpts = len(momentum_map) + for iq in range(1, num_kpts): + for ikp in range(num_kpts): + ikq = momentum_map[iq, ikp] + for iks in range(num_kpts): + ikr = momentum_map[iq, iks] + _ = kpt_thc.G_mapping[iq, iks] + kpt_pqrs = [kpts[ikp], kpts[ikq], kpts[ikr], kpts[iks]] + mos_pqrs = [ + mf.mo_coeff[ikp], + mf.mo_coeff[ikq], + mf.mo_coeff[ikr], + mf.mo_coeff[iks], + ] + eri_pqrs = mf.with_df.ao2mo(mos_pqrs, kpt_pqrs, + compact=False).reshape( + (num_mo,) * 4) + eri_pqrs_isdf = build_eri_isdf_double_translation( + kpt_thc.chi, + kpt_thc.zeta, + iq, + [ikp, ikq, ikr, iks], + kpt_thc.G_mapping, + ) + assert np.allclose(eri_pqrs, eri_pqrs_isdf) + + +def test_kpoint_isdf_single_translation(): + cell = gto.Cell() + cell.atom = """ + C 0.000000000000 0.000000000000 0.000000000000 + C 1.685068664391 1.685068664391 1.685068664391 + """ + cell.basis = "gth-szv" + cell.pseudo = "gth-hf-rev" + cell.a = """ + 0.000000000, 3.370137329, 3.370137329 + 3.370137329, 0.000000000, 3.370137329 + 3.370137329, 3.370137329, 0.000000000""" + cell.unit = "B" + cell.verbose = 0 + cell.mesh = [11] * 3 # set to coarse value to just check ERIS numerically. + cell.build(parse_arg=False) + + kmesh = [1, 2, 1] + kpts = cell.make_kpts(kmesh) + mf = scf.KRHF(cell, kpts) + mf.kernel() + + num_thc = np.prod(cell.mesh) + num_kpts = len(kpts) + kpt_thc = solve_kmeans_kpisdf( + mf, + num_thc, + use_density_guess=True, + verbose=False, + single_translation=True, + ) + kpts_pq = np.array([(kp, kpts[ikq]) + for ikp, kp in enumerate(kpts) + for ikq in range(num_kpts)]) + transfers = kpts_pq[:, 0] - kpts_pq[:, 1] + _, unique_indx, _ = unique(transfers) + kconserv = get_kconserv(cell, kpts) + num_mo = mf.mo_coeff[0].shape[-1] + for ikp in range(num_kpts): + for ikq in range(num_kpts): + for ikr in range(num_kpts): + iks = kconserv[ikp, ikq, ikr] + kpt_pqrs = [kpts[ikp], kpts[ikq], kpts[ikr], kpts[iks]] + mos_pqrs = [ + mf.mo_coeff[ikp], + mf.mo_coeff[ikq], + mf.mo_coeff[ikr], + mf.mo_coeff[iks], + ] + eri_pqrs = mf.with_df.ao2mo(mos_pqrs, kpt_pqrs, + compact=False).reshape( + (num_mo,) * 4) + q = kpts[ikp] - kpts[ikq] + qindx = member(q, transfers[unique_indx])[0] + eri_pqrs_isdf = build_eri_isdf_single_translation( + kpt_thc.chi, + kpt_thc.zeta, + qindx, + [ikp, ikq, ikr, iks], + kpt_thc.G_mapping, + ) + assert np.allclose(eri_pqrs, eri_pqrs_isdf) + + +def get_complement(miller_indx, kmesh): + complement = ~miller_indx + complement[np.where(np.array(kmesh) == 1)] = 0 + return complement + + +def test_kpoint_isdf_symmetries(): + cell = gto.Cell() + cell.atom = """ + C 0.000000000000 0.000000000000 0.000000000000 + C 1.685068664391 1.685068664391 1.685068664391 + """ + cell.basis = "gth-szv" + cell.pseudo = "gth-hf-rev" + cell.a = """ + 0.000000000, 3.370137329, 3.370137329 + 3.370137329, 0.000000000, 3.370137329 + 3.370137329, 3.370137329, 0.000000000""" + cell.unit = "B" + cell.mesh = [11] * 3 + cell.verbose = 0 + cell.build(parse_arg=False) + + kmesh = [1, 2, 3] + kpts = cell.make_kpts(kmesh) + mf = scf.KRHF(cell, kpts) + mf.kernel() + num_thc = np.prod( + cell.mesh) # should be no THC error when selecting all the grid points. + kpt_thc = solve_kmeans_kpisdf( + mf, + num_thc, + use_density_guess=True, + verbose=False, + single_translation=False, + ) + momentum_map = build_momentum_transfer_mapping(cell, kpts) + ( + _, + _, + G_unique, + delta_Gs, + ) = build_G_vector_mappings_double_translation(cell, kpts, momentum_map) + _, minus_Q_G_map_unique = build_minus_Q_G_mapping(cell, kpts, momentum_map) + num_kpts = len(kpts) + # Test symmetries from Appendix D of https://arxiv.org/pdf/2302.05531.pdf + # Test LHS for sanity too (need to uncomment) + grid_points = cell.gen_uniform_grids(mf.with_df.mesh) + from pyscf.pbc.lib.kpts_helper import conj_mapping + + minus_k_map = conj_mapping(cell, kpts) + for iq in range(3, num_kpts): + # Get -Q index + minus_iq = minus_k_map[iq] + for ik in range(num_kpts): + ik_minus_q = momentum_map[iq, ik] + iGpq = G_unique[iq, ik] + for ik_prime in range(num_kpts): + iGsr = G_unique[iq, ik_prime] + ik_prime_minus_q = momentum_map[iq, ik_prime] + # Sanity check G mappings + assert np.allclose(kpts[ik] - kpts[ik_minus_q] - kpts[iq], + delta_Gs[iq][iGpq]) + assert np.allclose( + kpts[ik_prime] - kpts[ik_prime_minus_q] - kpts[iq], + delta_Gs[iq][iGsr], + ) + # (pk qk-Q | rk'-Q sk') = (q k-Q p k | sk' rk'-Q)* + ik_prime_minus_q = momentum_map[iq, ik_prime] + # uncomment to check normal eris + # kpt_pqrs = [ik, ik_minus_q, ik_prime_minus_q, ik_prime] + # eri_pqrs = build_eri(mf, kpt_pqrs) + # kpt_pqrs = [ik, ik_minus_q, ik_prime_minus_q, ik_prime] + # kpt_pqrs = [ik_minus_q, ik, ik_prime, ik_prime_minus_q] + # eri_qpsr = build_eri(mf, kpt_pqrs).transpose((1, 0, 3, 2)) + # Sanity check relationship + # assert np.allclose(eri_pqrs, eri_qpsr.conj()) + # Now check how to index into correct G when Q is conjugated + # We want to find (-Q) + G_pq_comp + (Q + Gpq) = 0, + # Q + Gpq = kp - kq = q + # so G_pq_comp = -((-Q) + (Q+Gpq)) + iGpq_comp = minus_Q_G_map_unique[minus_iq, ik] + iGsr_comp = minus_Q_G_map_unique[minus_iq, ik_prime] + # Check zeta symmetry: expect zeta[Q,G1,G2,m,n] = + # zeta[-Q,G1_comp,G2_comp,m, n].conj() + # Build refernce point zeta[Q,G1,G2,m,n] + zeta_ref = kpt_thc.zeta[iq][iGpq, iGsr] + zeta_test = kpt_thc.zeta[minus_iq][iGpq_comp, iGsr_comp] + # F31 (pk qk-Q | rk'-Q sk') = (rk'-Q s k'| pk qk-Q) + assert np.allclose(zeta_ref, zeta_test.conj()) + # Sanity check do literal minus signs (should be complex + # conjugate) + zeta_test = build_kpoint_zeta( + mf.with_df, + -kpts[iq], + -delta_Gs[iq][iGpq], + -delta_Gs[iq][iGsr], + grid_points, + kpt_thc.xi, + ) + assert np.allclose(zeta_ref, zeta_test.conj()) + # (pk qk-Q | rk'-Q sk') = (rk'-Q s k'| pk qk-Q) + # uncomment to check normal eris + # kpt_pqrs = [ik_prime_minus_q, ik_prime, ik, ik_minus_q] + # eri_rspq = build_eri(mf, kpt_pqrs).transpose((2, 3, 0, 1)) + # assert np.allclose(eri_pqrs, eri_rspq) + # Check zeta symmetry: expect zeta[Q,G1,G2,m,n] = + # zeta[-Q,G2_comp,G1_comp,m, n] + zeta_test = kpt_thc.zeta[minus_iq][iGsr_comp, iGpq_comp] + assert np.allclose(zeta_ref, zeta_test.T) + # (pk qk-Q | rk'-Q sk') = (sk' r k'-Q| qk-Q pk) + # uncomment to check normal eris + # kpt_pqrs = [ik_prime, ik_prime_minus_q, ik_minus_q, ik] + # eri_srqp = build_eri(mf, kpt_pqrs).transpose((3, 2, 1, 0)) + # assert np.allclose(eri_pqrs, eri_srqp.conj()) + # Check zeta symmetry: expect zeta[Q,G1,G2,m,n] + # = zeta[Q,G2,G1,n, m].conj() + zeta_test = kpt_thc.zeta[iq][iGsr, iGpq] + assert np.allclose(zeta_ref, zeta_test.conj().T) + + +def test_symmetry_of_G_maps(): + cell = gto.Cell() + cell.atom = """ + C 0.000000000000 0.000000000000 0.000000000000 + C 1.685068664391 1.685068664391 1.685068664391 + """ + cell.basis = "gth-szv" + cell.pseudo = "gth-hf-rev" + cell.a = """ + 0.000000000, 3.370137329, 3.370137329 + 3.370137329, 0.000000000, 3.370137329 + 3.370137329, 3.370137329, 0.000000000""" + cell.unit = "B" + cell.verbose = 0 + cell.build(parse_arg=False) + + kmesh = [3, 3, 3] + kpts = cell.make_kpts(kmesh) + momentum_map = build_momentum_transfer_mapping(cell, kpts) + ( + G_vecs, + G_map, + _, + delta_Gs, + ) = build_G_vector_mappings_double_translation(cell, kpts, momentum_map) + G_dict, _ = build_G_vectors(cell) + num_kpts = len(kpts) + lattice_vectors = cell.lattice_vectors() + from pyscf.pbc.lib.kpts_helper import conj_mapping + + minus_k_map = conj_mapping(cell, kpts) + # k1 - k2 = Q + G + for iq in range(1, num_kpts): + minus_iq = minus_k_map[iq] + for ik in range(num_kpts): + Gpq = G_vecs[G_map[iq, ik]] + Gpq_comp = -(kpts[minus_iq] + kpts[iq] + Gpq) + iGpq_comp = G_dict[tuple(get_miller(lattice_vectors, Gpq_comp))] + G_indx_unique = [ + G_dict[tuple(get_miller(lattice_vectors, G))] + for G in delta_Gs[minus_iq] + ] + if iq == 1: + pass + assert iGpq_comp in G_indx_unique + for _ in range(num_kpts): + # Check complement(miller_Gpq) = miller_Gpq_comp + # Get indx of "complement" G in original set of 27 + iGsr_comp = G_dict[tuple(get_miller(lattice_vectors, Gpq_comp))] + # Get index of unique Gs in original set of 27 + # Check complement is in set corresponding to zeta[-Q] + assert iGsr_comp in G_indx_unique + + # Check minus Q mapping + minus_Q_G_map, minus_Q_G_map_unique = build_minus_Q_G_mapping( + cell, kpts, momentum_map) + for iq in range(1, num_kpts): + minus_iq = minus_k_map[iq] + for ik in range(num_kpts): + Gpq = G_vecs[G_map[iq, ik]] + Gpq_comp = -(kpts[minus_iq] + kpts[iq] + Gpq) + iGpq_comp = G_dict[tuple(get_miller(lattice_vectors, Gpq_comp))] + assert iGpq_comp == minus_Q_G_map[minus_iq, ik] + indx_in_unique_set = minus_Q_G_map_unique[minus_iq, ik] + Gpq_comp_from_map = delta_Gs[iq][indx_in_unique_set] + assert np.allclose(Gpq_comp, Gpq_comp_from_map) + + +def test_isdf_qrcp(): + cell = gto.Cell() + cell.atom = """ + C 0.000000000000 0.000000000000 0.000000000000 + C 1.685068664391 1.685068664391 1.685068664391 + """ + cell.basis = "gth-szv" + cell.pseudo = "gth-hf-rev" + cell.a = """ + 0.000000000, 3.370137329, 3.370137329 + 3.370137329, 0.000000000, 3.370137329 + 3.370137329, 3.370137329, 0.000000000""" + cell.mesh = [11] * 3 + cell.unit = "B" + cell.verbose = 0 + cell.build() + + kmesh = [1, 2, 1] + kpts = cell.make_kpts(kmesh) + mf = scf.KRHF(cell, kpts) + mf.kernel() + + num_mo = mf.mo_coeff[0].shape[-1] + num_thc = np.prod(cell.mesh) + kpt_thc = solve_qrcp_isdf(mf, num_thc, single_translation=False) + num_kpts = len(kpts) + momentum_map = build_momentum_transfer_mapping(cell, kpts) + for iq in range(1, num_kpts): + for ikp in range(num_kpts): + ikq = momentum_map[iq, ikp] + for iks in range(num_kpts): + ikr = momentum_map[iq, iks] + kpt_pqrs = [kpts[ikp], kpts[ikq], kpts[ikr], kpts[iks]] + mos_pqrs = [ + mf.mo_coeff[ikp], + mf.mo_coeff[ikq], + mf.mo_coeff[ikr], + mf.mo_coeff[iks], + ] + eri_pqrs = mf.with_df.ao2mo(mos_pqrs, kpt_pqrs, + compact=False).reshape( + (num_mo,) * 4) + eri_pqrs_isdf = build_eri_isdf_double_translation( + kpt_thc.chi, + kpt_thc.zeta, + iq, + [ikp, ikq, ikr, iks], + kpt_thc.G_mapping, + ) + assert np.allclose(eri_pqrs, eri_pqrs_isdf) diff --git a/src/openfermion/resource_estimates/pbc/thc/utils/kmeans.py b/src/openfermion/resource_estimates/pbc/thc/utils/kmeans.py new file mode 100644 index 000000000..06753c353 --- /dev/null +++ b/src/openfermion/resource_estimates/pbc/thc/utils/kmeans.py @@ -0,0 +1,159 @@ +# coverage: ignore +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import numpy as np +import numpy.typing as npt + + +class KMeansCVT(object): + + def __init__( + self, + grid: npt.NDArray, + max_iteration: int = 100, + threshold: float = 1e-6, + ): + """Initialize k-means solver to find interpolating points for ISDF. + + Args: + grid: Real space grid of dimension [Ng,Ndim], where Ng is the number + of (dense) real space grid points and Ndim is number of spatial + dimensions. + max_iteration: Maximum number of iterations to perform when + classifying grid points. Default 100. + threshold: Threshold for exiting classification. Default 1e-6. + + Returns: + """ + self.grid = grid + self.max_iteration = max_iteration + self.threshold = threshold + + @staticmethod + def classify_grid_points(grid_points: npt.NDArray, + centroids: npt.NDArray) -> npt.NDArray: + r"""Assign grid points to centroids. + + Find centroid closest to each given grid point. + + Note we don't use instance variable self.grid as we can abuse this + function and use it to map grid to centroid and centroid to grid point. + + Args: + grid_points: grid points to assign. + centroids: Centroids to which grid points should be assigned, + array of length num_interp_points. + + Returns: + 1D np.array assigning grid point to centroids + """ + # Build N_g x N_mu matrix of distances. + num_grid_points = grid_points.shape[0] + num_interp_points = centroids.shape[0] + distances = np.zeros((num_grid_points, num_interp_points)) + # For loop is faster than broadcasting by 2x. + for ig in range(num_grid_points): + distances[ig] = np.linalg.norm(grid_points[ig] - centroids, axis=1) + # Find shortest distance for each grid point. + classification = np.argmin(distances, axis=1) + return classification + + def compute_new_centroids(self, weighting, grid_mapping, + current_centroids) -> npt.NDArray: + r""" + Centroids are defined via: + + .. math:: + + c(C_\mu) = \frac{\sum_{j in C(\mu)} r_j \rho(r_j)}{\sum_{j in + C(\mu)} \rho(r_j)}, + + where :math:`\rho(r_j)` is the weighting factor. + + Args: + weighting: Weighting function. + grid_mapping: maps grid points to centroids + current_centroids: centroids for current iteration. + + Returns: + new_centroids: updated centroids + """ + num_interp_points = current_centroids.shape[0] + new_centroids = np.zeros_like(current_centroids) + for interp_indx in range(num_interp_points): + # get grid points belonging to this centroid + grid_indx = np.where(grid_mapping == interp_indx)[0] + grid_points = self.grid[grid_indx] + weight = weighting[grid_indx] + numerator = np.einsum("Jx,J->x", grid_points, weight) + denominator = np.einsum("J->", weight) + if denominator < 1e-12: + print("Warning very small denominator, something seems wrong!") + print("{interp_indx}") + new_centroids[interp_indx] = numerator / denominator + return new_centroids + + def map_centroids_to_grid(self, centroids): + grid_mapping = self.classify_grid_points(centroids, self.grid) + return grid_mapping + + def find_interpolating_points( + self, + num_interp_points: int, + weighting_factor: npt.NDArray, + centroids=None, + verbose=True, + ) -> npt.NDArray: + """Find interpolating points using KMeans-CVT algorithm. + + Args: + num_interp_points: number of points to select. + weighting_factor: weighting function for K-Means procedure. + centroids: initial guess at centroids, if None centroids are + selected randomly from the grid points. + verbose: Controls if information is printed about convergence. + Default value = True. + + Returns: + interp_pts: index associated with interpolating points. + """ + num_grid_points = self.grid.shape[0] + if centroids is None: + # Randomly select grid points as centroids. + centroids_indx = np.random.choice(num_grid_points, + num_interp_points, + replace=False) + centroids = self.grid[centroids_indx].copy() + else: + assert len(centroids) == num_interp_points + # define here to avoid linter errors about possibly undefined. + new_centroids = np.zeros_like(centroids) + delta_grid = 1.0 + if verbose: + print("{:<10s} {:>13s}".format("iteration", "Error")) + for iteration in range(self.max_iteration): + grid_mapping = self.classify_grid_points(self.grid, centroids) + # Global reduce + new_centroids[:] = self.compute_new_centroids( + weighting_factor, grid_mapping, centroids) + delta_grid = np.linalg.norm(new_centroids - centroids) + if verbose and iteration % 10 == 0: + print(f"{iteration:<9d} {delta_grid:13.8e}") + if delta_grid < self.threshold: + if verbose: + print("KMeansCVT successfully completed.") + print(f"Final error {delta_grid:13.8e}.") + return self.map_centroids_to_grid(new_centroids) + centroids[:] = new_centroids[:] + print("Warning K-Means not converged.") + print(f"Final error {delta_grid:13.8e}.") + return self.map_centroids_to_grid(new_centroids) diff --git a/src/openfermion/resource_estimates/pbc/thc/utils/kmeans_test.py b/src/openfermion/resource_estimates/pbc/thc/utils/kmeans_test.py new file mode 100644 index 000000000..67f9c762c --- /dev/null +++ b/src/openfermion/resource_estimates/pbc/thc/utils/kmeans_test.py @@ -0,0 +1,62 @@ +# coverage: ignore +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import numpy as np + +from openfermion.resource_estimates.pbc.thc.utils.kmeans import KMeansCVT + + +def gaussian(dx, sigma): + return np.exp(-np.einsum("Gx,Gx->G", dx, dx) / + sigma**2.0) / (2 * (sigma * np.pi)**0.5) + + +def gen_gaussian(xx, yy, grid, sigma=0.125): + x0 = np.array([0.25, 0.25]) + dx0 = grid - x0[None, :] + weight = gaussian(dx0, sigma) + x1 = np.array([0.25, 0.75]) + dx = grid - x1[None, :] + weight += gaussian(dx, sigma) + x2 = np.array([0.75, 0.25]) + dx = grid - x2[None, :] + weight += gaussian(dx, sigma) + x3 = np.array([0.75, 0.75]) + dx = grid - x3[None, :] + weight += gaussian(dx, sigma) + return weight + + +def test_kmeans(): + np.random.seed(7) + # 3D spatial grid + num_grid_x = 10 + num_grid_points = num_grid_x**2 + xs = np.linspace(0, 1, num_grid_x) + ys = np.linspace(0, 1, num_grid_x) + xx, yy = np.meshgrid(xs, ys) + grid = np.zeros((num_grid_points, 2)) + grid[:, 0] = xx.ravel() + grid[:, 1] = yy.ravel() + num_interp_points = 10 + + weight = gen_gaussian(xx, yy, grid) + kmeans = KMeansCVT(grid) + interp_points = kmeans.find_interpolating_points(num_interp_points, + weight, + verbose=False) + interp_points_ref = [37, 27, 77, 81, 38, 24, 73, 62, 76, 22] + assert np.allclose(interp_points, interp_points_ref) + + +if __name__ == "__main__": + test_kmeans() diff --git a/src/openfermion/resource_estimates/pbc/thc/utils/thc_jax.py b/src/openfermion/resource_estimates/pbc/thc/utils/thc_jax.py new file mode 100644 index 000000000..7451b4002 --- /dev/null +++ b/src/openfermion/resource_estimates/pbc/thc/utils/thc_jax.py @@ -0,0 +1,1049 @@ +# coverage: ignore +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Reoptimize THC factors using a combination of BFGS and adaagrad including a +penalty parameter to regularize the lambda value as described in +https://arxiv.org/abs/2302.05531 + +The entrypoint for a user should be the function kpoint_thc_via_isdf which can +be used as + +>>> krhf_inst = scf.KRHF(cell, kpts) +>>> krmp_inst = scf.KMP2(krhf_inst) +>>> Lchol = pyscf_chol_from_df(krmp_inst) +>>> nthc = cthc * nmo +>>> thc_factors = kpoint_thc_via_isdf(krhf_inst, Lchol, nthc) +""" + +# pylint: disable=wrong-import-position +import math +import time +from typing import Tuple, Union + +import h5py +import numpy as np +import numpy.typing as npt +from pyscf.pbc import scf +from scipy.optimize import minimize + +from jax.config import config +config.update("jax_enable_x64", True) + +import jax +import jax.numpy as jnp +import jax.typing as jnpt + +from openfermion.resource_estimates.thc.utils import adagrad +from openfermion.resource_estimates.pbc.thc.utils.isdf import ( + KPointTHC, + solve_kmeans_kpisdf, +) +from openfermion.resource_estimates.pbc.utils.hamiltonian_utils import ( + build_momentum_transfer_mapping,) + + +def load_thc_factors(chkfile_name: str) -> KPointTHC: + """Load THC factors from a checkpoint file + + Args: + chkfile_name: Filename containing THC factors. + + Returns: + kthc: KPointISDF object built from chkfile_name. + """ + xi = None + with h5py.File(chkfile_name, "r") as fh5: + chi = fh5["chi"][:] + G_mapping = fh5["G_mapping"][:] + num_kpts = G_mapping.shape[0] + zeta = np.zeros((num_kpts,), dtype=object) + if "xi" in list(fh5.keys()): + xi = fh5["xi"][:] + else: + xi = None + for iq in range(G_mapping.shape[0]): + zeta[iq] = fh5[f"zeta_{iq}"][:] + return KPointTHC(xi=xi, zeta=zeta, G_mapping=G_mapping, chi=chi) + + +def save_thc_factors( + chkfile_name: str, + chi: npt.NDArray, + zeta: npt.NDArray, + Gpq_map: npt.NDArray, + xi: Union[npt.NDArray, None] = None, +) -> None: + """Write THC factors to file + + Args: + chkfile_name: Filename to write to. + chi: THC leaf tensor. + zeta: THC central tensor. + Gpq_map: Maps momentum conserving tuples of kpoints to reciprocal + lattice vectors in THC central tensor. + xi: Interpolating vectors (optional, Default None). + """ + num_kpts = chi.shape[0] + with h5py.File(chkfile_name, "w") as fh5: + fh5["chi"] = chi + fh5["G_mapping"] = Gpq_map + if xi is not None: + fh5["xi"] = xi + for iq in range(num_kpts): + fh5[f"zeta_{iq}"] = zeta[iq] + + +def get_zeta_size(zeta: npt.NDArray) -> int: + """zeta (THC central tensor) is not contiguous so this helper function + returns its size + + Args: + zeta: THC central tensor + + Returns: + zeta_size: Number of elements in zeta + """ + return sum([z.size for z in zeta]) + + +def unpack_thc_factors( + xcur: npt.NDArray, + num_thc: int, + num_orb: int, + num_kpts: int, + num_G_per_Q: list, +) -> Tuple[npt.NDArray, npt.NDArray]: + """Unpack THC factors from flattened array used for reoptimization. + + Args: + xcur: Flattened array containing k-point THC factors. + num_thc: THC rank. + num_orb: Number of orbitals. + num_kpts: Number of kpoints. + num_G_per_Q: Number of G vectors per Q vector. + + Returns: + chi: THC leaf tensor. + zeta: THC central tensor. + """ + # leaf tensor (num_kpts, num_orb, num_thc) + chi_size = num_kpts * num_orb * num_thc + chi_real = xcur[:chi_size].reshape(num_kpts, num_orb, num_thc) + chi_imag = xcur[chi_size:2 * chi_size].reshape(num_kpts, num_orb, num_thc) + chi = chi_real + 1j * chi_imag + zeta_packed = xcur[2 * chi_size:] + zeta = [] + start = 0 + for iQ in range(num_kpts): + num_G = num_G_per_Q[iQ] + size = num_G * num_G * num_thc * num_thc + zeta_real = zeta_packed[start:start + size].reshape( + (num_G, num_G, num_thc, num_thc)) + zeta_imag = zeta_packed[start + size:start + 2 * size].reshape( + (num_G, num_G, num_thc, num_thc)) + zeta.append(zeta_real + 1j * zeta_imag) + start += 2 * size + return chi, zeta + + +def pack_thc_factors(chi: npt.NDArray, zeta: npt.NDArray, + buffer: npt.NDArray) -> None: + """Pack THC factors into flattened array used for reoptimization. + + Args: + chi: THC leaf tensor. + zeta: THC central tensor. + buffer: Flattened array containing k-point THC factors. Modified inplace + """ + assert len(chi.shape) == 3 + buffer[:chi.size] = chi.real.ravel() + buffer[chi.size:2 * chi.size] = chi.imag.ravel() + start = 2 * chi.size + num_kpts = len(zeta) + for iQ in range(num_kpts): + size = zeta[iQ].size + buffer[start:start + size] = zeta[iQ].real.ravel() + buffer[start + size:start + 2 * size] = zeta[iQ].imag.ravel() + start += 2 * size + + +@jax.jit +def compute_objective_batched( + chis: Tuple[jnpt.ArrayLike, jnpt.ArrayLike, jnpt.ArrayLike, jnpt. + ArrayLike], + zetas: jnpt.ArrayLike, + chols: Tuple[jnpt.ArrayLike, jnpt.ArrayLike], + norm_factors: Tuple[jnpt.ArrayLike, jnpt.ArrayLike, jnpt. + ArrayLike, jnpt.ArrayLike], + num_kpts: int, + penalty_param: float = 0.0, +) -> float: + """Compute THC objective function. + + Batches evaluation over kpts. + + Args: + chis: THC leaf tensor. + zetas: THC central tensor. + chols: Cholesky factors definining 'exact' eris. + norm_factors: THC normalization factors. + num_kpts: Number of k-points. + penalty_param: Penalty parameter. + + Returns: + objective: THC objective function + """ + eri_thc = jnp.einsum( + "Jpm,Jqm,Jmn,Jrn,Jsn->Jpqrs", + chis[0].conj(), + chis[1], + zetas, + chis[2].conj(), + chis[3], + optimize=True, + ) + eri_ref = jnp.einsum("Jnpq,Jnrs->Jpqrs", chols[0], chols[1], optimize=True) + deri = (eri_thc - eri_ref) / num_kpts + norm_left = norm_factors[0] * norm_factors[1] + norm_right = norm_factors[2] * norm_factors[3] + MPQ_normalized = (jnp.einsum( + "JP,JPQ,JQ->JPQ", norm_left, zetas, norm_right, optimize=True) / + num_kpts) + + lambda_z = jnp.sum(jnp.einsum("JPQ->J", 0.5 * jnp.abs(MPQ_normalized))**2.0) + + res = 0.5 * jnp.sum((jnp.abs(deri))**2) + penalty_param * lambda_z + return res + + +def prepare_batched_data_indx_arrays( + momentum_map: npt.NDArray, + Gpq_map: npt.NDArray, +) -> Tuple[npt.NDArray, npt.NDArray]: + r"""Create arrays to batch over. + + Flatten sum_q sum_{k,k_prime} -> sum_q \sum_{indx} and pack momentum + conserving indices and central tensors so we can sum over indx efficiently. + + Args: + momentum_map: momentum transfer mapping. map[iQ, ik_p] -> ik_q; + (kpts[ikp] - kpts[ikq])%G = kpts[iQ]. + Gpq_map: Maps momentum conserving tuples of kpoints to reciprocal + lattice vectors in THC central tensor. + + Returns: + indx_pqrs: momentum conserving k-point indices. + zetas: Batches Central tensors. + """ + num_kpts = momentum_map.shape[0] + indx_pqrs = np.zeros((num_kpts, num_kpts**2, 4), dtype=jnp.int32) + zetas = np.zeros((num_kpts, num_kpts**2, 2), dtype=jnp.int32) + for iq in range(num_kpts): + indx = 0 + for ik in range(num_kpts): + ik_minus_q = momentum_map[iq, ik] + Gpq = Gpq_map[iq, ik] + for ik_prime in range(num_kpts): + ik_prime_minus_q = momentum_map[iq, ik_prime] + Gsr = Gpq_map[iq, ik_prime] + indx_pqrs[iq, indx] = [ + ik, + ik_minus_q, + ik_prime_minus_q, + ik_prime, + ] + zetas[iq, indx] = [Gpq, Gsr] + indx += 1 + return indx_pqrs, zetas + + +@jax.jit +def get_batched_data_1indx(array: jnpt.ArrayLike, + indx: jnpt.ArrayLike) -> jnpt.ArrayLike: + """Helper function to extract entries of array given another array. + + Args: + array: Array to index + indx: Indexing array + + Retuns: + indexed_array: i.e. array[indx] + """ + return array[indx] + + +@jax.jit +def get_batched_data_2indx(array: jnpt.ArrayLike, indxa: jnpt.ArrayLike, + indxb: jnpt.ArrayLike) -> jnpt.ArrayLike: + """Helper function to extract entries of 2D array given another array + + Args: + array: Array to index + indxa: Indexing array + indxb: Indexing array + + Retuns: + indexed_array: i.e. array[indxa, indxb] + """ + return array[indxa, indxb] + + +def thc_objective_regularized_batched( + xcur: jnpt.ArrayLike, + num_orb: int, + num_thc: int, + momentum_map: npt.NDArray, + Gpq_map: npt.NDArray, + chol: jnpt.ArrayLike, + indx_arrays: Tuple[jnpt.ArrayLike, jnpt.ArrayLike], + batch_size: int, + penalty_param=0.0, +) -> float: + """Compute THC objective function. Here we batch over multiple k-point + indices for improved GPU efficiency. + + Args: + xcur: Flattened array containing k-point THC factors. + num_orb: Number of orbitals. + num_thc: THC rank. + momentum_map: momentum transfer mapping. map[iQ, ik_p] -> ik_q; + (kpts[ikp] - kpts[ikq])%G = kpts[iQ]. + Gpq_map: Maps momentum conserving tuples of kpoints to reciprocal + lattice vectors in THC central tensor. + chol: Cholesky factors definining 'exact' eris. + indx_arrays: Batched index arrays (see prepare_batched_data_indx_arrays) + batch_size: Size of each batch of data. Should be in range + [1, num_kpts**2]. penalty_param: Penalty param if computing regularized + cost function. + + Returns: + objective: THC objective function + """ + num_kpts = momentum_map.shape[0] + num_G_per_Q = [len(np.unique(GQ)) for GQ in Gpq_map] + chi, zeta = unpack_thc_factors(xcur, num_thc, num_orb, num_kpts, + num_G_per_Q) + # Normalization factor, no factor of sqrt as there are 4 chis in total when + # building ERI. + norm_kP = jnp.einsum("kpP,kpP->kP", chi.conj(), chi, optimize=True)**0.5 + num_batches = math.ceil(num_kpts**2 / batch_size) + + indx_pqrs, indx_zeta = indx_arrays + objective = 0.0 + for iq in range(num_kpts): + for ibatch in range(num_batches): + start = ibatch * batch_size + end = (ibatch + 1) * batch_size + chi_p = get_batched_data_1indx(chi, indx_pqrs[iq, start:end, 0]) + chi_q = get_batched_data_1indx(chi, indx_pqrs[iq, start:end, 1]) + chi_r = get_batched_data_1indx(chi, indx_pqrs[iq, start:end, 2]) + chi_s = get_batched_data_1indx(chi, indx_pqrs[iq, start:end, 3]) + norm_k1 = get_batched_data_1indx(norm_kP, + indx_pqrs[iq, start:end, 0]) + norm_k2 = get_batched_data_1indx(norm_kP, + indx_pqrs[iq, start:end, 1]) + norm_k3 = get_batched_data_1indx(norm_kP, + indx_pqrs[iq, start:end, 2]) + norm_k4 = get_batched_data_1indx(norm_kP, + indx_pqrs[iq, start:end, 3]) + zeta_batch = get_batched_data_2indx( + zeta[iq], + indx_zeta[iq, start:end, 0], + indx_zeta[iq, start:end, 1], + ) + chol_batch_pq = get_batched_data_2indx(chol, + indx_pqrs[iq, start:end, 0], + indx_pqrs[iq, start:end, 1]) + chol_batch_rs = get_batched_data_2indx(chol, + indx_pqrs[iq, start:end, 2], + indx_pqrs[iq, start:end, 3]) + objective += compute_objective_batched( + (chi_p, chi_q, chi_r, chi_s), + zeta_batch, + (chol_batch_pq, chol_batch_rs), + (norm_k1, norm_k2, norm_k3, norm_k4), + num_kpts, + penalty_param=penalty_param, + ) + return objective + + +def thc_objective_regularized( + xcur, + num_orb, + num_thc, + momentum_map, + Gpq_map, + chol, + penalty_param=0.0, +): + """Compute THC objective function. Non-batched version. + + Args: + xcur: Flattened array containing k-point THC factors. + num_orb: Number of orbitals. + num_thc: THC rank. + momentum_map: momentum transfer mapping. map[iQ, ik_p] -> ik_q; + (kpts[ikp] - kpts[ikq])%G = kpts[iQ]. + Gpq_map: Maps momentum conserving tuples of kpoints to reciprocal + lattice vectors in THC central tensor. + chol: Cholesky factors definining 'exact' eris. + penalty_param: Penalty param if computing regularized cost function. + + Returns: + objective: THC objective function + """ + res = 0.0 + num_kpts = momentum_map.shape[0] + num_G_per_Q = [len(np.unique(GQ)) for GQ in Gpq_map] + chi, zeta = unpack_thc_factors(xcur, num_thc, num_orb, num_kpts, + num_G_per_Q) + num_kpts = momentum_map.shape[0] + norm_kP = jnp.einsum("kpP,kpP->kP", chi.conj(), chi, optimize=True)**0.5 + for iq in range(num_kpts): + for ik in range(num_kpts): + ik_minus_q = momentum_map[iq, ik] + Gpq = Gpq_map[iq, ik] + for ik_prime in range(num_kpts): + ik_prime_minus_q = momentum_map[iq, ik_prime] + Gsr = Gpq_map[iq, ik_prime] + eri_thc = jnp.einsum( + "pm,qm,mn,rn,sn->pqrs", + chi[ik].conj(), + chi[ik_minus_q], + zeta[iq][Gpq, Gsr], + chi[ik_prime_minus_q].conj(), + chi[ik_prime], + ) + eri_ref = jnp.einsum( + "npq,nsr->pqrs", + chol[ik, ik_minus_q], + chol[ik_prime, ik_prime_minus_q].conj(), + ) + deri = (eri_thc - eri_ref) / num_kpts + norm_left = norm_kP[ik] * norm_kP[ik_minus_q] + norm_right = norm_kP[ik_prime_minus_q] * norm_kP[ik_prime] + MPQ_normalized = (jnp.einsum("P,PQ,Q->PQ", norm_left, + zeta[iq][Gpq, Gsr], norm_right) / + num_kpts) + + lambda_z = 0.5 * jnp.sum(jnp.abs(MPQ_normalized)) + res += 0.5 * jnp.sum( + (jnp.abs(deri))**2) + penalty_param * (lambda_z**2) + + return res + + +def lbfgsb_opt_kpthc_l2reg( + chi: npt.NDArray, + zeta: npt.NDArray, + momentum_map: npt.NDArray, + Gpq_map: npt.NDArray, + chol: npt.NDArray, + chkfile_name: Union[str, None] = None, + maxiter: int = 150_000, + disp_freq: int = 98, + penalty_param: Union[float, None] = None, +) -> Tuple[npt.NDArray, float]: + """Least-squares fit of two-electron integral tensors with L-BFGS-B with + l2-regularization of lambda. + + Args: + chi: THC leaf tensor. + zeta: THC central tensor. + momentum_map: momentum transfer mapping. map[iQ, ik_p] -> ik_q; + (kpts[ikp] - kpts[ikq])%G = kpts[iQ]. + Gpq_map: Maps momentum conserving tuples of kpoints to reciprocal + lattice vectors in THC central tensor. + chol: Cholesky factors definining 'exact' eris. + batch_size: Size of each batch of data. Should be in range + [1, num_kpts**2]. + penalty_param: Penalty param if computing regularized cost function. + chkfile_name: Filename to store intermediate state of optimization to. + maxiter: Max L-BFGS-B iteration. + disp_freq: L-BFGS-B disp_freq. + penalty_param: Paramter to penalize optimization by one-norm of + Hamiltonian. If None it is determined automatically through trying + to balance the two terms in the objective function. + + Returns: + objective: THC objective function + """ + initial_guess = np.zeros(2 * (chi.size + get_zeta_size(zeta)), + dtype=np.float64) + pack_thc_factors(chi, zeta, initial_guess) + assert len(chi.shape) == 3 + assert len(zeta[0].shape) == 4 + num_kpts = chi.shape[0] + num_orb = chi.shape[1] + num_thc = chi.shape[-1] + assert zeta[0].shape[-1] == num_thc + assert zeta[0].shape[-2] == num_thc + print(initial_guess) + loss = thc_objective_regularized( + jnp.array(initial_guess), + num_orb, + num_thc, + momentum_map, + Gpq_map, + jnp.array(chol), + penalty_param=0.0, + ) + reg_loss = thc_objective_regularized( + jnp.array(initial_guess), + num_orb, + num_thc, + momentum_map, + Gpq_map, + jnp.array(chol), + penalty_param=1.0, + ) + # set penalty + lambda_z = (reg_loss - loss)**0.5 + if penalty_param is None: + # loss + lambda_z^2 - loss + penalty_param = loss / lambda_z + print("loss {}".format(loss)) + print("lambda_z {}".format(lambda_z)) + print("penalty_param {}".format(penalty_param)) + + # L-BFGS-B optimization + thc_grad = jax.grad(thc_objective_regularized, argnums=[0]) + print("Initial Grad") + print( + thc_grad( + jnp.array(initial_guess), + num_orb, + num_thc, + momentum_map, + Gpq_map, + jnp.array(chol), + penalty_param, + )) + # print() + res = minimize( + thc_objective_regularized, + initial_guess, + args=( + num_orb, + num_thc, + momentum_map, + Gpq_map, + jnp.array(chol), + penalty_param, + ), + jac=thc_grad, + method="L-BFGS-B", + options={ + "disp": disp_freq > 0, + "iprint": disp_freq, + "maxiter": maxiter, + }, + ) + + # print(res) + params = np.array(res.x) + loss = thc_objective_regularized( + jnp.array(res.x), + num_orb, + num_thc, + momentum_map, + Gpq_map, + jnp.array(chol), + penalty_param=0.0, + ) + if chkfile_name is not None: + num_G_per_Q = [len(np.unique(GQ)) for GQ in Gpq_map] + chi, zeta = unpack_thc_factors(params, num_thc, num_orb, num_kpts, + num_G_per_Q) + save_thc_factors(chkfile_name, chi, zeta, Gpq_map) + return np.array(params), loss + + +def lbfgsb_opt_kpthc_l2reg_batched( + chi: npt.NDArray, + zeta: npt.NDArray, + momentum_map: npt.NDArray, + Gpq_map: npt.NDArray, + chol: npt.NDArray, + chkfile_name: Union[str, None] = None, + maxiter: int = 150_000, + disp_freq: int = 98, + penalty_param: Union[float, None] = None, + batch_size: Union[int, None] = None, +) -> Tuple[npt.NDArray, float]: + """Least-squares fit of two-electron integral tensors with L-BFGS-B with + l2-regularization of lambda. This version batches over multiple k-points + which may be faster on GPUs. + + Args: + chi: THC leaf tensor. + zeta: THC central tensor. + momentum_map: momentum transfer mapping. map[iQ, ik_p] -> ik_q; + (kpts[ikp] - kpts[ikq])%G = kpts[iQ]. + Gpq_map: Maps momentum conserving tuples of kpoints to reciprocal + lattice vectors in THC central tensor. + chol: Cholesky factors definining 'exact' eris. + batch_size: Size of each batch of data. Should be in range + [1, num_kpts**2]. + penalty_param: Penalty param if computing regularized cost function. + chkfile_name: Filename to store intermediate state of optimization to. + maxiter: Max L-BFGS-B iteration. + disp_freq: L-BFGS-B disp_freq. + penalty_param: Paramter to penalize optimization by one-norm of + Hamiltonian. If None it is determined automatically through trying + to balance the two terms in the objective function. + batch_size: Number of k-points-pairs to batch over. Default num_kpts**2. + + Returns: + objective: THC objective function + """ + initial_guess = np.zeros(2 * (chi.size + get_zeta_size(zeta)), + dtype=np.float64) + pack_thc_factors(chi, zeta, initial_guess) + assert len(chi.shape) == 3 + assert len(zeta[0].shape) == 4 + num_kpts = chi.shape[0] + num_orb = chi.shape[1] + num_thc = chi.shape[-1] + assert zeta[0].shape[-1] == num_thc + assert zeta[0].shape[-2] == num_thc + if batch_size is None: + batch_size = num_kpts**2 + indx_arrays = prepare_batched_data_indx_arrays(momentum_map, Gpq_map) + data_amount = batch_size * ( + 4 * num_orb * num_thc + num_thc * num_thc # chi[p,m] + zeta[m,n] + ) + data_size_gb = (data_amount * 16) / (1024**3) + print(f"Batch size in GB: {data_size_gb}") + loss = thc_objective_regularized_batched( + jnp.array(initial_guess), + num_orb, + num_thc, + momentum_map, + Gpq_map, + jnp.array(chol), + indx_arrays, + batch_size, + penalty_param=0.0, + ) + start = time.time() + reg_loss = thc_objective_regularized_batched( + jnp.array(initial_guess), + num_orb, + num_thc, + momentum_map, + Gpq_map, + jnp.array(chol), + indx_arrays, + batch_size, + penalty_param=1.0, + ) + print("Time to evaluate loss function : {:.4f}".format(time.time() - start)) + print("loss {}".format(loss)) + # set penalty + lambda_z = (reg_loss - loss)**0.5 + if penalty_param is None: + # loss + lambda_z^2 - loss + penalty_param = loss / lambda_z + print("lambda_z {}".format(lambda_z)) + print("penalty_param {}".format(penalty_param)) + + # L-BFGS-B optimization + thc_grad = jax.grad(thc_objective_regularized_batched, argnums=[0]) + print("Initial Grad") + start = time.time() + print( + thc_grad( + jnp.array(initial_guess), + num_orb, + num_thc, + momentum_map, + Gpq_map, + jnp.array(chol), + indx_arrays, + batch_size, + penalty_param, + )) + print("# Time to evaluate gradient: {:.4f}".format(time.time() - start)) + res = minimize( + thc_objective_regularized_batched, + initial_guess, + args=( + num_orb, + num_thc, + momentum_map, + Gpq_map, + jnp.array(chol), + indx_arrays, + batch_size, + penalty_param, + ), + jac=thc_grad, + method="L-BFGS-B", + options={ + "disp": disp_freq > 0, + "iprint": disp_freq, + "maxiter": maxiter, + }, + ) + loss = thc_objective_regularized_batched( + jnp.array(res.x), + num_orb, + num_thc, + momentum_map, + Gpq_map, + jnp.array(chol), + indx_arrays, + batch_size, + penalty_param=0.0, + ) + + params = np.array(res.x) + if chkfile_name is not None: + num_G_per_Q = [len(np.unique(GQ)) for GQ in Gpq_map] + chi, zeta = unpack_thc_factors(params, num_thc, num_orb, num_kpts, + num_G_per_Q) + save_thc_factors(chkfile_name, chi, zeta, Gpq_map) + return np.array(params), loss + + +def adagrad_opt_kpthc_batched( + chi, + zeta, + momentum_map, + Gpq_map, + chol, + batch_size=None, + chkfile_name=None, + stepsize=0.01, + momentum=0.9, + maxiter=50_000, + gtol=1.0e-5, +) -> Tuple[npt.NDArray, float]: + """THC opt usually starts with BFGS and then is completed with Adagrad or + other first order solver. This function implements an Adagrad optimization + + Args: + chi: THC leaf tensor. + zeta: THC central tensor. + momentum_map: momentum transfer mapping. map[iQ, ik_p] -> ik_q; + (kpts[ikp] - kpts[ikq])%G = kpts[iQ]. + Gpq_map: Maps momentum conserving tuples of kpoints to reciprocal + lattice vectors in THC central tensor. + chol: Cholesky factors definining 'exact' eris. + batch_size: Size of each batch of data. Should be in range + [1, num_kpts**2]. + chkfile_name: Filename to store intermediate state of optimization to. + maxiter: Max L-BFGS-B iteration. + disp_freq: L-BFGS-B disp_freq. + penalty_param: Paramter to penalize optimization by one-norm of + Hamiltonian. If None it is determined automatically through trying + to balance the two terms in the objective function. + batch_size: Number of k-points-pairs to batch over. Default num_kpts**2. + + Returns: + objective: THC objective function + """ + assert len(chi.shape) == 3 + assert len(zeta[0].shape) == 4 + num_kpts = chi.shape[0] + num_orb = chi.shape[1] + num_thc = chi.shape[-1] + assert zeta[0].shape[-1] == num_thc + assert zeta[0].shape[-2] == num_thc + # set initial guess + initial_guess = np.zeros(2 * (chi.size + get_zeta_size(zeta)), + dtype=np.float64) + pack_thc_factors(chi, zeta, initial_guess) + opt_init, opt_update, get_params = adagrad(step_size=stepsize, + momentum=momentum) + opt_state = opt_init(initial_guess) + thc_grad = jax.grad(thc_objective_regularized_batched, argnums=[0]) + + if batch_size is None: + batch_size = num_kpts**2 + indx_arrays = prepare_batched_data_indx_arrays(momentum_map, Gpq_map) + + def update(i, opt_state): + params = get_params(opt_state) + gradient = thc_grad( + params, + num_orb, + num_thc, + momentum_map, + Gpq_map, + chol, + indx_arrays, + batch_size, + ) + grad_norm_l1 = np.linalg.norm(gradient[0], ord=1) + return opt_update(i, gradient[0], opt_state), grad_norm_l1 + + for t in range(maxiter): + opt_state, grad_l1 = update(t, opt_state) + params = get_params(opt_state) + if t % 500 == 0: + fval = thc_objective_regularized_batched( + params, + num_orb, + num_thc, + momentum_map, + Gpq_map, + chol, + indx_arrays, + batch_size, + ) + outline = "Objective val {: 5.15f}".format(fval) + outline += "\tGrad L1-norm {: 5.15f}".format(grad_l1) + print(outline) + if grad_l1 <= gtol: + # break out of loop + # which sends to save + break + else: + print("Maximum number of iterations reached") + # save results before returning + x = np.array(params) + loss = thc_objective_regularized_batched( + params, + num_orb, + num_thc, + momentum_map, + Gpq_map, + chol, + indx_arrays, + batch_size, + ) + if chkfile_name is not None: + num_G_per_Q = [len(np.unique(GQ)) for GQ in Gpq_map] + chi, zeta = unpack_thc_factors(x, num_thc, num_orb, num_kpts, + num_G_per_Q) + save_thc_factors(chkfile_name, chi, zeta, Gpq_map) + return params, loss + + +def make_contiguous_cholesky(cholesky: npt.NDArray) -> npt.NDArray: + """It is convenient for optimization to make the cholesky array contiguous. + This function truncates and auxiliary index that is greater than the minimum + number of auxiliary vectors. + + Args: + cholesky: Cholesky vectors + + Returns: + cholesk_contg: Contiguous array of cholesky vectors. + """ + num_kpts = len(cholesky) + num_mo = cholesky[0, 0].shape[-1] + if cholesky.dtype == object: + # Jax requires contiguous arrays so just truncate naux if it's not + # uniform hopefully shouldn't affect results dramatically as from + # experience the naux amount only varies by a few % per k-point + # Alternatively todo: padd with zeros + min_naux = min([cholesky[k1, k1].shape[0] for k1 in range(num_kpts)]) + cholesky_contiguous = np.zeros( + ( + num_kpts, + num_kpts, + min_naux, + num_mo, + num_mo, + ), + dtype=np.complex128, + ) + for ik1 in range(num_kpts): + for ik2 in range(num_kpts): + cholesky_contiguous[ik1, ik2] = cholesky[ik1, ik2][:min_naux] + else: + cholesky_contiguous = cholesky + + return cholesky_contiguous + + +def compute_isdf_loss(chi, zeta, momentum_map, Gpq_map, chol): + """Helper function to compute ISDF loss. + + Args: + chi: THC leaf tensor. + zeta: THC central tensor. + momentum_map: momentum transfer mapping. map[iQ, ik_p] -> ik_q; + (kpts[ikp] - kpts[ikq])%G = kpts[iQ]. + Gpq_map: Maps momentum conserving tuples of kpoints to reciprocal + lattice vectors in THC central tensor. + chol: Cholesky factors definining 'exact' eris. + + Returns: + loss: ISDF loss in ERIs. + """ + initial_guess = np.zeros(2 * (chi.size + get_zeta_size(zeta)), + dtype=np.float64) + pack_thc_factors(chi, zeta, initial_guess) + assert len(chi.shape) == 3 + assert len(zeta[0].shape) == 4 + num_orb = chi.shape[1] + num_thc = chi.shape[-1] + loss = thc_objective_regularized( + jnp.array(initial_guess), + num_orb, + num_thc, + momentum_map, + Gpq_map, + jnp.array(chol), + penalty_param=0.0, + ) + return loss + + +def kpoint_thc_via_isdf( + kmf: scf.RHF, + cholesky: npt.NDArray, + num_thc: int, + perform_bfgs_opt: bool = True, + perform_adagrad_opt: bool = True, + bfgs_maxiter: int = 3000, + adagrad_maxiter: int = 3000, + checkpoint_basename: str = "thc", + save_checkpoints: bool = False, + use_batched_algos: bool = True, + penalty_param: Union[None, float] = None, + batch_size: Union[None, bool] = None, + max_kmeans_iteration: int = 500, + verbose: bool = False, + initial_guess: Union[None, KPointTHC] = None, + isdf_density_guess: bool = False, +) -> Tuple[KPointTHC, dict]: + """ + Solve k-point THC using ISDF as an initial guess. + + Arguments: + kmf: instance of pyscf.pbc.KRHF object. Must be using FFTDF density + fitting for integrals. + cholesky: 3-index cholesky integrals needed for BFGS optimization. + num_thc: THC dimensions (int), usually nthc = c_thc * n, where n is the + number of spatial orbitals in the unit cell and c_thc is some + poisiitve integer (typically <= 15). + perform_bfgs_opt: Perform subsequent BFGS optimization of THC + factors? + perform_adagrad_opt: Perform subsequent adagrad optimization of THC + factors? This is performed after BFGD if perform_bfgs_opt is True. + bfgs_maxiter: Maximum iteration for adagrad optimization. + adagrad_maxiter: Maximum iteration for adagrad optimization. + save_checkpoints: Whether to save checkpoint files or not (which will + store THC factors. For each step we store checkpoints as + {checkpoint_basename}_{thc_method}.h5, where thc_method is one of + the strings (isdf, bfgs or adagrad). + checkpoint_basename: Base name for checkpoint files. string, + default "thc". + use_batched_algos: Whether to use batched algorithms which may be + faster but have higher memory cost. Bool. Default True. + penalty_param: Penalty parameter for l2 regularization. Float. + Default None. + max_kmeans_iteration: Maximum number of iterations for KMeansCVT + step. int. Default 500. + verbose: Print information? Bool, default False. + + Returns: + kthc: k-point THC factors + info: dictionary of losses for each stage of factorization. + """ + # Perform initial ISDF calculation of THC factors + info = {} + start = time.time() + if initial_guess is not None: + kpt_thc = initial_guess + else: + kpt_thc = solve_kmeans_kpisdf( + kmf, + num_thc, + single_translation=False, + verbose=verbose, + max_kmeans_iteration=max_kmeans_iteration, + use_density_guess=isdf_density_guess, + ) + if verbose: + print("Time for generating initial guess {:.4f}".format(time.time() - + start)) + num_mo = kmf.mo_coeff[0].shape[-1] + num_kpts = len(kmf.kpts) + chi, zeta, G_mapping = kpt_thc.chi, kpt_thc.zeta, kpt_thc.G_mapping + if save_checkpoints: + chkfile_name = f"{checkpoint_basename}_isdf.h5" + save_thc_factors(chkfile_name, chi, zeta, G_mapping, kpt_thc.xi) + momentum_map = build_momentum_transfer_mapping(kmf.cell, kmf.kpts) + if cholesky is not None: + cholesky_contiguous = make_contiguous_cholesky(cholesky) + info["loss_isdf"] = compute_isdf_loss(chi, zeta, momentum_map, + G_mapping, cholesky_contiguous) + start = time.time() + if perform_bfgs_opt: + if save_checkpoints: + chkfile_name = f"{checkpoint_basename}_bfgs.h5" + else: + chkfile_name = None + if use_batched_algos: + opt_params, loss_bfgs = lbfgsb_opt_kpthc_l2reg_batched( + chi, + zeta, + momentum_map, + G_mapping, + cholesky_contiguous, + chkfile_name=chkfile_name, + maxiter=bfgs_maxiter, + penalty_param=penalty_param, + batch_size=batch_size, + disp_freq=(98 if verbose else -1), + ) + info["loss_bfgs"] = loss_bfgs + else: + opt_params, loss_bfgs = lbfgsb_opt_kpthc_l2reg( + chi, + zeta, + momentum_map, + G_mapping, + cholesky_contiguous, + chkfile_name=chkfile_name, + maxiter=bfgs_maxiter, + penalty_param=penalty_param, + disp_freq=(98 if verbose else -1), + ) + info["loss_bfgs"] = loss_bfgs + num_G_per_Q = [len(np.unique(GQ)) for GQ in G_mapping] + chi, zeta = unpack_thc_factors(opt_params, num_thc, num_mo, num_kpts, + num_G_per_Q) + if verbose: + print("Time for BFGS {:.4f}".format(time.time() - start)) + start = time.time() + if perform_adagrad_opt: + if save_checkpoints: + chkfile_name = f"{checkpoint_basename}_adagrad.h5" + else: + chkfile_name = None + if use_batched_algos: + opt_params, loss_ada = adagrad_opt_kpthc_batched( + chi, + zeta, + momentum_map, + G_mapping, + cholesky_contiguous, + chkfile_name=chkfile_name, + maxiter=adagrad_maxiter, + batch_size=batch_size, + ) + info["loss_adagrad"] = loss_ada + num_G_per_Q = [len(np.unique(GQ)) for GQ in G_mapping] + chi, zeta = unpack_thc_factors(opt_params, num_thc, num_mo, num_kpts, + num_G_per_Q) + if verbose: + print("Time for ADAGRAD {:.4f}".format(time.time() - start)) + result = KPointTHC(chi=chi, zeta=zeta, G_mapping=G_mapping, xi=kpt_thc.xi) + return result, info diff --git a/src/openfermion/resource_estimates/pbc/thc/utils/thc_jax_test.py b/src/openfermion/resource_estimates/pbc/thc/utils/thc_jax_test.py new file mode 100644 index 000000000..2055710c9 --- /dev/null +++ b/src/openfermion/resource_estimates/pbc/thc/utils/thc_jax_test.py @@ -0,0 +1,352 @@ +# coverage: ignore +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import jax +import jax.numpy as jnp +import numpy as np +import pytest +from pyscf.pbc import gto, mp, scf + +from openfermion.resource_estimates.thc.utils.thc_factorization import ( + lbfgsb_opt_thc_l2reg,) +from openfermion.resource_estimates.thc.utils.thc_factorization import ( + thc_objective_regularized as thc_obj_mol,) + +from openfermion.resource_estimates.pbc.thc.utils.isdf import ( + solve_kmeans_kpisdf,) +from openfermion.resource_estimates.pbc.utils.hamiltonian_utils import ( + cholesky_from_df_ints,) +from openfermion.resource_estimates.pbc.thc.utils.thc_jax import ( + adagrad_opt_kpthc_batched, + get_zeta_size, + kpoint_thc_via_isdf, + lbfgsb_opt_kpthc_l2reg, + lbfgsb_opt_kpthc_l2reg_batched, + make_contiguous_cholesky, + pack_thc_factors, + prepare_batched_data_indx_arrays, + thc_objective_regularized, + thc_objective_regularized_batched, + unpack_thc_factors, +) +from openfermion.resource_estimates.pbc.utils.hamiltonian_utils import ( + build_momentum_transfer_mapping,) + + +def test_kpoint_thc_reg_gamma(): + cell = gto.Cell() + cell.atom = """ + C 0.000000000000 0.000000000000 0.000000000000 + C 1.685068664391 1.685068664391 1.685068664391 + """ + cell.basis = "gth-szv" + cell.pseudo = "gth-hf-rev" + cell.a = """ + 0.000000000, 3.370137329, 3.370137329 + 3.370137329, 0.000000000, 3.370137329 + 3.370137329, 3.370137329, 0.000000000""" + cell.unit = "B" + cell.mesh = [11] * 3 + cell.verbose = 0 + cell.build(parse_arg=False) + + kmesh = [1, 1, 1] + kpts = cell.make_kpts(kmesh) + num_kpts = len(kpts) + mf = scf.KRHF(cell, kpts) + mf.kernel() + num_mo = mf.mo_coeff[0].shape[-1] + num_interp_points = 10 * mf.mo_coeff[0].shape[-1] + kpt_thc = solve_kmeans_kpisdf(mf, + num_interp_points, + single_translation=False) + chi, zeta, G_mapping = kpt_thc.chi, kpt_thc.zeta, kpt_thc.G_mapping + momentum_map = build_momentum_transfer_mapping(cell, kpts) + buffer = np.zeros(2 * (chi.size + get_zeta_size(zeta)), dtype=np.float64) + pack_thc_factors(chi, zeta, buffer) + num_G_per_Q = [z.shape[0] for z in zeta] + chi_unpacked, zeta_unpacked = unpack_thc_factors(buffer, num_interp_points, + num_mo, num_kpts, + num_G_per_Q) + assert np.allclose(chi_unpacked, chi) + for iq in range(num_kpts): + assert np.allclose(zeta[iq], zeta_unpacked[iq]) + # force contiguous + rsmf = scf.KRHF(mf.cell, mf.kpts).rs_density_fit() + rsmf.verbose = 0 + rsmf.mo_occ = mf.mo_occ + rsmf.mo_coeff = mf.mo_coeff + rsmf.mo_energy = mf.mo_energy + rsmf.with_df.mesh = mf.cell.mesh + mymp = mp.KMP2(rsmf) + Luv = cholesky_from_df_ints(mymp) + Luv_cont = make_contiguous_cholesky(Luv) + eri = np.einsum("npq,nrs->pqrs", Luv_cont[0, 0], Luv_cont[0, 0]).real + buffer = np.zeros((chi.size + get_zeta_size(zeta)), dtype=np.float64) + # transposed in openfermion + buffer[:chi.size] = chi.T.real.ravel() + buffer[chi.size:] = zeta[iq].real.ravel() + np.random.seed(7) + opt_param = lbfgsb_opt_thc_l2reg( + eri, + num_interp_points, + maxiter=10, + initial_guess=buffer, + penalty_param=None, + ) + chi_unpacked_mol = (opt_param[:chi.size].reshape( + (num_interp_points, num_mo)).T) + zeta_unpacked_mol = opt_param[chi.size:].reshape(zeta[0].shape) + opt_param, _ = lbfgsb_opt_kpthc_l2reg( + chi, + zeta, + momentum_map, + G_mapping, + jnp.array(Luv_cont), + maxiter=10, + penalty_param=None, + disp_freq=-1, + ) + chi_unpacked, zeta_unpacked = unpack_thc_factors(opt_param, + num_interp_points, num_mo, + num_kpts, num_G_per_Q) + assert np.allclose(chi_unpacked[0], chi_unpacked_mol) + assert np.allclose(zeta_unpacked[0], zeta_unpacked_mol) + mol_obj = thc_obj_mol(buffer, num_mo, num_interp_points, eri, 1e-3) + buffer = np.zeros(2 * (chi.size + get_zeta_size(zeta)), dtype=np.float64) + pack_thc_factors(chi, zeta, buffer) + gam_obj = thc_objective_regularized( + buffer, + num_mo, + num_interp_points, + momentum_map, + G_mapping, + Luv_cont, + 1e-3, + ) + assert mol_obj - gam_obj < 1e-12 + + +def test_kpoint_thc_reg_batched(): + cell = gto.Cell() + cell.atom = """ + C 0.000000000000 0.000000000000 0.000000000000 + C 1.685068664391 1.685068664391 1.685068664391 + """ + cell.basis = "gth-szv" + cell.pseudo = "gth-hf-rev" + cell.a = """ + 0.000000000, 3.370137329, 3.370137329 + 3.370137329, 0.000000000, 3.370137329 + 3.370137329, 3.370137329, 0.000000000""" + cell.unit = "B" + cell.mesh = [11] * 3 + cell.verbose = 0 + cell.build(parse_arg=False) + + kmesh = [1, 1, 3] + kpts = cell.make_kpts(kmesh) + num_kpts = len(kpts) + mf = scf.KRHF(cell, kpts) + mf.kernel() + momentum_map = build_momentum_transfer_mapping(cell, kpts) + num_interp_points = 10 * cell.nao + kpt_thc = solve_kmeans_kpisdf(mf, + num_interp_points, + single_translation=False, + verbose=False) + chi, zeta, G_mapping = kpt_thc.chi, kpt_thc.zeta, kpt_thc.G_mapping + rsmf = scf.KRHF(mf.cell, mf.kpts).rs_density_fit() + rsmf.mo_occ = mf.mo_occ + rsmf.mo_coeff = mf.mo_coeff + rsmf.mo_energy = mf.mo_energy + rsmf.with_df.mesh = mf.cell.mesh + mymp = mp.KMP2(rsmf) + Luv = cholesky_from_df_ints(mymp) + Luv_cont = make_contiguous_cholesky(Luv) + buffer = np.zeros(2 * (chi.size + get_zeta_size(zeta)), dtype=np.float64) + # Pack THC factors into flat array + pack_thc_factors(chi, zeta, buffer) + num_G_per_Q = [z.shape[0] for z in zeta] + num_mo = mf.mo_coeff[0].shape[-1] + chi_unpacked, zeta_unpacked = unpack_thc_factors(buffer, num_interp_points, + num_mo, num_kpts, + num_G_per_Q) + # Test packing/unpacking operation + assert np.allclose(chi_unpacked, chi) + for iq in range(num_kpts): + assert np.allclose(zeta[iq], zeta_unpacked[iq]) + # Test objective is the same batched/non-batched + penalty = 1e-3 + obj_ref = thc_objective_regularized( + buffer, + num_mo, + num_interp_points, + momentum_map, + G_mapping, + Luv_cont, + penalty, + ) + # # Test gradient is the same + indx_arrays = prepare_batched_data_indx_arrays(momentum_map, G_mapping) + batch_size = num_kpts**2 + obj_batched = thc_objective_regularized_batched( + buffer, + num_mo, + num_interp_points, + momentum_map, + G_mapping, + Luv_cont, + indx_arrays, + batch_size, + penalty_param=penalty, + ) + assert abs(obj_ref - obj_batched) < 1e-12 + grad_ref_fun = jax.grad(thc_objective_regularized) + grad_ref = grad_ref_fun( + buffer, + num_mo, + num_interp_points, + momentum_map, + G_mapping, + Luv_cont, + penalty, + ) + # Test gradient is the same + grad_batched_fun = jax.grad(thc_objective_regularized_batched) + grad_batched = grad_batched_fun( + buffer, + num_mo, + num_interp_points, + momentum_map, + G_mapping, + Luv_cont, + indx_arrays, + batch_size, + penalty, + ) + assert np.allclose(grad_batched, grad_ref) + opt_param, _ = lbfgsb_opt_kpthc_l2reg( + chi, + zeta, + momentum_map, + G_mapping, + jnp.array(Luv_cont), + maxiter=2, + penalty_param=1e-3, + disp_freq=-1, + ) + opt_param_batched, _ = lbfgsb_opt_kpthc_l2reg_batched( + chi, + zeta, + momentum_map, + G_mapping, + jnp.array(Luv_cont), + maxiter=2, + penalty_param=1e-3, + disp_freq=-1, + ) + assert np.allclose(opt_param, opt_param_batched) + batch_size = 7 + opt_param_batched_diff_batch, _ = lbfgsb_opt_kpthc_l2reg_batched( + chi, + zeta, + momentum_map, + G_mapping, + jnp.array(Luv_cont), + batch_size=batch_size, + maxiter=2, + penalty_param=1e-3, + disp_freq=-1, + ) + assert np.allclose(opt_param_batched, opt_param_batched_diff_batch) + ada_param, _ = adagrad_opt_kpthc_batched( + chi, + zeta, + momentum_map, + G_mapping, + jnp.array(Luv_cont), + maxiter=2, + batch_size=1, + ) + assert np.allclose(opt_param, opt_param_batched) + batch_size = 7 + ada_param_diff_batch, _ = adagrad_opt_kpthc_batched( + chi, + zeta, + momentum_map, + G_mapping, + jnp.array(Luv_cont), + batch_size=batch_size, + maxiter=2, + ) + assert np.allclose(ada_param, ada_param_diff_batch) + + +def test_kpoint_thc_helper(): + cell = gto.Cell() + cell.atom = """ + C 0.000000000000 0.000000000000 0.000000000000 + C 1.685068664391 1.685068664391 1.685068664391 + """ + cell.basis = "gth-szv" + cell.pseudo = "gth-hf-rev" + cell.a = """ + 0.000000000, 3.370137329, 3.370137329 + 3.370137329, 0.000000000, 3.370137329 + 3.370137329, 3.370137329, 0.000000000""" + cell.unit = "B" + cell.mesh = [11] * 3 + cell.verbose = 0 + cell.build(parse_arg=False) + + kmesh = [1, 1, 2] + kpts = cell.make_kpts(kmesh) + mf = scf.KRHF(cell, kpts) + mf.kernel() + rsmf = scf.KRHF(mf.cell, mf.kpts).rs_density_fit() + rsmf.verbose = 0 + rsmf.mo_occ = mf.mo_occ + rsmf.mo_coeff = mf.mo_coeff + rsmf.mo_energy = mf.mo_energy + rsmf.with_df.mesh = mf.cell.mesh + mymp = mp.KMP2(rsmf) + Luv = cholesky_from_df_ints(mymp) + cthc = 5 + num_mo = mf.mo_coeff[0].shape[-1] + # Just testing function runs + kpt_thc, _ = kpoint_thc_via_isdf( + mf, + Luv, + cthc * num_mo, + perform_adagrad_opt=False, + perform_bfgs_opt=False, + ) + kpt_thc_bfgs, _ = kpoint_thc_via_isdf( + mf, + Luv, + cthc * num_mo, + perform_adagrad_opt=False, + perform_bfgs_opt=True, + bfgs_maxiter=10, + initial_guess=kpt_thc, + ) + kpoint_thc_via_isdf( + mf, + Luv, + cthc * num_mo, + perform_adagrad_opt=True, + perform_bfgs_opt=True, + bfgs_maxiter=10, + adagrad_maxiter=10, + initial_guess=kpt_thc_bfgs, + ) From 1fbd233870684d0d82d9dcd7eec0051a3f2d63fb Mon Sep 17 00:00:00 2001 From: Fionn Malone Date: Tue, 20 Jun 2023 19:37:23 +0000 Subject: [PATCH 02/19] Add k-thc notebook. --- .../pbc/notebooks/isdf.ipynb | 508 ++++++++++++++++++ 1 file changed, 508 insertions(+) create mode 100644 src/openfermion/resource_estimates/pbc/notebooks/isdf.ipynb diff --git a/src/openfermion/resource_estimates/pbc/notebooks/isdf.ipynb b/src/openfermion/resource_estimates/pbc/notebooks/isdf.ipynb new file mode 100644 index 000000000..7c194c91e --- /dev/null +++ b/src/openfermion/resource_estimates/pbc/notebooks/isdf.ipynb @@ -0,0 +1,508 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Interpolative Seperable Density Fitting (ISDF) " + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The ISDF implementation currently provides a THC-like factorization of the two electron integrals which should converge to the FFTDF representation of the ERIs in the limit of large THC rank. This differs from the assumption of using RSGDF throughout the rest of the resource estimation scripts. However, we typically are only interested in ISDF as an initial guess for the THC factors which are then subsequently reoptimized to regularize $\\lambda$. The assumption here is that FFTDF / ISDF is a good enough approximation to the RSGDF ERIs and thus serves as a good initial guess.\n", + "\n", + "Let's start by comparing the ISDF-MP2 energy as a function of the THC rank parameter. Recall that $M = c_\\mathrm{THC} N/2$, where $c_\\mathrm{THC}$ is the THC rank parameter and $N$ is the number of spin orbitals. $M$ is what we call num_thc in the code. \n", + "\n", + "It's important to recall what we are doing in the ISDF algorithm, that is we solve\n", + "\n", + "\\begin{equation}\n", + "\n", + "u_{p\\mathrm{k}}^*(\\mathbf{r}_i) u_{q\\mathbf{k}'}(\\mathbf{r}_i) = \\sum_\\mu^M \\xi_\\mu(\\mathbf{r}_i) u_{p\\mathrm{k}}^*(\\mathbf{r}_\\mu) u_{q\\mathbf{k}'}(\\mathbf{r}_\\mu)\n", + "\n", + "\\end{equation}\n", + "\n", + "for $\\xi_\\mu(\\mathbf{r}_i)$ given a set of interpolating points $(\\{r_\\mu\\})$ which are selected from the original real space $(\\{\\mathbf{r}_i\\})$ (FFT) grid of size $N_g$ using the KMeans-CVT algorithm.\n", + "\n", + "For the purposes of this notebook it is helpful to use a value of $N_g$ which is smaller than that required to fully converge the FFTDF error. We will investigate this more at the end of the tutorial. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "import numpy as np\n", + "from ase.build import bulk\n", + "\n", + "from pyscf.pbc import gto, scf\n", + "from pyscf.pbc.tools import pyscf_ase\n", + "from pyscf.pbc.mp import KMP2 \n", + "\n", + "\n", + "ase_atom = bulk(\"C\", \"diamond\", a=3.5)\n", + "cell = gto.Cell()\n", + "cell.atom = pyscf_ase.ase_atoms_to_pyscf(ase_atom)\n", + "cell.a = ase_atom.cell[:].copy()\n", + "cell.basis = \"gth-szv\"\n", + "cell.pseudo = \"gth-hf-rev\"\n", + "# Use a smaller value of Ng that would otherwise be suggested (~ 26^3) for\n", + "# expediency + to allow exact ISDF factorization for comparison.\n", + "cell.mesh = [11]*3\n", + "cell.verbose = 0\n", + "cell.build()\n", + "\n", + "kmesh = [1, 1, 3]\n", + "kpts = cell.make_kpts(kmesh)\n", + "num_kpts = len(kpts)\n", + "mf = scf.KRHF(cell, kpts)\n", + "mf.kernel()\n", + "print(\"SCF energy: \", mf.e_tot)\n", + "\n", + "# converged SCF energy with appropriate Ng = -10.388904514046914, mesh = 28^3\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now let's find the ISDF THC factors using the KMeans-CVT algorithm to find the interpolating points. It's easiest to use the helper function `solve_kmeans_kpisdf` which will perform the necessary steps. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import openfermion.resource_estimates.pbc.thc as kthc\n", + "# Let's use the whole real space grid for some correctness checks first.\n", + "num_thc = np.prod(cell.mesh)\n", + "kpt_thc = kthc.solve_kmeans_kpisdf(mf, num_thc, verbose=False)\n", + "print(kpt_thc.__dict__.keys())" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We see that the kpt_thc class has 4 attributes, `chi`, `xi`, `zeta` and `G_mapping`. `chi` corresponds to the cell periodic part of the Bloch orbital (i.e. $u_{p\\mathbf{k}}(\\mathbf{r}_\\mu))$. `xi` corresponds to $\\xi_{\\mu}(\\mathbf{r})$ in Eq. (1) above. To understand `zeta` and `G_mapping` it is helpful to recall we want to build\n", + "\n", + "$$\n", + "\n", + "(p\\mathbf{k}pq\\mathbf{k}-\\mathbf{Q}|r\\mathbf{k}'-\\mathbf{Q} s\\mathbf{k}') = \\sum_{\\mu\\nu} u^*_{p\\mathbf{k}}(\\mathbf{r}_\\mu))u_{p\\mathbf{k}-\\mathbf{{Q}}}(\\mathbf{r}_\\mu) \\zeta_{\\mu\\nu}^{\\mathbf{Q}\\Delta \\mathbf{G}_{\\mathbf{Q}\\mathbf{k}-\\mathbf{Q}}\\Delta\\mathbf{G}_{\\mathbf{Q}\\mathbf{k}'-\\mathbf{Q}}} u^*_{p\\mathbf{k}'}(\\mathbf{r}_\\nu)u_{p\\mathbf{k}-\\mathbf{Q}}(\\mathbf{r}_\\nu)\n", + "\n", + "$$\n", + "\n", + "So `zeta` corresponds to $\\zeta_{\\mu\\nu}^{\\mathbf{Q}\\Delta \\mathbf{G}_{\\mathbf{Q}\\mathbf{k}-\\mathbf{Q}}\\Delta\\mathbf{G}_{\\mathbf{Q}\\mathbf{k}'-\\mathbf{Q}}}$ above, and `G_mapping` is a 2D array yielding the appropriate $\\Delta G$ index given an index for $\\mathbf{Q}$ and $\\mathbf{k}$.\n", + "\n", + "Let's look at an example to see that everything is correct." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from openfermion.resource_estimates.pbc.utils import build_momentum_transfer_mapping \n", + "\n", + "momentum_map = build_momentum_transfer_mapping(cell, kpts)\n", + "num_spatial_orbs = mf.mo_coeff[0].shape[-1]\n", + "\n", + "# Pick a particular momentum transfer\n", + "Q_indx = 1\n", + "k_indx = 1\n", + "k_prime_indx = 0\n", + "k_minus_Q_indx = momentum_map[Q_indx, k_indx]\n", + "k_prime_minus_Q_indx = momentum_map[Q_indx, k_prime_indx]\n", + "\n", + "eri_kindices = [k_indx, k_minus_Q_indx, k_prime_minus_Q_indx, k_prime_indx]\n", + "eri_mos = [mf.mo_coeff[kindx] for kindx in eri_kindices]\n", + "eri_kpts = [mf.kpts[kindx] for kindx in eri_kindices]\n", + "\n", + "eri_exact = mf.with_df.ao2mo(eri_mos, eri_kpts, compact=False).reshape((num_spatial_orbs,)*4)\n", + "\n", + "\n", + "kthc_eri_helper = kthc.KPTHCHelperDoubleTranslation(chi=kpt_thc.chi, zeta=kpt_thc.zeta, kmf=mf)\n", + "\n", + "eri_thc = kthc_eri_helper.get_eri(eri_kindices)\n", + "# Can also do\n", + "# eri_exact = kthc_eri_helper.get_eri_exact(eri_kindices)\n", + "\n", + "assert np.allclose(eri_thc, eri_exact)\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now let's check convergence of the integral error, and corresponding MP2 error, with the THC dimension or equivalently the THC rank parameter." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from openfermion.resource_estimates.pbc.utils import compute_emp2_approx, build_cc_inst\n", + "cc_inst = build_cc_inst(mf)\n", + "eri_exact = cc_inst.ao2mo()\n", + "emp2_exact, _, _ = cc_inst.init_amps(eri_exact)\n", + "emp2_exact += mf.e_tot\n", + "delta_eri = []\n", + "delta_mp2 = []\n", + "thc_ranks = np.arange(2, 20, 2)\n", + "for cthc in thc_ranks:\n", + " num_thc = cthc * num_spatial_orbs\n", + " kpt_thc = kthc.solve_kmeans_kpisdf(mf, num_thc, verbose=False)\n", + " kthc_eri_helper = kthc.KPTHCHelperDoubleTranslation(chi=kpt_thc.chi, zeta=kpt_thc.zeta, kmf=mf)\n", + " eri_thc = kthc_eri_helper.get_eri(eri_kindices)\n", + " eri_exact = kthc_eri_helper.get_eri_exact(eri_kindices)\n", + " # Note pyscf omits a normalization factor of 1/Nk in their definition of ERIs\n", + " delta_eri.append(np.max(np.abs(eri_thc-eri_exact))/num_kpts)\n", + " emp2_approx = compute_emp2_approx(mf, kthc_eri_helper)\n", + " delta_mp2.append(emp2_exact - emp2_approx)\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's look at the convergence of the integral error" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n", + "\n", + "plt.plot(thc_ranks, delta_eri, marker=\"o\")\n", + "plt.yscale(\"log\")\n", + "plt.xlabel(r\"$c_{\\mathrm{THC}}$\")\n", + "plt.ylabel(r\"max$|\\Delta(pq|rs)|$\")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's see how this corresponds to the MP2 error." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plt.cla()\n", + "plt.plot(thc_ranks, np.abs(delta_mp2), marker=\"o\")\n", + "plt.yscale(\"log\")\n", + "plt.xlabel(r\"$c_{\\mathrm{THC}}$\")\n", + "plt.ylabel(r\"$|\\Delta E_{\\mathrm{MP2}}|$ (Ha)\")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We see that apart from some non-monotonic behaviour (which is expected due to the non-linear nature of the ISDF procedure), that a relatively large rank parameter is required to obtain say $< 0.1$ mHa error per cell. Note this could likely be reduced by carefully selecting the orbital sets we perform ISDF on as oo, ov, and vv blocks exhibit different low-rank behaviour, but for quantum algorithms this is not relevant. " + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Optional: Effect of Mesh Density\n", + "\n", + "You might be worried that we're cheating by only including 11^3 grid points, and that we're not saving much with the ranks we're choosing.\n", + "\n", + "Let us first see the fraction of points these ranks correspond to." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plt.plot(thc_ranks, 100*np.array(thc_ranks)*num_spatial_orbs/(11**3), marker=\"o\")\n", + "plt.xlabel(r\"$c_{\\mathrm{THC}}$\")\n", + "plt.ylabel(\"Percentage of real space points selected\")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now let us crank up the FFTDF accuracy and see if the results change significantly. This cell will take around 10 minutes to run." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "results = {11: [], 15: [], 19: [], 21: [], 28: []}\n", + "#results = {11: [], 15: []}\n", + "for mesh in list(results.keys()):\n", + " ase_atom = bulk(\"C\", \"diamond\", a=3.5)\n", + " cell = gto.Cell()\n", + " cell.atom = pyscf_ase.ase_atoms_to_pyscf(ase_atom)\n", + " cell.a = ase_atom.cell[:].copy()\n", + " cell.basis = \"gth-szv\"\n", + " cell.pseudo = \"gth-hf-rev\"\n", + " # Use a smaller value of Ng that would otherwise be suggested (~ 26^3) for\n", + " # expediency + to allow exact ISDF factorization for comparison.\n", + " cell.mesh = [mesh]*3\n", + " cell.verbose = 0\n", + " cell.build()\n", + "\n", + " kmesh = [1, 1, 3]\n", + " kpts = cell.make_kpts(kmesh)\n", + " num_kpts = len(kpts)\n", + " mf = scf.KRHF(cell, kpts)\n", + " mf.kernel()\n", + " print(\"SCF energy: \", mf.e_tot)\n", + "\n", + " from pyscf.pbc.mp import KMP2 \n", + " emp2_exact = KMP2(mf).kernel()[0] + mf.e_tot\n", + " print(\"Ng = {}^3, MP2 Correlation energy: {}\".format(mesh, emp2_exact-mf.e_tot))\n", + " thc_ranks = np.arange(2, 20, 2)\n", + " for cthc in thc_ranks:\n", + " print(f\"Running mesh = {mesh}, cthc = {cthc}\")\n", + " num_thc = cthc * num_spatial_orbs\n", + " kpt_thc = kthc.solve_kmeans_kpisdf(mf, num_thc, verbose=False)\n", + " kthc_eri_helper = kthc.KPTHCHelperDoubleTranslation(chi=kpt_thc.chi, zeta=kpt_thc.zeta, kmf=mf)\n", + " emp2_approx = compute_emp2_approx(mf, kthc_eri_helper)\n", + " # Note pyscf omits a normalization factor of 1/Nk in their definition of ERIs\n", + " results[mesh].append(emp2_exact - emp2_approx)\n", + "\n", + " plt.plot(thc_ranks, np.abs(results[mesh]), marker=\"o\", label=f\"$N_g = {mesh}^3$\")\n", + "plt.yscale(\"log\")\n", + "plt.xlabel(r\"$c_{\\mathrm{THC}}$\")\n", + "plt.legend()\n", + "plt.ylabel(r\"$|\\Delta E_{\\mathrm{MP2}}|$ (Ha)\")\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "### Optional: Effect of Basis Set \n", + "\n", + "Another concern is that the basis set size is tiny so maybe things get worse as the basis set increases. Let's look into it by increasing the basis set size but still use a fairly coarse FFT grid, as we've seen it's not super important. This will also take several minutes to run.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "basis_results = {\"gth-szv\": [], \"gth-dzvp\": [], \"gth-tzvp\": []}\n", + "for basis in list(basis_results.keys()):\n", + " ase_atom = bulk(\"C\", \"diamond\", a=3.5)\n", + " cell = gto.Cell()\n", + " cell.atom = pyscf_ase.ase_atoms_to_pyscf(ase_atom)\n", + " cell.a = ase_atom.cell[:].copy()\n", + " cell.basis = basis \n", + " cell.exp_to_discard = 0.1\n", + " cell.pseudo = \"gth-hf-rev\"\n", + " # Use a smaller value of Ng that would otherwise be suggested (~ 26^3) for\n", + " # expediency + to allow exact ISDF factorization for comparison.\n", + " cell.mesh = [15]*3\n", + " cell.verbose = 0\n", + " cell.build()\n", + "\n", + " kmesh = [1, 1, 3]\n", + " kpts = cell.make_kpts(kmesh)\n", + " num_kpts = len(kpts)\n", + " mf = scf.KRHF(cell, kpts)\n", + " mf.kernel()\n", + " print(\"SCF energy: \", mf.e_tot)\n", + "\n", + " num_spatial_orbs = mf.mo_coeff[0].shape[-1]\n", + " from pyscf.pbc.mp import KMP2 \n", + " emp2_exact = KMP2(mf).kernel()[0] + mf.e_tot\n", + " print(\"basis = {}, MP2 Correlation energy: {}\".format(basis, emp2_exact-mf.e_tot))\n", + " thc_ranks = np.arange(2, 20, 2)\n", + " for cthc in thc_ranks:\n", + " print(f\"Running basis = {basis}, cthc = {cthc}\")\n", + " num_thc = cthc * num_spatial_orbs\n", + " kpt_thc = kthc.solve_kmeans_kpisdf(mf, num_thc, verbose=False)\n", + " kthc_eri_helper = kthc.KPTHCHelperDoubleTranslation(chi=kpt_thc.chi, zeta=kpt_thc.zeta, kmf=mf)\n", + " emp2_approx = compute_emp2_approx(mf, kthc_eri_helper)\n", + " # Note pyscf omits a normalization factor of 1/Nk in their definition of ERIs\n", + " basis_results[basis].append(emp2_exact - emp2_approx)\n", + "\n", + " plt.plot(thc_ranks*num_spatial_orbs, np.abs(basis_results[basis]), marker=\"o\", label=f\"basis = {basis}\")\n", + "plt.yscale(\"log\")\n", + "plt.xlabel(r\"$M$\")\n", + "plt.legend()\n", + "plt.ylabel(r\"$|\\Delta E_{\\mathrm{MP2}}|$ (Ha)\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "for basis in list(basis_results.keys()):\n", + " plt.plot(thc_ranks, np.abs(basis_results[basis]), marker=\"o\", label=f\"basis = {basis}\")\n", + "plt.yscale(\"log\")\n", + "plt.xlabel(r\"$c_{\\mathrm{THC}}$\")\n", + "plt.legend()\n", + "plt.ylabel(r\"$|\\Delta E_{\\mathrm{MP2}}|$ (Ha)\")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Effect on $\\lambda$ \n", + "\n", + "Now let us investigate the $\\lambda$ dependence of our ISDF-THC factorization. We will revert back to the minimal example from earlier." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "ase_atom = bulk(\"C\", \"diamond\", a=3.5)\n", + "cell = gto.Cell()\n", + "cell.atom = pyscf_ase.ase_atoms_to_pyscf(ase_atom)\n", + "cell.a = ase_atom.cell[:].copy()\n", + "cell.basis = \"gth-dzv\"\n", + "cell.pseudo = \"gth-hf-rev\"\n", + "# Use a smaller value of Ng that would otherwise be suggested (~ 26^3) for\n", + "# expediency + to allow exact ISDF factorization for comparison.\n", + "cell.mesh = [15]*3\n", + "cell.verbose = 0\n", + "cell.build()\n", + "\n", + "kmesh = [1, 1, 3]\n", + "kpts = cell.make_kpts(kmesh)\n", + "num_kpts = len(kpts)\n", + "mf = scf.KRHF(cell, kpts)\n", + "mf.kernel()\n", + "print(\"SCF energy: \", mf.e_tot)\n", + "\n", + "# converged SCF energy with appropriate Ng = -10.388904514046914, mesh = 28^3\n", + "\n", + "hcore = np.asarray([C.conj().T @ hc @ C for C, hc in zip(mf.mo_coeff, mf.get_hcore())])\n", + "num_spatial_orbs = hcore.shape[-1]\n", + "thc_ranks = np.arange(2, 20, 2)\n", + "cc_inst = build_cc_inst(mf)\n", + "eri_exact = cc_inst.ao2mo()\n", + "emp2_exact, _, _ = cc_inst.init_amps(eri_exact)\n", + "np.random.seed(7)\n", + "print(f\"FFTDF MP2 Correlation energy: {emp2_exact}\")\n", + "for cthc in thc_ranks:\n", + " num_thc = cthc * num_spatial_orbs\n", + " kpt_thc = kthc.solve_kmeans_kpisdf(mf, num_thc, verbose=False)\n", + " kthc_eri_helper = kthc.KPTHCHelperDoubleTranslation(chi=kpt_thc.chi, zeta=kpt_thc.zeta, kmf=mf)\n", + " # Note pyscf omits a normalization factor of 1/Nk in their definition of ERIs\n", + " thc_lambda = kthc.compute_lambda(hcore, kthc_eri_helper)\n", + " emp2_approx = compute_emp2_approx(mf, kthc_eri_helper) - mf.e_tot\n", + " emp2_error = abs(emp2_approx - emp2_exact)\n", + " print(f\"cthc = {cthc}, MP2 error: {emp2_error:4.3e}, lambda = {thc_lambda.lambda_total:4.3e}\")\n", + " if emp2_error < 1e-4:\n", + " print(f\"--> MP2 error < 0.1: {emp2_error:4.3e}, lambda = {thc_lambda.lambda_total:4.3e}\")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we can try to improve $\\lambda$ by reoptimizing the THC factors using our ISDF factors as an initial guess. Practically this should mean we can use a smaller THC rank parameter for comparable MP2 accuracy. Note reoptimizing the THC factors is quite expensive. This cell may take 20 minutes to run. You should see that for a $c_\\mathrm{THC}=6$ the MP2 error is reduced by an order of magnitude. The optimization can be sped up by running on a GPU." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "from openfermion.resource_estimates.pbc import utils\n", + "# Recall we need RSGDF integrals to fit to.\n", + "rsmf = scf.KRHF(mf.cell, mf.kpts).rs_density_fit()\n", + "rsmf.kernel()\n", + "mymp = KMP2(rsmf)\n", + "emp2_rsgdf = mymp.kernel()[0]\n", + "hcore, Luv = utils.build_hamiltonian(rsmf)\n", + "np.random.seed(7)\n", + "print(f\"RSGDF emp2: {emp2_rsgdf}\")\n", + "cthc = 6\n", + "num_thc = cthc * num_spatial_orbs\n", + "# Here we use the helper function kpoint_thc_via_isdf which will first find\n", + "# the ISDF factors and feed these into the BFGS and AdaGrad solvers.\n", + "kpt_thc, _ = kthc.kpoint_thc_via_isdf(mf, Luv, num_thc, verbose=False)\n", + "kthc_eri_helper = kthc.KPTHCHelperDoubleTranslation(chi=kpt_thc.chi, zeta=kpt_thc.zeta, kmf=mf)\n", + "# Note pyscf omits a normalization factor of 1/Nk in their definition of ERIs\n", + "thc_lambda = kthc.compute_lambda(hcore, kthc_eri_helper)\n", + "emp2_approx = utils.compute_emp2_approx(mf, kthc_eri_helper) - mf.e_tot # only compare correlation energy.\n", + "emp2_error = abs(emp2_approx - emp2_rsgdf)\n", + "print(f\"cthc = {cthc}, MP2 error: {emp2_error:4.3e}, lambda = {thc_lambda.lambda_total:4.3e}\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "pyscf_pip", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.2" + }, + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "61e04ed291edad8fb55208ca4954976506ff083febf358c737688b49027371c1" + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} From 4cee5e11dd4f58807b1f1cb7a2fbb5097b43ccb2 Mon Sep 17 00:00:00 2001 From: Fionn Malone Date: Tue, 20 Jun 2023 19:50:10 +0000 Subject: [PATCH 03/19] Add utilities. --- .../pbc/utils/hamiltonian_utils.py | 183 ++++++++++++++++++ .../pbc/utils/hamiltonian_utils_test.py | 86 ++++++++ .../pbc/utils/test_utils.py | 46 +++++ 3 files changed, 315 insertions(+) create mode 100644 src/openfermion/resource_estimates/pbc/utils/hamiltonian_utils.py create mode 100644 src/openfermion/resource_estimates/pbc/utils/hamiltonian_utils_test.py create mode 100644 src/openfermion/resource_estimates/pbc/utils/test_utils.py diff --git a/src/openfermion/resource_estimates/pbc/utils/hamiltonian_utils.py b/src/openfermion/resource_estimates/pbc/utils/hamiltonian_utils.py new file mode 100644 index 000000000..5822ca8a7 --- /dev/null +++ b/src/openfermion/resource_estimates/pbc/utils/hamiltonian_utils.py @@ -0,0 +1,183 @@ +# coverage: ignore +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from dataclasses import dataclass, asdict +from typing import Tuple +import h5py +import numpy as np +import numpy.typing as npt + +from pyscf import lib +from pyscf.ao2mo import _ao2mo +from pyscf.lib import logger +from pyscf.pbc.df import df +from pyscf.pbc.lib.kpts_helper import gamma_point +from pyscf.pbc.mp.kmp2 import _add_padding +from pyscf.pbc import mp, scf, gto + + +@dataclass +class HamiltonianProperties: + """Lighweight descriptive data class to hold return values from + compute_lambda functions. + + Attributes: + lambda_total: Total lambda value (norm) of Hamiltonian. + lambda_one_body: One-body lambda value (norm) of Hamiltonian. + lambda_two_body: Two-body lambda value (norm) of Hamiltonian. + """ + + lambda_total: float + lambda_one_body: float + lambda_two_body: float + + dict = asdict + + +def build_hamiltonian(mf: "scf.KRHF") -> Tuple[npt.NDArray, npt.NDArray]: + """Utility function to build one- and two-electron matrix elements from mean + field object. + + Arguments: + mf: pyscf KRHF object. + + Returns: + hcore_mo: one-body Hamiltonian in MO basis. + chol: 3-index RSGDF density fitted integrals. + """ + # Build temporary mp2 object so MO coeffs can potentially be padded if mean + # field solution yields different number of MOs per k-point. + tmp_mp2 = mp.KMP2(mf) + mo_coeff_padded = _add_padding(tmp_mp2, tmp_mp2.mo_coeff, + tmp_mp2.mo_energy)[0] + hcore_mo = np.asarray([ + C.conj().T @ hk @ C for (C, hk) in zip(mo_coeff_padded, mf.get_hcore()) + ]) + chol = cholesky_from_df_ints(tmp_mp2) + return hcore_mo, chol + + +def cholesky_from_df_ints(mp2_inst, pad_mos_with_zeros=True) -> npt.NDArray: + """Compute 3-center electron repulsion integrals, i.e. (L|ov), + where `L` denotes DF auxiliary basis functions and `o` and `v` occupied and + virtual canonical crystalline orbitals. Note that `o` and `v` contain kpt + indices `ko` and `kv`, and the third kpt index `kL` is determined by + the conservation of momentum. + + Note that if the number of mos differs at each k-point then this function + will pad MOs with zeros to ensure contiguity. + + Args: + mp2_inst: pyscf KMP2 instance. + + Returns: + Lchol: 3-center DF ints, with shape (nkpts, nkpts, naux, nmo, nmo) + """ + + log = logger.Logger(mp2_inst.stdout, mp2_inst.verbose) + + if mp2_inst._scf.with_df._cderi is None: + mp2_inst._scf.with_df.build() + + cell = mp2_inst._scf.cell + if cell.dimension == 2: + # 2D ERIs are not positive definite. The 3-index tensors are stored in + # two part. One corresponds to the positive part and one corresponds + # to the negative part. The negative part is not considered in the + # DF-driven CCSD implementation. + raise NotImplementedError + + # nvir = nmo - nocc + nao = cell.nao_nr() + + mo_coeff = mp2_inst._scf.mo_coeff + kpts = mp2_inst.kpts + if pad_mos_with_zeros: + mo_coeff = _add_padding(mp2_inst, mp2_inst.mo_coeff, + mp2_inst.mo_energy)[0] + nmo = mp2_inst.nmo + else: + nmo = nao + num_mo_per_kpt = np.array([C.shape[-1] for C in mo_coeff]) + if not (num_mo_per_kpt == nmo).all(): + log.info("Number of MOs differs at each k-point or is not the same " + "as the number of AOs.") + nkpts = len(kpts) + if gamma_point(kpts): + dtype = np.double + else: + dtype = np.complex128 + dtype = np.result_type(dtype, *mo_coeff) + Lchol = np.empty((nkpts, nkpts), dtype=object) + + cput0 = (logger.process_clock(), logger.perf_counter()) + + bra_start = 0 + bra_end = nmo + ket_start = nmo + ket_end = 2 * nmo + with h5py.File(mp2_inst._scf.with_df._cderi, "r") as f: + kptij_lst = f["j3c-kptij"][:] + tao = [] + ao_loc = None + for ki, kpti in enumerate(kpts): + for kj, kptj in enumerate(kpts): + kpti_kptj = np.array((kpti, kptj)) + Lpq_ao = np.asarray(df._getitem(f, "j3c", kpti_kptj, kptij_lst)) + + mo = np.hstack((mo_coeff[ki], mo_coeff[kj])) + mo = np.asarray(mo, dtype=dtype, order="F") + if dtype == np.double: + out = _ao2mo.nr_e2( + Lpq_ao, + mo, + (bra_start, bra_end, ket_start, ket_end), + aosym="s2", + ) + else: + # Note: Lpq.shape[0] != naux if linear dependency is found + # in auxbasis + if Lpq_ao[0].size != nao**2: # aosym = 's2' + Lpq_ao = lib.unpack_tril(Lpq_ao).astype(np.complex128) + out = _ao2mo.r_e2( + Lpq_ao, + mo, + (bra_start, bra_end, ket_start, ket_end), + tao, + ao_loc, + ) + Lchol[ki, kj] = out.reshape(-1, nmo, nmo) + + log.timer_debug1("transforming DF-AO integrals to MO", *cput0) + + return Lchol + + +def build_momentum_transfer_mapping(cell: gto.Cell, + kpoints: np.ndarray) -> np.ndarray: + # Define mapping momentum_transfer_map[Q][k1] = k2 that satisfies + # k1 - k2 + G = Q. + a = cell.lattice_vectors() / (2 * np.pi) + delta_k1_k2_Q = (kpoints[:, None, None, :] - kpoints[None, :, None, :] - + kpoints[None, None, :, :]) + delta_k1_k2_Q += kpoints[0][None, None, None, :] # shift to center + delta_dot_a = np.einsum("wx,kpQx->kpQw", a, delta_k1_k2_Q) + int_delta_dot_a = np.rint(delta_dot_a) + # Should be zero if transfer is statisfied (2*pi*n) + mapping = np.where( + np.sum(np.abs(delta_dot_a - int_delta_dot_a), axis=3) < 1e-10) + num_kpoints = len(kpoints) + momentum_transfer_map = np.zeros((num_kpoints,) * 2, dtype=np.int32) + # Note index flip due to Q being first index in map but broadcasted last.. + momentum_transfer_map[mapping[1], mapping[0]] = mapping[2] + + return momentum_transfer_map diff --git a/src/openfermion/resource_estimates/pbc/utils/hamiltonian_utils_test.py b/src/openfermion/resource_estimates/pbc/utils/hamiltonian_utils_test.py new file mode 100644 index 000000000..dc28f60b0 --- /dev/null +++ b/src/openfermion/resource_estimates/pbc/utils/hamiltonian_utils_test.py @@ -0,0 +1,86 @@ +# coverage: ignore +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import itertools +import numpy as np + +from pyscf.pbc import gto, scf, mp, cc +from pyscf.lib import chkfile +import pytest + +from openfermion.resource_estimates.pbc.utils.hamiltonian_utils import ( + build_hamiltonian, + cholesky_from_df_ints, +) +from openfermion.resource_estimates.pbc.utils.test_utils import ( + make_diamond_113_szv,) + + +def test_build_hamiltonian(): + mf = make_diamond_113_szv() + nmo = mf.mo_coeff[0].shape[-1] + naux = 108 + hcore, chol = build_hamiltonian(mf) + nkpts = len(mf.mo_coeff) + assert hcore.shape == (nkpts, nmo, nmo) + assert chol.shape == (nkpts, nkpts) + assert chol[0, 0].shape == (naux, nmo, nmo) + + +def test_pyscf_chol_from_df(): + mf = make_diamond_113_szv() + mymp = mp.KMP2(mf) + nmo = mymp.nmo + nocc = mymp.nocc + nvir = nmo - nocc + Luv = cholesky_from_df_ints(mymp) + + # 1. Test that the DF integrals give the correct SCF energy (oo block) + mf.exxdiv = None # exclude ewald exchange correction + Eref = mf.energy_elec()[1] + Eout = 0.0j + nkpts = len(mf.mo_coeff) + for ik, jk in itertools.product(range(nkpts), repeat=2): + Lii = Luv[ik, ik][:, :nocc, :nocc] + Ljj = Luv[jk, jk][:, :nocc, :nocc] + Lij = Luv[ik, jk][:, :nocc, :nocc] + Lji = Luv[jk, ik][:, :nocc, :nocc] + oooo_d = np.einsum("Lij,Lkl->ijkl", Lii, Ljj) / nkpts + oooo_x = np.einsum("Lij,Lkl->ijkl", Lij, Lji) / nkpts + Eout += 2.0 * np.einsum("iijj->", oooo_d) + Eout -= np.einsum("ijji->", oooo_x) + assert abs(Eout / nkpts - Eref) < 1e-12 + + # 2. Test that the DF integrals agree with those from MP2 (ov block) + from pyscf.pbc.mp.kmp2 import _init_mp_df_eris + + Ltest = _init_mp_df_eris(mymp) + for ik, jk in itertools.product(range(nkpts), repeat=2): + assert np.allclose(Luv[ik, jk][:, :nocc, nocc:], + Ltest[ik, jk], + atol=1e-12) + + # 3. Test that the DF integrals have correct vvvv block (vv) + Ivvvv = np.zeros((nkpts, nkpts, nkpts, nvir, nvir, nvir, nvir), + dtype=np.complex128) + for ik, jk, kk in itertools.product(range(nkpts), repeat=3): + lk = mymp.khelper.kconserv[ik, jk, kk] + Lij = Luv[ik, jk][:, nocc:, nocc:] + Lkl = Luv[kk, lk][:, nocc:, nocc:] + Imo = np.einsum("Lij,Lkl->ijkl", Lij, Lkl) + Ivvvv[ik, jk, kk] = Imo / nkpts + + mycc = cc.KRCCSD(mf) + eris = mycc.ao2mo() + assert np.allclose(eris.vvvv, + Ivvvv.transpose(0, 2, 1, 3, 5, 4, 6), + atol=1e-12) diff --git a/src/openfermion/resource_estimates/pbc/utils/test_utils.py b/src/openfermion/resource_estimates/pbc/utils/test_utils.py new file mode 100644 index 000000000..5878ea7fb --- /dev/null +++ b/src/openfermion/resource_estimates/pbc/utils/test_utils.py @@ -0,0 +1,46 @@ +# coverage: ignore +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import os + +from pyscf.pbc import gto, scf +from pyscf.lib import chkfile + + +def make_diamond_113_szv() -> scf.KRHF: + """Build diamond in gth-szv basis with 113 k-point mesh from checkpoint + file. + """ + TEST_CHK = os.path.join(os.path.dirname(__file__), "../test_data/scf.chk") + cell = gto.Cell() + cell.atom = """ + C 0.000000000000 0.000000000000 0.000000000000 + C 1.685068664391 1.685068664391 1.685068664391 + """ + cell.basis = "gth-szv" + cell.pseudo = "gth-hf-rev" + cell.a = """ + 0.000000000, 3.370137329, 3.370137329 + 3.370137329, 0.000000000, 3.370137329 + 3.370137329, 3.370137329, 0.000000000""" + cell.unit = "B" + cell.verbose = 0 + cell.build(parse_arg=False) + + kmesh = [1, 1, 3] + kpts = cell.make_kpts(kmesh) + mf = scf.KRHF(cell, kpts).rs_density_fit() + scf_dict = chkfile.load(TEST_CHK, "scf") + mf.__dict__.update(scf_dict) + mf.with_df._cderi = TEST_CHK + mf.with_df.mesh = cell.mesh + return mf From 5fa6bc908b35d82735588287f4c4c7642236130a Mon Sep 17 00:00:00 2001 From: Fionn Malone Date: Tue, 20 Jun 2023 19:50:23 +0000 Subject: [PATCH 04/19] Add reference data. --- .../resource_estimates/pbc/test_data/scf.chk | Bin 0 -> 527616 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/openfermion/resource_estimates/pbc/test_data/scf.chk diff --git a/src/openfermion/resource_estimates/pbc/test_data/scf.chk b/src/openfermion/resource_estimates/pbc/test_data/scf.chk new file mode 100644 index 0000000000000000000000000000000000000000..eaff6247d2d5726f66a6ea28ba94659b4bacfc55 GIT binary patch literal 527616 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≪yHUhAi`bJSw9dlj259>u$Kw|n zgRYna%{wQRfyTRNhlS^LFuBq8x1NI&%HVF`&i`r!_8I4p8PxXQAu}?TcqMCV76w@K zqLj85-++v7GUNI6SCDVmqNVHyebH6t63g$K@{kk5Xoc~a9B{GzsX=^UIP%l=X~>Zb z0?RH|lNq!sc#;`as;%|(;K)WL-!7XEu9igH;`_9ry9Hu!_D>n5g~N-tv@*9xXM!_Y zS}!7NVvy&vuMzbH#mMpGBJHq|EWjhk@%d14B$B)m{3Cq(DNwj`p}Pd~LQm&9S^b^t zP&0Q+Q=EhzDsGe9)3-VZeJ-ya619#(V>*#l)2W`w$MwF6?dh9nP*yTFEW8SypRp(B zA)hG)avsR!e?ra!vt7TnnDfwCvtvP;j?ja{>N>Y6Shlb1EOJC9LfXANp zjojC(B|g%8=xyAWuqWsmapc4D^z@TyaIVrzsdhYwI7{^kI(93}oN!;fwXjjB zmKddZQhB&B+K8hbHtKx3Vl%RRsOY>NA3G*62^Lohv^J!1>`9~G=x#ctFGl}MByDr5K zaFWNoMmrvi;aPPQKB4(Br}?>@j(Mp&g?VWgz&yRAc`Bs&A<8g6LZz6W!W)<$XPTFB z^-p#qOkQMYo{rKysnPs^dAou2oz=vAQS;HPt<}WT0`|K{-J0NS`n-uwo;tUvYQmPU z&HR%>K0LKAzvHfD5cBUhDgU4tFYz^zGmY@Ux3vdY^#UQMx2!kwi?Ct4ky6a>ibfdn z0mK=IWW!%HZ&mw#j2gecNv!GT^;%h4NyyQ>ZR|?eD;!Znyr%iN>skbUINgD1C1+zC zOl}gV-v3$4^be{ClqUTHUbClAehD4yq5LAif%ADfZ(AQq`RU^@<|*)AIO!jxbXfTw zI^IlVKEwC5Xq6ImUzgOqB<~|o6jS=nypO2(z;5ckDyGX*_jOkF1Lcn`8)wP;3|?!$ zfaHChy0f3UuTEWqCj~Gx%cq3F!aMAN*Bi0`4mb?db-&j&UjcfX{g!JjAf>O$#-90Xp zKQ-wbPUoZj1*C5QyO06tTQqt}nDVKRa-=Eg)A5-?%6E(Tv44)u;v7ro5o1Fwrf)&$ z+@hxJJF&1NPufN8_#liwU#>dM+f2Oq{UJnKu@rQEXo`4K)=Ah29ZLQ&(ncuC``Hvq zXF#bl;UgLWVPNlCHnVlb!GzzK@uuFjH{qmYiP5gu7+`T2&8+#+LddTiFKYP>328HG zJ+i;Fe8sk11yHcAF}KyT2cFnTtw(*yQ>!;;JR(dF-!2Vj3xKviy_a46+f0O4a{2QF zH^aBK%A+!cap0Bxg@_%mgVDCtM$dD1=Yr$8VKLryQ6RVQiTGI&V>G`?O(R^;6db*^ zQi7QQ%Qrw~q*1!-+v3N;AT3;>ce_#^+$>}g>HW_Y*~D~5X!W?F#mUVJIxCZreAUy* zi|?<4R~1WNc)krmd23Zhel%SHFMj^*W@ex+uS8~~QuM1OnvsoKC-ih!Z%H7Rq3vD& zK3XE#RoP-eigyTGTXV6HSmy5w=jDW|JHyfKH@_a+)n_4|r8+ADdJ=*7O!`2LLnNx- z=oS0j-yS$@#WZ*PEI1K;76FIS=@CWs>v2t@|sQne)Km{-)AXPs#uYu$O$m{JyDL z@nzKfMgGw(vjfKPZsvf^pezJB7J(9TEaRH|W(73OC# z&ClC>%nLsk<|S7I^Ryrv^Yp3+^W*dx^OIVG`57CE+bvL805i?;`40cX=j%F+&vWfs ze4fvH@OeL2hR@rAz~|eNgU>hC8K18ReZJr6^Bm{N*uQ%G1+jJ&=2JNl^EplP7`zVi z=#h$fb-IgrT{wyP+(%$Ohl(+ugo!@IXKOO%r+FpC572swd94(&{|BREh=nviLpv}( zF?yJnle(Cf#hjR@S(>N#W0;?c^_U-rX3WpwFPNX9KFrIN3w76Knco|xNAon@jCoqR z9rII>;Cg!Z-e%a^Z!UG)e;o2XWR(rd?;!>-KPYtXu&LufC6qMTHu~yFCvkKe5`KBS z2A0q~gvQ^SP->_l9)09J@d6M#}lAyUZ>C?|;C6w=`tN1DZ{Gf9mofC>Po-=(5M609;!*7WM=M3O{ zabbwCx8KMvD=-L^f(%bTy%GUVi=3+8A^w2KH0&1GoAC#R#s9G^j~^q5t__P9vtI$J zUm62d4>l4v_tWQr&^Z3x0=#x?f>DZ`az_ruU)JBWgYBUUvP!F3S=a zQuOTpUFBcI1sQ5RGGBUbHe$bSKMO_Obz=w>>*6tT?1-^1TJX63VZ3P^%2kjNMf-g~)|MF&e$z;FF2OwV*ufKEG{Ezp74KCb z+1@j09I6FmR&|#KZ953;>_*S*8uLY=&ejX2t%DJxfy-m>xD#k~nF!ju7h zok_U5Hk0#ElYb&P5Bw3nNzMb08ABq>c_=aY2-WW+FET~;-)QTg=6kS%2y%X;&yyPF zJS67uiJS*?4LQkqz*wX0GIJiaASo4KRBJLUHBY)o&_rcc>h^raZb5cGZVR$Etx3S(UV*3rumkgJxuZ`Fo z)P2Wj={yhvS1V#3mY+6URMj&|Jl3x3&OZKv2x-Cm=q%M)8J&Nh*t9xbahm-rwB#t( z?oV$a_MKb&$!7B`(WHTYe(3`(JE5+x#K%wBVU1d^h(G3-pP4ROik|>>%!}AiFvZI& z2h7t3j=z|v6wJ@RgyR%HJQkQA>qiUz4USC^w?5BU#pfG+5ufMP9;KSS;$zUu9P=sHiusKAg?W^;z&yUT!n_*X zz`U+yU_Li}#C%4ZVm{5smr;C5xL|(Tx+#88YCh(*g}r0;on9SL=!p5r2*&(8PRG2w z2*JE;*^PM`SI0cHH)4LAtT8|N#*Zm}mbhSkp5MZ}q)DKytXrZPDQ1|bbp+<=PY32F zrp%}zYpfA2je~h-jxT2xrBTJh!8mM?IRm?#*VVt&5AW?+uyt48Abs!a#;wtgaRuzn3aQge@3dLQ$)^mh8D zO^^BrUCz6og!I@L?b4VR|LE_7hZZkptR!$QRU(qw4Vj(h9IJEpS(?6)? zyz*71f57^QU6fx`%-7x~{n9guvtK*T1y2dePuG_9QamLc2dzy1AR{s5xh1Rn;QN+x z>b^#B#ZvcG#s4XJA3^x+QSv^biUS(deJ%gUPu*9)*mTMtDgiIZ`wTv8m8d1}YuEla z)O~%s@*#O2f%t9bQ-SXHh?f<(KVT*9mzTzU`P&4jzJ7Y12ahH-FERo5=cVEPC;8i` ze$Ab@-=lv8b^j0Ny{fi7_JgS1jprGz*V0Opy$hqRpQC*GW$$*%r=l;UD1Z9#Q9L52 ze4H(Wa30iUCw+@JKR+OS3#>wFDWAskZeKw9)P>uP@||#QE9IZ7OL4Z(#JPumi7wN( z;JGEI)Q9xfu>5hrj$6a?#D?xi{>ots88CB&UxCR*@af^Mf`Qk+pgEhp(^#}HLx!t& z;2sxC-Iwm)U)dylz~>{WGRrr-BL22l9$o0jQa2WnDl<&zfo=EyoXPyVq0ZLq>c_2B z(*)loYCS5H4iq@hGDj5K9!s!K7OL~wK&?l{+`7kivHXUXVc$d~zyF4xoqb-bY+qJa zWM?w=!RZ?@cA`G|N76+!+R^>t#e^I9J(xba;H@`!W*g>zPDKW+O8kAHG%6mPxbv`Q zxu7F5Df>%ibj-Vs%%CRGmqj_i4_tjM|KifwSr{`u4WnK2z|jrQuT~UAgZRednNrd> zfp`h;&G|3akYdLRjjGXFV#$%N30$c^aH zmMzS07}rNvhq4Qg?j45L&ty(K%(Mm-+TiW&Wp2pMXi&K*B@9I!$O%`MvjBQu_8;LM z4oBZla?JVbXaOY{FYQe^jYF zxMA2c4rI#1fT<a1~fHp+{!RsaEfd5$RD_9G3LQc@foKI+seAsQ;C|4HGc>T zQ_PQnZ<=?gZv@wg>Y$8io1PFlSj!@|m+5^K)Yl z#Shw=vytLe?fUR0P&qjd37gsM`vuTFdzl7(_#ph#i`~-fUR&#gnHn5)IZKW>ovwnG< zU(=T39x)TJ=e;gl=-YW>ETQ1S#x>92{a68tx3;dtRT|aX>UQ%utEPss)h+sjc_|a{ z;$IfZUk7wCe^K&FYZtIc)yWiR+1?TTOR#JXBK-pd4$YJPL344ucBEexM17?Ea_B0~ z@oqR5^36~@9sH2zOZutd`6gDTf6(^s>k4HR}EssD4`0)W^z5 zXkSIm3vPd-ZQJ}7s#vhpkv=UoX`p#>N=9wnXFXGcXq8WDgQKk;~ZFlGau?mX8IN=D;Qi9eV0HK@tody@1`{x zXWt>Y%v22y&W2`9x?95uZmu7z>&^qElk06qb_atK8E?l~S6+Z>9Y)zN!_2ua~emzr5(39jsiF!&`M=4>8V_)pT~ld_CtmR%a1u7nI}G zpTF*&8eB|g81dzly@e92=dqs3yv_g~C7z65V(bK~!J=2ok2)jKywi@C&su_lX?|(e zgeySJ%|BDppV{+2d9inChf?@nBy%TrByAIZdPS`zokL!tO3QtBZP%&G+t7FhFYKw#Hwd zDFMV{x}ki%4e0pMkaqZl6c`q3j@jz20lc(kHb<5%C1&XUNV;E!?yIN!^67b->3KbL zzY^WwK=(J({npF&27FX^02dzSbt)Mv!en~hY_Y+beQ!6x>kF4;MSBS4}}z= z5PjYp%c%22w~kEmJ(^lU6zJ$ivZMWQ7W0L;=<}B~uSn@PlO@zJKggXOJ*)X@0*TMQ zR-z_WaE|7Y1Fr{uyK;&756196{=T{Siys*@`2U}`(>^@fvCDxN71=@Z1DZ@UNPf`L zbIUIGd(xlZl3 zwL}fI-@c;~^mbd5HBoPv)p~sQETsci(Iu+6~Q4c&#swX@mv!x%JgmD+nh> zi-d!k1~lk6f7m@pANi=XNm#PvBK7)c_sXRbaIsZ_=;8yD`>opWb4dpCgZ8$#qUz`Z z3tJC)B8guAwI-r!hv81b@7F^#s;&iZG?D8q(bZk#dP|h!6TlEiG$u^{%Q7C5U*)uj zr_|3^0hZwSWdz|)JTc2MFzqOt(p5hME;$ZXXkWdyqG2~U z5vDq$-L(e@raRp~C~bwJj`81XsL=swhjR@xa{|!&+sdp)-c}%mMY|_JRtOAj>Kta% z)d0nM#k+|M#!=9`$%OPfiaQ_(He<}_4-KF~)j^KHZL-4#>y8i*)U-Vat>Q~>4 z`=bx$+AE&31nmf6~`kh|P*JF$1lI|HX%AyliG^n5f70=(=U8-nK@BCnZ>^2~n^^o~`P62_{`OJBzz+2Ac z;K$TXz$uKg7Z1+0qj}8N8-Qvi;`=y3F;R@)zrbrfjtwR12Z^s`mp;weHQbw`{X)MV zq3sv7-4l*E!pq;fZC)Dc!HA{!{b(8OpJUo3>rZ^%MR3!;T35Za_r!+Z_xPiyeX~_g zLst6zu8@Aeds$$5nrE3Dv4r+fYfo(Q+B?UH|K|B02^7~%GK2SN|Gc36^Y z{_EIB9<-1C^Uqz{KVSA@|ETY~k)heCLY$-hph^2-Iqd^!+6St%FQjN++@t-#ri}fd zMEk*!_JjHp?1ROh=KW;0?}F^K-;8O$9j1NeNc-$R-_Lr=P%=QF2?H|VMhDMiT2M|+Bf60Z!~BhY0y4urv1}R`$wGi5Bo~&pDVO) z%F`A*y=MAmCG8__+DAjbuz!SJMu8GR0Nb_rhrgdog=tzEI*(6tK^ZR3qSr?z?hT(< zACP+13{00UeDj#Q9H{7=`8z4Nn;07Hc2_ANGdo40DK^1bL z2P}UdKz^nr@0*5|P@Py+-vPq_6ASwMyeM`1Hx}Q238x>lX!xoZgmk{X!vHlQ?|(a|_6gGXYYV%4=@0C|cKtzM%O3yxPSoF$0@8c;39H5^|e1F(C z;aCl%rWBi5$wTSZq7E$~xY!&e! zxGN)V(V7r+iq39ycH$giNEGWJRnmQ3}r)y!Y^s;<^dtjRe-Osg%-2`oB< z6y8_u*zb4_JQA{)SWy-OQqS^z2-9>xmb*?0DXo-8I}~sGA4)%mekm$FwRE>bhkRS3 zC4m8W6`(ONy4MeAUcHlR)2Ia0V;6cZZaWKDj7MKspIHm%RdN3oy8pcl?&G2RKG5_0 z>3RKh|9QH#{Ct=X0eMB1HcELk88g#G$+F1@H~+P!EY5jP7sILIt1U;W}tWD z^BJ#S1f%EK@A((^+(hQbE_F$LoD=CBsy#SrxJDHX%o|BQ+9?OeF+V_1A|hvP zy%F4Hm^cvk#1yi6;`6q`=ZWlNS{>xy=|I1BpLK^G_`(^?5A%0(n1?y`RreOGG={G* zKS(TT!NKaS;lvr2zg=3ek-qICHE6O^pz&}6ZA;*Q~N(ru>3T+-?4LPKbiXp7MDqmnagMpwR8GeOg=$&#$A$6RF`&?;!pnV5XIlU z5Kod<#52fC@(Ri?2T{C6ca>4R&MVlFe4^AtZ*rJ?qKsw!{p5Krne8Ue69r28xHEZ0 z`kpb0Klf-87C@bxhc<=UQukrpb~8B--1Dy>=b@2J!t2QU@JYdm>SxV!pyv0M{CiK% zf8C);-G|Rg400ZjyQR>_oCoUsF+WmU3@CnMb=HS4s?RdaqO;t{`7+n|HmI?#CLV2D z(UExTJy8;^@w{#ACW4QLx8v2PWV=W9WBX=VSlyg*oZ>6@PXtQ!MKoE|jEbga|XZ5|R^ z=8g;=EGmD$(FyE0f%8E+b1KF&pNFE?tN-13ztJBk8m{qvCcV4v#4>k|j1~TbTINlz z4=XOBsDjBiuQaVe-oN2Ra{^m|r`x2_K#V*nUeLd1(LzV0$|c#AZ(szLvaC_z(TYbq z3kHS$XuANo@H>a!s2EtgI$!;rjtMaMFxtDeBmxv~4$nB79|6O&a6f+$?zdTp`>dAY zzEqx1)I8g8Ja6WoFxCI}7VZ~Yj{C>xelg3>Z0n8uz@!?t&WrW6L@+(?JNGZ`$-GFI z)a$`~J$?x7+S2n%(;X~+dg$w_w5#BKRC-&6s~u9Hb7?rv=o|aeIe~qsMO5n4K+Itz zUE##mB?nko{PYB!58Mdi(tE){v(aA`vHRuXW892|32}F`Fc~(;gjsuKesf( ze%e1B8o5blmYszhm$wT$Z6jo?$iEBr~RW!`{o+$n;BE=qu;cT?$iFMr2RAb5Bq18cI9cl1P7QrkNprx z`=N#Q!NdUe!9Us;h5gtUm))=*JZL{O(0({c`yq$+L2Y8_?;BYs30>N6+iAaz(?0t^ z`^wIDea@_TvC|6!u6~pume-xWv%jlAT&a>AMLJSb>$ zcx4u085FZ4oAu8YzNWbH*Z{?^~9t6 zWWRmECC)%ogSXCC_%h(y{p4+Orw-!b)bXnLBaNPZ%nU!zYKM-u*=0GuOhQBQF+26k zgP`Of)sJd$KPbR`APe`QS8CKeaAyrQ59A!C`q5fBsvoRdM)iaCA*vs2m^o;eb?q^f zHM%vJBccd)<9R^$xNp1svOu^oYWT|$mP)wH)lyBn-wm32>R%nmi$F_mHTW9OI)Fl) zfd$Tpb#=h5B?afH=M!ZE1w``9SjtQ+?+Dx3H3^5{*LjGatQH4H*zYE>?AwC$pjcw{LLk zTqqFRq2^!fmjxVKH>4k6cZIw+A6P~1&xR`W^APT;bNePL!B#cfJ6rE=f(&~7H{(iW zo6pIx`=?!HGgmlKC>24|rzR)-W&WbZXciGeuDLciA@FcmTwXN*hSGJe~IgX8ajLvBAB}CrOOI#g1 zRJxod)L{-pw61);f7zFKyb1Sj9mDe^2)(&DoE~ zk41yv^T(Th#7DphfakT|(cB{_;{t2a4W52HSc*RLha_-lB_P?k7u>&wiqN;2qz8`7 z?`d?V^C+E9$Uo(-{*nSLA6}4omvsZp&USQ$YSyFkR;lB=OqYRq>&soz?pkR3x8BXn z&qfE2tZ?2z=a8wk$@CiL=Sn1VmrQ=WL$ofV_yKbDf-Qp2%!z943CT0eKew~mkI$QB zIdz`suBQ0OP5Iu$-E7-UA-vtH zsnCk%tqZRQnymZfe=jm60%`u98!jnfOD%+3>7Vz1C1l7i5k~kddrk5K`q;Edevsjx zhruKtS)OMpJ}gGLNS@H**}q$uJb^#&mQ#GSwak+liF+kgF!=%N+6OKSAv<_*H zsExDTtWgHXio(?sP0HZH#X7f{d?GE2S0tZkX-^`>U+VHz6o2Dl&LpqspsgjzD=_&H zK=B$VWl!;{b4r2a6UDmT^kMReMEP$%C(l#$$s6)K5uaz<4JNN>@4iDye6E&+t4sm_aQ?;i0U8NbcUK=>!wc4uYsGW`%n$cy~+EK z)KS>UoCiD_FhBg6=O}*l-5%MozO`mpuzXJ@=ckvk9VoPLhZ=W7t9cE7K${UxM{i(3 zEUD+L`B#}j%o@uzs%TwAw&g>&SbpT7=+HgR#zh3tpu(qkLq7tpKZl=73BEDy5s5(o zE*-1~E-Aw2r22Z@!3pC^j zfv^K#cs}IWgLQ$5`y&I>KvC~WL#q!BaLxz!KR=86=WgM?PYt;5$qhWO|A{I!Z}u_n z-!_T+cM0SE4h7t=o0y;ax%etLXLh~xtbaGO4Z!nq`*^xCH{64-PLHzb=S8CaJtfT7 z69l#fPOswrlLU;!gxMR#T+kXir*6S{Z^?p%8;=|Y8aH&39tdnj;mi+)Y{=UN0y{o2 zUyl!v8zATT09857p)>JP;2E7`_u?FsN6s%n9Ybf@skm(z{HteG_w2$`w9jE*g&n^GCW7roqq_ii# z+nPbV%P(EAZoygjFT`sw-rE)~(MsbVnezhs+>}CHS;|4IhrOVC_f?pxn{jTRqbUs2 zrJf`G1%^3`Hg^-Yb|U9nZ~DOiydFvI+`IdPpfA*2GoUMc`W$ik=+k239k&Q`xZjHB zRwU{U&#!(L7)(6E8O#(P-2D7%1mQ0l-_vJV0HxQeq|X;KV8G+&5BJ@uBnpjtQvS}~ zfi5zm-E+ecK#4==+5byY3STVCRZG$Jor!Nrld`m$I9YTixZu;x-sJ?2ZZ>We^`FA)Dk%il?}Ue!hx#8 z>g%sRW`Tj}_!APoc~D#R!a5E{Gm$|*Pv|v&e{$d!2oiSDtWGZ_MCtX`eI|Q*Qk@{N zptJU~a4f9W$j+4z*@L*n?nM7mibe3WugbMefy5pWBGLLK zWg`JOAfC=!=)BPT;=YUeS)fF$CMnk^6D>^9eC#KD9}QU!B=P>5tG{r)()qx_P3Vrs zA!4z}O>or)=Y>abzOpLr$UB|Apj71IslTC35E)SXfJc%h?Z1N05PIxNK7(Rua9jqT zcghTPp6H4*ir80s23~t+c4{dj4u)fX5DR_&EP=`Hs;(JO9P@*=sh3Gzp2#OIuV}4N zGzf#zG;a&>dhlD&H$uxZg9xShli9rZaFm}V6rg`TDEYaMMv^CFEqqAw10EAiBtPin zks^u@_wVc!AFS7ONS@HK`9zW@kP>`?;_Ia^mC;k?zprQVgIG9*1b%$+C3=5VQTwgd z%1`Zg@{ldLpXhMwJ#s(6oL~{P|1SwHYX9B$=aU(Dedi=IGK$6feW2xwIJw`K=QTDn z_Y<|Nn~7>(zDq<@LXuBpBe|XA6B(_Hr}#U}F_B5~$CIBz@{0I4u9Lh1vyl#p*W#e| z%_OhwgcQjqY77X2D&Z<&I>+vK2GbeefPx zRYu;2f;3lh9ylxZm7Ir;JDF1bHTgDF|4uJ|YJTRrKM~~o080t#J{$}&d`;em=@CD5 z<~$&;i+S0w*oWemcR6uspCI#le-A8_BIo<^?PkQI0C;hS8K>3ym5g;?o-NRqz63qp zd*qbf=R+aS$GomT?NNg1W-g!9YbZd<@+$vQU#M8Nv;Oh&bYi;~ejX_wT`4_u75!V< z-#@$~2JSY&>oZI`zWp9bAk-r`E=HJ`5jEZ9dLQ&>;3{*y531PWb>-(&8u9rp&W_&< zf>Q<4i6h43&*y-hU!-FZJD0 zo!W#9Yk$k_RA&DBs!&n=KHpsMLH2LVj8Y$AxF7d3Tyg&sW860=hWo~+@Vp=qJkR1j z?swgd`~CTFf9*Zozjj`i@osx8Snr-Lsr{;tXb!{k^lx9d&YAOq$lBe`d_B>~IBqTX zXNOP_(o{QlTQ?aj(m%H@e6I^y*oU-W7npYzS0kkw@xzIIkVS**TV+Uxfxv+d7>d5|2HSp6W>irCSdL)tmt2P(v(eq+wTRz|AzDG3Q z!u~0x{qvu19BAL<(mvYq4g1J}_Rm&7>>u8LTPXjO%UDf}3&aufv>)1bU_U&|!#+4k z`=D$E_C+=AivzSDyaKQvYH2?NGO!=IPv%iRXyKAkUd`b|Y@fn@J3;$xjP}_h+GozR z@3zpslc)W*fc9HE?YBXh5z23sF4#Y&*gxPe+Y!olbt@%19$hXZa#msgIDf(Z(R0SW zIdvQRhWU3zl#k-KVjsPr{j*67`)8FE_K(T_lazmoXy4R!WtTBOFp6Ql_zLACVcJI_ z)(a^Al(@<|q&kHX8g1Hc73cfm&GS6{{(MG+5m#P&P-qtH6RFwRRNw)oCwzX3RcC^Y z_rRs*)I{RRK%no^!X&~}n0k)te%#)^C~zF|9QV8E_M(*N|4FS!Qq9uO3!i2aM}!R#vC1b{HOMXnYIahI#Tfy&G5~z`o)yxAZxKkZRVRsF|-vK)F)T{uj*8 zlT2q>yrpVkDpEFkmtUUd3Z^dGu-=c42Gx0jCyXjDAe8#^ZdJxEu;F6B?)(fNv{36T zgWIc|`8{w}b0*c5#1!sFpKw1og8RT83#t#@g48?|yqlVbZn#tZD6N$02fs9^ew5-! z^@Efb{SSo$2jDZwGF}!A1$65eH4mKe-7U8)<_Wyn)*v6Jiiqv#dEdv`7$TJ4P-Ea- z2(rx!pW_NK0jpO~8ED?3GKzIfYOwv$L{vq^N$g|(ihF9%D4$@YLC@yWM~AW+;L4D( z1~!kY;Pc@$<~&Jo3uh#Mh04fm!C0Pw?hE2;X0X`;;Z!Kt?%42D&=Vc`7S`ocaf{g0 zIbG7F&~O z(yBhi{Z~ujx>%XAH{+(D;_!6b{I^;{Y~7}WvyR>r z#>gEF()rsQ&fNUV($^(jL0V_~^w{bY5rbuIo0zgThR%ZpX8U>x_CPU3zQL);e|h5MAld6$s$ zL?7aLEq=H^nt}W6dvSmI2i*U+E=4kRe>N%)=>KKD=ssMTj^}yQD(sFgi6hS3U*tFZ zC>>4ArF~S@3r6nG!oy!lryym1?y2y!II!tC&hvaYimfqmHu1-<@<#Q0N+ zAMj3}eNS0O4oqsUQYcC(g}p=gyl=)+=ZPxPO;+ad*%2SkS(-*i)x!$R4^pOim^4wl z`|X}DjK{p8ze?e`!E)VjLSu2y?(`s7d>Qjr^^4*KC^{=nTkLLtMMIdsl>1kXUqQ{H-N}K5YCcPR>JIyFQYrKE;kx-IT1fMR-Qy7^^=D^6m8M- z5!pTL3N1vulS8Y5&Rt^01wXG8xkeN-e?40%T@d;~zKrMm_T*kDC-(2g`Dq1L9dBVdOx!ue{CN++k?X0}I$cDV zbgo$4x8gj*_O7l+U7lmzFY`LhSKm!hfl!6Hz(je)fc#Li5#9csw_y&+q)h2SLNgWE8{1qem@c1zbk_ycI3Ms z7~Z#acT(giG@ZorwhARFasC*9n@8vWJ!bwt`9%*qnXktKtbd&+HpcwCY#T4uppOH# z$f6HtTTh%%DaJ5guQB>4u`_zR-d50Ml&y1kQVRX$HDkUWNANk1ojLC!Fp9XfBYI{J zdYXx|lON8}Nwdt?TZX=xxM%0HP7(So*gpr&wmqpwSwznC+T2eyZp3%|{)HyzsP`k( zy(B7B-o6!{Q~LWX`bQ%%7>xbYI)UGRO2&$=ubF)S*IvcGy6V?2$?&=fA8ujGcKuLA z*t%i=q|@(r>o4=8=leepW7F72l4GhozU%G~TmImA1(Pmoj_rH_2ftze2yaxeD7zZUEV&YSZBXLvaBym-ZJVtApuyjkrWQR>90c4XRvC{wo!8d17K)B^*tjq5GJ^)yL` zr`KJ9&|&}jPNVzq$G}pqz?niQs75_U@481Sg%#KtS5KdQ{ebluv@fF8BTl(3!;FG< zC_J}+UrJ~rvCQN3+hr9!@Y%no=LTzCQK^`*&CJ4Ugzj%D146M{?9AhE*w*x=FC(i8 zCim<(-nSu~=v`1<+yA{kUhoRU-q1{^2T zOiz3uQf5xEK=D?1sNmY4jcPj{2K%kH>+OQHDQCyZgXZNpWXpZWeCW> z{bY|uA1B|6zf_E#S!C>KY?+2RAE|!yeT3=iILyY57IJ4g(>{J%_Sa{U*v?@cNsT!u!ppCgDfHzHYDTg^b}#!9r)4A8D~7 z=Vz~SvL3Ro-tAU;zYLx+#`*b^nw{GX??cCTZYR$s8UVhi2xIf_O;GjxGKKpQ8bD=u z6JLP#deqn1a%dn)9pu(~ajdzw0G-u&K;_{3;Z%Morg{5tw*)`z{aEbs?)$k8c3- z+u{C&{kT6Xn+e=+$}vUtH>u(Ne=|)!16>8^Q1$08 z28e^PVjG^f_0RmI;o|quWbf@nb-mrdEJB)ZkA(nW9e+4Iq9_X5yUSk;B}t*R+>fao zEL=wAULU^#HeFe?&95(1%5xXGbgC{ru*(}%=NuPZJtTmF{BBKDFSP;!Hah6#X6E;# zTj5+|f1JvTsw?V^vz35AE2E#~fD}Bi^AO1o$aG!jcEIo@)LkODH=}w1zM$|DgLF~N+Nq*42A)1G_bE&%>2Z_nW6hDaFY9BGZbB>7oF~+}H z`vtKm8S}^;L#+pw73A$Bth$K4zTXso4^F)wzk4JV3j1#%c|}1FG>yFXw7~NcVGJfe zsC=xDpUDrXNZLd3u|av&Fv*AMRdJFZrpWJ^m z5zGCOc^=@YT|KqmoBGbJCHGtDI3o8GrQbCA-FILyW9OYvl224yxx}2wCkXdgu#w~s z{rXAqcZQ2N!Q>U(E_+P!idOr^z9o6ROw3!8yyled6kzg+l#b1ke1g`}V(L6=Dh(T% z=ZO{tQv3jK?!+y+e}54V_r9R+!*gFm-G`ja4w83N74AyT1Diuv3{w4u78JiM%`@bE z0Y59wQuBG_o>RQeuKYvY2i=SQKbicXPj9R#UV^@0ezy;-i2Bp%U-$hooYBV!W4Pl@-?qCoPhqWc>Vhslijgt--+=*-j5S)2O-bHFUC9Ui0o|WNwk9hfyyw+IHIGg3E(X_+~{U1wb9uH;rhH*PtvS*1D3CWfs%s41( z$dWZmC`%iORz+DNLPd*Gls58Gh?;SfN|IETP?R-U%9^F*_ndove_o&G%ym8I+~4P# z_YE^ukzrKc9%M44>B+7Xjs9Mh{yEwZh+n*5>Ag`bz5el{0mHpvSb0n?JSWBiKcc>u zYb8@3Ah@u)2|P|W&GR>?@w6OP#OB*#I4+1u2o$$bDfM4!@(D{cdJgr z`_ZTEto{krvHCF^{Dfi0ahSaS;7buNj;u86qa7Rf$UXCMi00@EeT5>lW}$CNjknGp zw4P%Oc(D4&>+G`mCG-ZyHXBy|xLJ$-a`yR7s~czak9%S+^iSMfP3Rl*%dOBi-gB%z z8c~*lJ{ky}f&QtUJO=%vzw#LLPi)!kIX9|Rv&!LVD&-oGge<9 zk#XpYyeq7J$S?g1{m>Sm3H^}cFa!NyNc}=C_5Ou;HXM67ymgv(WchXIH}2~$&~Fa! zSbbLY^Aq&hby_9#-Ai^;=)1h&4(PYlstcjt)V)~!79Z;k{Z_QS0QyJ$tQYAYWOS_x z`p!|z*P&IQg)u70>Ysq%3h18zVJYaFB^mzEH#!VfAAMQN>Lb}3to|u+XZ6qPj}6d2 z9>1aQaOeN~=2B-%*!reZOgn=Z=p$RcK4sXy!0uX(&!TK}LHg#`Rh9NAzf$&XL8>!uv8gMIlO3f^iCQ)9+f~GfpMdK)D#&}F z1)DP4JQX{R-~P;SYl8L2MgHD|q|rZGZ@DwO+>Hjt;pnA{3jC)Tw@Xh8ym{+`L*jb8 z*Y^%GGCqMu3bv)8%2rJ@osmszeQqeygX0G8Yh<0G1^MO7nfR`vpF5~NcsFnnJzTr| zqA#yEDl!vxy_*!-5x6HU653z+zKev>m=9Vc&R&U#FM?+8apgDi``Y~sGk{2{S zUIZFdENN1jSRqEw@L&9Q&pSS5fwzm|=sH=ZF~7-1+eZg!wLcGW99-^?>f9ewc{-R~ z1~hinE+rbta9w|?t|d%&ust(;HYknuSZt&BSK2bfINcynx?YE#@zGH6JoS0Cx5{7d ze;~FDulTX)*@@>%vC1=x&mVK;n2~<0>-jsn;W}+zr{1$j2dmo4zpmV_!mMN*f%U~4 z1AK!Aw3#kTU_F)=TN+0Bq2=g2S&!lcR{G6x(3$N5*FmQ>?Vfs^YseJzds<2Ith!De z>iM^v`F&-&_T++W^u6))&kb3zs3T_VQ10t2lx*j}HvDcHJ{!r>V#zFBgons=e8vJa zGK^OaqyJ^1wjYuWgO!5J*TRiQr6ry4$-|GDpKMr-3#0jMc5 z!`3xuTf--FU&k%DM$*86(pa^nmS{8-b!Y+GElq5;;v)vLAtCnb1QUH6x%4aj zjx=4>y%zjkA}YYY{%SV(i6*v!9}*IPJe3HAT9T(SNCSV92*{~OaM z>P^Rev8$<()QJbO%*I8mJc&`G@I^o6=vDC>k5l_W^pVsyYCq`5;IK8h--S0cH<0_y zb{YW9JI~Tyb4HNg5NGq9BbdQ-Fz3Do>Dt5a8@KM9|iN_9y<{vYloG|_i`5pixiwUcemkC9vzRrZHRQCeji?Y z`ZwERtsQs;xAt*LV~4aMqESFdP5#_hC7h)-=Crpi1@ZJKj{ON@BCnBiiyKz((;Y|N zf`3%{H2AwZH-X=d%~RmV>&nWr8Mp~~3+OC=P0eBOw=XUMf3cEL@bA%H;BMBYhLuV< zrIg(HnX7)W@}6;7RyfIs(+^6BI8*yUKU5#Tr1pdPP0dIie*T3Y{Am0vT{FSb^kN^_ zZXD&&7kKR-H?Qhud*0b+z z_TR0i*^jfXUrk7W>m2HK*(!S_(Pn#XF)I-Py4KooSYKc`zUMJEBC z8!C%OcK@E6-7w2SpE?Vxg{seC_Gy+L{>;)5JQd_Rwuu0ZG75{z8jn!@aBF+ZNH?iWiS}Yk6L8~=lkQj8(Bo- z^NomT^kxa;T5^UEI(%g932c){{d<$>B~HE~RH?4#cE6;HL7(jb|IqaU;D1SH8~6pC zV);#Yv+`CxQ-!>uV=Vt&+AMz?v0dO_Tg>uTNi=bl@w7%_hu%3YG-0Q|WoPACz2DWe z=J_9*GUq@cwI6gYXKFdMAN&@VY$5j>bNTQ!a=(k2Ed6K~OS2DaWRm?ZI4{ga_S<75 z`!6X&2p6`kA^X9PU*@<_dDzKwRwUc+9P@H#Ht0o>HyNeg=a>)kgkis5>qJkLR&g`? z11mo@sIS1sc*5#Ao@+Dp@Zd+IZxQGrSErW#j+N+l-h!*v+$KT zZa(9qW^{q_8qn!hqlb#y&FGR}!!t>qWd)DO_o=0H7x5EEN?)WRg~5`U%OMe{Bt`I; ztV}WrxbQE>AtD;Loo4BMd@Nl*Cqk~{)pKk&sq4soj&r(U;1&`|3|<-5xQOnath-Ix z#~Jf|yOLtH!U8?I>9IAp`Uq}v<-iucdU(^`vzKn}_CdQ9llc8#xZ(w7X+-15g>gh9 zZYx8vBu^ssfOA?0PEruT6>p{|HS@@Sk^i0REqCq&QF9vOsoq!f*cTlc8_?kCkV+qeeth zMwWSaFw2M959nM~~m-{&pANbYxa?xqFgeuElWdY+A?d%7whU-QGyCuF}bS{ym4 z{osx}w@4n|9(sVC%EPA|cdq*tahhq7dk}PPP{@r@{nO0V+W*0R*UIjRFLIust=7KV zTl@y$*s@>Ghn#Gf**fPF8by7O*-oC7mmF2m=dbI|4c@jvtJfS@S1qhXFW-IzuGfF` zg6pr^isnv#wxu}hG2N|aO5bpC3#>moaqNNRrSd zw?<;6IX%L_2=xAI!r%WLUQe&O`!J2CkmTxaN6dF)Vpp?*_^aN-D^{N zRrv#u79ai0xR3`nEIbcdu5UGH>(LKHW497dqS18E_?8n5hfv<(J4*!OB$+f3-MrVz zu1LsaXPP|cI-HpBcXHKwZ&ci~$o}D=JqjxgaTpJtHg3Fm`irIbGL(DouL7kjsLz*c zr!;1~ikZtZu|?d~B`NhUy^zfY@kH-`ZfIP0e`|iX@o@4wse z;J3t??>EW&>&Xdub)(Cc68{-4SMdLFZa4VfIaUMyPr9DRQy<%jyw3^OIhkwFwa@%c zAbEGTeh$x!Hf1)ue7{HS2cJw($e{LvjIFMo8Ke9r58L;X`zH%G@w z{_(r@o5+4$idRoi`@vPcCrBQ?a8NIf%EMzbSpq62W9C!O8A&OoUA4H{yp}q;&0ymS(vU}sE+o?qKQ%+&X3r0C zaof}97HFxG>vHD|dCB#C!Yk}5hRx7m@qQV5!Oe8V;ci$jRl#iLFtw!H-clxh*z$b1 z0JR<;&p2jJt;gHGMg2~_=tJLo{5$By+Ix7jQ+?@|H{=_Vyp|A`dv_-Em?yrt6z#Cx zf>+pAX1R=-;3m&op@9eOF@M0?A6Ms1P<3+-X#Hn;ptChTgizO!y153?n09W>vj_7_ zG4-i4LrL;pOhNIg=7M{ksHADz6r#Qv!@p0d( zeF{9l-_NfJ{6KjX&l(wI6inpN<*1U&FxTdE|Z+zciMAoBUeQU`23iBXNvK}RFIb~F@DUI*9I_s*_MVUpD z+u-$wYCPS(<-KE62mE+&a>WLGD0itx@=`OjN_Jbpo+y2+y@{=?$Xy4Q_ZjD%{v*=;5*jkaTr*YijO0U(mNrhY4BOu zc>hR+?9Wr`zo@@w=1kpV{e4$G!-DZf(|>+7dbYRakckGhZ%va0$Aq16#NTya7c$o% zV}sCc)Mth7p_^6RSI$-JeZj_Pm~=r*j!peq>Qbmuf~Qook2 zi&4n4-y-g7Tp0b!p)AOk_pTcq^7upx7`OubM6A{99D3WqcpbPI_&DdM3w&%IH-UMI ziyD68_y54SdcOqt`Y$&h_$riK2mE-j@dFPWH>QEN9s2Uh=as%OLVj2SKXQ>Kz)$4V z8Q>+fPQmGAAs4f^5BRH4dd3c~*Za%jC-mf|h3ADTXwj*Iz|Z|4CE%w!mBmYFq%81K zw0|@3Bz}||c&dNA2Kc$>Aq@N+6c7S_MB+R90?S8e^Snrl%=h1pJeco|V`ebVPbM!W zd#Lj~)~*KgR;&HhLFWB1Z~)GKx$}V^MwJEA1}>znCC&Z zbHMAdlYTXP`2)0rbQV8*_HzM02QFm8{n~Ej23|fSTn3(khZBIO#Oz1Fk8}~A72(J7 zv<2{EcF-I6Inv7FrQqXA>VKv}=#s5dz|-aq7EdEpIl#}!+m=&Z+>NwRCuPD9zFDwQ zG~`teP24{a=H2~rsGao1y$Wu^4{A?z!`-htXq`9!u3KC7!}ZdY3u&Z39$fEuC3)fz zO?){Itmn}D??Jci2iiG1cQw*y`@>J@l0KVL%4{TkwQai_@WR*fALvds4A9(;DB_EdqA94*W{X|_EG>=xp1JX~Ayw!oHpT!)6 zAFL(ul#%m9mTbne{a4NzUo{Jd^S@)A2qMs4l#0z;_ujwXf71huh zJ=CC2<1U_oKFx`*fd0%A6>74%$i>p|u}MYL~!s6`{1V8mF9R|s5L zh2px>vebfmDpH3}zbrXqMiJKI^yACexhZKoIZ zuBttTx*Ga@&DhSOS$BzCuNTzk7=38@6u&45)o2wR*}xZ%?HykbjU$c~5RGWF8|kG* zMaVI$o?#*~Rl)E3w1&6O7@u3}o2fJTnz7qjlILQDG3r^VZ}d6R3cHmR6e9lZSnDgt z>q~BCNLpjKRB@*(9@D8fEI?;N%5T)IOci$^xv1F~WlJ5z8*-w-+MoJ;fs*4>n?g3? zEGHY#IsVu`<0eI?|-rTgzE`Oe=Wny5T{q#&K-p~HKx(5XC>$$yvR;UrYc z*Z(USS$`_s%ikVCH+Yf*`Sa`?O0>hhv_o(106#CcbbQZB=x6kF&H*2sZ6|<_#~kr6 z&-=XXjzV9*G1^{h0ACZUlCKlKisC*1KVc40z{5_-c;HRQuJWPttszFIF7SqZ^OJ#} zfuc6x<<8vNB_9o`zeH@Q0iGhGSg*HLI|F&KAI1FFrGB7QJyHgK60_xipG+SXFI5a0 z@G^3D$d~YB_pcIoa&z1V{4CkH7Wh%xtpxnsF#lzrp)yZ9C0hgYO|6rF`I`8=f%AN@ zK_h~k=N8Ei%)TmpW&uCaKrD$snGt{O;xd%psBLjUeSzIXZ^*l&HiXz-KAT{Js8cft>L zKZ=EVjDFMRck+BDeIY9&0P}9A+mU&qPM?GstD8Nv-HTY)jRZO2`Y9fVFQh-BK0kkN zaOgeF)S9*a$`7Rs<%6GSN>0td+bQcqR|s#{??#N1zADl$1Kt+xO#m&%T@Kn|UjX3` z#otM9_P907ut9TgsQy8B)+!Tz@GvbL_@J8@0w4P=SbA*~OABx52A=+QJ_4Q=2#OGX zFkks?^Car`v9;Hz@sj6NJhlwZd%&`coF}?zq83K+gR5qTElD37nbwXY&+E70eZW)v zfk5C%!m*R^gFhL0!Fgxqs*v+UYgc94Gz9)-+`hL0{N>_V{s;Yjf}i(@7Wl=jE{D8? zjRufsdpHFA^L7V;{|EY4@OLj~`QLSZ2G9S6FtKgBbVnEiCTft!C4T+Z2jym(<}?TN z>5p+f=u@TPCg{%=c^0pd9V~r8ou$=^>qy_?EpB3@Z;|>^7Oxk7gjNz>Zw6=rujRS- zftSDMwLo*0vvjI==R2xz(U!gZ@2>DaU_9JwIwiVO3~^~On?$d&F(2*Nci8^59?~}M zclY@3Lxpr~4)x;{U7Y&<&-|^=Q?#p5>egnZj)-X+-@Ch67-tlEMSOoeZCn`mb}u7Q z4|n(Rm$p1r!c~V;PnI*s7=9Nt$$H#x1?!QKpfFiqF+TI2tVik0$JcgU6vZRwmv*`< z%G3KeW=qKH59MGfv+-Yy{!ACyzf(SVlj5b;XpyaGzw$UAudF4~i4c(EOx3`jt0wNP z%?L+<97D2d`?uiIpB(oojd}Eb5RIPRANCYAcSLP190A{)#%alq{NGGRC!?rG!q+eS z3rF<_-y3as5R39SXnA|pqp^S7LMcXh3_2-OdpUkQ3jcEFBpORAydfIB3jeE@@ai^t zQIoUyK*Kg!ojJAL7y+`VVEXy2ow-8XBTF!#i;zqWf2J~P*_ zS@P{hw6*HB`1YIIu~6By9J6p$r0ZyQ=0oKk^y*Y-Zp)k@+UB*dt>}ADusoHAmtILBdFbJ2HOWIE z`*KMhKJM)c{zaQsga5qz5ad^^QMpO-`4qmAJpA^PD#=4%0bL${q|5=$xs^%E&E!`aW$>gZu{^Bym2sT{>eDLqN0esx!75WWuGa{_+uY1aS`dV4PeZ%v6KE+^kiF&yHwfS-=MB;tqc z=idM?M;T{NTSO_+I~Ohnp5(LM!Rr@2n*e^6Sl^L<+S5c+A94kLMszcQpBwU%z>69e zix;1Ze}SjVR}_G!lV7-iAMx!2z>m`1e&9!6{Y<;5sQ^7%>;ugAxkUubx3a4W&NIVc zGtBeIxqg_py?PbQ+mnqB=f7*k1vvk$hp)qYx2%_d`HIJ|=K1(fYs|`~?+mfWEIu!m zu=u=b&Ek=hhsEP+6&9~=#96$$b#VZnch|7^%sb5DGx=;X@VR?35cv7;>RFhl&9i#o z^{n%D*CP^K^y8U_fS>jyX~0i@ogduq30`fu-=W2CfTy_HTfmb??=b9F<)aSpW3{CN z`1#LF4fu)jX7Ms?>itAB&Vl}bu@`vK@nZ4xl#>qp7#Zqo?xem?O7F|V-&Fr#+BG5l zVhe8iDh^g(>}8(rA@ly4nLy@=gmm2=3*GYezHT}Eze*FN= zOF9wOx6L;86+En^?H0QUyj@!NViVzQJpRH_(pMuY!@x_dQZeXN89zav4B12YL+dk@ z4i4n}W_({@N%%omPhBJY;Nq*Se&NsT1wJ$~S$gytO9vkt1fJeb&MhYWG@Q~x_`&a` zCyPzj^fJ^==M#SLu`P~p-U{qYa-Jytbt2&hOO?yR`7?w1;r#X9JOiFg%1#4Mv&|UJ zyGLWblRU4n<4KKFAE5UqKi%kUTfkiIb|3uJI~>4YP-_YJX;ps*zf0R#c`c2}koUy= zIrvMTN&x?U?+WnOC}H{kI_M40|KG?zIfsOQGH!;XKwedHDEocYHrn~RAn4P~5?<(2 zofCY}pH4?>pg+w6S^D=$miGJbjPxxQ_+do)7B!z>@yb!qr9k>LTfz-^t+|v8ylP){ z2c2BQ(sevW(Ny0e>t#)A+g?=C9QB3`s;*LBt=OHYa5iosbNZ>Ss7kysy5M_2pz+%` zv6$Jf3hC`=x!nFjl1TJ$a-{f@CiqjojiK>Z28~u244Da z7X^5|$2Ym0?qh=t9}BNP?nRsN^<{hbgpRtRqra+8MTYG_+qj(G8WtO%2;XgWt2R-e z7vL%MhEs4e-fHhfG{wh4qLH?vRGXl$6Egen`MbS&3+Ydf?FqV37>%x<=rwNt5{PEc z#*JS$I)GgIr_&}{LhvWAvR~{H2a)U0T%_5}{kUsyCDAzB`7hDv!56U;vF}pR)r~Ic zv(@7jQN7RlJiN^DNlyozsxS_^b`JN2{4rOwMR;*`_I)?}PvXc(w%Q)N*tKk{uC4=` z(wLj}Z{JD%e92C^&dCHRZT6YqHrq>miRi0Qzbhb$}B#E2&$) zImH7%aE|{t7`79o`d6Ou_bv(L9>|T;Q;9* z@IRk!2L9cCt7b^PLqsy<-!mN`d6?@_GRZ^9(F!V59y(`xisa$xqsJQ~R-Q#p!u+R& znm5pYr;I>;z*AiZUDr8Upu$h!r+bNgiQJ;3It96;=d(+`rL)2hS6?fVbqXi$8YTEnr@lkq3Uvj%)>fHaA`dUc6@h zBv^lCqi>yW2cFpc*1+p+jI@BC(4p|D+p1jj=us9w)2H_VKUTFYUPhHzyhJZ~4Ln7p z+5k^k!QQ}+<`cM%j%o`5KY8DOx|UvCMDLX4h53$skcIhX4YKAbR%s3AscCo^=6y5y zG0gkr*==zC54Y9A`R__;hWWCOvF5w6@+F+7*W0i1oR0<>d3G#5C-(ONpDy_<9$(h8 zc;qx=@j9zB1H7)%XYpyC!s64ihQ(*emYfTO&)PljfuHs-zz;T!sR3TSIu!NAi^b{f z=`4OyYYl;)JxOQaeh-*h!2O=%u>+ob?>z#ZrvGXHKXt)ru;0Ty{lHJBQ4;VYdx^!% z>hu@#x~c2wLl(<{Cy%<5z|%pl->~242bC{P7PHZ>uQxEH`Uf?n%M7Q~NYnM|q+s4F ztukTWSM)Q;Jn^mv(~%_{leF>|(r{h%c@bP!7d^vH`XfH@A4fvTU)qY6Yp{N_z&+*4 z=Q74GGfCi0O!1L0;myyp_Y~TeqI9=1(dP*y&5!@b{Ngsm0^vf z$CEGF(Rk>5JjVKwSIp5{`wzd(oR!ewEd8*X4qP~`Jex~jkPA1po($*ytcRr9gndTI;~s_l^(M*E0%@B>(9$E?rmK! z$n1B|6dn66i%nuWr_V3bM?U_$j`ltCLr2T6rG1+KK+rJ)ZZP{ z?58xoHPb^h8hF~cEUU@{9Tz@!^^2Yqol&^t%?@LK>iszTM$x=ALhZc8nd`KW;?sM3 zvXsoQSVZQNU8Y`0NER*aT<3xVe)bZLt&i&xjRxd5WqV)tL+R!#x~VUtu9y`%wroYK zHdfE@oR962qRT(6dcJ4*CRAZ^LeOcU4K7!)t=PHK6PxRduMoRxgtE0y&ON8zkH+Hy z{!@KPM@hR}c%Erf_pw=aO}XJB{I?zd{^4zoLQ2!6Jkn{nEVOOtO|1#8tUQtkC9_|gk4E}y{6X2gIl?VCu z+R2cA;q@rV!zWMIkUV53#63dgAy*fgjmbKZjJLHR;c-Yhb?BpCVzt!F$fYd3rT^!Fg(1Si!vgX8T~?QRg?p z`AeJr&?WONFlmJO`n6nz`7U%EhIvM&bvze~Yh^48mIXfF-MtBXPW4;?9^2}TfyWnl zj7Nl5ezgMNHTX_9@L9Tp#iy>X1Mu10`|KOx^YYjb@Ux-s1k6+KN*?ffFDB!0Mu#H( zPHGJBQ}`hZ`1vAs1@3pT$tU22{{@Su$s<$1lil?a*zbPd4A}3<+;QM%#_k#LQ!>fo z#cpzNvbS{kf^>s|(&Q>0G}7drzFl9vOwbEOE=9l2s*{m%9lt$R}J zbhS}q;4MN#lAH9|3K66Iq_31@XW2>r-~xWoQNOh~h<4N*BYlU%zDvXeO$jphN=Qgh z{e#*&p?~nzu^ixI>zc3kti<4WGj|~ z^FH8DeWN(_ydLQt{7dx#;$Ia{&J*+8Qh@XSxJ&`g-{PVa@bq}aW8i7$7LFPPrNB8M;8ec(}H9&uU({z7Ti;Lj1f9Q?*Ui?Zff!^#^DXXRaLSp@#&r%S-! zw`30HDdZCd{*{UT@ch>g{%mK6^fRhb!XWQM`{7@g5eHo;NE3MFj6Mdu%2e(FUfE2R z0Iy#Uu(a;50_aD*i^?f}F$%ar_(htotUjd`ogE^)sw>q3uSy=Vz)RKV2GAFOCxUis zy0Dz;TXcEjgYAjY4;juI9xvT@P#BjjQua-{V#RE#jv-2 zH$)R0^?F5ap7g52GS|L5HNbBVOty)LEkF`wBVYH5OjP_`YN0dm(**OrWnU}vnFk%c zZ>8m4D@u1}T)9H6N6{hC->CKY*kY|;)Out4O?0vz6_|17araB2g$o-}<;A_|25t(X zq-tpWw(7~}ew8RGqOL40G$M~(cE%A<$ zd*|7d*C2f4z-daOG8ZYL@hkS2V!dU{aMofsu5CeX^q3 zT@6y$h=z~&mwdgw9_4Shn!A_fiGIaL+&iPUm2Mv^aEi)9cRzjGOy%L4Ppu>m^Xzge}9-iGFFIxKc zJYG=3U0YIS!K{>2nr*}I)5q|FYow-kV@qTi)8+8TJ?f!fnB|NQ1D}kr(?Z0Fo&+VAZx9n~7%cu4< z6Mop=6mS!Mq&(e#motahfEUg>e&FdUM>FtLzPW>3M`f}?zz_e*{8GYCc|C^mB5XeQ64 zhRipUrvT;|eXILi!=}yjb(7V=XRN#tCVb|pxGeTUZ0Hv2RzE z5hG{pUbr$WDR%WzAiP$M+XFvWdCk86kX4~4X`S?>_(5ye&pTzl&|_*{42OBY z4qxz{%zNNpv=B8<%sZs6ml{lCa%R=T^^0DYddT(9Z=Vzr9@N509Ona_nXV%ylO@$w%5?eJ?@*x9wUEH+^`ZMO%^(TZ^C0>mJ!te8?rfo!rB;I&H^)1@S zv5D}D{Vp8zCA<#4sk8tT9Lb_p>T$)7G-x?36zTsX01m!|-}xhuJOqv{GgCHDAQn5ii9n0*Oc zmsz3&*Nya4)t_GBM)_g~zx|a_WbWYg|3ucy=eOA#$fz>Q{0=EM%Zyh@pe|-bH`93j z-Fl+H*izB2Dr+#0 z`3p@mUKLc{U$0Dkj5NCT;k%8Bq!zxQPzm~?YAa}!^ZXWS`Rh^Xsmj~@57bbz7gvIH zl_a_}w(ZbN*IK5d#RH2%b}t^sdh(EGZ951gmaT5GxjFR8pYPct}4gF5qLaUip zV%F`-2LBL)F7Pj~bOOJL3P^myyolo!GGXzA^7um>w>@IlYQX-B=m(N z-}2k2J?5S`=c1+b?J}j1cjk-O=G*nPj8yfaANxWO=3KES_*|?K4qQ#&U3_Z+y52Fc z@#~q-6$~YlT||r4hixJH=X}q@;N>f2GEs^j{L^FghIek# zOy9Ies$|{^51?mc-dTeZTGTwzhb&=sEiZ5(k*5E<$h30ByR+9PGHy;?K z=8yT-{2=_Gj)4)uqootH7twQX^I~lo4J2og3?NQ`m16%Z-+W%IByHygXBDMs`L$Vo`{pNaSu7~ zjZvc8$a%}%Rtuyw-fC*JnbO8d_R2HlyoFLPs*>~mw`|1&>O4`{=3@c#8U3`nebIy; z)Ny^3@PmiLtAP)f=|12iA@m>N2mcC@BK)8)QLBM3)x%4Gua&o!5q{9oC60t29DSb+ z=C`@@8W7G?7)~<9PNy1#Dro2=r^G643OK$=AcXjM2US3{SK7Y2hz9WvZ1O zQ?&e!kS`xj7c$C^2;CT1o0bvJK%BIWA7TMzY8w9F!blT+gj@9&cR$-N!#$Q zF28bPK1(F9V`gMvhcB|SzczFy&<-!r)T-xC9jr)ZTleyjg9#RH4ITWLb^{B(+9EHb z{f6dofaPC(m*t=0#`0@nu>4Ntvhof~u=2Jmv;0pdv;6D&S^lm(EPusC8o&G5eef(t zL5@c7XGZNOR^Il7Pd%eQx6=gn_go)X6o6h@yr%Z!gSoP@4cHfj;_lrePGRdjQO84; zPN1{&nj6Qd{W@aj)fcJ#nq#$_V$^>ARFp&+Qu}eh4PR+go&#>)v0wA)Gj_aWH68Tc z=}^#tBP72N+1;$*-126E#vT$cLHZ|F#B0|PHDxC9enZ}$@Vt=(^b7mBap;q@il-Zx zjq=Rq_1*A(bpEMACh4bJ)Gwry{(1OYkMs}v8xjwF_3gM?py?+ux}Tgiyx(ab3L}2_ zIPVbjjrj`=g_w-$LTAUw>m9EtL0;L>OAms2wCQ56mq7oFNk%~bgl)Ei zzM(hNLf@!fYk@wxladO3bnuHk^w0gAgU~uXf^TU35hI^rZj&#C)koxEw=$pXu=+4A77R+;Q*PxFc zC@MmJKlgvI-}3t(_;_n4XpOG2z1sJh8L4}dKb{v!WeDA~^t_np_guZi2?)Bl$hF ze@aOO^ecxLW&hS|@7(f%_Ajf!F5UD6E$y<1^BH$TY*OYlJ#}3jwb=<1jbbt#iN*r= z^&>VpKd0@CUYT~h=RISvZL7dVn?^GWtbkMx2!5_)J1%Ir02>fuX9QdKLpCJ#a>O&sV5CVU6 za6kCt3)4x&zrtXKjre0)hZf%ku3Uy&-jw}h6c_gLhCDnx>2y1Gs)td>7o?v;eShEG zy)q6j4@A>mXl1kr8>^zhAkWCxVe+_3B%Nq1EapKp^*vqx67)4kXvukfg+CXR&=maX zSKZcCGlCU$p z7{lP$aw}@SbP0M~xx*}-Tb5Zb`Bb(vT@5u>UfI5~KnCj?4ZI%RqKC!3D>GyNlV`Sn z{|eWOm2Keqo#sbvzOp>Xdq!S5?3D(y)g`NjtlxgqkRxXQ8s?v%!h1EXZ1{lK_dn_p zO~&P$&v{y84jLyM`2F~ZCKsBu;@m>?jMrcZ(cQCr>Cy%2xb|ggbVuhZ=EcUA#9A+V zJpZxTTi4nIX)`ZfIIHT2PHfd(baCr?9Q`vDbhDENXpTnO0=4vQi2gACMMmp-L3Rs;45%exk?Tz4+qt6EL@L?9P!KglMQ*bv$l}8DBBYJ*Y6Sre+#S9Wa2NhC>8um znx@2}rEa3xOChbz(JSe9Dup2LGXJb$r)oPR`|-b#&)XgG*!3sU-XUu-m!Qy+&n6s* zKU`?*oAD9jz}~NmiEgsXcOyC{NNUCDZcn7rQmD20xGmoHfwPV`j2-QieRodz<_F_j ze3O1g6YhBJspAC-CTzIFG~A!)V1ehLbIMlbs%+K9%fpK&xi7Azzi?GKLh*wYV?wIk zb(S;ltoxWx=6$~P-5i;>ZR>#yYMv;+%y#LsR6Y78NBPARUpRBy8Xt-;RB|LZn9RR{ zZ~H@P{?u>f&l7&oa>K$?*CRRUi!)xz5FSs=Opx_hi!IlOT3_*6VHS7^c>Ah_@K?97 zWqNCpDLsFkD7-#WNX!3-yfjmya)9uIT-ABEQvBdCr%67-$FjCz;Nz8#BjE?v9bQTJ zLEARC0Z%HOgOY?V<@MT~6hFA1A;BSw^ytG#c$A#?;(%>%-Z-UBnL1BgS-Y2>AU9sTN$A$eG;K^gp~ zgl55C(yIXSeRi2cezw&hJP*6?2LP|hDV!t^DQw|%!wDz|kY?1L2^cms3p#9HJ370PyLuodRZ%0akXx_~tC6OL< zW(X3xS7czt+$~T#));Dv0_&rCPq?kdXMY!iMzSYBi;oph+7YQ8{6J|lydkK|0)6DC zD<-sPwwhm}-3(2L$#C{T{c=CLcdS19X_C_lh3#BZoh(5|*Yv}T&6n-KnHA36 zr=#pqE8C-!EiZ!6zCrfgrE;$L=)URHOuN|%v;Bt-nV4AM^bJ}Hp~G2t>dK?3m2rc# zv=c1<*7|<%cM)g#^?R}WoU&3NFW*}i@_Mu%g8y254)7oKVfpV%WBCiqANT$9bO-(< zuIs&X={Q4IkCo>(z%%*IeVTScB!Jpa23W8lKcXcDRAp%+-E_0c*~FDq<;*fvahW^p~GBR-$UM?FmvS6HqtLg;soE2KJmH} zDp>bjl_{g2e4q4_(=&&kq@TXNW9%aRv(a;=h`j%#N_~dDGVz%725MaTJ9254qS*^8SG|toOaU)}%{c_RAdlhaPVT{qyJi zYPer@lPS30=tnuwN4XKlp^w%}zJ&d{Wy1R>5<0v*g}h(#E%Jl@3Dslu&0)E^Nv{D{ z=88QA&`1AW{g+4j$aR(n_FFO2*}QBqJ6*`H*R@J?gmHSb@3ErRO-7qT)oJcKYf<#F z?#>tf6BRpEPx|!k7DjA*sWs7Dw;6T1a2<=(eIwV6J=e>fnrV|{wx0icQ_{~2zpFYy z*5g!zDtphj?X;$IqD$y0RkU{dU3WS&TNt+gWaf>|{xk!+2JF!%Rgw%LykPhtlMdq^~ZGt_3iPYlYV?HFA$R1HA*yE zKDnJ}ykOerwT50FjmyqA`EW}gV@pFCyCL;^pC24zw@%!D%0QoN49zTe<5O*U|C*Kk zk*A!7j?LT6IHjDaze>v*7yf*5lk;pivQAa=wN&4Zv#uUCuo(`+!xy-W4*P6H^n1_q z4_EUbX{Syb8FOb;5qj#s;@9`l*vyrfyQ`*YD!;)WO*ex-R`~;dxTFC55MPQW$wRN) zDo7q$Tnhfk^8xtdiM~4GkDr-+Esu#t>uhTbHhA#URjhLd z|f8eb!OEST7yr})1;5Sm^)!n)p!gi+IA2$+Ry?T3kcrd^E$eg zA^oCM{B}<$BdSh{FD`N^(s6mmy-HVxdAXvdOT<|hNsV*gIqW8j_0;vFBQ6-?_m6~6 zrN=E{W*umS>t>IOZj@B0t+SSoU1 z@wlngmP&obC#C#K4lu>he|60tPu84>AU3SiC*8z+eC9H}LO>TLb=f znk&KIZ(+qh>#G@PoZiQr5m-t0aOs7-^q<8)1`hTzWRf@DJ=Euhue7EGucm(9v4s8c zuF)9|Y+Mw}t!*!g!uSpL5#6w5ij(M*a`l3JQ`?c%Yi3AuffF`e8ERPkrQcZMQ&EGz ze4nvsyOhy$MR)wb(CNwc(r?B*4>p2cW1&+{^ioYljclZc)e=2lKE1V;{ye*yi{b~5 z-M9V0eP%iH%AZ3&$h>bW_^cuGmMD5f=7|jF;zDF;8eX6aJ7^NriG;uryQt{6@kLS}1Ey_`$X>bb$|s32ra6x~$$1M#ZYSr7q7MD530cNL zzkcf@;RiiB1$^RA*Q>yXM*8|!gumujC4?WmHyZdsdoR2LzWn#t0$wJ)nT0!@5n${pqk3V zwXqk#KV2*r{M%a2L4FQzG~pM8zcUpg&x6iGPT;lvj0ed>yxLnU2`^^N&w$^lsz7PfM`nmuLy;t&zyXzf2YBA1tgT_9}j3tk&}}YVPwy75SF*t9?%RR=n)Gv_0#Qy++ws-RWP7W?3_?H2#deC1#Y$FcGPoFD(4Zxp0UPYF_a!D#aZbt=yn zzkXKe?UN9Ouko1mCIoCl%Jd@8^Pf#XA9yLPpSH&tUyMH#nc8E4H@)`dN_)kF=jPO? z{W#)PFKwwj>fhz7Z%In<3Sp^jPN2{H$Og?wKSk~L2C_6Zlk{1qNPqlEg_rctr!N!V z<=$&EdCT8Gzr;yPLciRaqeGv3P>PmbdRLV>I%)i!^wU|(o2y7a#fbGo|5)ZF9wL2m zqHGrW%G?&M+ZVo+?xki7?{}M$oQNO3J9QiSX0TXz)<)c%nfgEW-aMMh@BjNZL@1FA zDTM|ZLZO7~*oH{6BJ)_Ll4Q(OB&p0(1BoJ3$`BdqI(A7VR75Fct`wCaLkYiY@9TW- zwZ8ZIeb>F#y?%fF?z=zUdbaoVe4Ty09c_{4V*`YN&Rop_1^6_asG5tKR&qq zD8Z;=q5RmL6LQ#ju&MX%y~>&Yy2M0uuV{u@7kR9vdTdDmo@ zg!7JlbTk>|n_{aI&Nt@S(M>4dVhd%4QNDc-#CZT8JN2Oa(Y|q53g?}hyz%mj#i|U2 z3_5>;|8Oip&&R5BS@`iDjiR3~d1hU39v#vCht2~%m=S{wA^zxQ@2bsbfaQ=bq@qRU^{-@s`aQ-MoeYl%u$j+#(w{3FE<6&;Ge4Wj| zGKZv|eP;YsGyzAuW6E3azf}aAdbW{2^>bhrYAgRe93Z0a;dSV=$_A}#3q<<|)S4?W zPc6zetMxR5eDxd9emELAQMPfQl^FI|t(5b#h_F7n{>!g|5$fkVN+rGz&IjyT`>6e! zf&E)1(opyY*6_fG!jZ~liy2?5uQFOYE|5z5rS(&8aWD=wJY0Axc9>8Ykjq$pYd`#b zbHQlTEq#!6a~;y4F_jl-xO2Gjj})VqnEjD;g8#I=GoXefW3c~?XjuMnau(Jywf5@ML{b5pmOaRZN0aD;(Vf}$>L3nTr zt_O3g)*an&PKep|IidSN;2bFxVo~LrTtN=*gGQOEy5O+vo$5_f)aw?$F_EVJ9(N0A zFuOiJE<#I^F{igQbuZs%qCMh#Qhdx5S+q#ELg8EsDZ+hm&FC*1*sWpk@<25k=;Fm1 z7_u264cn}Dt+z;;Cazo)+FDU{kyJcX&$o6UyXsrG^`b8o(?pz+MYa&J0k{qoysnYy z)V|C9ZNEV_JG`yFz5GoWH}lEd0ldEC_un^YU30=~M06z|jO~Te*Js(8A{x!6X#ZdP z9nX(QNirMjH<9WM162nI`KVG99VUSB+KLbYUCplM8n#oY?{Ur9W4BvaJWoWx5mR_wa%8 z;K-Ei@+(2H?GmMWb1iU`tP_wCS;f5l_4|J0UppR%{SC8Su-}|f0``-Bo{8%nHMGX{ zjw{^2{yW+$vH#cU{n+0qZW#MB_1LebemVg>on`kf-?Nwzvswk$ONm@FX5-sKUazQp z?r>=zv=cN1Mq4()$)@3Z1K!GTY0&Wy>ea&_Xud@O(#9nlv9{21^4R2M3NGH4h=Ss$fgk_vE1KHYw7CxJj9fg@`DOMe*-=`OQ;eLM&S>#Cd6Zl@eb#2!H2}Ww$ zUc?V97+Cq9;-`v@Kh+2Eu-r(B4f0KDuLQG*AE5O7DVy!K38IG$WFa1J|IBMdyy>L9 zL%cwj^T&T8Ugli-F@Jgk)0NxSIWa$q#+#t)H(2(69d6-bLL(2v5BNSah4_Kj_FiE_ zd?fVc@FG4$Ul<{N;87nl#1H71_9;Vr>AyJw5nnb{s=q0Isy_Y+{@J`lhQa>f%nNkC zZV+|cvyZRx}QM7$08ry?~W_MLFoREuT6MLX>emjzzIsj$*5-;=zdpU z(X>PNyC<;#-A~{%oGI~7Z2_aCu>@+C&^(yj z4Z*xBScRZ^U@hUa1o2b2j}7y?DlNB2`s)#9zuBB3s;_#Udxcr%V#c;-pF zZW>70IzU)!TouZAuo`{{tYb^Jf2Fo$T%2j%{K_m>9!XNK}k3-saAhL>3xr{#fn z(jM(OTT{?!YSTCAt`FUMTZv;|TeZLZ^jPuw$vSvHZ!$Yk>nLn#=Go}rEymz%oWuUj zFWs^Ke#Up~XS1Oi`;kfExZYdpZ6Q#-E3fsizxHcK?9Y=lf&FhKdSidX;PEX}=L})S zu-`S#OH{rkPvCkRgDvh{H(Se?zw7IPzu!zjhICBQ+he=n*y^3ha+~bnoYn3q`-M9| zo|P%qzMl=Tj>xV*V_T;S|D?oEe)p$749f-R9{c)LyFqL5%GXcS;cuT-jj3~+p<(Rj zzAUX_?Yh`btovMhu->&w;AWy$2pBdHUA*0X3xjyku!Va5($2qE5LBxy##G|jhV!K> zsqX{Im(D)k11Jwp25E{r$Othze~ElV`7|X7ucGH8r_Fb9{?uj~qx`Ar5jlqQYH?9n zweapqLZ|8g&L98gA(U6zz2Sdx-rNW&TX*l2Gm}HR3Fna&V>y04+x{X3=a=>l#Us=I z)-V=XpTPMud5A9^e=6nNR1>?Z(szlt0AF z%D(ISWf(Hb)cbN%`5>r$1m{ESrYf8VA3V0>d6cc*iSy#S??;>$4!5r0`Q4QC6z7BG z(=+J#1$H(V<9v{86T^9M-&NnE`sWJ9&G)BpzKx&F!ufXfVALIyXLXD3;yjzRJAm`9 zHb4O9-Pv4coNo)d>~X$jI=5${d~4X+c?jj(OZ_OEKcU7ID1X4)Pw6=Cj80B2EZ#54 zn2x@P^T#QZ!1?p{tQ3B{ZH8PpZ{9!q4k1UGU;Pcki_<#Dojh*4TGC+!>QJQI6Cy^fe zy_@{_DrR2R;*Fpr?xfQC;~%RU=3h72T-~896O_9<@Ja(g?8WPlBX})Z*A{79Fh6-v zpBYklOeX6L0}h`4wx8NxMVPm9uKmEnIQD4iU%iFH!~yE{E{C*vnBALsm-xzYfXn+i z*Jhnj1ixg~Aq{IQ<|7T)lzz32=3dWOp}AyP+x|nCkosQ<1-DOnz*e|Q{$fdgRhy-Z$6S&cxW*|espk^_1RT$wo40K(W9s~F*AcYE=7tg$Sw#PcbIa@OL|0rnJgTz*7(?QqT5$(&!#i_ zut-*ENkmm|Rp?5r!6g?-q_y|%x?W_uK%DU;+2?bh>U%OZ@$9jk${O&>ss)!ANu~@d z(zA0ItO6iU^Ju{;>h*a2hkCPgO+kp3`~ilv5~F{yEz9+gE}Thge@DPSAWcY3GtQFkLl{=Uzc#pF7$sW16}gO(&bnm4A(DXRzaXMV2? z@mvqD)f%ZGePftY5^2Yt9SZd(PVl6`&6$e&9gsiOCcWX3G)ORvx3)>QVCeFTG&S;@ zf^9L?CF&1!VQGHfF)vwVn6%@nfAGjUaJIAA?@GEA*jf^5#(W6Cvy3%y5A96A^Pt7k zuf%u54xT}F|7V-QN0V0!H$F-D#qUgeZIUizi;e$c-?oWa_wGw0^54n+1N(Q~>cf7s zQP;5FK>DiVs2-nfFRmBH>xTU=NqzW>{L|~tWB(tk8?}-D=|?N1UG%B<@A@$Hs-#z) zc|GbJuJ@#TtBzxrFe5lwtwj5!4Qvo{PHNhx2iKiVd&NP0KegbW#G%|yW8l8s73)4R zL#)k}-bE?;?1Ml}Nbb3wK2SSt)R@D)7#h@*ay=2V+9?jpYJL7J1FuG%lYY90fu1|Q zSW8#$!}{;NOE&}ubYbGq;n$xhw3$3p(MKqLz=T7D;E{jA%&}c<)jCwaBd)n}M^XJQ zVDfiU{e;O^W;?}q8Zu`;S0jF4=#dwQAISCK&KxfSsP^LSzfS@%78D|1Hn{-A!Da zO!!UlLvgj)lHvzYBch1-SY9Ic7xBSyTpIBM*URQ1e!zPC^6C%>JdM%AdRP#;uE~-3XDPg zy-F&aLVN@+$U(eZVAjNBtM%9DGdjrO)dZ6TpPyq7pG^V~u zO!;^I)I5snGrC^j`rR?@Gk(-OY|l5w^RP7IS`AeXmYCA}d@ZpWj(>3W9Xnpy4<#%YkCBJF)?kXj~yUx%w*vpVP|H@swzxSeF$mh4R%okGL zr;9#|gE=R8vLS6b820KE*ASS?_< zg(-AWR@`BJ0dr8naLhL#j#3FZDjC!4gQndeG~QwEvaujv~Wy zK)ksndBco2yjsp<+tJ{~7<*8w{^MyL@m;!FeL!Y4Sgm~QmaFkHs5#n@x1mlGMw#)Y zR+ovv#yB=+{dP@Qq$lw2oxTFt;oouib1(IN*uGVYN8=a5?zYBf!YhT~r1sub?2CVB zUzBvaYS5|*b!xiiEQ_}?js#Y2LH@f-opOIi4#l66UaoysH z)PRgCux~n+>}o6zd2LH%GOy{ve}_)Ge^A{BF0Ed&4(X8eUq_H0H8SqmP`3kQJvs8G zx@;Y|WV)bAc;zY>@^_-2ZSjn@*QAf=9+OG!ws+4ozACJPx1W9$L)tN+9P3@1ax)ED z^})z{@64X7?u;Gd8$D3|Y%Z=I%M1QYt_)s^p1Ns zZ$k=EJ~A4IK(zkMgI;O+XOkPmdaV2Ff4T$~&6|vhCCz=T`A_7Eel_v{Hd|~hVy6NK1G~2 zs@pH&yy0|Oh4Uy4Sm8X{v8@y5UyNuFKCj5(44gk<O9nASo|+ZX@HIiV0c=`(L}GbzpQ!bJ)Mtn;EM+?fX}KH5jtYMf>3= zr;28)8E)pWQ)S;QZVwXkyB-=uYN;~)t-a3nWXglv>ldB@Ne$A07D8a#|FO9vM@i zG*FOL{7h+hGCps)c$or&anVVpy;_eMao_uw&QE=2#0l5(x8jSKReN)4_%3aL6`4lw zKfjcr-git$z)p1=Fn*u*?%0Jj;L=|89ZODYfa4{cX`cVq!AD6xO4il7u*A5`Sf8mz86ZmAoSkDmm{J37~=c7kw2J}+l2h#QkNab4>}&V zKz_i)Eol)|588a%iR!`4xs}KtoI6p4{NWb`<(-s2Y_~WlK>2I;S4hMQrV`A=i&|z$ zPpEtm{(|a3_ASd|%@0~KrF)zgBq(fQZn(ILY|B<*s`9yD1%0rBkpKtXni4$QY7<+BFG_KARx|%y=YV?O3x}M>`TXi02pJ_8l zda?}I5&}rWU6x;whD~+mKDPyTF(&(qnZ*H(q84NqfXK4{m$ToH(oI z$oSPEdKv8xu;dZ*-*%WGDLj~cI$i?qHsxBgRQglZwLINJY7xU#k(rZ@D~n# zLM69!-&hJBukboFZ>1QRT5`g3XZ0e;=8(JTG4=PN-&~{VQx5B(a?!;!iwYTVRmC9A)b}G@Fvy4o!G2$e%Czj1%*T0p~V~AMj+rm+PIjGjqlF z=6=*~ur_k8fQT#v$wyh_t#sipwAFIA0-lYBIauIL)iyu`{`?Ys=?sG-FxLYgUMErReJN6c= zz0Q<=cq##1ulCZxz$e3$Nvu;Bp!fl%EAOyV{6H@+{xZaeS%h3E;^SP(M;nSK_`ADC zk>Uri>Bg%fzRKM9+(@K&Y7+itMezf#vfUE=JLyOLeCXz*=zjYRSn{L$9dVAR-DeAqIsQaxFkm!c)xA48D*VO$7=eV16C|#A_?D{_KVW~$xDLe+JpKF1J;X<~&EbB;N7zEcB#Iwc*D~^$;sO@H`=LsASj1J2KjRcmEA`EMqH_>sQxeFS`Mmz z;KM1HK=FE*Cz2b@gQtx_OatS!c2v|!U&M<~cI_v`?}AuUO<0@EeBb@42i5OvZ*$z! zW5l>R&-QwUiWie)oJnO{w}+wKsNO$kAO>14wkB$`&j_O|TD>N>l^~Itk#k=fQfye`*bqgoF7~paJ8MHrR^oF_k_)g|8-R8+TgLPob3Rwqj zb}=y6y()f}oe=oBhf7B|UlGJ5=q4h)d*2aVq{A;*WakI12PPAadTQnN%i*fS*(n8IyM!7IM=b%f z=ZCrT3&j|Zf>Mj7LU%C36`%ODmkw5)^c2aEdq7sLlUNvVr&0lOy;1`ol-OX1q0?SQ zdrk@}$}UfmLvL z^}Gb-BbJOyW6o?Hl>f{x6(CRf*Y3Dziu^9w3-lpBK`ZJzcvQVVk>y)ZJxQ%AqR9VC z&!)Gs3OPmYWdnOnjaOJc;v$U9iE@l{woV_TP2c6j}?qyeg2!a)&T((FyrSF z_Jhxb;VrrT9Lk@C`NrFd{`oQ2e{P;c`LbHz3eFebk(q}m4~*jvmkIn*WSB;`enR=Q zqq2}~Ih9XmPO4>~{ArfX!g(U^)?JVC%C>&KgY%A4Ok=~`cPM`v)8rCS{sh;)uS9uc zdeK>kZ#0Xk6e)=F=%zK(3FXn2wA_oR-nuq!+vu4i%pKC=f+&B)yWfA=L*-A;v$pLh zZ|vrZ0Lq)FV)sgvNADA_Fi{?zh&3}q`J*5)l8f@EJZeKc%Ac5fcG7nneiMhGavI8q zZI{d$C?7Nm_nt-bm~&?h6V0RL!t+@uFSs}E?L&Dnc6&q%&F|bilNdC=&*$Bm&ZP3e z(Yh4PE2uOcZ$uG`V?7rwYf%0zq zO!z94ceh%D+flwX_N|sd`F7s5IRfQdu*KY9GL>&_nxD3#{Ly7fnV>xQBDXIK<=wQ3 z$f_TlM;J}XLK!H3KF;?!iSmchGP@c*-oJ8v`RMVgAABc`@`$m^vIFH&_BOc~bY7Dy zpGb7x40%C+ls^_+%Xm=!+}BHfiSlONvq_B|2h*4aqhdo;eeHECGQ3fJXP)>Lbl#=m zJ(XgjM;RGQTh>{vvSuzK$Wcc-Sw?5%gWt$Hj435cBHyyzN}l8-Z`_eUjP_57}7ez4p~+cIhwf>rk>} z5QA+uSD)IBPf%F?&BtS%?r|jJ!LD|@4oXszE=?95c#Gx^j;N}HK#W`6(b?4_A^A~~#35o^3y_ZAY%eVElH;91hd%pcUi{?XaNy{D~r*`d47ea%dPD+6f8_$`i z99{q?>K&RsY*Pj&6+MnUxMRiW_8Kap{Hty@&TyaWsI5AoizqVvalbR;X$jcdt$_b+2LW1Ed z&sGW0IhUSHX(*iOTTf~DW6dXftHMA=$tgWO2KY>v?l&-CV`FEV`=5TXv3_j{W5>Va z`OhEx->?5i-2o#5U1M}$3)ZOxSzo(DX)aijZ3F8!|M^p+|1A7(el^*%cPl%^<$t(4 ze&K)9=>PD`*x9(KyGZ?FHHMu{7T*ze4*Cy{vhL3s*4O`8bFziuOGP=@*ii4iS^NH1 zUlvPIKXQXN>9DCSU{jmpWMezby68VW{qH>f->&7N+|VQD;GiEk58G0FIVbz|v#g_y z{a3&LkosT$6fuSN(^s>xEykx}W2*?KT|Pw6U;kIRcs+o+A=Iz`)miYS|NEc+-`#=# zE*Ga*{QT#@|6k|gf5-1s5Y6M#ll0gBZr}B8F8t62Lg{_!Lx*K{I9CIPQ`ZZt(!0dO zL2W^=+EQZA_{X+m!WZEEKORR5imSmDW4BNXpBTczV_6n)qL>h3t#j}5q56&~r87S2{0T-&$`uJX2e?K;k z)`5%tZL|)?fa={Lxh#14Ls27#crt0M@hdS5T!;12rh{u%rNHEL>6NedM!~7_po95g zBEa9kdh{pt`L?&C4d&RoOu*@v{l6#;yt(C(hPSy?>|5K-4*H5k!qiJpEJV5{DE_5ECGd+hNZ*Flee!MkpeZ z;`4qKm2^Sp%}o1eN1X>mjNM1)fiF*Oen!;;0&YfmJE;FJC=aD|C`Z`3pW;Ex5heb2 z=)C`=-ktY1xJQg$Va^V$r~!J%PIovw7p@!s+^LE|5Fdp6syuRdR1OyXr zzlDZ}-67}M2QE`8e@h&Wr`P2=oRy>aR6#M1;pOVPh2(SA{tkj*d9m!5M1aK&;2O|% zTzShmSQ&OOKt96>9yM{B>Avd@%>rpnn9|zAwjQlh?|0yX)`29K%yfx&CY)H<>ydw} zh%gO48Y7)?10IVIo7cvB8M;~foQhd>0qXlQCz*U&;AT(MHG#X1)IUF;sf*@!1e&2l z2Bjex=z}!mRQlBU`#=OV>-z9OdDaP3i*#5Q4P1pavU^9CalIxASpM>nwExwgv|rQ& z?f2e_uJ{ukHN{^f6Je^@-{sS{}kWri(vTU8$u$3^LS8E1Eo?6R*V z(p}8ac|c^N06Gt7Z`+I?FFaO=AFoI+t$+H`dfxrB_`F&Q4pZp7Xxs2B7|5St%j88CAzKr4X#wl(s(obn26O+|Bs>so(VaidmtW zrW{LfAi29lc`TdQ`kCHuqd)!8bn|Dj>@BD3yvb?OXA${)lU2J4mF_J+ zv=0jK#$Y`(NNcljb$4oAyESh+S_iIq{nK@#mtf8XX!(?@g`40IaG#+w|snfLpcZi)7fG0K;Cgozk#P!3b$MbDS%S@jV=_ zdTO;?|CKRV^R(dRpO#Cov{3YwX!IxY&r;fd%MIE;>j~|5m*qFJhOW2QkgoU0kM{S_ zqy1lc)BcAiY5&4!g;NcU>Y(x&cMp;9vNG78J!1gEH6dp0b={a z@#F1IK8+vmb6r|n#nC$PV>dpp+;|y2?;DQCH>mS~%IGe19=NZD{|8kM$`;Jltp4T0 z9GO~(wN~46&Y)2r=Dy|6@OdL75|+kGG!q-ytYu4n-U3fLL-{{HW@DDF6@^mCyqK|}k6z!L_o2Qcj(WXzRWbAE(;kw8 zwO`@l7`$9`pR6Nh@ndF2NmLYKYTe=aAM@=Z_-8ZBf1{m%0GE?Sr;es+*Z1t zs@I4|jnWs=qU3qPxbPKlI%8qobNw)QY$Dx1!0Z?}k=+YEL*4*Yb5bb~C>F-3F39I#%*f6=eJMN%c9}&G0zd8t6-;9ZsY)#_pOzqG*qiKLmKKG5;$V-bqb0vQW(&kvxR(lDN&kThoD2m z_`|T=KB8KS_K#1c{nOXber#g2-!U1w9+$ZuuE*9&`=9rq{bizQe`jmj|1f=6P=csc|scnj(ndN z8)4Y)eRk@t|48mia?1x5F=m_&z0SM$mBYA~5!{kGf4qZVnDI7^3-4EzSG;c?Bgo*Z z{QlUlc@=Z_db}U>u6>F2gT>=Zio%$)gzfWstYtpmyMOAW6r;n17hk_aI(X@1!csnYW{cfO^>9K8GZ=wS6(sOS1`(aoG=@MWG>s;A*ec*Z{L`Mzo`NJu!HotU-+ zndDmkC|@%Wt#SitSUkA_X{eZ|9egz-6!IGJs8-xR0Urfq^T(aJ3m#|19i4wvi`XU&={Ata5rtRLJfe6L~ zZyG-DTFumbJ_1XbuYI)kt&g?@+Ns02mM+?ihei+Qhd&TwToQ~w>9|Y_zP*!nyHm~` zjt%c7)(xy-o_1pomH@g8MaNvct_v+pEX)tWk|$1D`eIv{#&1*b{s&ih{1%jx3!shzGLZ}jHpivExhSqz1gy-w|!18&)%8dJNFeiXfTxnPWmI@yq=hc;GNGSit z{O{XQ1w8i9S8TEhp^7+~P$uaXFt}G5$qNlgEqxE++SevqKkK&TO#BZON zKyBk-a$8ht`r}+bQq6KnwS-MLU~3w`Ja2;!aWwAzmzX2bWVd?szoTETz=}M!Y3@lw z;;HJ_0N2|&S_Hixib~sQIXj#t^+#WnK4@J}_R{;Q*TrkeCK)*p#@Vy!TH^-^t1Ivf z^(tS|ah_KDjHUzed~Qc!`J))1y!3Kq`!5=OVE5PIHm-r=pnUI& zm%^8CLbEZRhnj3zu zO91(r!NJ7CzJQlr7*1LSYZkG}6ll7XiZ@alI92b;u_7M?$!2S;Q3nhxmQfSNzk zj6RtM!6uunLcBs-p~wYQHy?rHP)FKF-uKh<^PJd#47 zbjE~lthEOyA63$GEQ^D_^B9XsoiHLe?=Pwc9XgazJ^0u>7}W!Z3))dVaNvb8_P5!Q zCWic@Q{8a=h5oMuQT;b+3H?+(5Weaass|lA>`^_yrANI))Y=2uUsf4)YfJ?jGD6ON zl?`VeB2{ty_?eD8zUu~L+Qn6p;xdt>!L6m-<)%8sN){jTZ*q?9NIpqyVVK^SZX4qdw%xrcF1blKlu4KcGI(`tg~jWKx5-dTX}EaZ-WBPsNj8 zAv5DHAe~fs6aQk=4&-c62}0 zS^fUT>NgLozmps3{*JNwTh8h)V~pnwQd-p%i{Api_cFi9s^lCma=%YWbtaw z;?s}Cr!|YuBP>2&viQ-b`GK42XkM@RdV-fC)?^-wpW`fk>{+~=VDWN`#giY4CvO%% z3;bw)vNqEEc(VAhWAXC)S@gw0ikG7-o;uM>jwzn%So}<|b$xpXj+3W(TUWNI%9ARe zlux-AQAGm+m9OsL__(hyA$5~pLr3GVRaXy{N zq4P;28CFh zAC9A+aDFVcGsg3J;AcCY*W_Q0Xdc1yho|ZW$HR%YEdOkl|817vTb3U`t6pX|U9Xnq zZ_e^Bv8VktS^j&rYT@~3G=}ZxwGJR-S@nFTWI=V!it zB+kzumM&*$F<&8+x1jns7s^{uJl_-NX~rpkf0U;|25LC(?)2+pUXob4mZe4WU$9eo z3r>Bi8|WMJCbazF2Z7QXM0sdR!c5*3@|>WL%hsDOz*EzAEB?H=K^_gNy(k;3Mo2Ch zIk)iEO``P15{Jhbv7kyS#yuc2oiH-?v~#h_BlhJ_jlKSs0c*_U0zZalkg!?s>pqP` z|f7QdY?i2$BVm+8E{DdhHDT*{B0gu>H9+&8~(4hEpdd5gRDZ8*(v z_E1U6h21)0Y3+1U@Y3r= z{?;)6vfY`(oc_d^jX1a=a2*)1De{RwjRk3I+{YJb6+pve>BUbyb71A6OS`{_MM6JM znP8iD`{8OSiTg`Mf}v$RXSw%2H{f*I+Pc7b4>XQd{xh-gG}s+l_wvY*bI{_EjbozS z36NxQOZks-Doj?M8=gIMncN_$hU&p-ktL`eY;4|z>Vd$gBB&m?sb!4)&&EfWA%BIu zBe?#SC0_$k{Wo8VOsIMwgm)vV2iJe=LG{48ZKpSM{Syg|VIKbk;`_4$-x$t7*&BGI+%~9#Za~4133YFRuzS%^! zP5-)Gxu;3R0@D<8&K<-q?-1@)=F#L4*7YsCcNh40UnYV_CjL3*1`^dQey*_iIm6<` zUV-o28?)9bnyNT6bVOBr6`4XL%ZcZn2SbPq!_#9#Jc!$NKEsNL3EMCvE_&m?z zQ(+a&XIL-IXBvwiEt(&Az=7tq+fv!#@@`+^`%0RhjiNL^Z&|!3uz0z};>n7|)8Aa0 zA8!^vCOtGir&;{0X7R#xuf6t*K^)VF#nVO>PrF(C*cn}Zs-h7?2zKdjQ(ag>j`2T= zIX)asX0iIcXUWds6v1esc)hK>;^C7-U5#@wU-qH@!Nb(ScYbhZ9=YyGta^ppIpVye zx;tL)7S?6{RJa@dVHWB$XJ${*;WQHt^h&O3D<=L@@k9L^W-leF&lrFGJv zCeEio&sm<jg4tsQiJ6%3C!??q4Ntu+#TDu~Q7stMN`>G>=q{Xa`aA2rgXI!1KCc zV=A6kY5#*bKSJ4-;{2G<*oo)$EFkc_ZsTu3^9U{kdMvgIyh+|>`Tt`1Ggy9u%Cw&t ztKLsnx?Z0j?LRdqhW&5K(f*L-KQ3*8=f6TAnNQaufhb|s6V#5&9&b)1SABNGdHO7^ zF&pJ+eapX8l%MIxNt~a4EFH+wc?vctZ$aE^2b8zK!G#a!X?gpzy(mwAj6&y7e96QwS-PqSh16eTCqwnO9Y?<}R^wk#rT0PBn;#bLzX1 z_mFsbuj`B5)|+IQ>-!O+BA*-$jH>V2ng|Ze?Ot2g@sMm*`MO;xkx4pll@Aksl@2$x z&HNPh$|dr~joeT4T}a*hCQS7&+^Ar5F^KLq(U6RM@}T zeah^(54_F)nE%!fH|XGygEa8?bRTJ8tkA0N?i2;ocbq)l6p%?6{Z##Rt127@M9p8w zwJi+X@hfm0UXup{88)I0b~!Mx>WxK8el!d<34NTS6bXgADv$ErN!Q3}t z)^Lz>-!di8B)4TB8R!D}cUm?{CZ8ihZyRT=Psjpx#>VerD-%It(~p>{JNKZs)%<-? z-?O2x2={jqGQ9j)5lrn(AZ)Gx)SlX;a6tNH9=R z@mcCH_07Hgv1OCjLO{fcXO8Z+*J0nn*<~l9%Lv~;o~Rx?JE4v0!ICAfQ9batdCHio z2c)x1u)nXR4EA>oul&)e8~-!bnpVUxx;L4<$dWCYE&S$SR(ALhh#d0j<$Ck}6Y;EJS_nX6#?spukzm`*Uf5o}z{@!QxcTWJ_PgVEP^%3oliSZvapHD8( ze9EwR+|J^$)c3#gdgwUKr!R|7P8OfPzSDeWv-lA;!TiA8nl!J!xv3B0I_65WviR{~ z@e`d%^K##s=A~Vj=Bb^8CHIe%^7M#oK#NFV_gG z8|2WuxWBxM=Om(o3Z;;Cy=dh309?qiB>rFfOuEq%!9zdAEt4*ZWKL@VqW( zzDM&2SSu-d0l4MgY#qE>c427p~}8@B%0UZ`&xKjeVY5wJW}7s z3ZIcIctAXDr2VVJXnz?!+OOmq?N`gHN3NmkWwZPjtfl?457GW2EdMt3L_GhoN-l=` zf0vR1ta`pzpKY@)s3NRCy5c;|sQ$J9<*D?YBAlOXZT>hvEm=B@rHww_L3s)>vJr^?T^iY`RA93)DG}z3rfxsT^NgsnE4f<#min;fgu(LMk<)u=#HdF;A&u|D04c@z-5{P&Gdpob>dF zQT%WY@GP@=o2!-!RCA*ePch@*tNhpU_L2@Tb*bUbXpuwk$ZatNN`uND0BNwNXyZ@b zSLcABQ&3aN#C3up?ihP&?gYG+*b-yhb^_c?eahMRAPp`pd-L0?I~9H|UHC4f^E@nC zvGw~%bvSGuooS&o5ZBa08XUMgUX#GM34i`I+s}Sm0rY%c-#pjv3S%xTm}1{~kCb(H zUVA<%4NT8!h*va5gV>MbuZ~q^!}kmJi$=6vhMWFm_EsK`g`ZcQOd58iKBq%MC;Pmd zCCt9ntgz>f6L_xl=B1mRDQt=Uxo_K%2%w(+ptQ^=2JR7gUOhbF3lvTZ9oupw1E!Sp zIh}n`L+;vs5!Hj@8^lmOXv5!$>VaAVbyN?`+x+Y%@-N)C4Eqo5cEI)dG#e67{ldxA zr&K-AKQx5u!E4P~s2*s4Ci;}B2lsrcF0y`I3?$<;QmPeFm=ELyaDAa`(SdK$(}+7~ zw8pQd^poC8T`R69ejpCm(R}QElWSIgkR+y(D>d}beIgb$I6i$PQb{DT_l9{eoP3h>lFRoQ>aHh;DnDADhReFT6tD5Im7v@~q~+BU+b* z9SJ0($=QGP$%Q<Hb1Ly1)Bb{r$-5=lLsKmRB_1kvo>sd@5e1`Q(2<^SEav&12#pN-uG+PNVm^? z>NP}VoW;fZ*T2X=dB^!?C?YQhtbhzFw8sRwI%iO~jMbCKP}Q|i z24pnv!};={g{42y`XK*7oKFej=Wssl@!>%E10%0IGw?mwNSr&Ag6H+mZV5cE0)ofU zJc6rLa*5PDf?Ef-<9T&Fvk}kh)%LGAKg4A4Jj2x?&Ujw`E*{16s+nMj<`M9^yx!j5 zoKIT1(f*(I(EdmF(thb$WR!>$x z`AFQ2rRzy%@Lm)OdrgLNW#T;b*LW$*jL8Y6_8a@sxrYk}}M@5L<%y+dT;)1fA`pzfT?%nkMGsOmBt;!q0lU;&-?EZ@X%PGm^}6G zdqExvuVS`>59j5%Wq%|9@u`3Qf1|yq_mdZjud+G=zPg<+Q2MtMbe|SM8cOgSKpKi? z{HVPl6#@3{Ti`y-^@V(O*i`DoofvQ+Gw5)j#}O#D8tQjnJqm1;+EtXDjKS{CVz*Tq z2Z5>Wf}76TUO;+XGSYCcnFDE9yT7z+tSc5sXbZA;%*g;-YiOJ`W(Q~cjxV{Bokl7# zqt#+#&H#z(68rBCVLD&qkPy zD$lEkc_*HtdeA1~GO7oqC9b1-)cc`+mZa)|Wof&xe-)6NK>h`F4YQQedSrwPdV^`Wu&~e)5mJt@ARUX42#$%~x9Y+ttH& z{}J)?-A@)D$|3Ur%|k(rVOgQ$5K-tLaq+&z8`89f=EttXiTB<_HJMafdd4(uh@3yv z5}{4LBn5;<#X~Cp67f6e>s_~0?w^qTN&J=(`F@PQgLHGE`FZiu9rGi{Me{N-at`xi ze4OSflxqg_ByyGJr#8(L^Fw`VCFbYCnP;2L9*vQi19X2)nRI{i73qF@y3qa1YNY%9 zNt5okbq?L%!fkYaZ*jiI{WS}t`y1{>_j4?dO*7Q%JMr6z=2P?q&FA1a&Ep1Vn#apW zXxRc~gZx^W&VT#yM7RL|$|G!%X#E%y_3OEZw6yNgVk=_j`HUkHPK7#)xe)SA{eLevlh- z*^75N_mg2y>Gd<;+juxq1_%X{FH^+}dI{=Nl3!PIKOzr!j@aAFeI@2~+nCMfl#(Bx z(!5h_GVNdbNfgh&f53|OsVhgwWHq|pWwEY*94WO#&y|BXPoJu;$9d}UZUfHG z(?Xa>Fm{{PbK4{Lx=X>)tF{ z`z~H!Lgg*E7kKUgF#AB%Ei0U=$(SQ}K1JuM2Xivt#fzT4@ove z$>ogEQjXmAp})l2S5rSY6heWzr|VhKmPu$%2R3E^_2E5V2s3!h&4_pF!qv>) zney@TmQ!S>2i^}eLz5;~3D1yH7hII{eZ-lU1n_$Q3VG*VNMUZY%(LG-nQScAf%1*F>E;xK$Ner%ZVi#if9# zvKMWOMZCdq;SAES<}4Fwc_}y%Xc)=J44GL8C0og8h$!ycL0VFL z(vVc5H0)9$<9F{l_xt=R;6Qu}H% zcxm)=*mv^tIouBoy&tik7Yni*o;ZG=Y3m_ZHb32V)Z{1m^63)PuilDfy6U^vu>*X! zq#ZT*M<$EmK5SZBe(?oY=J{*QwXev^kB&n$lYL6|jnZuabw zs5z{L72BvET`pt$d0wXb2l_WiwX1!XWPj6^(H|NcA#xVs{sbJv{h2<4`%>ME`_jyp zi~3Z?K8N~r^$hOMf91G8L*ZelKMrHnw%zX*vj$A^(D|M>$LH(TgU|B^KMy+3enWiT zr&;*C#j5c6-ur;hSES?>I$yRuK3^$Ke4g!JpNxzy_(6`0;(l&ig!_4%8~3rq8u#%u z8}~K;D(f%_@ciTmlEfcqIOK4&@IPcbpvA1+nYA5fgV7WK6|tw@1icOfhFI_}TY zpE9UF#lLW0*!4G2UrZn3K4p&MK6Odr{)D8YqW-M*!Tm{lg!^-r5BEjkW3YCgUNqZa z0q&F53f!lcb8&y_!rol{aAAs=`S%jYy&oncDq3|~r026%SmX0nI=acYf1e1ud$2m= z`lfMWiY3T7|M4hs)C7+o?lt%xxJr_pedpq}XWT;Uli^L$H4$Tkdf4=qX8&ozv3ycB zSfZ6U7Au1K7Bsn6(x+xUE6wV@-=(FT?5qFbzWBrzv+o}gWM(ynheBxmEZ*G)4{FRLTNsUK9ZV4 z@dt9vB~8B4mP>U-h5S{x7|fhxie6 zjsx){>)l~=y{=JoK-cRNX)*eG1RU?@o?g}=z>eYSLH-H@*#9Ac{Y)pY-#jOr$BI)x zdHdY4zt7}sEX}8ly6(tdZXx#H8L5x1{~*z%lU8n=?Ac~floz7HEoXhbf>`6i!KQio z-M$|2^tMb2;^(rj{D_~2wqR}i5$j$v9h$e$mv0fxTk3n*wGmH$`|Gcw`wH^25br9R zUnBll2Vw18iFLuvmP-_GLE+wy6AMtX4h+&-2dn$*C|T_!&L4)8*=JPGh27 z;K-l+i!AC}6-c$-YA$llUY<9CyL_NT(SEOhgRbz#xUlk^v@<~8FFap9{1hl=v>v0S zgKxEH$EiZr?F50887+8di(22|o=wm+=e>+eqb`wYyM9um3&42oMen95ev^KGawRy= z0O_H$mg-eb;MjH(yWt1AuzKV1YIx8b^z5E_r=+nL>JPiy5B{eOn>d`-R7kpk?$P>e zV@pLiDt$kBo&OH-!A14-?-#Y|Q;dHC*aubc7LV*DR6{%;t6_eH7^ zpN%{~;$h2g!rhug9+Q_iV8ZiZi8OI>W13ZTNgOmg2~U6Y@B$~A|LnF-JqPPah4h)S z5E#nnI!2$nrtIr>`6PJq-~KfU4c@?S3HQm8kW^Un^7Mf`J`Q-pD>dUunIg1E?7q25 z$P03MV{P+4y*h$hN`DK~$w&-N4bdk{aeu(IpHm&YCwGu-=GoFkHGq_3&U?iobe>Sf zm-{G(yBMjkc|Kcy0YOgUet|{I`S(BA@gXv1J86Xb1FueLy*vHFfzbDjc68)EO#Wf| zxDd|=m9i4nyQl4m5~hDQ?`qYAXIcIK>yuWyHQp5QAO}{<(ftAS4m;@n0L4p6s2?Zv z3Dgg+?inSjPw-un7u_dVKgx&tRq@IP^-FSl<18LKD?=RI)pqq&L30SrByXgKc-5P=VC$?cx9@SU4`+N%BS9orj1?uZBZBNwK zEwz{EenOK9F}k0iEp0TNKF@~WkMwzhYb`$$slLK83E?Z%eoBOJS3R8vgMHdN>FdE( zxu5PkI9O~)=YgOI=l%5cFdncO`9Cz6hw_IEBT>G(#SwHp1TMXjMPCo=gtyaqKu8Ps zCwjdc>M!xEW#ZXOLw4EN4wUaJ$og#aM1|O#?k8E1+(4$rN;clDT1w_!o7k}O*bdUF z<*2d$S!)oY+q}`0<0?%1Wp=mohAeRoGGpz31jK`H&TP?54`ofS-VUHJ9+sO=O z{@BfrrcE^)Nt432br~Le$n-{fzAHGdM9+7Hg*&%SYwfltT^K#i|4k93?;_jK>#KnL zmWO-dcwuS13fL2?H(}bo z1&Zc)KQK@}4C8kT*Vf-NgL%eRZSt=~zz;Wd=GX-vhFKh@oe^8bz$dA;K2Gx;@Rrqe zr7w?90xs*C@2uTd$mz4#zn}5npN;)07{6OPab8ldT!zu!x!d$1iiPqu6886 ziP^8V5#LIz$UJ_0KN4a1v+Z}zhCZk5IM_`kpIj3XSqOSaga0dfZ@+ehCdt(Z#o&?{JDsE6vpuAfBd<_ z@aKXm=1+b{yCCnRF6qqhVGqLxA%+JE3=eiOyij6zk;(9ZLkIK0fZ;YZ^Db0* z+$CY96Y0S4=M=-A7KS&23~$UB9vLz``X7H@G5k?r_#?Co^CyDg&6P8k4tG$zkz{zp z$M9(QH|9@}t~2;4uR}cBBiuLQm_^!_?=^e2e<30A#qDsX-@n>kOUa0=gU)dL7Wdm{ z{1u?0v+PfYiVDfqT2`ce)tGR%LgS$MU!=stx+t=6QxV~3twv1X`LJW{Z@p$i8$vB} z?2xO5G67uVcKkI-C29*+zm4EO3FV@8Xy#PCQ*d{Vq7WJ6sFu`oG4SV+#5T--cYQOffa$apxhcj1%=@WUGBU=7s;dNRFEq>*)r z`}jA%*g|l9EX!D&m`Z9aP3)Hl-cR0^J977Gu`77c#vjCE=KzYg$7s3!rM^ec=|*>B zpEjtIuIkdViGTrTGGE4bAA*aHsq3t$a{?Qc-x$djs)6>bnX1Cq((r7E*x7f*)BFRTDe+?y@`u;4Kg`B{U@rEfp3jH!K#McVgF@IJc4L2d`6BWMy4WA) zioLHP?ZMCp zn)UOL*kVQ=b{PjL_kg6-jWBO?y;d{3e9%_n(rl~U@4xJBz?g_#ioRz5{radp4XGJvF&uD)} zhtL1P+IZaz>c6S_eplEUx+~w^y<8hWo_}fq5l(>oq0fCKvdj?bbyvx$dpW|VVuyY$ zEl&n%2ZcVxns|UtMO!iLWomGf=FM=!97iy#som=8e;jItK1q@Xdtq0^j-GziFkqT^ z`>I=mHndCO4qVWD5QL5y6}T2jlcRdre?8+bu^IdEFn+yEUKo@2o$)`y_`hWQi6rb_ zdEGgMCp`j8%(O)={pd`Vp2vCnD_j4Z6g4LeI9ryyt;vI(!!vmuucE-K!fxROow?9q zx98(Vsq>J{=wpno6jUw5 z`{470-W-FkR9~AB3Z};+UCKhqaoiv9ggO6_P1TVVm+grz+#h)R_sOj~nsH?E(;6*1 z!${%}(>JCsphzz*pmNlmRAl<+dMD%Z(!XcPH_Yo(V?*yQ5z`{#%IDWp{Q*sNeRO|7 zjPrTakJ>;Z)DO*5)3b9bQ=y0VIl52qqwqr1ulo8#)UQ8NsdiLA$|6p_=-B-AvcQM^p_|E?< z>T65pQpg&OZq&)W%&?)s;^LVi15{ie%&dB`xX2-Nf9BL|gMpDf0b% zX-;EPOLA|N@`oK0u3+ib!~d2=mB3e)1%^_gj>PCWH-YJUHV^df)i`^9)Fy4QL`1xu{; z8O^7eN{z?B*UM*bcdreHC;iF~|EH|NK6FO=LBY~+QZGMu;isjZFsfwi?HiLLFnG3) zXG&xpi0~V;>q%CFx4GVL;oa7fizA2Gyutq|0Ur(|zP^+ytv zT;HiYPav?}`tgoIbqF}ImR7;~pXFC-y_ewv{ym}l4H}3}hCkc20xq)`+Y!$_%U{(i zyHLL`MfWf8p5ejLx%;C3{W?olImGM|(eNaKLNK46GW>DXmf7Q&>Q0ahuMTp(*IR@6gPj#>a^)P0XLq(U?EGWH4|17~YgJJmO<`GjXOb!2EGWf1`pWR=CBvT|&5zHW7_}fbj14~*mWw1UKBoTN zEVq`Fb{q-?3sL=}ihso2##;X!nTc?EySwIbhWqjAvWzG-_C z_gm7bwzsOa^%ObVf#$=an^8}Mi+#wBA9;zpf6EdTf#o}7MN^3pUkO(pp9uJ@a-t+; zyE)m8HPHTe)6_xCo7f##{r0bV5@8Tv;&4D$gizem88??8$!|kqBYl?(NYzX89d1-a zf;$n9*14Y#1tAxahWoIFYh*iI)7m2mo0s!E{VjaRAcG}t#>)zb`kTr33+DV&U*3~u z7=AGbbo5TZH$QxUnT|*B{C6tQVfkd^B~Amt(fRh&f1Rl?_fpuls2e`ea>rtJsCWS2 zReAp5QkxmrFyAbw@}Dw%@;N{5=$zxgrule*&+81h(c4`jxj%|f>_z_Y9`=Vf-y%P7 z75jlweUu08u0VOP$Qb!Uq3y^Ysx3nP;OQ6S51SNoE~lhDBNgq6d#|k4gxhf*2vBEf z7e9+6W{o}kjXCa+PX^@nWp()xhXSn=dy3-W!s7Z+`$-RY3v1wlHJGfjqv{r<_QRKZ-o_V_HbXAN+JN2vYPII7!?5Do$3B5$X>Zs_fILZgn8ZJa2B@ga9H}9iO*|AUaR@tS7bfonR;#5_w^G zxHN{)!u z{e;HZ&(Z$BQNsH_)?QC*u;pDkeIDSO$TPIxJj+H_C{z2rSAUo@_nr>uspv48Z&^-k zUto5V>L=WD=ndUZu(UHB^)Fjc6!mXV+K28dG;nvN`wGKGBT!!>6;GhPTJO`K`w2h! z=7v!H1d77BFX;0G&)?GL36=#mXHtEI^G~0$f86OxCYW{7d7xg+6kQJvY;QUbc)c3k zL-ifh&Z&{4uZQy*V#t3$>Hx|w_cKEIBq@cihkoD^OkWSzTT0ugJP=Ti`?Dn95%u>? z*!4@(?~kw#bBtV~^Oy2Gz4N>>fC$KpySvzClw9NajCzXDAyTLAkI9+V%fui1?G3tn zJi(!gFU1_ASKub`RKq=MSmd)en}@E3#gl6t@wm#8w@#g_li`QfRT4Y=SVTKB-|XSK zjea#}2(47h3kOE+$xFNF`C))xnVug859u8?5&V)xNNmP>f88sWCsCnZvo?Km=>FX1%KT56@bS;(+kD0YU_r2?NPUqQh}QZp@TuqoTp6jU zel9W_RMsb|K3d#BaD`(3wu9JzP6_t=P>=okGI8G0Ry~wA`3(DSAH)8O#Ib+72KKj0 zzn1mwdLppCKG#dd<0Ww-0_SbL+3{+6T`lope?O;n(K+~G%MEHhXW_=kaZ&z%8Ng0j zd~SpEDIm${^Rig0-QeP0yVDq?XPRX^r2gMvTIDW9pCjv!0Fl zlc|aM)1QZVGix4v(%{1pc1APi(SC+U>eIuBKfYr<8;cCWi2BP<(?71uBTB@|m#*SE zNSqhwbbZ4D$_jxG-+*sFWY#rHcBd+YXk?2;=cT}k!B0>$T`qwI|?h0ZAQ zDb^r*$;if$XK|#LL~3W3YYF-LVxMwPDT}bJ*?n$H<{d&`^WpoN$=ihA=Khyceepnh zo|)}}{bzx{3es>J57IEb-FGgJWHGVl=aH2o4|ft=a~@}Qt;r!ePaS)dK68=mGvZvd z<3BwK#nDRFXx8$RBEBe>jBwz=s6n z2mUibd5{o8dGJ2=hj*KiKWx@V{@^Z&{Gp<=WS?T;TO#94`1(ny&D8gfpgizSrnck5 z&fDZ=bJo$6vRg#3qtcHCGLt;r?e^eTQ!ib|A`Oa+-=|!Wy-ZpS z2*gMlBoagSvqkPYT*EaA_%&KHq)+V*t{8>D4c&PQ^+58$JkEOdt$t(_Pc`U!!@wot)v&)1R zr(Pv4`0~jY&$&;eFyo=`T)J(0ioxp;Be?nMEmDe^pV!a&!{z5qc-Vuz(VQUNx!vI;Jb*{C4F^g$!(1GmAtpuShkQHo*Z`15I72!)NOX3r+khG8Z6xX zc4G!Skk(}wE*ua2j2!NyR_1}Z7s{@SN2P!uMt@Dk`kmsFUjMpK*sB}8Aorm?Y&ftm zywqkTkjfC$dZ~SlxVinnY!r$owF6nPNS)?zh zHW83LUuO#@3=4$q&w4@m%X13sgdL#b%1vu8y-fw~tM|DX*^O(69j!UfsWYx|KJUqt zPfiF?s*3%0bYuTdlGra%2m6gI#Cc|kI8Rs)`+rZz{?p5`zmTCV^3PrP=rT*w6Q~NY zSU&ej(p#2|^7MD>8@?+&L;kR;kZbsU8*&>;dY9k01PZOoUvl}~f)aJSz*I&{ zF?wFG=QdC40#G7-FXL)RKH#}*@+?gJ0eqI)ld<^slm>OJ`e+$Q!O9(m#DdkiP~{lb zb9Q2_v;6$dk7lYM!OU7Tm*+kK_oDs)arowT_k=eYeACNx-@$A$YYRT_+zE7^@K4bA z)XpSVBF2Ds+cUWfWEAcX7?r|(2nzGEZmP&5lyHCGg6_vgJ1!KHB1!M0bUwzCdQ9JV zabG}6-??@Zqdf8k(?4^5p32wYASuGUe#_5`=NhShe=jI~ME3{0A{y!bfP*{BP(L1y z%tif}N;RYV1b5D)(|v;ZQI@D*ouNpB8{Qk{QT>4>dv)$|6PUl+xUqv+V6}n z?(}{_{U^2be!{6GWoZ99h=pkXgVe9l8k`!LPit@>1@HIH-`_XT`>i^?Lx$Q<7#Wj2 z;GKAvJb#Cz`w2T0HqreAuFFzU|5WDv%BTCc^4ev(udrtBMY^vr`bP`uYtq@LGIU>G z5Q=m^;q{Ye3#oqA?d$%1ls?bUMg#gh!JDkBkV9v@?$lXZ`SGz_qG{;_x*op#5G|*# zhdbH6bRKY&9;EZY32!IlUw_RF`EL#iNBQ}y{>9VzajqND_0aBO(?MSk7yHAEs64RE z9QTELK?v$^ej&HG#S(q?do6B7IzKwprHWh)uqq0`WyqU8C1K|ZkNH`ar< zu|9Wc;6>G;I5>GZiyDuF#iuP=5AyG0P3>N9%|SL1qO3ng%QuEXRqsK*Zyx@zWn9|h zO@$_`v^}!1O>QeNbqc*%zt#!7P}(ZEcJ)s1xUsx%>oE2IrLA{ruimEqp4v3)-PArQ zxTogtb_E?T_&Y|zx-0Z5SU&RfNYaTe(pnw+Z}-LiAMLSUzclu1`-AfuSK~bI2iV_4 z1^ZtU#Qs-mvH$8BbJoXADZsINxJ=+n7x_3A=T&%ziF-eKMHZ<%rPh-K?ar^{|KOc0J1;41@ zzspqc6l^+rg;ZqtW%AnBe|-%END>xf{!}vj znR5a2#+TtuF~g%nLzqX$82*TbVgA_sl12P6u8x14b~l&Y!tlXe9`oU45$3@ch6m(Q z%!^uv7j6t6;v+C0o-=%C=fivmFfT$pSnKl8aMI;4sq_c)&6MF=AH%bU49@}>-bpgN zQ)l?b#qjMJ!?%8wABbGrZ;N97*fadG_rbg=yNP+D zpM!anu@3X7i{THCH0IBS!b8hKwG3~jf2Fp(bK zfv3VTq)D@>U**yFSl^XC-Ve5+}vcI1t%&R^)QhFj;-9I<~yy7WwBVnh$$8ABc<2yFyNid|HxZc$Vaf zmfCRQVFJ-)Fz20aP8`J#;n%zg=ZPO!gMck6HbWBm>vE-0H810xR#>Zmhn126)RL4Odqp4Qq6xYG-YpkSQikCB|~O zi!R1kB%aki{QV z0cHJz<*!Xtl9SjU_G5o&f&GBlA>;>QNt6fWRZt$t^hf^C_ZISpb~}(i%h4NQuW2LC0=-*X>rBvu_CU1a`W0nt3$#<`7^O-@%UpEWP2C)P#})N`Ir z1bcU8Q+e{R3TtS725I2THBhwI{1vhO!^WF@;#q{zQ?L5gC4umry$#^)C?;=N^E$W~ zwvjQXB-Zb8^?`cRl2`RX0{FbAE`LjNEBS-1FCXpjg!J>n<5r(rt6ZqhE56*El_o3n znoya+^KG0bcd)AJ$*Sf$%e?P5$(|L^put4}@QiPGpRXJQhuQLd#&50?>SI_3^VxL1 zg13mNXt*!D}vXi+P3OkImj7=4<2Y7mGCD;fzwg#YM-!bMs|CU&oq(f6s&e z<5mv`uiuUIZ5_7(MscE_#fDEoVk|q|xuBBxF^v5q$FToq8|?QW8T&;9EaIi}Bp%_s z{4nfq!NUFr-eG^~PuQQU?y`KAdLcA+5Z5r>{D2V6!FdK&?YBA;GDzY9PgvjM94IrL z{aMc{3bg(g7xzZ_GPDrp{}Y#e9$LS^dPWFq11AY{pN}cv5S;53MWle)w6{Wod6jUi zeK_sLYcY_%O>Uo1m?K#EJjLjcP$CRgz*@5f>(9q+gC`D|0O`Cs2Pe*E(zg}$2Y6rp z^3=MxgxE9RRpYT-IVnGk&)YZ^ohMvecDMPvz8_gL&1-RPkWDVY{Q<>)@c9?~jC`Xk z9!7ZKzQMv-doh(?FNv*@Lsxf(`;wOfaNnwaqxs;0`9*Fcjd~)J>7T0jL-+qKwUaiu ze{i8n+2`0TX@uifBf39e)!cfzKcGQxHkNBdR~t^XQv+p6_83#6cmn--%lj(0X4b=yH0-B<7+)*totI`@^gbYH!iC6P?@B7UyDV+&5!j~SoVbcbPW z$4EWhCi1MiW6IgEYC^~tkKb8+fw)fnd$uG#blu{qTjU1*-)O$>Q8nq+M;Zy;L$~an zPF4}h8tRmv8`!ccgPQLS%ddF7JK$VFnyX-KU~4%Yn@~YaDEy&be;N$my$!C+xkdKh zR9@Rfeb3~)4Q`(7A}^M}A-2hz5huvnU%7t!sRQtU71n+3SbtV`p~k%-uTD5MejM_0 z>a2I+_h+xsi)t8f>mhUPw{tun@PK|~<_EbxqUSZ&MPZVU94yZtvM}@8IMW8RPp`McikOf>z`jo66WNAmJ?~i z^88;2rvU8#u?qX&O~Zc6Utz!3+i>2v<`R^*Y7_P^Eyn&v1D44D&1&p#ls7Ns$4D5E zOmOPl^`oEIKZf%*sjc{+_NRy7@1L1{MtyF5=wTbRp3`uCN0IaZ^}Ou)L37T2?r{gZ zy0F#{#9B!+nOd(sER$HqXYQ5#qZYpl!+>YO*^nJ{q)5l)x#V;=+pB7)s6Zz>xj^O(jcrk_UM{qDI z`jkV_V}c|5^nIv52UdGP5T z=EdGim=|FTAA0%=5g**PVm=tEVm^$|#XJ~QFo+r0nns*r_%_Axt&icEFvGLIa+r6? z4>0fMuf%+7D8YQ&>4o{0@D1~A%}UIlKmCY5urK*I;$1?*wWjJGNGxB2`O`Up`SV!^ z^G1r5fOsROhIzCo5%cJ?()&g9dU-=Ie_n}T{>U|9{;XkmGsaUeaF;KbrOJtU)Numy zNX8BGNA`trse<^Gy;jl{dS%Ibv|jWD z9shhXeEc2|tAli-8VkzE7Ckf$1WvDU*sDL66;)!QwR5_S_)>!A!%M=f2b*p`CEPpJ zw_T2DAda=o`6*V}N&Gk4>eyT33oE%Mt#(;tkmi4p2FDNZJMN50B7)NTyYdRE$T@#C zUVdH^N5a!Tho(Psurs#hUtg2w<)(VNmb&$8C>dsh=lrFsrq3fsU^~3UE)K1%5Jm$CQ4#fs?iK^Dga|0~_*0)INMY z0s5a^Do#Cl9i9&0=(v+HPVjs|{?y-wAb%*`gZ#ifG2{nCt56;|ZF!!~177cuKU5P( z{xJ9ZLfRj?IAdMZD6u{r?RUoRf*aM1@W38aD4+)K2E6E6Fn$iAhXJ7=7^&HdPOa#Gsnb7MS7 zRPEZQe|O#+@)@~FWP@-B;O#S_^47!S_mPHbdyxjG9-B00!D&)`=5fNZXT?OJpuv~f zR0F7S<9gz&U5|;QYm;|sR?TNUSWs)E!_tCEM`djL;w?aN#QvCk^)VuH=j-hQ@dM=K z91@M6c=AV$=eIFZhb zol92Gn2i0zv;Q{n#uV#aI=@T29Gi&1cYd}9)Axdi6%RD7DmD@3Ba1X1#P0yl4Wxu3 zOeH~WZIfY7h7ri_Ix}xYEf>%eeTejtZwW~6Sz~h3_+=pE6lnXhwaFGNa4Pxg_reTR zt&Ls#NHT}ik@&Q(7kYsd^@q;)1djku!`~K?3cj$%=7{?1U}w1HeOu3lEOqe6ulSJD z1_Dl-)vnnWei}4i4)-VjI)T4IlcJY?S%XU#TRde_4}f##eiA3zPs4f6DRMC$pNVrG z*x&Xk_E+?JhWr-ml_9^oZ*bn|7o7L)6ZRJ!!~VxO{~&*{_1HgsvN5Emy#$II&0Bi! zJP%t@9_RIT$KR=z|3G9`-$*yIQUO+KNiNHvm$KyXVB>?|y#j7tFil8U%8PcLM zlka&WUUf1{GhdB$j^>0WPt{zwYuK22RFi@U@H*obQQs zT%iTh9QP{g>ghxC%&f~Y^xrQiFgHa7>GBYs8X^y$|` z+)xebTSqvW540l%-Btg-Adi0ii~4tSJJo1--7zVFDlwc_e0WU1Kl6k_uu1L@menEaPhAJ-7ofDk7-H+<%_5LsQy4f zp`9rsZj0HewxwzGem8ZSiqQMrKvrL)_7k*4Xwv%$-z94PrT71SVh7sq6aMe%{f9h1 z`Cn7#0b7sPq5ambb6!dB_gzIu?9-k+ea z7jV4lAj%im{sQ&&>pzqS+5*CVQvHGJx!q7-HvPc;m3Tc;>d_j`TE^rAf`e^Tx;}_>&KXG#>mf4t4n
;HIE_`;alhC2wJ9&1?eef!VM% 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

h(?Yv9N4iqMd_+FA@97I z3^fZgmuASBHobTZPx1y zKyJOBM8A=0|`K!`t!HZ;{#A2@e42KB%5I%)LM1BGk6**^Wk5BM1}#!pcZa+_~- zJ)c^Na>GvgMO!DruZ1b5k=y_@A8F2!{74ymZ!?^=(g>$r7jRuhXzqTHOD~hoaSTA8 z3oq#F=h>p1-pzH4C1Jqz&`|VL+BR6&{;}pdT_B{bHxH+D*}`T6J=SemsmS}whRvz8 zy}OyEoWsFKW~kO#{q#cGjSH)3`3E{Auyk zh<8SYHl{O%!Um>FrUr&5oV576h=3mS4x05=eF$M4Zg5$ zWuhk}*9nnJRd}K)N=R^y<$&&zCv3NsF}g=*54%im6*6e}qKq!OPenq`U~~E)li#mK z(u2qhW1F=u@JzCZ@w2)=dihl6W!tlt6lZCI`P>7pbcs$-iuIqyY`gyunQQ)Si0S%F zi7qV7Tqmvx;xmG7oG|_V2<+Rm1@8a23uh-Dn5JVc9maOh$ksA?!H z^@*YtN7Qril1P5a+#QO?sy~~9@N0+>e&#)d50VLA`X!__qi2pOHE)6FgnWBwcyw3(5N1VPoTO zDTw#qMvr4rjfm~Za{fCx1NdPO1QV)O=#8Ghrjblz2)85H*P7tDs(3H9Jtl~uuub^% zV=KsTDq(4=kAX99pF||ld4hyT)ES12{%Fanq*CG^0y`gqU+*J$**`V5C$5i_@_nJu9pt{bsJ;ynRz zA0Oqn2^GS*v|SoMyK*3%U3H(KSOP3^vR7r6`NBnpp04wbzF?TimfI!kjB4T>C0W&+ zPy_8FntL}XBcUpuzEiuskj?`w`MxPTwDg$pSG5rS?rFj&Tu%6wriprtZxZ#)tonbZ z5`O>ngg=g#@ZU}$e2!v-uX6r5UXT2FqMrFXgujML_;(M;SaWX-hos%@X@^^cQBS+` z8ueO15MB4~{-WGdg`QHJs99T`97Nhbc=$HJT~ zP^{Wyi02P$l;Wi8y z_|*Q(uFsI~`@LNz5(5+wi@5DI;ZG^8^+;yxn@kAPF^G@7{gn3|RTyyeRM7a+HdWD{l z>$XOYaz1CtM?H|cqi6lE1~c#x9P@f2lL;H+$#t$VPNyQc)TIKnrP8j~KC^rxSQG5k=1N^h6Tw?o zZkX3q2#%K7aGD#$qOYt{n}-CvP{-d!jN#e_S&Sibc#(VS+9aU=AU+*e%8m9OjvY=~ zDu9_6d#2w$%Y_^MC27Kuw9lvidr=q8;*9E>29juuBIY_UhPb(`UnZ@oV7TK^kE)>) za9R(78~(fq_M5gG8cH-s_v6yA!;@pQ}&e z9otD#LPn1% zs(H;8GyGoYuiVzan9sq$3{@(Z%Em~}dOEsOl5I#LM`-G6&@Aa}=|!P~Igu#2^fJ@) z#(9!ql}FCm*B0okLgm&Umu%3xrbS`9i!Ez1I3kcQmDRcHkVx8;a-s>nl$J9#UqlGb{z&ZP4!X!)?36 z7b!mn|MD{w{IlL`+P{{< z*jHKCXFpF7^D1gq9le6CzpwCC$e1C84{!UH+nNb~ESZk+j<=Egdy1rv(0;G#=zPZP zV$+OT*-5cq>>XgB%i6ds>N0A6_|Q!g93f}s_I-_X2k>AnSh2LcfDR8!@*SJ+EK>o$D-D7f4kwj`!$SzBrQNoS`^=E;CWaJG+1k$={wpr>`)O?-14um;RfRUdc(!^Xmf zcD6z^m6aOJtrm|SZM#zYq(cd|+B8rn#chy|q=-@SR0#-46@G|ND1fQ870>9~<01C1 zTlAT91LzCVyi)6!i%NO+q*(sFf$olL%ntZP(_S|juJ8Zh4f$8ZrfVdxp_yRzJ7X2D zNZhC@x$KiCYKxXxf0=gPgTaY&fof|K0bLmOzT}~Zmad$+Zr5)RGHlTqg*2rHZ7Ur%Xke0QrSf?pC>HjAO>Bq< zmA2j5@51BYvZbo}p>hNbl1fv_H6^HE%;HzMase`^pifIJl10DQ>%LtIaYla(Dy6dp z@?gT)+_mr-?f5TZc_TdGiQZ}_vr>>Yk}?fBw9_mUp1m35|NYz*tTsHqp*9+gKFbCb zCdv4~a#fS|Tl9!j`MMnNUvKa}}#iN8t8mQYxw!Lk0g4UC|Uxgx3yoR$g}3(WI{1XY4lP7-X=Hy$v+ zmIm>CdUbYmeDFM!Bs%2n0Y#*Z**D2=Nf&=`+>GdJCK===umlZ11C;Z(z{Lr@ur!2e^rPj;ta4 zXMQi^e%mi;x!>KNp(9Fk!EGv?=#x3Nz$DJx3Y7- z<({4=p>-U$rK>X$_xB`sbL(bg*F$D{!Qu$#yn~YOYk45v=zQNyXD4V0CiqS!!Horz zEz_>{Xg&Q=PJtyCFsVR?^t7^|+kCC+Gv#3T(kM}*^7J}V+7R0Dwm=a^fMB|pAvkl% zxe8Egs?cA?Nc)=^sbFh#@fL?%Iy6@@G}^>#p)JlqDwCk)X?+v1Q!N4VghhzvI zY!jhhw}tk;;E~}>`zc}MWdCzmKd}&$J{)`J&=e2!vBMQ%XOxlQ!#iV#t8IYCQj+mV zP#g#)_1wPIa|}!!H;nZFDo?OU8NsFx;xf#a3y_?ZR82*FE^5AcoP7gl z3TX8NM*6eZLRdB_=kJ|Rw9S$0`O#5pI9WvSXehx`dD1FtSp329>0=M;$cwc1QC-Sj zHaw;1__alQsQ96<6Fnhn98}bQP^UWg-~C2|H4F-Z}NEBT({Mhx;B$u;9MXmL|NO!Ut)1J%8Tzv6b=_p)A+_Zy#F&(96*yub;J5 zqGG2+j{bK^w9nCsQ}p}dpu8r7Z+o-}^2(CM7#TmR#Tbqc$aUv^EJlx6VjvJ_ohs^$w2+ zhr{A;wi}6igHfkhn3Uy1Yjpl$9_EMfGCs@?cPmCQKjN@e#QaDjFA?`yX)fbFkz2dW zuzHq1_Tu&IqNA&!`JpYK2J?fFW-8_f@f%T?A6Sj(ai0`3KkidkFURXCi=D>nsY61S zuCd>t*j@a6T9P{e`SEUwxPRLZ9Q9A@aa#Q(u^OUlWuGaih+W~W1ScKY+EI*s*)s+u znQiA-kzpbWhWUM>7F66GB>n`b^*~`fhgphFu1qgCliXv?r35i}!5hP=i8QQCd&my2odb6YBYbSMssqMa`(BGAUh8&rj}TUb^? zQPDjwi~Id{P_Gf%vFs8ELUrl?O1TVBt3WJFPlTb zN_I4KBBBM2l{o5b*Y}1Iv2Tz14qZnx3K8!-XuoHKDt~tvwY&rdC?H^g$qns|LSbW@ zqT${-ZievV-l$S7G(hxnAeg@{lgQG4PwFl8tXntpoANY&Il_iB5YB8rUOXx>O_AJr z==dj@L_`%Y3V9ICL=6?^k(g3*Lw80a$o4B9@WMOBo@DEdl*6u@n{^hzi|K%G<7Yk5 z$?t!TT1fby@7IJ3wr(|mA}O~MSKo%A$sZ-tlgUod+LpP|)BYCnlsLA|X($k#>#Ymv zz8L{Ee#Lont=&7ms5m!DT=!1 zGJ8zv68MF^OrHwOL~6o?+;h5-DCVm&>$le#ke4Y?By6pV_$Dop+^?H(ek4z<_fr&X z3^fg&;HyAm40*;qW3<;HVz&SM`pp&=FBxbF3J&_q0l-Z_9%p+I@E7 z4#_a_!TbORyES-qdfxltT8^Th>3n!zl!_dl?_%0zpNr1YKfai4aRb&=to#&ih($-o z)_s`U;ELKds%gz|2O?uz{n%I)JLEkY3G4qQz%G5u?LtrNPzGO4fREcn@JVfX9}sJa z9*X);^Ni)fTIW4U8eez4DM*1 zj=_yz9Jis$HlkukIUP=trT3X$3`Dl)c(S=OEK!P&$i~IcD9E;Zf1&5DE0~9Nez*9N zi$1?+<;lsr0oRO=dw6W1kXSVz>2}1@juTObtI>G@=z5oCMNY>L3O|Wp^+2N|w30NEgvt zIKsGhW4VC?s%fdS+cUQvSGQYT^`}<>( z_C20D#|I4L#2tjM-#B)#?>Pfi=_^r>+*9-ScBlI(%ngM96D#5G%q9Gi?S${!N5Xe= zwVvaX;&?r03JE{21>w(>bchNv>p+>6Dx8Up*Fvh18E(vhAm%kYDV|HJ`6u zQ2CeO59tIy{8YS+fyo)!&hF9aIP40sMkj*)4Hv_BrOl3xg&^o|29l&%6q*zhmln1= z4%Qt6dyekGd9His)Q0C3$Sc}++hSc3#7Ih|&9Rol_ZB*$;9DPwPVaKx%75r8%GL}!T(sK;rC59&jcJWRZSG8r`WBba>^{Q( zjCld~mo*T+X17_~Cvlgkr_qt9XNZIFa}^VQoA-p@eLdkX+e`SKo+f;9aYQ{AYKeMA zYzcq7H{lP9G`YZaItI9Jv$tdxxuF~KnYA~UY(ct*r?)rH5}BzI{Q49x&b|B}1$X?+ zMSdRh+T|G;Xn6aJ;dLTuFdUNuRDn(a4UN9t;(64N~6q+q6Y!TM%@BxUtv-`wsZYU)g=%exhs{aI~orQu4~3Qb_Alm-Dvla zNB|Pd|2Ez5@+M;V2^d4D-#o_Par*ZduVxd5?*^Aj zW=4Lv8={A5;@{m*W}|$z6GJkz&&lZ8Dq#%wk0@b`q-CsBpM>S1@QeFfmo5V3cj;n| zmd8fwOqBBr*%B3~e)hRz?Z7&+;(@-vxpQtvcvtoJx>r3Ue)BDb-y0)9S7UAY%YOyv zbRqZdhHnwj|B&zWT{b$ZUzii-N9WG%QhBOBLHTD#_qF*6vWPPJT&VY?QG z1jF41B=}>aB)K&PwCm-|8dKBY*KMbL%ixIWL>?dwDGQUfhcZ1*pt^vH$FiEYSTnlva5w4V^f?wC1s3 zB$$35#27L&xz^GcZPb;adof>wGy_69|Ctw|n#A{Kr3y1aPvpMkzfQxLND^5IsYJ3V`Ca#Z ze}6vydOYuVpZEKH&g;C+tvhbeAk1rVaU~ErHxMln4aLF~os;i2haalbpK6OrxnwqCb1J17$ysw%ti6pe0tjuM#3f$mBL8Wj#lbh|y?og^+4 zY0k(W_~Bhy!hs)ZRk{xRNW2*DN3+Bfc%R#sIK0p8Z5lq$%WIPOJc=Xv2Yyh%-Z=0> z(%RU8AD%v$JMg1tT?p@US`Whee0ksC^F$WT;`2CPGSSC~O=4OM4&P^<4?&`F*NNE7 ze4!&>#l-sh7N(}zUHBMfDY91zPtJKsCHuQ8P`?lNe9%9Byo2`UaoN%jO%`qDvFO+Q zTfqv-FjUBNfu}Gv5cRSL{I|gM5Jgki-<;1)h4;aS&Ig)?qbA)4=lyv@A-cxo?|)kl zA>h?9x4zb5L@8!O<3zHG^}Rj2<<`D{HQjq972V*9+F3bYk7&`gvcc@;!%1#<@xA-R zpe|)AtqO@>$Jco9i^#mY=}?9=E>2_+e~Sl4`yk@iBWb88_Vn|Mi5@^(r#hvLaffym z%(EODFTibk*ARsYT)!#Th*tM#Bz-LW{L9idRFugc@357JEgCO0(~zWT?ezPuNm8GT z-d8fK-8VZTd$(2g%_FI3*qn0mfB$d+<9)PN44I}tkkcUBxp(bIBHZuU8TEL88DR%@ zhI}O8Nu%?@ECyb&&XRnWbcVxAxf%{R`iN(e#xVe?7kg^?84WE&V6@AWV5uJ!RlT}MY|`Y zqB$MST$(>_73v8e`&x5{#h)VmUasNVXJ)X!b6VP5ISyQ+jp9CS+93l$5twkOLp0H%;yK_ z=#rE+wlfp5)_8IijNH(-B%XK!tttp!d~Bon`5~y9v1eS_dWt+29xD;Jnwo%LtWXCFPc4ayW+UbQ+O6Ym#e>Ml?yM}@Qsj<(^FT2+ z9|EL`$~f<4LHD<&Whsb9vlLw}0aef7S^&RB@VPRe!ls>i^FA8Vy>LfNu-ihIPi?2A zcrN03G4x4TAQkm5f7i01afhbA4XLJwg3%drNuQJ-jZk@#>(Y6ma$tA7$-(l&772X1 zXtUcDid2yW)Aw=QdsYwmI_16t;$`o?v^xG2UF_+-|9(0bIu|VL=dFHV_>WO_xZ|Gx zTWqhC&1(7~RWknO*s{Oa2mS<>6-&_*b*FAsQV{2>pD#gYS>y)}j86!y8M%R+iR8qs(vz|>9`qA6xa-?o-1+T-ET!xa?aaap zj09>dyqy-IQVu`Xxt=HPCLm^|n60(!bTA}+IiIBAiB9S1Pt;adz>%R(eY{iA=rHEc z?qaevHwiw+?*w0yA|a3JuMT{kwhdyupS^?Nm)reRLr((erfdHD z%X?ec4Picd-N7TWSrGQ`m1znR{^%0U6GtQ~`INwQ{{6Qk#7@YrUBAybKNE#KO?pHx zlXoB`&Mv{eTM3Z+*Znxd@&oiMLGp~OWGp=TM_}SC0?(v931ix`MH)R(omIGV%eb~& za{hjM3Kb0BRC~Q{qXkMUIU>2c$lud;-}9$EEHDyy7&pss!1SUe5iZmDXfwn6kiJnU z>`!3$+pAmP82T=wl#+tBetWM=eyxP)zB`lq6a}!eO5lcO0;hHh-gw=Y4(pV3y${ZN zqmaoL&69P{K=sUy;!X21FyQ{x6g=yRcJ~8_bp`|B^Ka`c2#AJH!u0{SNbKNJtFgW% zZvA%hiq_&jRVu7d=Mzl~H-fK8iq|)Wa!}5)?`Kqg21hLzi)jz#qvQ}hzEirlk-NPj zo%UcDnnN!fPqrkXI}yy+s>EF2;wZt-BTw+(9U}PliwHh@N$b_5@7Iw8*sMM9nw6@s7Ci{M{=?9oRl=#H$N?XQj7#UkZAp$9pZ zZqV|+&d-<84k^|VxZ3F{p2^TVzJYi55Vb+X;~w4+L?W=7pZ_`#D#(bSod;J{<(~g& znK2t-rf3t~$r7M2n812#1a76hb+I(842;;X#r5SpK(u-%j&?+1u|H%G$x#J&G^KmH zfaHujytAbLv3q(QBX@o%UE-c2Xz)?ImC|3w#8cuKZQLRI@5~^HY}zrtZu`R-$WYq$ z$w}ON*}XmgjIJhcWKC&mvo8^i?k~CKjpc_Uv6sSD&DYD(>6eNJ3^fyC2MqO>KEKzA zszXPQKdLHY`-)vqoD8A2?!a7A`Xnd16@vW7iZ>-MkhPw8=*Fx{(+~z^t}?SQm9MYE*2tJI8wuB0U`s z7|se795A|jr=aCLXBDccd`kJZU;&#<^&^{ZpQQC^-s`2zsVqb(r6I^?O)k6q8iYL; zebLUddkGK4X0TSSfgTp^NpNdTgVgh5Ev|opEO&lKCeV*+*)s0=gXxmua)CewT>c~N z-xr>NB%*f5bKCsk_FG!&?~1s2Wq}>*y}<;e=Y;nIx;*9E88d-VaORxYv^vyUSb+C~ zzZ{SCN4a_IKWBpPo_l9RG!0p6 z(;K7%rdf79LlN0mxdTR_l%Ee6YMcuLhAKMHv8j)YBB5pQLA7fH-rhnRA`21aX^~*e zdgqAXW))f&(W}}JD?u!ZMh6Uq59|*ZNb9;rSxO4f+FRWd;c9-!TkWLP(YxNjwH|bH zEaon{`~fXCz6%gC`n)Sg$kLuEuWt;NVSo6@z+mJROs~c>>=Dbe)e(tlgYD9 z2)*5sahRw=JXIh6vuXE)XXlFU{x!>l@fr4`o1HI^(v$qJkpYt!R z9KMe*4?GV1u%>B$;0Nlt>;peiI1zW?hkf?F9|ykVH#>M=ApZ(JPw*@u4@U6VfgfU8 z=rA}xj5^65_>ui;!GRx@*Jk2-o7Bz&Hgh897`t#=p$b&CYsN7g8P znAb=l$`Ck3ZdJ_KcVX1RFo~Kk>|vQJtBiksAA`x=tu@=Yjq-yAP<-elhLKI-_0gSJ zG<4fsDWJgvi4%XjvLbR9a!@_jeHrVki4aE=Z*Cu#hoZi4`tCfnfYULYljm^# zYC1B7c50jwpuu?Vu*+cqO3B5O(G2bq?hPKJ~*7|d4hgxWiAMD*EXwcck3E0Z~RY~CXs9?yTh`FE%mJ=t!qq)>^5 zTXfVV!MM*oT+m4Dhr*%!qhb9pUpgrK+y9{)5(M7%TLbAyS-5@jk4{Dw#Gy!&oCh7G z4?!V-yzHsdG&Vm?`$a_g4Cb+PWWU}$7x)yBvgW)qh9OSi`?j`{2K2anZxJqkus^uc z+D~OT;pUl(&3XS7gSJGX)&r7wL@PQQa6GIO44t~(3VL{;?dQ$p#Zftk(Tl{*SRnLf(f(sn}vF0_ON7s*R6UJ!G%4$o8vpe%-tzh-22(8~3PDF}DYxd<;f)NR(IdG4?W>(6uG4Y?VItnd?bYk$i} zVhH!%|KU5Ou5>PVyewv;#?7PO{`N<{=&=3T6M_v#`@^`{39fE z#B7d_-4zOEiO!!%@I52s}$Otnu zA@UD9vO!_BX#SYYqJYzL#75~Kxbq_q+_c_ZiB-A_l6M&`Snh{|onlep=j=Ej-rbD7 z&J>Bxvy|>DrMdw5Cz*n#kD3z>&N7`nri z-*WyxC-k>DW}sI>38`>B;6qLnebea@T=ph4m<8ooy0 z&W~afuc<49xLR_N7h7InX7#|$RyG6qOjZa_bd|v|2gUcDQx9P0jK!D&!?T3_7DwQM@^`cE&A(%lRfxOIEv#Ymt$RCf-5^GN zWzFv^ZoQk_`TYQ+pd&2ZG|(LO@PhW6UuW!x3UKw@1ok&1FxISWV&{`IaMI=XX%gLs z`zIdfCb}d+l9J&7CZH6nc|=qiBeR8h^hipHOU(=(C;y8xxSx+56kC8t3%K*U(rH+S zrRFi;w&&ka@+BZ)yIws5=`_$eIy`e{DH64QkBwTkN(UKgw|9cgMu-_hm7{VqA8e)M zuSRj+M&oZz$g)WmL1f`E6F%H{WV`VQ@1F=^-PmpWg5h1Fr=lVs!x(E5d`a($qgx|3 zu>YS&n3cWYS@$oj&~JiY%$eYyrY880C=q<@R|vkrAA~&gM+kX7^$`5SxdeYy7Mp(1 zU?~c97u*sM?7+5Mu5a8o_=kOSS@f`fMk2acRzK&MOWeAqMBorhzs)zq@3GfwXsy2; z^#F~?{E>#H$4D-7MZq>Z5LXv}_>@UxA$sV^pZ(vD5crx$U}7f%%jKMYneaFpO{wJ8 znzi|%PpiCnq8|z&TI)s7!?RIHNodh0h1(pR*Lj$vXJ`X#Bm}-_LtqQO1?#)S4sh&< zVQf~aH!AHJ;O^b5273N_)p7bX)bcI)2}@cje5{&rVDv14nneP$R1(;S|2FNv$zmWY z|LR1tRti*tjKMEkD`3pZvxIe}7zSPbvY({3MNVWlRNZjv*KFm!vC)T3k&T7GlU%Ye z&`!+xIWuepY>d?X#?7*5;n|L9fJ7a*DOpJr-Y*9}I`ybC1y3Lh~9rNB_^;K#u3j&g>rM!Ew_-}t4myDY5p+E6T@VBs06OAU<-4U zt03S;XHm9fH2T1>^&d0EJ=oGGussujJ3e0M|NJo$#nO$N_4MQ*Z;GTZc{~f)69vb# zsT*&=#~M|YvE%mOcJ!FA<`H7qL*k@-6>ZSIhYD*QDh>)`{oUuNM>w-r_E>rZo{n@0XfO^@EuMfGnpmLkT8WwS;zY#ctzB2JE|Z^AQS$TeSg{akuFl-L~7)0f4~=bw13 zyJwaQ@GAM}ptK>7JS%jX3o?h4KoR?jIB&Gl^;31-&KEs4!297c?cw{kC$})~j!&Mf z%>0C1*1`Lsk2k_x{_O(RB%~S1VlZsoTBo!5OnBLy*uywLrQhA zqq0rbW{RcMjD>hL?{#(ZDIe1BNtbfdhtF@}HL_#xjs*PB$Hv<0))VCe);^ zSi!sRS`R`C^HU$N)kJxPGN&S}Nw~+D2n+FU^s`m|X3@0NmXYi6HqLEDw&JPL| z!2iLRB>J%(7|(=a{^%zo;zz%yy>0#BFa3|H3VtOtzdLonXh?{C8pq&xLw)e+r#K|- z^`ld3uO31*o>&QA%>fCXx4~6ct3mN%)TNIBVX*b=rApvd5-bj1KVYQx)%$=E$9Rye z!=D^99CP1@47W~yPJxzMUdRR<*yqDr-2ISAymbOAmm{1Cbrdc97KuhGjXOghgrbM3 zlyx$mu`s74N<3qhjhgu4QrD@yP&?HjjLd8vc;(72DCvbmlgnJt)4fo{BmFBW>Texr zmq^T{{_}&J;NSu0>bpqWDdoTq1A4Cx{7_mxMTPU@&fBa%@Z-(_GQ|6|Q%~W2PDk(J z^YqcW;qx@5DC*WW0|_>N`G<)P-;OJifEK zJw0*Xv33oYUE{eG;7gmR)u53t?)+76gLi?HvYlhWVxF)4AiObJ&Zy;-Y(!Yvs3?mz z@V5I$W_$m}JPsJ&W;5_cTV-={=Wkm9%ZHac;YIdvVePN2?Rq6jrLn3$-kN~^ZQKq& zSMvyt)$|Hkn&F;1o14xhybA@>8`al&YD~}zE7{{6M1QeuOp+hC^p40neUGuMd375N zEs4mV^T%7w8yrUr95Rqu&xKS0kU-_T^4>376X47A?~}ew z8K|6Pq>o%Y3Ng)bp2dDC2c$4~@A=Uz;5KjjwO@|={p=r{SmyGCda;VMcJdhX{4D0) z{YaWt;>z>Ee?~}Kt6a7t4Oe`SBxBnlCB~mvDo(TDIOaRJr#an*wqZ`$bK~?M`{y4( z=~-GOrc>dloimctWkd^>*aSV!#}%NSAF} z<2E~61dN9;f!kKJvY**c8zpX9p@d>N);$InIKI|s_--x?u^u-KTl2_*kZ>95XP=WrmW<{ z;OK{-pL5vkfmtFFO*%p6aHbBiD~@qYOvFI8LG?F@2Td?~%X})I#|11?`RB{HuEC#r zx2tzd@1uOBshjb{b%@$uG=g#I7L>R|xcfR?1csg;a0J&sA~)Rdp7*^c z5=dtG+0U5*Vc$CAcuF$S?mchOe`e3oeoseKGEEVxnY7Cq`CJN}6yLHih7Td_Yc%zj zkXtAusY8knH*a`CfQRIol{4DU4BOS#^n$1-_aydw>T%CY?ZiBte(+l0vhR+nCrnJb zWQgr)zm@bE4!L6-8 zaJ7(Md5P=f+NUmX$L3xXd^a>@kA36NVe&+rq4}~cajcL6mmzA>f|jq zX`+EL-FkgT-2%kLxVD*o6oT^`CI2Y%2$a@rmU*PR488yHXo!gRG&&?~c-1t)3DIp( zR7a{j1gW|wEU6?j*k6B%MI6;Hq3W)|oULjX_z={;`aO?9_U;z~n<+8+SqibpTCk3v z{`w~fteHne7CFwg%KuT77d-9+O&JW+o1sQ%=R1ih%j4UK_GK-<*-QbvI!s`}CIa`i zrSf!a#G`1_z_sgiwQ$sT+vr{Sb(nMX(OH}!l^w?l-SKtZ!G^f3Wl-Yw6IUu%@-SxLN^qg1k0-33)J;wuAQ%Pg+cP{yx;&C7!Qh z9!n%kbB5q!&UH>$(eA^pJ|X1cDRWF&Ea#Awx=8TbiN)jnlwSz`3n2ub(g%Vs-++*( zI-8J3^bEmodx79@eCc%I;5{_~LBNS@Vo%iYPVeNUCER`+S#o~nbI#~I4Z+{RLGUk? z5PYIk1fOX%A-B@Gvs(JMHdyQo*<1~R&1@n9) z)?Q+coVoUXGLHj(EZoo1UO9n?=iZ(f`J#g2S0}zEr(J^P!rH@99fC;s8-c462pk~b zREcp51An#Uri@xqxWk-g@P4uk6Z(APvurvwt}d8ygQ(U4z1e!YzPt1q>-F#S2&;Q7 z>a5JXV$k*(JNqteXIDiFy>89Fc^Ue#K5hYCpKa9<|D-QXOy_1>AA?k1S@ud>qP2rj z;aO$KliJv9>hgheia*@9Y~>(?E|TlOp|HFJ@|A+T~{C}VM zm7uAd57_OOggi+qV-5X5)0oj81V8cfyS9qvUokFb1bM`HuIQ%1Pq$ z)cO$ecu5odx+(;JHj|z?cibS>Kkcp)&3P6GXOHZ={?7`iZx;$|boFE17zj-A&Ul=8 z{6EZ<^ZkPvGt%(8>gGN+y$4%jb?>OieJ$ijd|o`>a$T;K<2}{+8hN<))m%}zOBemr zAn>0B0>|`d>R>(iP_IM(EiES4U0%u8d%^R*dL2&P}E`v^1)KC$9I%`rF?$2qnd&X%2Nq=(k3w6`)du@8> zJVrG1nLx&Yqurv?9W;9{SDnlkfbTMTY9ftV$UR#A%GQb$QlcerZytdq-1I5aE~~-Q zuV3|9tEJGx@6omTSOqjfxtGf@^AxM`_UFE-QYU8cxVu-yAO~o0Np(tie8PkU6qtD? z8^gWGtB{3HPVRJ zirdl1vm2{;W1Qrezcol)+m?I8J%_0xEvOCrYlTRK)b5|-oxxnw>_CwzTBu1;W@_GU zT}}gd=;S^4fSKC0@r{cP!VPYlKH>5Z&wj?}_-kF%X+f3O^GqH|e^Ru%^-337T4ksW zezjmjM9Y(#bEwg)kJT!)h8hSrm+SLS<};>!P)o4erBfplS;d!e zeOKeqn; z(fr9@qv}j1$Y`;ZBFS3MfwS{d7!jB;wUeD(?^Y42VgT~;>G>{AGv6X=Y7T%~fr7`76R`^W^VsYE4bF z>0YaGP0SiOtqyC}OBtYy0H*z|c55ID4w~it#sw_n#I@qOwx}bP=x&$iEy#Vc-AC7{ z3y{VxrIV}$fox~{PTQQq^;1`UQ@|qyU$b|^vS!V2^GXX{KEm3_CGY&TXZk+ix3|;n zC})OS*OE%|+`a(65^t-fY!P|x*y9n`Ti}=D zTBgu!4MN{E3KD*C!FKgXwOD(o+;#San8=1?j8cJ!!f3by##`c5UeZ;4s0n*oCu`}4 zi3y3zIi{iy+^j@~9CLnHkrRuKY*%bxaj`!^dO@dcM8vKnH9fj*Vvl*S`G*R8x^dB3 z^}YbmJ&esA`J)0;VIOU=IOc3to339pfRkk0XUo!MKym1n|I=a_%)hwR?`%0=+D_gr z?>-z>0?W}`Sibva*bTqs<_j20I2A(f?UrwZNe?{zCi#LVr19qX{isyMY`W+*`3usa z?uwKo`ShK(?h!Wg9bBIuB~0{^QY$~2y;#gVwj&D1-u*E0l_7)pzPprvmK1^hw}t*$ z7CPig))`8CN)5AEkaFj1^_R9N2_3^PvX*c!if-?^j3G8oXw!2ESD(bY&!urD*cNki zq$9vx&J)584VvD3?uoT5_VazIN{8#aDP?Yj^DkQDJBQ+YwA0#EOsr(kn)n>`;PSi` z=>8Yn&X2kbeq#D&6hY;$3r+iRI#iU^ox`7Njrr!T)H7AQ+BWa_z$yKL{AQKqK zN^TUz^^Hl$d;W$MH{U`#sWK>vlvG8jR(e>W=67838kr+lc+$&sK9|JxAyio-Wy+E( z`Z)M4;Yg(1&*AMm9lmz((5z(a&VmIPB+?1hc-g_spT!D+uNFY%l<#lpVBh}-u7^V^03JG-8P29BZzjx00NU*-|XI%fy5$@TN&F1pjNBbU63FHCthV9 zq292C*Y$!gS(DV@#NI~fbv*~rf3ZJTJfIIISh{x0R%z&4{bks@#}9vxFr#a9ZczMi zhb7Qe7Z?h^I7fztLt0S^eG0DMw4e83ZJ8Gipxt+Ol^gV+KK&H8tCul4ad(gA3He!| znhkY3ds`hyUvN+CZe0Y4$j4rviniqhuVVP(*)Af*hyR``;`)X#?lE?J!1*UR1zA^5 zxK$?5$EO$!0(h*N-uYY{)eXf_(N!{%a5g&f6Isv&S!iDf7m6m0>|dv3EH&Bc>DktV67` zJ&_s(m65*XEph;nma=;!;qNdEdfT2>sv0PK<>lRKv2je%>V=2baQDNvpUug-YXQ@l z`pfo;tvbv)ISGpnY|7cpYQ+^(tjO(9r}ueWQby0(CyuFmUk0khN=eRkQS>ML+=Zx4 zANZHY_ewZa4%o#~7bntsF`NuW`ffZdz>qD$E$V~oTc!JO{L02S=7S7t`4%7rc$+TB{HF^wT*NA+)6X!`#gZSF5~zVcpEXDj z_xWhowH5duzr+}^%h^n+Y50yDsV#^DWgNAOlEWx9-v)Ra=sgPzsI$v{H#Q6g4n_wA{m2~f!)OSmh52< z6n!_Gg(J%WMJsysEynA?6A^A*UT<}vW2)3`KKHxrX39Nf@fHOXY9#sY*>5MX7CXln zeS{5-88rXems*u8lK-mz3isa0o;R#@qE8p;{q+%b&DKI^^zUO&Vxck1|Q4JMMfG4NCUg18U+8#00Q?FZaNBfY} zi;LFaCVr8uNZJ7AAA7j8w_3x}C8R*8^2?WlE?0oqF&11s@T1Z zD7i1!EYZi0q=)RugD|<-c!nDR2B%N&Szr~@XULp(sxdx(dwGi)4=~dotvUz(tHuO~ zr7RLP>LUGIjl15n63F{9p1~;w&v1sH%z_9yFzxz3NmIFVv4<0{6d2sG!w%~3vI6Id zT-BI3TVqEZy5DQb zk$KLaHyA*m_Sf$ZWiFVxFucF*k2T(hu4P^cm@9I?z98g*q3j?_li#73s%f1AKb(4T zDmjN!N&v~q6niqZ66^hrBx>lmKV(wx-YBP(Xu=V^tnP8-)@Et;lHp5L~{ ze)RFF-p96Ko9sPAyywj@73H`C=FH_GGpv=Lykt!-BL>gtk$C$7x7`R^G4_&tilO{! zbDHMFV@xP1GiihRAjbTC_%qhh|1c4Bd=|kMybyAXPCZ)XSh9*@Hr=^ z4r7*0o*FM$j~R%SN@hqI#mvx39A4@f!#Gyhjok(-boPj^Ki7l`dQpmJ2%qseU^wjY zvU@A32j=Hwr8+q7i*qnN4iI&46`9)5tpj>Bi=IQ(5sEM0bo5dv* zc5X`NOyZt5?aa(pD-EO(H}^07>*}It`sK8FH@6QW8Ard5k60jzAD3XR#}ZyAn~I0t%dW5j~ry4MZfCD_4FgfarO=A9OA7erV>x`*3|>@ILe< z3!evCBJg>DTn_ICsxx>$D7WMN!1o^ShZ#w{56U|6K9K*1&x2U|%?|Q_)(GB@{JvWp z_|ev^rFj?JC*(!w)aBk z+^_Z=ET6%tA^OM~?Cf&y1JlKJY@^M$pn#tUx_(~Z)#sXSd%=fiw8y}8z;c4Tz2r1w z?=dzP%ZA8L3}ft#Vs6@LqNUnK&tQHNddV2Hpah-)P z-*#>2ZW`hC`Sz_J`w|73UmNdU!p(=9I8~Jm4w=BDP}Rb8&ovZ#e@*4vf0LNDHVwRA zpoZX+IA&B#6}*Jy-6rHo9(ZVUIDZ`@9z*b3zY8zaJGqBxW+V7lq8{V@XQBu`Avb~# zQ4;bTCHjNUvzkZnXQfx-{Sx0FCO4RV#g0u%HJr2(0l6ql8ik-M{AB1cSk#}#{t6(l zksT*l%E%;UeI(@HT)Z~i)jGzokL!cHnVC32eZv}gHC?^4yZl$Kv_^$-D*Xm*d1t@8 zH|&6>e-gOJm%x0ltf`JPDI;Rf%}Bpzw$NeV>F)c;5sqAty81`c0IqfGx#YoJ5c zV;|&g49}*$@VvX)jc4z!jhVwMQLrsvS!8FtwLh-+8Nt63+NSk` z))2N&Pho?EV#);FHfo9~6io~8ZQd(vUB%Rep&zlHL{+GZLU(%ir z^V>s;aN~lY;Wd0bEZ*mN@U30_mh!XxU&ogf5LgI31}5 zW9$8O?~m%DL-m=&^lE+cZzh9a;Oe(E39 zYur2xBK2oM_OOuzbOjnZ{7kuVOKAL=Ev7G1B?yoJEz^ z2ClzijF34KLwPAL-1y9q+s2|tiwO-B-_LDw z-!ws9H&% zujCtk1}Jh$eIjwh4f#K>(ONY%f!(eL^>(h;{0)a?`T#eG6ipg+DLcM*`+ABvePWa{Q452m{py+_vsp9|8}<9yuuh+q=~W5 z2weq#rj(x#LXF`PZ%yHTB<`GPP9|cZyYkXpj7&GPePumjO%)H*_G{a6(!Fd7DH?O4dh{6S8ubdM}I~t0-vv}y8 z`8=WS)0kqydH|65J(V}7m4L>lcD-AIAa|GUXxuC35=?Sk<42>W0xW2oiWO$r0LAig z*7WuwtjNh9P6a78z#N_;Fmm=OX7IF+$Oqg$jU$c|9lJkb+ptG-e9ZJ-x9wFNe_Pgd z3v9CUlU~1925+OsSxi^W;CSVErE{aokXpdln$_~y0| zhx)HJB1T!0#Po7FIq{5+xFZw$5tB(Ao$mo7ocRpb1JW>G-Zj03FA0`n zmyS!e;6u-jccxvLqJ|gp$v=Ke8zHZ<>Ye&_8T4GlbIAPPHF#>D*dbLd3~uR_TUtL% zp>l$a&%vJy^vD7(&;1vNkrDoxYc8_VX6k;rUas5&<_vo%X6{8{@+}xoTuCbj>TpH< z_&o`%QWJMeD_tbS-F=b#iSaR}c@QO3UggL26?`y8IZq8Xcm4Q7x2{2jFWs7=n=pKy zFmY@VH$slV(c3hiWf1+9cTJ;(rtn`0-=^{`7ih<%J_z7W#d_Ce9=+PI-!|HR!zXRi z6WM4?y~+Jlf=TS(8K1iAiHg3Ye8R95VMyal=fB=-L`*GK6;aV;n75O(_7wjlkm}ur z?*l8$5b`l|dV5X_DX8{8c(lm^m2m|TR&v^ie`xzq>?jMoJaxlJT*?863)*TFaO3lL zE^6_x{tt4TXM`BJ|Y~5k}$|up>3qD}+YMSA(xjV$47Mf|g z5CetUp5&26;cx__y6oTbt}U5VsCwR|yX{S~_ttW&D{Sv3<$mZffX2j1)$jpV(Ek{< zcxcD~Jh_InN+A>;2<|+yAG3wCa-;#4lcDgFh*;g;*BPRc|A>RFv%S=!zsAkz4+rIIFlq{|Zx5+SgM8m$+`p+2#o=JRTed~d? z)~BNxF{+?o8SATMZVNSIKd?ixdN8rYrij|)!AeHv(-9*KlK!1jb#f^bq^@wUTnTW7 z%mKA<8O;zN6O(GDA~%P=fusvVXH0-zEPJOyloED+PN=5(q=MAO-Pe)-c|qkRqMm_o zVGxVu$Zeo>0nW>VKOZ{Tg6Eb*QkJG3h#lR_m!0rHS+4T3H43VbURfGADyINMt2>;p zn=nYcvm+eMq`<#wvnaQSV0c`4(@EiG1RA}kOT}I7fbQ2WjCvSHAamW#-(e*V=+~QU z_Hr^uRJQD2eMXT1-Bl5JEHj-79#!euC#gfA?66;xBfTYTr9AV`tuTSg6pMgaMN8=3 zrP+O2XaWlu?y9wk2w0R;Ig~1Hh>ngIsUPdrMX7oG*hbC|SVJOimjV?m_IY{c(fANM zpd;bu_!CizSsgg3$toiSYWJRyKc_k*J0j;AwwPsyhU=%)6!STtD1>!dy4w^OA8U%L zH|rpcCEVWa{}42feVlvsHXUZjP6~Hsslgvo1Qqvnp!oJc;=^r(zQxRD6h(|-xx-3D z)4UjvJZ zqpQgdHk~9bFP6_8z0Q*nqzc}^oY^J2RzZh*{s`jMy|B6`*OISN?hv< znVZb-XrC(s<;57rh%dINkn7s{oQ&(JkzY0<__qVBZ%?0n#M+1P;!b*W#*G1Zmxaq=xH??6dh-Gw5ak^PwgOz=LlM4|r^_aw_a)PF zLpuyr47hhwKleeWI2YQ3N^Qa2!u^Mk+HIJ1I3sVAcooqnRXb1N&hPL}8q#50h=Mk@ z%Z9bjBvAjg#@+Hw73i#h5}HC|$l-W-UNzwkQjAje*sZWcS_QjGv}zG>>~vXFsJR_r zEWBoKC|`goo&QV>)3i~5@FngKpIo3rRp{D6nJH2*_KYknHAHG>Voh}aSR?T>`l6h; z`8*0;;kZZq5|C5u`YPHc0J(p48#mN*LG!G7=>y+G&}lQlq(~)C@Nm5H&np1ee{-CI ztAg?-5|Rke*e;5LowS1Ajnwa&dn3i zA5{+!Vu=S*&v$kwJQRUM!RCtIhBXohU4FB;tb>B449!5p4z(>COSt%51Iu2X(&Zo4 z@PMdfg20etO(^$&%f0(cKgsveIoySQJ7hA2WC4 z=V*i4A)$X{S`lEcSFg2<`+iVZX#9IpLy+%({>o|88pfXRcjf%V_3@}U{PLWFCs-S7 zD#hLk01=BnDn#loP-{AEGH^N&u)DKMHs%r_x%69XwCOoE>ixfkg*V<PF&wMsbS%=y_xH1h8)ijHG^k(GfcC7wrLQn6FyZGqg{`&XXcDgoxY6~*1y;z zbn}m#deJ9^35R;bX-s;DX(Jc&RV#J=W^5SL59CXTKext^r~D|U;^ah*0#}aIOWebr zajmvh$-%AbJpChfUhp(pOctclX~3Mv-Bw$nh(+## z86#S5Ced`r5CEsoWF}*tKi)p@1CJ2tfgiaw zI#b+R_rdzx?pH2_)M3*qh+Bqn*WaAdr7yt`%CJXD@r-0#i7JPFL}BHe8Ehx-KgKlL zOMZL2osO~m*|F^QWdI{)^Oa82wglr7?Dj8Z?k4;*6HA-(5rV~BJfoOzct*rcV=Q+2 zfX$c@@AOEo$Hu$8A+emF#2(A7h1l=0JGgw~6Gf;a_ zTlZU6VvFS1WCUzmu-MQUCaI`-EN#ISxw83J?5Bc-L|5Vslv?o& zYn${hVtk4)PaNlayHiFmJgjczHqwdcgjge2i@iT`ZV@vsHnl^|Z9}M=^Avh0We@sNIVbdA znSd$T4;B|le(d*6@}ZhEk`G;OBISWLF;X4~eM9oY_+^qGZiSHiu+mEM1EnO%2g4d9 zACx$d@}QUt^8e?7fLA0xV$~w~(OJdc;|34f2#-q}l}raZaF3>1dxynRV4Lw5CrgM2 zC{K_Wy=EXWs(x%*^RXFl-i5q0-Uo|uiG4$QoL%*}Z*fl;g<4>r)H_1 zPhPzOvs)xa(ub)3XXH0wJIn7oL)gg8Iw2}PL$IX1kBy8pgF)$V7!n#F$w_+sY( zG5vzZZpoL9BRLUP`>gj;I4# zs{bKWA?XUTG)kFS(Kxh=NamJFMG|KhI}Zfnu)b~92TwSUS-{{suS$!vL%3fx^ZW}F zn^MmBzmb}3K`6pw(_)TinNUU@2d?|8Q10Ea=8^n;#NyEy)6&Q8NIWk51<#XpLQ>%F zGu-ShC{XM9vuDxV&?n7V;7f_Ue|Hq|j5T&}c#Edz-)*dav3`M;_!)EHq~~5Xr}l)_ zgTF(6^jn~)O<}j5BTKlQwHT7Wjh5{D{Kisu@8mY2S%;j5E(AuPGeewMJ+@8+4z2 z9S@lfpJ^?6TtQvfr&_9Vjes*I^Eb@JyHcJ}8xc_mx?~Q@TlCPh{qXA@YcQcZ%Q?^1^roM`4H3b4J7qv!y z^8)&-CGpR9n?qpuQd=BWANJwU#T2pWK=dvU5S&;Hh!|v(=aD9?_1)rsa zb|}tWAEyk5H-^Vj3$VY7CR^_Pnd?!gNYWz5OV9>+wY!|^m+;5dAT4?N-WG`Y!S=fb z>vJFjw)A~3u>QGs>W>L^hk<@q*OXU&AW&qydw&G$I|_c`Jd@qWQGVt9MbB4wLf`Ov!oDa>@Rm&&dAU zf@J?3(^HaPu8Qo7J5TmKzD~}w>25{J6UavPOTQxfZK7VB^Oq{d`be!=b-j~DL#OWl z&9+Db&if`QwS57QNJHk(*JKt<54~zbCkTZlLLUSTRDnliGU3y9F|;-;%!O(jgVU|0 z_&+!N&^u4FN*;D|^el+Xzrx6zbl=#Af+Z~HMHLjz{Ho=|ZmM?^4 zzxcWfXBKe*&z)TS95vyz>!#_S=skGzG%tt51sB+e52}1H$bhfmj`>r}TqAUeAJP@Plg_^ zGhV(&F2)6=@a@+4vzHd1db;}LQ44Pr8)m(0t$Yt)O}$RUtXg8{u`#l=LP6*)BkRrw zXDxU;Oc-gT4nl_)MUo!w#nz3^f&BxAgW&11vPHk*G~r+4y64*VR{P(X2D4~YD^y?^ zWX~*m5I-`~mU2%Z81xCJdylPb6D&F(wF&>LL`+J?5henE34Q;}dLK%=!n2yUEelFK zNJGRpq2aC@+=utstzMSs>gk^7*j8<1=$Do^$E@r z+7fd1B`0a3=O1}p7>Ew&wFEnTRiif&Qtgl^Ej35GTJcmIeql)DOF^)Zm@6b1eE<3H zP7Fk^e7Uc)6a+Rm-rmf*h|SZ@xcy_!JqStiRZR~`oDcH=iM)VOAA%?Nd?+zK!d48d@_w{3c8chy zL|aEkEjQp_PVLraQ$iYii=2YXSlyo8cl#m%tbfWKZ$ELDH01sAs>va*0OV#Kx%F!d z>(AT1R9GdY4;uf{azwFtAIIw*l-t?Pg0$3<+My+H)cw0S$xsKI|672!xiDkq2wd|E39Aq!>Zk{7H4R|hHf#(pVJEI6gK?KJ{b*r2nu4CciTdJ zYU}|9QGaNmdC6Atz#Ihx34MAI6^^p>AFp(j2O(jK>JV#+M5G=pYJV&?0zLUBuhe9l z3^94SGhE-;;m)-${Y)>@kyYzG(|;~8;H+fk9DgVXdOgcBC{B1nnOx<*_(U~i>GYu? zx7PwXDf07f_WMAe{2$h0Z!5IY<}QW59tB&culFDMVGY6Gr$g>!1j2c#&PS(>17Tfu zf`9l`64*6vjPIYu&P^}wrK@DlKs*)+|GqCrL*a00w_s=@WGB|D`rB}V2)p2H(%-LA z6STM(-jZ4zoG@V18SW$SQI*d8Om@fmNnK(u>m4Lqa{J|?6YLJ>yL~$jZhpWKu9e7l z*@wf^^WoinW$Q#7f&NGKV zP4Gsc>g8tKSxtvJ`qCswXJsfTGEKx)gv2W#zjNrFOKblQ!vWwBJ9e(Y65FrgX*9Cf zMCi}OCdzcR>kvvqG594+2Y4s#yqM{{!Gq%-yMd}1G*(c=&eL|_mT%qJuB?6AE+N)? z@}yD-#25}+5Hi|v(wx()F_R@Q<4Kt^zMhNQ&(i-hkQ?i#W?g^Ar*ja8O#r*cHz|(F zYtAfa)g1rRP=QO4V%_l`+^h83 zYmdeokoKWER`-ft+}w$apLh%8P<9o?CK@^pPbh;-TjcG~TdVF3UoJUR!Js$pENG8j zTa+F*W0phi$AYRSlAO`TTe=#GO?9{szZA71up3EDE`Be_TaXGYu{X+43Wa^G`OId* z0kDks);u&E0s_zTn;)?SfN`+TrKQ;vV5!i1$@DEAoHfnvxS9WHx7`q4={~s9-l2MQ zXaTG5P7Kxa^KbEh29b z1-XSE4Fm0}#x5@|516FkZQEFo#_k2_zrDy11um?L3}Px4*nJH{ha{~Wh%G6OH8|%F zzTa5K?rdj3Jy)wC6IKU(Ee)?+bTk$vHkrKFjzU>74ISw1s|l&{waE70jG`)$v&QShpQ}# zYBjksD5}G*Tv9X#g4iCs6JYg+;?=AD3sRXdIb5OIxE=xZBe|A-hT#xG>4;<6?FlD( z{@@dBV$nZ=i~Xj9)^OeS-WSs*M_BnFEa>+_8Un;`H9r=(4Q&yMLxl=ya9;pgJlzsd zQ$(&6$7MIozOXfoh+ z{aPOv)w!Y`*-7dE!9(cyX&Qk^%-#6L>=F0Yg&6s(89ripdeU+uO9J!8Dd`{4R zK4pPkndHlV8luJruoOO7IaY=A8!6jL4VQ2OyFJyOS35)1UaRL6ygbO=O)+!HMhp2d zaGs)j>WB=umJK_9xk0MqB6VDYH4^%B!9|5V8Kj#z8m?pO1ogDwoW!D^xFQ8&@m&Ea z_+2?{`tp|#$ocNvrKZ`3Ur=+MJs_x$T31^-;<6496MbD1P6zv<{I2ZOA8UK@6jGtB zblqNvzUlFf#^qhm+Y~qVxLXHqVI8mXyn>LxFW=8MBP^jwmGwmYy&xn?$cU@-(?uLl z{P)B4Ac$POf4J}23~ob|vT3qvAM$!ARw7SZg=U0VzD70tz!gxT8SdqBCryU+_Aq>pGA5HEmx)e<@3kF?QYQ&*YxP7nBfWY=SVMK5?ckPb!3 z!RYQ*jHrD)8aS=J+Qs^?dwfMrGR}8pfmYp$XwGYa&Iq6TsCCi@97g7x zafhwY@w>|vE^NVQ(5H)XtttrdrM7idHYb6xxk9~c{BdmGhjQ>(3fAA@^&h+Ro(Obm zg7IBH)~E0U|BoLb#aR8^7(;-|>)T+w!)tnCLj!5G(wH9|O@+qp1w`NF|FHdjt_+5%0r>c*C35HxSz>DwRV3G9gs zFY9*u0$<&YdfjwObg%wIk?KBKw7EZg1&_0UmJIt{wQ|wG|3WwFWUCEWP&YpI=m><6 z7op-pn}Hx#GLdGUm5wy!7NcXbV_@_4426nSA{d8$Dha5Ifb(9Js{B}=S}SVdC5>Pn zSk`g465>6H`}aP2irOxlpcv@b5$@Ma@K9KJ{Wm-q>z3%cU{d>-;3`jIkQJ5D^Yghw zc>CgSFr7{Xp^si#!Yl7RPGeh|Cr|%9PWJBDje!h*l+NY5rP$|y8VMwZ(BmYA3-+=~ zPxz*B3HhkHh+SMvY++keH9 zd}!vhb4A{CGa-VU2kNWtdVI3TA;=L)e$+{1&rg2y7Pkxg{Kou9d!gZfKd8JT`A~cg z$%l;2k@A4cHYpD(lw0}V4?@xt|NGIAh;H-KgPMeWTAI%(TOQ(S|GiP#)f5lMD?ZpH z)AbWHw@Hkm{IxDmEwvKQ^oBI@3ok2WQX|X^ctb54v(T?D{7gxahKBW-rG2P7_#bBQc_y_WYlbhU|4S!}HVeuu;m?m07%X=&(BnNo2Wqt zvvEp%*zW^HVTNhQ$*7B|8L)NgHI=W8me#B*LkIk^-0T~{wPf4iGq`#7F=ymN0Q zk9n(w0>8yqPK{zOVB#eC!9$hghqVQg4{_fi`Ose_QXZhzBjrK)<|IF?ERy^nvqbX4 z-_ImJ;1;|7_rb_Bk`Ds9NqLZhF)0t|5J`UYMVI79t8~;gkG~ZVKKy)|dPQ;$dpqzB zPMBq4_t}^H3#h!HDS^aj!;!?OF7Mo8#KuQLZ;xPpoMs|HFOcEBZ_sDLV~z%odn;cF z)ECaotKfgj|&BM zA05QmR~(64_`yc>U-n11oea^2(t5i_BF~WS(}+$9IO5!}%`bw6W*DU?_ZD zFIaCHHzh4$D3Xsu^U4DWS_7WwHNSSH+0S*t=-*REN|KV1iKzV0RYz9hA>qOIN3r>J zA&&|NUgsPnvgR(-Xl9tBZP|h!@?sKLzu_aU8iH;hcyDBTM8zJC733V;wo3wUi??Z) zQfweVVn6UsMS|Pm1iq0+zR0DArJlL(53YJkfaEV_<|X;!A7=ADe0qqeagCe@4(0f4 z=rH2d_mchU7L=yHWSQ_9Ib^?rS1!rlo=)~fWDk&hLv`dl{{7@UXO+o*^}cwL|G{iy z>7AQYMAzV=gqtGLaI919??-B^j$`DwaCiKEqS;3>M^ZDkh@g3#X!`93w$wUMe`=T! zn*vJ=+pJLZzo?4_(%$x;p)|?w_`g&+SXvKQ!PZXjt&L(kDFqzH0tp}nk!ca@2 zIq%P+cyPKiQ{z?U3j+RW0uIS;usVh`nwo=Ait*RKBf8!=|xb;QGJ6NCo z6}P`tuk-xDqBv0HOREPsSq_|al=FnI=kEUcbtnszzw=Bw8B;aediC20`Y*5mFQo{oN-(Omm{Fd4mIDI)oQSCIXgLLW#zjk7Bx-;6Ff zPtG25QXa7v^d!IEOR`^W;WEiTIY#!2QjvXe`^i3fU2>km8edYL$%kZrh6CAetM~Gr zQpO$V%cCud@AQHvmyc7(?2U!#>Y{XVbUpfgC1Vy-ab-`r;Ttv})bJdc8PT}rv| z%1L)LDhwSEPwANQh`c`HxF75|L1_seGJU@!9Vo}nQ;9vV(tTk=&rSv(8;v%DlrCA# zs3T{WE2>l)W-xYZHb!5Q2gx!QQKe@eB8ugE8~6{)Lg}yeI1%ql$WlG?@Mh~O!5KI4 zzD~&-RJ|E{+;_O~gzSyCN#+));N0IvWfpdP>5}Deu~9g3Jy4lp-y;Yd9I}UO&xJsd zn%kb|n+a&?yqH0hstfwNc4OM$bPPJ%98)!?EsT7hMf)!D#X`b>>N(4@Z-kxwE^0Ji zufUP4dSVcl4D!wR@qLP&6L0f-Zei<+FFgI!@NKs1FtJ}F?eqxNza&y-irM4beq!G7 zk$~P@D+u~$eMm;;5|S7h!f#QBK>Y0Q=KEv*=s5kqxfib3d+2xog;$$1>c4SfW1}!0 zUM9@*9*A-Q)yZ>)?{ijg;*ynP88*`B+Jjo|z)RLZBeBH4clbEIdZcc^=Uocg%D?ky zM4Jk4cVd{1Qppp{1OE-NyB@*6x^-st_LMw|E~a)~4?hnf_z_{g`;mxR#Bo^ZX$X8S zs~g4c_Hh<@Bg?NJS%}X9(O&<$%z`RUbmJVm)Y14b z6xIFBP~l?`;Cm?eL)XJ0L#y*g=nZekTWwSlWAH|H+(A$U&E!f^OCPjY9)YH>+_{QE=C#vQ0508f-sD z3i*asfxgRXt@Axs=;7tqc62p@r>paC4*u0cm4Ed)nUkWS;SS@#%6@OCbyYnw78i?* zMvsN%?6@KR=r;}prS;I=|LD59k_U)ho8QqmUyODQBzh?v2uH;Q_Z8CIED`(L7b2E+ zo+wgGGOKzi0z7Zz^);OH0FJLA$96Afq3&4X_C!cHHa9pNe-Wz_yE;_%t~bI1Ihdww zMs54Tep=2|i}b5-^7S4y1`Z!UmxSCdu`3|ceerL`uir+8mAHm`22+sm!qJl(O$o?I z%t(1`HXRw+eAl(Y<|J*KPaHjUArQrmS?^WPxq)g{@9`eD%Y?PG4a3P>5n$@%DI&7! z4~6{v_oOSWP{Sm*cS(^eNOZlf{UPcMKSw_P&fRT^k~2n6`pJ7kRLqO64N6&{NW7S5 zCywop2KR8=y2U|iU+U|NT^SH7SH(5Lk^%}mH8rc(a?r$;roBcc=@2++UGVrt926<2 z-BCFkgj9H|KW$oZqGkJ1&G+iw1mBt3A*Y}z!am;4Tba%tz~GyL4`BaDn6OdyI$`Dk zI;O{h<1PQ-v>grC91X&OGJSI+o{I^I=bq?KHRb}(Nk%5>P3*f3LfAygJwT9gc{<^? zCp@Sd*^kOR!C_MH*J%YmplM$9G|GtpgJx;IrWZp5M=lDTyrKPw@9I;*Uc*$lyeCqZ z)wGE)Ydm{Hr4*~js=oY6I(!!Q?aa;l|GMHqTE9VhrE>sxg@$K=wc-#u!tj!XBJCu? z@9J@SOB)IXgR1yCF-3^{pwpN_bcV&OPZ@gzJs^u`X!HeDEZFr9S8e|ahka-GXc<)o zaR(W&Z_-!10ga=ceXoalJ|1uctqyfLcQXuk)F=gi}v~BX_X-s7vmw znu5dyT*5aq-ho^^ax7s|W%$j3?q+;luyA#NwxQt99g*%ZYAg4vWi$-1O@*x?ao?BIJf#{RXc{csK<2XvX}p_BLcesU-O!0~nRe7i0ojApVV2NeT_k*jEQ-bKYgk!<_SbKZ7!#x)3x;Hdv95yYM!TSgX$NN!CJ&&`?lSVG8w=~Jith89}kSh2c};d_yO8IT;%#k3#paj7;9*Pv3^7B&!wO1!NCL- zlfF0UaARSe`sOnmFg7`)`~GweREh4s#l@2j$JiwBeq+ff+RsX=?}8t=QCo$N4Pf*1 zEV9FE45DG2%CPRDWeof-?^f2+^Mx(t4<)9rvf!i(%b1>a0xVm!N(YvQz)=?0swS*{ z;h3&U*>tNfR12wjg<9*NjKuVPrb6kEg+Hl0|I`}x5+ns|C-dRC%4OC&Q|a*Q__(+0}E2s|A#y|uC(4yQy`{0-ZK;epJ9e;FTd z!3#N08=3e7WbS_?XyK#{n#7Iz4ND#-z6u(h>gfD~`>iM1_rTo+z9oE%vJ<1gOA6`r z8BjREnFD7;hUB>M`4xOl&uKl-HywO;jF$-#*5ucgqqYTBiVs0a*;eSz*}F>8yFN+L zoiF+4mh}(R?973$rT4=zC0Ug8Ter#6YA>&uyUROEHg z4KLuY6~oORqQ$Ss8S;5Baf0gaqUiA-T=U_lp~WFD3)65k zEF!)riPhn0WJ+S{T$82O!CH<=Aibf!JKpS#hJ%c$9pUdLn=^5uGy9vt!7phBp=`-o|WspqSm2 z3Jw}EAi#U9Ty`@SuFU2%WrJzN$udm9Cw&l@add*Vh!D#w5BdP+9*8f;FIfSu?Thcvu9X&pi!q}>Bh5l@XFWJ z+j*ZX960!BlEcdn)lEI%dMX=%%=2Q)6FCYI-D*H?US1S>As)_PTkiq!XFhSS7lpz0 zwsl~pR3tdu%yg$834k!%=hQ7@XE0RAQ=HV%NBT9bu`CvPh)$|-Wg6?-;3L(MQS=_` zhw3uJK|df1x}xc2NH-no2x2A0aS1?QIX@5;zkr+F-siX)fX(uRMdHs_$e}+)Pcv(#jJ6z{5q~g?L-9>!+cJEKRUN0m( z$gkWG8;VQ^NesspNeu5Ap3Qb94HG^ZFm_-1SBTqv(k?YE0y}@RVNzVBISC8m10q`! z`RJTz{X1rz_qg_(=Vh5zW1#qH?0TF^6QShG9p^+#1z1%b`v(j3>>9iNr*?U8 zdtf=YDmsc|>>~LQVao#%sGBvsa(H)LKiHRl4)Yv_oBmE?X=r|I?$HA=&B0_fv7qWKt3CfM* zE^fa2;eF{4@egI_gYDby#qSC3A~eWU)^lFuCJ4I*N_hFjpgPHpZKg98 z;BvmToAIj#a(88loy|&vqx>R`3v|(tMo;p?h$P7m(_|ld_=V&{2SZ7D;DtXa54t8w z^26opBtN>%Me@THHa{9$*L|`B5te$&VzJUlN4= zwGgf_KjdbY9U)L{XY4;EmjoTW+f1*Pt)SyCiP4wiBt|jYYAvpBdvF~Yz&jDwk6YiW z$#DEWkF%YLv|LZ>A`}MBMCvYO!1K#xHby5SfjOMS*!KvD(ZMqtv3fW66PeQEwgf~e zh~fNnCpDIhU{rd2f-jQ_KRwKFaGx+YQI~$Ja5upWEerWEnVb+H>MU=N_-)+jrU2R9 z_-f{6=Q0i(2&@~fv)*=s#fi0T-N25_Wvo%#DP9t5hmx6z-zPTH zDFaTljBv<$N5QW0=;{VLN_;D}{qo#j5tX>T6G-2&Ku)+Or!;w5qH(cpNQ;pK%^Mwp^Eki#U~If==v2sx_y7$~1}4N_Fj*=d2bS>OaxD?}`FcdtckwA1B~$J=A`7H2}&hm$E2-groi>Y{o(O7aX13JjtJa!;<7ndiI1n zONpKMf@PSLr{gK7;BOgreE-NXlHWRkN`1dFGaf#W{dF9AB>y9JvhSuD70FjOM9$-W zfSl*fJF;KaiR@?ED-eGooQYU`D1p$pd;$6--Y{EvTR?=kt=`gYR-)ZAGK+M4xB6xt zONjKZq79=n2gBK=%dIO%hrMp2WD2B|- zGi1JUtL>i9cocFM{Gw5u5dey+G;bpt;$b%b&ezS5Z1DT^!0pztqhKe(rEeE%f(*3^ zNxaF+L1JH)#h>Hn4G~k!zYi8zT~E%>{#6yB+wgYJ{q%^$JFpzKV4F*u1BQHT6zp5( z@L=;BiTUfutY+5j_t>@!+@*a@@oS+_JofgE!G|=U>zlu58D@^?kG|YyShq(8_L437 zpKZ`)VS!pxjS1Qmd$jV)?ix7U8-De>S`)S4GR&k|eUREijsg$qQaGMKaZ#Mt9j(c{ z+pb>?hdXP${3^WVAV+aQ_Nas&a+fkv@%>=ulgX#FYo-B zmcfgVyGpHJjGdniXp{un*7U%!CtPx-xzLw2D@goxAyGI0y^II81B$`SpV579kFPNKHem=&($^( zr)sJ`>7=g)4nt0)D*E9y>E!)49fbDQ zWk!ue!BEe(x5?j7l+w1YE6`vAb^*}`RwVu5Y2>1UUaH zrQRr&c2iVY`a17wTs^_$?~|9`)GfeXX;^mu7h(KUH3^rM_ZCPh>DdZ??iBvl&t_s| zP8zx>a{+g8UKU+*pQmGKih{POa(Ag`*t~Bp7ReET5X5%&&;{#Kg4 zPkZQKC@>#xRd-r@j|)}rIv0CF8Iq-w#5L~70%3kWYg6Sk{@&yGzcTVxQ0eh`>ise+ zG5)252+i&Dz?R{v8%$3{G^({Pj+#zFh?20)cUlKcsW__-Mf*deKbhrAaRp0Y^&l=zA{rjJgRKTe+W|jpUw*FoM86v(9Iz1Q`lO5wzmljtS!)9eZDj_g z?ujU>;EVECid@vBn9+ir5F*ZQZV85K9x!_07R$eBHnbB*Wk(%p2Dh{IKiQiKVEZWH zwR>-vBL|xg5rq0;6cef{eN&+ZrS$3NyEl{qaXi80sBb8AQxc?NZ|0)BkAu^RqS*eG z^V*v(k1WU;<=J_YV+ax_i!}K!+=ODGUQJXO2cueFdu9q9p|0ed`R5)R_*0Uek@`Xl zp69ry&|R@Wq8%SUR+*?H<|BHI%4Sw@E+;1Qj9e-7mVD*kXOsmjyDUY&IaMQly%>Z1 z@e0JYVH+**I39M6Qau_LutxQJT3X%|C7`?G9V0YHu|9cW&#e#9WuR-{lc&E|TR?K@ z{+#q+b?p2kLgHhr4|1zH)pWSs5uEZ0BFa7OU|sx|&ZRtKR2mWKJt1iVv%_>buan9U z7njv3PZbZCvl_RSC=Y|j#&;yE%HvR69H(FG=}2^1rT*u)%nDRzRCS%^l__dB7_s`} z8x4L!Ukvt0CW9YkMV!WGTXc^;@3IfN3h1EVTX^6DZRLigSgScC_aP$x%L4-tNS2Cy z{eQo)ceTd%^z2}$uK7#t-(qk-m^}K^$_KR_-eOkA>a%Xt@tF7)#KL6G=Vj2bKu`ES zhFm-s1)bmX&%7OtLqi;qm|jVHkL=-Yc@3ppV%P(t*Nh8 zy`HGCW{2`)b`+?L&<8R1t0Dd7g?n>vu)250_c_;V{L%FUO4?GaZt+Uk-5lANQNsJz z#)>M=6o~Fn(1&5X9b8g76O^mJgfs2BxORV!3DneVfj4*=9(@RbhTIh^ z7Gk=R1{ED+L1Jq9!IdR%7@eX>uFJ}VO7~BKzru1rwDzgX8L=hY1A5N~D#cRBKZ8c* z%`P`26u~sF;`kjm≧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

!wE$K+ncRC@OJKE( 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

AvKk;ZLM#v5qZhlnx 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 >opR-od7RG>`N`8k(m|?Fr(4EINev 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 Date: Tue, 20 Jun 2023 20:02:59 +0000 Subject: [PATCH 05/19] Add missing __init__ --- .../resource_estimates/pbc/__init__.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 src/openfermion/resource_estimates/pbc/__init__.py diff --git a/src/openfermion/resource_estimates/pbc/__init__.py b/src/openfermion/resource_estimates/pbc/__init__.py new file mode 100644 index 000000000..ad8696161 --- /dev/null +++ b/src/openfermion/resource_estimates/pbc/__init__.py @@ -0,0 +1,18 @@ +# coverage: ignore +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import pytest + +try: + import pyscf +except ImportError: + pytest.skip("Need pyscf for resource estimates", allow_module_level=True) From 13a505bbf757231c7c72588191ab8955205883f3 Mon Sep 17 00:00:00 2001 From: Fionn Malone Date: Tue, 20 Jun 2023 20:20:25 +0000 Subject: [PATCH 06/19] Add missing init / skipifs. --- .../resource_estimates/pbc/__init__.py | 6 ++++-- .../pbc/thc/utils/__init__.py | 9 +++++++++ .../resource_estimates/pbc/utils/__init__.py | 20 +++++++++++++++++++ 3 files changed, 33 insertions(+), 2 deletions(-) create mode 100644 src/openfermion/resource_estimates/pbc/utils/__init__.py diff --git a/src/openfermion/resource_estimates/pbc/__init__.py b/src/openfermion/resource_estimates/pbc/__init__.py index ad8696161..094156b37 100644 --- a/src/openfermion/resource_estimates/pbc/__init__.py +++ b/src/openfermion/resource_estimates/pbc/__init__.py @@ -14,5 +14,7 @@ try: import pyscf -except ImportError: - pytest.skip("Need pyscf for resource estimates", allow_module_level=True) +except (ImportError, ModuleNotFoundError) as err: + pytest.skip( + f"Need pyscf for PBC resource estimates {err}", allow_module_level=True + ) diff --git a/src/openfermion/resource_estimates/pbc/thc/utils/__init__.py b/src/openfermion/resource_estimates/pbc/thc/utils/__init__.py index d0d41a5f5..73f1ba7b5 100644 --- a/src/openfermion/resource_estimates/pbc/thc/utils/__init__.py +++ b/src/openfermion/resource_estimates/pbc/thc/utils/__init__.py @@ -10,3 +10,12 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +import pytest + +try: + import jax + import pyscf +except (ImportError, ModuleNotFoundError) as err: + pytest.skip( + f"Need pyscf and jax for PBC resource estimates {err}", allow_module_level=True + ) diff --git a/src/openfermion/resource_estimates/pbc/utils/__init__.py b/src/openfermion/resource_estimates/pbc/utils/__init__.py new file mode 100644 index 000000000..094156b37 --- /dev/null +++ b/src/openfermion/resource_estimates/pbc/utils/__init__.py @@ -0,0 +1,20 @@ +# coverage: ignore +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import pytest + +try: + import pyscf +except (ImportError, ModuleNotFoundError) as err: + pytest.skip( + f"Need pyscf for PBC resource estimates {err}", allow_module_level=True + ) From 22c833dae1dccaabb7d39a6e4c0b0a33cfe15a83 Mon Sep 17 00:00:00 2001 From: Fionn Malone Date: Tue, 20 Jun 2023 20:25:48 +0000 Subject: [PATCH 07/19] Fix formatting. --- src/openfermion/resource_estimates/pbc/__init__.py | 5 ++--- src/openfermion/resource_estimates/pbc/thc/utils/__init__.py | 5 ++--- src/openfermion/resource_estimates/pbc/utils/__init__.py | 5 ++--- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/openfermion/resource_estimates/pbc/__init__.py b/src/openfermion/resource_estimates/pbc/__init__.py index 094156b37..1f62ad09e 100644 --- a/src/openfermion/resource_estimates/pbc/__init__.py +++ b/src/openfermion/resource_estimates/pbc/__init__.py @@ -15,6 +15,5 @@ try: import pyscf except (ImportError, ModuleNotFoundError) as err: - pytest.skip( - f"Need pyscf for PBC resource estimates {err}", allow_module_level=True - ) + pytest.skip(f"Need pyscf for PBC resource estimates {err}", + allow_module_level=True) diff --git a/src/openfermion/resource_estimates/pbc/thc/utils/__init__.py b/src/openfermion/resource_estimates/pbc/thc/utils/__init__.py index 73f1ba7b5..ccef0d8cb 100644 --- a/src/openfermion/resource_estimates/pbc/thc/utils/__init__.py +++ b/src/openfermion/resource_estimates/pbc/thc/utils/__init__.py @@ -16,6 +16,5 @@ import jax import pyscf except (ImportError, ModuleNotFoundError) as err: - pytest.skip( - f"Need pyscf and jax for PBC resource estimates {err}", allow_module_level=True - ) + pytest.skip(f"Need pyscf and jax for PBC resource estimates {err}", + allow_module_level=True) diff --git a/src/openfermion/resource_estimates/pbc/utils/__init__.py b/src/openfermion/resource_estimates/pbc/utils/__init__.py index 094156b37..1f62ad09e 100644 --- a/src/openfermion/resource_estimates/pbc/utils/__init__.py +++ b/src/openfermion/resource_estimates/pbc/utils/__init__.py @@ -15,6 +15,5 @@ try: import pyscf except (ImportError, ModuleNotFoundError) as err: - pytest.skip( - f"Need pyscf for PBC resource estimates {err}", allow_module_level=True - ) + pytest.skip(f"Need pyscf for PBC resource estimates {err}", + allow_module_level=True) From 83a17cd737affe784284abf3770dbc89350378af Mon Sep 17 00:00:00 2001 From: Fionn Malone Date: Sat, 22 Jul 2023 05:14:08 +0000 Subject: [PATCH 08/19] Resolve review comments. --- .../pbc/thc/utils/gvec_logic.py | 141 ++++++++++-------- 1 file changed, 75 insertions(+), 66 deletions(-) diff --git a/src/openfermion/resource_estimates/pbc/thc/utils/gvec_logic.py b/src/openfermion/resource_estimates/pbc/thc/utils/gvec_logic.py index 64dd1a41b..f9cadfb72 100644 --- a/src/openfermion/resource_estimates/pbc/thc/utils/gvec_logic.py +++ b/src/openfermion/resource_estimates/pbc/thc/utils/gvec_logic.py @@ -1,4 +1,3 @@ -# coverage: ignore # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at @@ -10,28 +9,46 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +"""Module for testing how to map k-points between each other. + +Reciprocal lattice vectors (G_{pqr}) are defined as linear combinations of +underlying primitive vectors {b_i} via + +.. math:: + + G_{pqr} = p b_1 + q b_2 + r b_3 (*) + +For quantum algorithms it is convenient to work with the underlying integer +representation of reciprocal lattice vectors (pqr) (G-vectors or gvecs) rather +than vectors given by the literal sum in (*). + +This module tests out various mappings between k-points and reciprocal lattice +vectors in terms of these integer values. +""" import itertools import numpy as np from pyscf.lib.numpy_helper import cartesian_prod def get_miller_indices(kmesh): - """Calculate the miller indices on a gamma centered non-1stBZ Monkorhst-Pack - mesh + """Calculate the miller indices from kmesh. + + Assumes a non gamma centered non-1stBZ Monkorhst-Pack mesh Args: - kmesh: 1-D iteratble with the number of k-points in the x,y,z direction - [Nk_x, NK_y, NK_z] where NK_x/y/z are positive integers + kmesh: A length 3 1-D iteratble with the number of k-points in the x,y,z direction + [Nk_x, NK_y, NK_z] where NK_x/y/z are positive integers Returns: - np.array 2D that is prod([Nk_x, NK_y, NK_z]) - + np.array 2D that is prod([Nk_x, NK_y, NK_z]) """ - if kmesh[0] < 1: + assert len(kmesh) == 3, "kmesh must be of length 3." + nx, ny, nz = kmesh + if nx < 1: raise TypeError("Bad miller index dimension in x") - if kmesh[1] < 1: + if ny < 1: raise TypeError("Bad miller index dimension in y") - if kmesh[2] < 1: + if nz < 1: raise TypeError("Bad miller index dimension in z") ks_int_each_axis = [] @@ -41,57 +58,53 @@ def get_miller_indices(kmesh): return int_scaled_kpts -def get_delta_kp_kq_Q(int_scaled_kpts): - """Generate kp - kq - Q = S for kp, kq, and Q. The difference of the three - integers is stored as a four tensor D_{kp, kq, Q} = S. where the dimension - of D is (nkpts, nkpts, nkpts, 3). The last dimension stores the x,y,z - components of S. +def get_delta_kp_kq_q(int_scaled_kpts): + """Generate kp - kq - Q = S for kp, kq, and Q. + + The difference of the three integers is stored as a four tensor D_{kp, kq, + Q} = S. where the dimension of D is (nkpts, nkpts, nkpts, 3). The last + dimension stores the x,y,z components of S. Args: - int_scaled_kpts: array of kpts represented as miller indices - [[nkx, nky, nkz], ...] + int_scaled_kpts: array of kpts represented as miller indices + [[nkx, nky, nkz], ...] Returns: - np.array nkpts x nkpts that corresponds to - + np.ndarray mapping D_{kp, kq, Q}. """ - delta_k1_k2_Q_int = (int_scaled_kpts[:, None, None, :] - + delta_k1_k2_q_int = (int_scaled_kpts[:, None, None, :] - int_scaled_kpts[None, :, None, :] - int_scaled_kpts[None, None, :, :]) - return delta_k1_k2_Q_int + return delta_k1_k2_q_int def build_transfer_map(kmesh, scaled_kpts): - """Define mapping momentum_transfer_map[Q][k1] = k2 that satisfies - k1 - k2 + G = Q. - where k1, k2, Q are all tuples of integers [[0, Nkx-1], [0, Nky-1], - [0, Nkz-1]] - and G is [{0, Nkx}, {0, Nky}, {0, Nkz}]. + """Define mapping momentum_transfer_map[Q][k1] = k2. + + Where k1 - k2 + G = Q. Here k1, k2, Q are all tuples of integers [[0, + Nkx-1], [0, Nky-1], [0, Nkz-1]] and G is [{0, Nkx}, {0, Nky}, {0, Nkz}]. - This is computed from `get_delta_kp_kq_Q` which computes k1 - k2 -Q = S. + This is computed from `get_delta_kp_kq_q` which computes k1 - k2 - Q = S. Thus k1 - k2 = Q + S which shows that S is [{0, Nkx}, {0, Nky}, {0, Nkz}]. - Thus to compute map[Q, k1] = k2 - Args: - kmesh: kmesh [nkx, nky, nkz] the number of kpoints in each direction + kmesh: [nkx, nky, nkz] the number of kpoints in each direction scaled_kpts: miller index representation - [[0, nkx-1], [0, nky-1], [0, nkz-1]] of all the kpoints + [[0, nkx-1], [0, nky-1], [0, nkz-1]] of all the kpoints Returns: - transfer map satisfying k1 - k2 + G = Q in matrix form map[Q, k1] = k2 - + transfer map satisfying k1 - k2 + G = Q in matrix form map[Q, k1] = k2 """ nkpts = len(scaled_kpts) - delta_k1_k2_Q_int = get_delta_kp_kq_Q(scaled_kpts) + delta_k1_k2_q_int = get_delta_kp_kq_Q(scaled_kpts) transfer_map = np.zeros((nkpts, nkpts), dtype=np.int32) for kpidx, kqidx, qidx in itertools.product(range(nkpts), repeat=3): # explicitly build my transfer matrix if np.allclose( [ - np.rint(delta_k1_k2_Q_int[kpidx, kqidx, qidx][0]) % kmesh[0], - np.rint(delta_k1_k2_Q_int[kpidx, kqidx, qidx][1]) % kmesh[1], - np.rint(delta_k1_k2_Q_int[kpidx, kqidx, qidx][2]) % kmesh[2], + np.rint(delta_k1_k2_q_int[kpidx, kqidx, qidx][0]) % kmesh[0], + np.rint(delta_k1_k2_q_int[kpidx, kqidx, qidx][1]) % kmesh[1], + np.rint(delta_k1_k2_q_int[kpidx, kqidx, qidx][2]) % kmesh[2], ], 0, ): @@ -100,15 +113,14 @@ def build_transfer_map(kmesh, scaled_kpts): def build_conjugate_map(kmesh, scaled_kpts): - """build mapping such that map[k1] = -k1 + """Build mapping such that map[k1] = -k1 Args: - kmesh: kpoint mesh - scaled_kpts: integer k-points + kmesh: kpoint mesh + scaled_kpts: integer k-points Returns: - kconj_map: conjugate k-point mapping - + kconj_map: conjugate k-point mapping """ nkpts = len(scaled_kpts) kpoint_dict = dict( @@ -129,47 +141,44 @@ def build_conjugate_map(kmesh, scaled_kpts): def build_G_vectors(kmesh): - """Build all 8 Gvectors + """Build all 8 Gvectors. Args: - kmesh: returns tuple: G_dict a dictionary mapping miller index to - appropriate - G_vector index. The actual cell Gvector can be recovered with - np.einsum("x,wx->w", (n1, n2, n3), cell.reciprocal_vectors() + kmesh: kpoint mesh Returns: - G_dict a dictionary mapping miller index to appropriate - G_vector index. The actual cell Gvector can be recovered with - np.einsum("x,wx->w", (n1, n2, n3), cell.reciprocal_vectors() - + g_dict: a dictionary mapping miller index to appropriate + G_vector index. The actual cell Gvector can be recovered with + np.einsum("x,wx->w", (n1, n2, n3), cell.reciprocal_vectors() """ - G_dict = {} + g_dict = {} indx = 0 for n1, n2, n3 in itertools.product([0, -1], repeat=3): - G_dict[(n1 * kmesh[0], n2 * kmesh[1], n3 * kmesh[2])] = indx + g_dict[(n1 * kmesh[0], n2 * kmesh[1], n3 * kmesh[2])] = indx indx += 1 - return G_dict + return g_dict def build_gpq_mapping(kmesh, int_scaled_kpts): - """build map for kp - kq = Q + G where G is - [{0, -Nkx}, {0, -Nky}, {0, -Nkz}]. G will be 0 or Nkz because kp - kq - takes on values between [-Nka + 1, Nka - 1] in each component. + """Build map for kp - kq = Q + G + + Here G is [{0, -Nkx}, {0, -Nky}, {0, -Nkz}]. G will be 0 or Nkz because kp - + kq takes on values between [-Nka + 1, Nka - 1] in each component. Args: - kmesh: number of k-points along each direction [Nkx, Nky, Nkz]. - int_scaled_kpts: scaled_kpts. Each kpoint is a tuple of 3 integers - where each integer is between [0, Nka-1]. + kmesh: number of k-points along each direction [Nkx, Nky, Nkz]. + int_scaled_kpts: scaled_kpts. Each kpoint is a tuple of 3 integers + where each integer is between [0, Nka-1]. Returns: - array mapping where first two indices are the index of kp and kq - and the last dimension holds the gval that is - [{0, Nkx}, {0, Nky}, {0, Nkz}]. + gpq_mapping: array mapping where first two indices are the index of kp + and kq and the last dimension holds the gval that is [{0, Nkx}, {0, + Nky}, {0, Nkz}]. """ momentum_map = build_transfer_map(kmesh, int_scaled_kpts) nkpts = len(int_scaled_kpts) - Gpq_mapping = np.zeros((nkpts, nkpts, 3), dtype=np.int32) + gpq_mapping = np.zeros((nkpts, nkpts, 3), dtype=np.int32) for iq in range(nkpts): for ikp in range(nkpts): ikq = momentum_map[iq, ikp] @@ -179,6 +188,6 @@ def build_gpq_mapping(kmesh, int_scaled_kpts): 0 if q_minus_g[1] >= 0 else -kmesh[1], 0 if q_minus_g[2] >= 0 else -kmesh[2], ) - Gpq_mapping[ikp, ikq, :] = np.array(g_val) + gpq_mapping[ikp, ikq, :] = np.array(g_val) - return Gpq_mapping + return gpq_mapping From abfd6923394be25c2d65508f6de544aa460ad217 Mon Sep 17 00:00:00 2001 From: Fionn Malone Date: Sat, 22 Jul 2023 06:03:10 +0000 Subject: [PATCH 09/19] Address comments. --- .../pbc/thc/utils/gvec_logic.py | 2 +- .../pbc/thc/utils/gvec_logic_test.py | 6 +- .../resource_estimates/pbc/thc/utils/isdf.py | 726 +++++++++--------- .../pbc/thc/utils/isdf_test.py | 168 ++-- 4 files changed, 446 insertions(+), 456 deletions(-) diff --git a/src/openfermion/resource_estimates/pbc/thc/utils/gvec_logic.py b/src/openfermion/resource_estimates/pbc/thc/utils/gvec_logic.py index f9cadfb72..123338bfd 100644 --- a/src/openfermion/resource_estimates/pbc/thc/utils/gvec_logic.py +++ b/src/openfermion/resource_estimates/pbc/thc/utils/gvec_logic.py @@ -96,7 +96,7 @@ def build_transfer_map(kmesh, scaled_kpts): transfer map satisfying k1 - k2 + G = Q in matrix form map[Q, k1] = k2 """ nkpts = len(scaled_kpts) - delta_k1_k2_q_int = get_delta_kp_kq_Q(scaled_kpts) + delta_k1_k2_q_int = get_delta_kp_kq_q(scaled_kpts) transfer_map = np.zeros((nkpts, nkpts), dtype=np.int32) for kpidx, kqidx, qidx in itertools.product(range(nkpts), repeat=3): # explicitly build my transfer matrix diff --git a/src/openfermion/resource_estimates/pbc/thc/utils/gvec_logic_test.py b/src/openfermion/resource_estimates/pbc/thc/utils/gvec_logic_test.py index 97ba1b4e8..548331287 100644 --- a/src/openfermion/resource_estimates/pbc/thc/utils/gvec_logic_test.py +++ b/src/openfermion/resource_estimates/pbc/thc/utils/gvec_logic_test.py @@ -14,7 +14,7 @@ import numpy as np from openfermion.resource_estimates.pbc.thc.utils.gvec_logic import ( get_miller_indices, - get_delta_kp_kq_Q, + get_delta_kp_kq_q, build_transfer_map, build_G_vectors, build_gpq_mapping, @@ -39,7 +39,7 @@ def test_get_delta_k1_k2_Q(): kmesh = [3, 2, 1] nkpts = np.prod(kmesh) scaled_kpts = get_miller_indices(kmesh) - delta_k1_k2_Q_int = get_delta_kp_kq_Q(scaled_kpts) + delta_k1_k2_Q_int = get_delta_kp_kq_q(scaled_kpts) assert delta_k1_k2_Q_int.shape == (nkpts, nkpts, nkpts, 3) for kpidx, kqidx, qidx in itertools.product(range(nkpts), repeat=3): assert np.allclose( @@ -204,4 +204,4 @@ def test_compliment_g(): q_idx_g_idx[(qidx, g2idx)]) assert np.isclose(sub_val, nkpts**2) - assert np.isclose(total_k, nkpts**3) \ No newline at end of file + assert np.isclose(total_k, nkpts**3) diff --git a/src/openfermion/resource_estimates/pbc/thc/utils/isdf.py b/src/openfermion/resource_estimates/pbc/thc/utils/isdf.py index ee4e13b9c..1b19015ef 100644 --- a/src/openfermion/resource_estimates/pbc/thc/utils/isdf.py +++ b/src/openfermion/resource_estimates/pbc/thc/utils/isdf.py @@ -12,14 +12,16 @@ # limitations under the License. """Module for performing ISDF THC factorization of k-point dependent integrals. -The ISDF implementation currently provides a THC-like factorization of the two -electron integrals which should converge to the FFTDF representation of the ERIs -in the limit of large THC rank. This differs from the assumption of using RSGDF -throughout the rest of the resource estimation scripts. However, we typically -are only interested in ISDF as an initial guess for the THC factors which are -then subsequently reoptimized to regularize lambda. The assumption here is -that FFTDF / ISDF is a good enough approximation to the RSGDF ERIs and thus -serves as a good initial guess. +The ISDF (interpolative separable density fitting) implementation currently +provides a tensor hypercontraction (THC)-like factorization of the two electron +integrals which should converge to the exact representation (fast fourier +transform density fitting (FFTDF)) of the ERIs in the limit of large THC rank. +This differs from the assumption of using range-separated gaussian density +fitting (RSGDF) throughout the rest of the resource estimation scripts. However, +we typically are only interested in ISDF as an initial guess for the THC factors +which are then subsequently reoptimized to regularize lambda. The assumption +here is that FFTDF / ISDF is a good enough approximation to the RSGDF ERIs and +thus serves as a good initial guess. """ import itertools @@ -50,15 +52,15 @@ def check_isdf_solution( Very costly and should only be used for testing purposes. Args: - orbitals: Orbitals on full real space grid. [num_grd, num_orb] - interp_orbitals: interpolating orbitals (those orbitals evaluated on - interpolating points.) [num_interp, num_orb] - xi: interpolating vectors. [num_grid, num_interp] - Returns - error: |phi_{ij}(r) - \sum_m xi_m(r) phi_{ij}(r_m) - orbitals: npt.NDArray: - interp_orbitals: npt.NDArray: - xi: npt.NDArray: + orbitals: Orbitals on full real space grid. [num_grd, num_orb] + interp_orbitals: interpolating orbitals (those orbitals evaluated on + interpolating points.) [num_interp, num_orb] + xi: interpolating vectors. [num_grid, num_interp] + Returns: + error: |phi_{ij}(r) - \sum_m xi_m(r) phi_{ij}(r_m) + orbitals: npt.NDArray: + interp_orbitals: npt.NDArray: + xi: npt.NDArray: """ lhs = np.einsum("Ri,Rj->Rij", orbitals.conj(), orbitals, optimize=True) @@ -74,21 +76,20 @@ def solve_isdf(orbitals: npt.NDArray, interp_indx: npt.NDArray) -> Tuple[npt.NDArray, npt.NDArray]: """Solve for interpolating vectors given interpolating points and orbitals. - Used for supercell and k-point so factor out as function. + Used for both supercell and k-point ISDF factorizations. Args: - orbitals: orbitals on a grid of shape [num_grid_points, num_orbitals] - interp_indx: array indexing interpolating points (subset of grid - points to use selected by K-Means algorithm. shape is - [num_interp_points]. - orbitals: npt.NDArray: - interp_indx: npt.NDArray: + orbitals: orbitals on a grid of shape [num_grid_points, num_orbitals] + interp_indx: array indexing interpolating points (subset of grid + points to use selected by K-Means algorithm. shape is + [num_interp_points]. + orbitals: npt.NDArray: + interp_indx: npt.NDArray: Returns: - tuple: (Interpolang vectors, interpolating orbitals) (xi_mu(r), - phi_i(r_mu)). Note xi_mu(r) is called Theta[R, mu] in keeping with - original ISDF notation. - + tuple: (Interpolang vectors, interpolating orbitals) (xi_mu(r), + phi_i(r_mu)). Note xi_mu(r) is called theta[R, mu] in keeping with + original ISDF notation. """ interp_orbitals = orbitals[interp_indx] # Form pseudo-densities @@ -99,21 +100,21 @@ def solve_isdf(orbitals: npt.NDArray, optimize=True) # [Z C^]_{J, mu} = (sum_i phi_{i, J}^* phi_{i, mu}) (sum_j phi_{j, J} # phi_{i, mu}) - ZC_dag = np.einsum("Rm,Rm->Rm", + zc_dag = np.einsum("Rm,Rm->Rm", pseudo_density, pseudo_density.conj(), optimize=True) # Just down sample from ZC_dag - CC_dag = ZC_dag[interp_indx].copy() + cc_dag = zc_dag[interp_indx].copy() # Solve ZC_dag = Theta CC_dag # Theta = ZC_dag @ CC_dag_inv # Solve ZC_dag = Theta CC_dag # -> ZC_dag^T = CC_dag^T Theta^T # rcond = None uses MACH_EPS * max(M,N) for least squares convergence. - Theta_dag, _, _, _ = np.linalg.lstsq(CC_dag.conj().T, - ZC_dag.conj().T, + theta_dag, _, _, _ = np.linalg.lstsq(cc_dag.conj().T, + zc_dag.conj().T, rcond=None) - return Theta_dag.conj().T, interp_orbitals + return theta_dag.conj().T, interp_orbitals def supercell_isdf( @@ -132,62 +133,62 @@ def supercell_isdf( called xi). Args: - mydf: instance of pyscf.pbc.df.FFTDF object. - interp_indx: array indexing interpolating points determined through - K-Means CVT procedure. Dimension [num_interp_points] - orbitals: orbitals on a grid of shape [num_grid_points, num_orbitals] - grid_points: Real space grid. Dimension [num_grid_points, num_dim], - num_dim is 1, 2 or 3 for 1D, 2D, 3D. + mydf: instance of pyscf.pbc.df.FFTDF object. + interp_indx: array indexing interpolating points determined through + K-Means CVT procedure. Dimension [num_interp_points] + orbitals: orbitals on a grid of shape [num_grid_points, num_orbitals] + grid_points: Real space grid. Dimension [num_grid_points, num_dim], + num_dim is 1, 2 or 3 for 1D, 2D, 3D. Returns: - tuple: (chi, zeta, Theta): orbitals on interpolating - points, zeta (central tensor), and matrix of interpolating vectors Theta - of dimension [num_grid_points, num_interp_points] (also called - xi_mu(r)), where num_grid_points is the number of real space grid points - and num_interp_points is the number of interpolating points. - + tuple: (chi, zeta, Theta): orbitals on interpolating + points, zeta (central tensor), and matrix of interpolating vectors Theta + of dimension [num_grid_points, num_interp_points] (also called + xi_mu(r)), where num_grid_points is the number of real space grid points + and num_interp_points is the number of interpolating points. """ cell = mydf.cell num_grid_points = len(grid_points) - Theta, chi = solve_isdf(orbitals, interp_indx) + theta, chi = solve_isdf(orbitals, interp_indx) # FFT Theta[R, mu] -> Theta[mu, G] # Transpose as fft expects contiguous. - Theta_G = tools.fft(Theta.T, mydf.mesh) - coulG = tools.get_coulG(cell, k=kpoint, mesh=mydf.mesh) - weighted_coulG = coulG * cell.vol / num_grid_points**2.0 + theta_g = tools.fft(theta.T, mydf.mesh) + coulg = tools.get_coulG(cell, k=kpoint, mesh=mydf.mesh) + weighted_coulg = coulg * cell.vol / num_grid_points**2.0 # zeta_{mu,nu} = \sum_G 4pi/(omega * G^2) zeta_{mu,G} * (zeta_G*){nu, G} - Theta_G_tilde = np.einsum("iG,G->iG", Theta_G, weighted_coulG) - zeta = (Theta_G_tilde) @ Theta_G.conj().T - return chi, zeta, Theta + theta_g_tilde = np.einsum("iG,G->iG", theta_g, weighted_coulg) + zeta = (theta_g_tilde) @ theta_g.conj().T + return chi, zeta, theta def build_kpoint_zeta( df_inst: df.FFTDF, - Q: int, - delta_G: npt.NDArray, - delta_G_prime: npt.NDArray, + q: int, + delta_g: npt.NDArray, + delta_g_prime: npt.NDArray, grid_points: npt.NDArray, xi_mu: npt.NDArray, ) -> npt.NDArray: - """Build k-point THC zeta (central tensor) for given Q, delta_G, - delta_G_prime. + """Build k-point THC zeta (central tensor). + + Built for for given q, delta_g, delta_g_prime. Args: - mydf: instance of pyscf.pbc.df.FFTDF object. - Q: Momentum transfer (in 1BZ). - delta_G: Reciprocal lattice vector satisfying Q - (Q-k) = delta_G - delta_G_prime: Reciprocal lattice vector satisfying Q - (Q-k') = delta_G - grid_points: Real space grid. Dimension [num_grid_points, num_dim], - num_dim is 1, 2 or 3 for 1D, 2D, 3D. - xi_mu: array containing interpolating vectors determined during ISDF - procedure + mydf: instance of pyscf.pbc.df.FFTDF object. + q: Momentum transfer (in 1BZ). + delta_g: Reciprocal lattice vector satisfying Q - (Q-k) = delta_G + delta_g_prime: Reciprocal lattice vector satisfying Q - (Q-k') = delta_G + grid_points: Real space grid. Dimension [num_grid_points, num_dim], + num_dim is 1, 2 or 3 for 1D, 2D, 3D. + xi_mu: array containing interpolating vectors determined during ISDF + procedure Returns: - zeta: central tensor of dimension [num_interp_points, num_interp_points] + zeta: central tensor of dimension [num_interp_points, num_interp_points] """ cell = df_inst.cell @@ -195,27 +196,29 @@ def build_kpoint_zeta( # delta_G - delta_G_prime because we have Gpq and Gsr and Gsr = -Grs, phase # = Delta G = Gpq + Grs phase_factor = np.exp(-1j * (np.einsum( - "x,Rx->R", delta_G - delta_G_prime, grid_points, optimize=True))) + "x,Rx->R", delta_g - delta_g_prime, grid_points, optimize=True))) # Minus sign again due to we use Q = kp - kq, but we should have # V(G + k_q - k_p) - coulG = tools.get_coulG(cell, k=-(Q + delta_G), mesh=df_inst.mesh) - weighted_coulG = coulG * cell.vol / num_grid_points - xi_muG = tools.fft(xi_mu.T, df_inst.mesh) - xi_muG *= weighted_coulG - vR = tools.ifft(xi_muG, df_inst.mesh) - zeta = np.einsum("R,Rn,mR->mn", phase_factor, xi_mu, vR, optimize=True) + coulg = tools.get_coulG(cell, k=-(q + delta_g), mesh=df_inst.mesh) + weighted_coulg = coulg * cell.vol / num_grid_points + xi_mug = tools.fft(xi_mu.T, df_inst.mesh) + xi_mug *= weighted_coulg + vr = tools.ifft(xi_mug, df_inst.mesh) + zeta = np.einsum("R,Rn,mR->mn", phase_factor, xi_mu, vr, optimize=True) return zeta def build_kpoint_zeta_single_tranlsation( df_inst: df.FFTDF, q: int, - delta_G: npt.NDArray, + delta_g: npt.NDArray, grid_points: npt.NDArray, xi_mu: npt.NDArray, ) -> npt.NDArray: - """Build k-point THC zeta (central tensor) for given Q, delta_G, - delta_G_prime. + """Build k-point THC zeta (central tensor) + + Built for for given q, delta_g. Note single delta_g (single translation + vector) in controast to the _double_translation variant above. Args: mydf: instance of pyscf.pbc.df.FFTDF object. @@ -235,188 +238,188 @@ def build_kpoint_zeta_single_tranlsation( # delta_G - delta_G_prime because we have Gpq and Gsr and Gsr = -Grs, phase # = Delta G = Gpq + Grs phase_factor = np.exp( - -1j * (np.einsum("x,Rx->R", delta_G, grid_points, optimize=True))) + -1j * (np.einsum("x,Rx->R", delta_g, grid_points, optimize=True))) # Minus sign again due to we use Q = kp - kq, but we should have # V(G + k_q - k_p) - coulG = tools.get_coulG(cell, k=-q, mesh=df_inst.mesh) - weighted_coulG = coulG * cell.vol / num_grid_points - xi_muG = tools.fft(xi_mu.T, df_inst.mesh) - xi_muG *= weighted_coulG - vR = tools.ifft(xi_muG, df_inst.mesh) - zeta = np.einsum("R,Rn,mR->mn", phase_factor, xi_mu, vR, optimize=True) + coulg = tools.get_coulG(cell, k=-q, mesh=df_inst.mesh) + weighted_coulg = coulg * cell.vol / num_grid_points + xi_mug = tools.fft(xi_mu.T, df_inst.mesh) + xi_mug *= weighted_coulg + vr = tools.ifft(xi_mug, df_inst.mesh) + zeta = np.einsum("R,Rn,mR->mn", phase_factor, xi_mu, vr, optimize=True) return zeta -def build_G_vectors(cell: gto.Cell) -> npt.NDArray: +def build_g_vectors(cell: gto.Cell) -> npt.NDArray: """Build all 27 Gvectors Args: - cell: pyscf.pbc.gto.Cell object. - cell: gto.Cell: + cell: pyscf.pbc.gto.Cell object. + cell: gto.Cell: Returns: - tuple: G_dict a dictionary mapping miller index to appropriate - G_vector index and G_vectors array of 27 G_vectors shape [27, 3]. + tuple: g_dict a dictionary mapping miller index to appropriate + g_vector index and g_vectors array of 27 g_vectors shape [27, 3]. """ - G_dict = {} - G_vectors = np.zeros((27, 3), dtype=np.float64) + g_dict = {} + g_vectors = np.zeros((27, 3), dtype=np.float64) lattice_vectors = cell.lattice_vectors() indx = 0 for n1, n2, n3 in itertools.product(range(-1, 2), repeat=3): - G_dict[(n1, n2, n3)] = indx - G_vectors[indx] = np.einsum("n,ng->g", (n1, n2, n3), + g_dict[(n1, n2, n3)] = indx + g_vectors[indx] = np.einsum("n,ng->g", (n1, n2, n3), cell.reciprocal_vectors()) miller_indx = np.rint( - np.einsum("nx,x->n", lattice_vectors, G_vectors[indx]) / + np.einsum("nx,x->n", lattice_vectors, g_vectors[indx]) / (2 * np.pi)) assert (miller_indx == (n1, n2, n3)).all() indx += 1 - return G_dict, G_vectors + return g_dict, g_vectors -def find_unique_G_vectors(G_vectors: npt.NDArray, G_mapping: npt.NDArray +def find_unique_g_vectors(g_vectors: npt.NDArray, g_mapping: npt.NDArray ) -> Tuple[npt.NDArray, npt.NDArray]: """Find all unique G-vectors and build mapping to original set. Args: - G_vectors: array of 27 G-vectors. - G_mapping: array of 27 G-vectors. + g_vectors: array of 27 g-vectors. + g_mapping: array of 27 g-vectors. Returns: - unique_G_index in range [0,...,num_unique_Gs[iq]], and delta_Gs are the - unique G-vectors of size [num_qpoints, num_unique_Gs[iq]]. + unique_g_index in range [0,...,num_unique_gs[iq]], and delta_gs are the + unique g-vectors of size [num_qpoints, num_unique_gs[iq]]. """ - unique_mapping = np.zeros_like(G_mapping) - num_qpoints = G_mapping.shape[0] - delta_Gs = np.zeros((num_qpoints,), dtype=object) + unique_mapping = np.zeros_like(g_mapping) + num_qpoints = g_mapping.shape[0] + delta_gs = np.zeros((num_qpoints,), dtype=object) for iq in range(num_qpoints): - unique_G = np.unique(G_mapping[iq]) - delta_Gs[iq] = G_vectors[unique_G] - # Build map to unique index + unique_g = np.unique(g_mapping[iq]) + delta_gs[iq] = g_vectors[unique_g] + # build map to unique index unique_mapping[iq] = [ - ix for el in G_mapping[iq] for ix in np.where(unique_G == el)[0] + ix for el in g_mapping[iq] for ix in np.where(unique_g == el)[0] ] - return unique_mapping, delta_Gs + return unique_mapping, delta_gs -def build_G_vector_mappings_double_translation( +def build_g_vector_mappings_double_translation( cell: gto.Cell, kpts: npt.NDArray, momentum_map: npt.NDArray, ) -> Tuple[npt.NDArray, npt.NDArray, npt.NDArray, npt.NDArray]: - """Build G-vector mappings that map k-point differences to 1BZ. + """build g-vector mappings that map k-point differences to 1bz. - Args: - cell: pyscf.pbc.gto.Cell object. + args: + cell: pyscf.pbc.gto.cell object. kpts: array of kpoints. - momentum_map: momentum mapping to satisfy Q = (k_p - k_q) mod G. + momentum_map: momentum mapping to satisfy q = (k_p - k_q) mod g. momentum_map[iq, ikp] = ikq. - Returns: - tuple: (G_vectors, Gpq_mapping, Gpq_mapping_unique, delta_Gs), G_vectors + returns: + tuple: (g_vectors, gpq_mapping, gpq_mapping_unique, delta_gs), g_vectors is a list of all 27 - G-vectors and Gpq_mapping[iq, kp] = indx_Gpq, where Gpq = kpts[ikp] - - kpts[ikq] - kpts[iq], i.e. returns index to G-vector (consistent with - G_vectors) satisfying this condition. Gpq_mapping_unique provides - mapping to unique G_vector index. Delta_gs provides compressed lists of - unique G vectors. + g-vectors and gpq_mapping[iq, kp] = indx_gpq, where gpq = kpts[ikp] - + kpts[ikq] - kpts[iq], i.e. returns index to g-vector (consistent with + g_vectors) satisfying this condition. gpq_mapping_unique provides + mapping to unique g_vector index. delta_gs provides compressed lists of + unique g vectors. """ - G_dict, G_vectors = build_G_vectors(cell) + g_dict, g_vectors = build_g_vectors(cell) lattice_vectors = cell.lattice_vectors() num_kpts = len(kpts) - Gpq_mapping = np.zeros((num_kpts, num_kpts), dtype=np.int32) + gpq_mapping = np.zeros((num_kpts, num_kpts), dtype=np.int32) num_kpts = len(kpts) for iq in range(num_kpts): for ikp in range(num_kpts): ikq = momentum_map[iq, ikp] - delta_Gpq = (kpts[ikp] - kpts[ikq]) - kpts[iq] + delta_gpq = (kpts[ikp] - kpts[ikq]) - kpts[iq] miller_indx = np.rint( - np.einsum("wx,x->w", lattice_vectors, delta_Gpq) / (2 * np.pi)) - Gpq_mapping[iq, ikp] = G_dict[tuple(miller_indx)] - Gpq_mapping_unique, delta_Gs = find_unique_G_vectors(G_vectors, Gpq_mapping) - return G_vectors, Gpq_mapping, Gpq_mapping_unique, delta_Gs + np.einsum("wx,x->w", lattice_vectors, delta_gpq) / (2 * np.pi)) + gpq_mapping[iq, ikp] = g_dict[tuple(miller_indx)] + gpq_mapping_unique, delta_gs = find_unique_g_vectors(g_vectors, gpq_mapping) + return g_vectors, gpq_mapping, gpq_mapping_unique, delta_gs -def get_miller(lattice_vectors: npt.NDArray, G: npt.NDArray) -> npt.NDArray: - """Convert G to miller indx. +def get_miller(lattice_vectors: npt.NDArray, g: npt.NDArray) -> npt.NDArray: + """convert g to miller indx. - Args: - lattice_vectors: Array of lattice vectors. - G: Reciprocal lattice vector. + args: + lattice_vectors: array of lattice vectors. + g: reciprocal lattice vector. - Returns: - miller_index: 3D array of miller indices. + returns: + miller_index: 3d array of miller indices. """ miller_indx = np.rint( - np.einsum("wx,x->w", lattice_vectors, G) / (2 * np.pi)).astype(np.int32) + np.einsum("wx,x->w", lattice_vectors, g) / (2 * np.pi)).astype(np.int32) return miller_indx -def build_minus_Q_G_mapping(cell: gto.Cell, kpts: npt.NDArray, +def build_minus_q_g_mapping(cell: gto.Cell, kpts: npt.NDArray, momentum_map: npt.NDArray) -> npt.NDArray: - """Build mapping for G that satisfied (-Q) + G + (Q + Gpq) = 0 (*) - and kp - kq = Q + Gpq. + """Build conjugat g map + + Mapping satisfies (-Q) + G + (Q + Gpq) = 0 (*) Args: - cell: pyscf.pbc.gto.Cell object. - kpts: array of kpoints. - momentum_map: momentum mapping to satisfy Q = (k_p - k_q) mod G. - momentum_map[iq, ikp] = ikq. + cell: pyscf.pbc.gto.Cell object. + kpts: array of kpoints. + momentum_map: momentum mapping to satisfy q = (k_p - k_q) mod g. + momentum_map[iq, ikp] = ikq. Returns: - tuple: (minus_Q_mapping, minus_Q_mapping_unique), - minus_Q_mapping[indx_minus_Q, k] yields index for G satisfying (*) - above, where indx_minus_Q is given by indx_minus_Q = minus_k[Q], and - minus_k = conj_mapping(cell, kpts). minus_Q_mapping_unique indexes the - appropriate G vector given by delta_Gs[indx_minus_Q][indx] = G, where - indx = minus_Q_mapping_unique[indx_minus_Q, k], and deltaGs is built by - build_G_vector_mappings_double_translation. - + minus_q_mapping: [indx_minus_q, k]-th element yields index for g satisfying (*) + above, where indx_minus_q is given by indx_minus_q = minus_k[q], and + minus_k = conj_mapping(cell, kpts). + minus_q_mapping_unique: indexes the appropriate g vector given by + delta_gs[indx_minus_q][indx] = g, where indx = + minus_q_mapping_unique[indx_minus_q, k], and deltags is built by + build_g_vector_mappings_double_translation. """ ( - G_vecs, - G_map, + g_vecs, + g_map, _, - delta_Gs, - ) = build_G_vector_mappings_double_translation(cell, kpts, momentum_map) - G_dict, _ = build_G_vectors(cell) + delta_gs, + ) = build_g_vector_mappings_double_translation(cell, kpts, momentum_map) + g_dict, _ = build_g_vectors(cell) num_kpts = len(kpts) lattice_vectors = cell.lattice_vectors() minus_k_map = conj_mapping(cell, kpts) - minus_Q_mapping = np.zeros((num_kpts, num_kpts), dtype=np.int32) - minus_Q_mapping_unique = np.zeros((num_kpts, num_kpts), dtype=np.int32) + minus_q_mapping = np.zeros((num_kpts, num_kpts), dtype=np.int32) + minus_q_mapping_unique = np.zeros((num_kpts, num_kpts), dtype=np.int32) for iq in range(num_kpts): minus_iq = minus_k_map[iq] for ik in range(num_kpts): - Gpq = G_vecs[G_map[iq, ik]] - # Complementary Gpq (G in (*) in docstring) - Gpq_comp = -(kpts[minus_iq] + kpts[iq] + Gpq) + gpq = g_vecs[g_map[iq, ik]] + # complementary gpq (g in (*) in docstring) + gpq_comp = -(kpts[minus_iq] + kpts[iq] + gpq) # find index in original set of 27 - iGpq_comp = G_dict[tuple(get_miller(lattice_vectors, Gpq_comp))] - minus_Q_mapping[minus_iq, ik] = iGpq_comp - indx_delta_Gs = np.array([ - G_dict[tuple(get_miller(lattice_vectors, G))] - for G in delta_Gs[minus_iq] + igpq_comp = g_dict[tuple(get_miller(lattice_vectors, gpq_comp))] + minus_q_mapping[minus_iq, ik] = igpq_comp + indx_delta_gs = np.array([ + g_dict[tuple(get_miller(lattice_vectors, g))] + for g in delta_gs[minus_iq] ]) - minus_Q_mapping_unique[minus_iq] = [ - ix for el in minus_Q_mapping[minus_iq] - for ix in np.where(el == indx_delta_Gs)[0] + minus_q_mapping_unique[minus_iq] = [ + ix for el in minus_q_mapping[minus_iq] + for ix in np.where(el == indx_delta_gs)[0] ] - return minus_Q_mapping, minus_Q_mapping_unique + return minus_q_mapping, minus_q_mapping_unique -def build_G_vector_mappings_single_translation( +def build_g_vector_mappings_single_translation( cell: gto.Cell, kpts: npt.NDArray, kpts_pq: npt.NDArray, ) -> Tuple[npt.NDArray, npt.NDArray, npt.NDArray, npt.NDArray]: - """Build G-vector mappings that map k-point differences to 1BZ. + """Build g-vector mappings that map k-point differences to 1BZ. Args: cell: pyscf.pbc.gto.Cell object. @@ -424,67 +427,63 @@ def build_G_vector_mappings_single_translation( kpts_pq: Unique list of kp - kq indices of shape [num_unique_pq, 2]. Returns: - tuple: (G_vectors, Gpqr_mapping, Gpqr_mapping_unique, delta_Gs), G_vectors - is a list of all 27 - G-vectors and Gpqr_mapping[iq, kr] = indx_Gpqr, where Gpqr = kpts[ikp] - - kpts[ikq] + kpts[ikr] - kpts[iks], i.e. returns index to G_vectors - (consistent with G_vectors) satisfying this condition. - Gpqr_mapping_unique provides - mapping to unique G_vector index. Delta_gs provides compressed lists of - unique G vectors. + G_vectors: list of all 27 G-vectors + Gpqr_mapping: [iq, kr]-th element yields indx_Gpqr, where Gpqr = kpts[ikp] - + kpts[ikq] + kpts[ikr] - kpts[iks], i.e. returns index to G_vectors + (consistent with G_vectors) satisfying this condition. + Gpqr_mapping_unique: provides mapping to unique G_vector index. + Delta_gs: provides compressed lists of unique G vectors. """ - G_dict, G_vectors = build_G_vectors(cell) + g_dict, g_vectors = build_g_vectors(cell) lattice_vectors = cell.lattice_vectors() num_kpts = len(kpts) - Gpqr_mapping = np.zeros((len(kpts_pq), num_kpts), dtype=np.int32) + gpqr_mapping = np.zeros((len(kpts_pq), num_kpts), dtype=np.int32) kconserv = get_kconserv(cell, kpts) for iq, (ikp, ikq) in enumerate(kpts_pq): q = kpts[ikp] - kpts[ikq] for ikr in range(num_kpts): iks = kconserv[ikp, ikq, ikr] - delta_Gpqr = q + kpts[ikr] - kpts[iks] - # delta_Gpq += kpts[0] + delta_gpqr = q + kpts[ikr] - kpts[iks] miller_indx = np.rint( - np.einsum("wx,x->w", lattice_vectors, delta_Gpqr) / (2 * np.pi)) - Gpqr_mapping[iq, ikr] = G_dict[tuple(miller_indx)] + np.einsum("wx,x->w", lattice_vectors, delta_gpqr) / (2 * np.pi)) + gpqr_mapping[iq, ikr] = g_dict[tuple(miller_indx)] - Gpqr_mapping_unique, delta_Gs = find_unique_G_vectors( - G_vectors, Gpqr_mapping) - return G_vectors, Gpqr_mapping, Gpqr_mapping_unique, delta_Gs + gpqr_mapping_unique, delta_gs = find_unique_g_vectors( + g_vectors, gpqr_mapping) + return g_vectors, gpqr_mapping, gpqr_mapping_unique, delta_gs -def inverse_G_map_double_translation( +def inverse_g_map_double_translation( cell: gto.Cell, kpts: npt.NDArray, momentum_map: npt.NDArray, ) -> npt.NDArray: - """For given Q and G figure out all k which satisfy Q - k + G = 0 + """for given q and g figure out all k which satisfy q - k + g = 0 Args: - cell: pyscf.pbc.gto.Cell object. - kpts: array of kpoints. - momentum_map: momentum mapping to satisfy Q = (k_p - k_q) mod G. - momentum_map[iq, ikp] = ikq. + cell: pyscf.pbc.gto.Cell object. + kpts: array of kpoints. + momentum_map: momentum mapping to satisfy Q = (k_p - k_q) mod G. + momentum_map[iq, ikp] = ikq. Returns: - inverse_map: ragged numpy array. inverse_map[iq, iG] returns array - of size in range(0, num_kpts) and lists all k-point indices that - satisfy G_pq[iq, ik] = iG, i.e. an array of all ik. - + inverse_map: ragged numpy array. inverse_map[iq, iG] returns array + of size in range(0, num_kpts) and lists all k-point indices that + satisfy G_pq[iq, ik] = iG, i.e. an array of all ik. """ - G_dict, G_vectors = build_G_vectors(cell) + g_dict, g_vectors = build_g_vectors(cell) lattice_vectors = cell.lattice_vectors() num_kpts = len(kpts) - Gpq_mapping = np.zeros((num_kpts, num_kpts), dtype=np.int32) + gpq_mapping = np.zeros((num_kpts, num_kpts), dtype=np.int32) num_kpts = len(kpts) for iq in range(num_kpts): for ikp in range(num_kpts): ikq = momentum_map[iq, ikp] - delta_Gpq = (kpts[ikp] - kpts[ikq]) - kpts[iq] + delta_gpq = (kpts[ikp] - kpts[ikq]) - kpts[iq] miller_indx = np.rint( - np.einsum("wx,x->w", lattice_vectors, delta_Gpq) / (2 * np.pi)) - Gpq_mapping[iq, ikp] = G_dict[tuple(miller_indx)] + np.einsum("wx,x->w", lattice_vectors, delta_gpq) / (2 * np.pi)) + gpq_mapping[iq, ikp] = g_dict[tuple(miller_indx)] inverse_map = np.zeros( ( @@ -494,9 +493,9 @@ def inverse_G_map_double_translation( dtype=object, ) for iq in range(num_kpts): - for iG in range(len(G_vectors)): - inverse_map[iq, iG] = np.array( - [ik for ik in range(num_kpts) if Gpq_mapping[iq, ik] == iG]) + for ig in range(len(g_vectors)): + inverse_map[iq, ig] = np.array( + [ik for ik in range(num_kpts) if gpq_mapping[iq, ik] == ig]) return inverse_map @@ -506,31 +505,30 @@ def build_eri_isdf_double_translation( zeta: npt.NDArray, q_indx: int, kpts_indx: list, - G_mapping: npt.NDArray, + g_mapping: npt.NDArray, ) -> npt.NDArray: """Build (pkp qkq | rkr sks) from k-point ISDF factors. Args: - chi: array of interpolating orbitals of shape - [num_kpts, num_mo, num_interp_points] - zeta: central tensor of dimension - [num_kpts, num_G, num_G, num_interp_points, num_interp_points]. - q_indx: Index of momentum transfer. - kpts_indx: List of kpt indices corresponding to [kp, kq, kr, ks] - G_mapping: array to map kpts to G vectors [q_indx, kp] = G_pq + chi: array of interpolating orbitals of shape + [num_kpts, num_mo, num_interp_points] + zeta: central tensor of dimension + [num_kpts, num_G, num_G, num_interp_points, num_interp_points]. + q_indx: Index of momentum transfer. + kpts_indx: List of kpt indices corresponding to [kp, kq, kr, ks] + G_mapping: array to map kpts to G vectors [q_indx, kp] = G_pq Returns: eri: (pkp qkq | rkr sks) - """ ikp, ikq, ikr, iks = kpts_indx - Gpq = G_mapping[q_indx, ikp] - Gsr = G_mapping[q_indx, iks] + gpq = g_mapping[q_indx, ikp] + gsr = g_mapping[q_indx, iks] eri = np.einsum( "pm,qm,mn,rn,sn->pqrs", chi[ikp].conj(), chi[ikq], - zeta[q_indx][Gpq, Gsr], + zeta[q_indx][gpq, gsr], chi[ikr].conj(), chi[iks], optimize=True, @@ -543,30 +541,30 @@ def build_eri_isdf_single_translation( zeta: npt.NDArray, q_indx: int, kpts_indx: list, - G_mapping: npt.NDArray, + g_mapping: npt.NDArray, ) -> npt.NDArray: """Build (pkp qkq | rkr sks) from k-point ISDF factors. Args: - chi: array of interpolating orbitals of shape - [num_kpts, num_mo, num_interp_points] - zeta: central tensor of dimension - [num_kpts, num_G, num_G, num_interp_points, num_interp_points]. - q_indx: Index of momentum transfer. - kpts_indx: List of kpt indices corresponding to [kp, kq, kr, ks] - G_mapping: array to map kpts to G vectors [q_indx, kp] = G_pq + chi: array of interpolating orbitals of shape + [num_kpts, num_mo, num_interp_points] + zeta: central tensor of dimension + [num_kpts, num_G, num_G, num_interp_points, num_interp_points]. + q_indx: Index of momentum transfer. + kpts_indx: List of kpt indices corresponding to [kp, kq, kr, ks] + G_mapping: array to map kpts to G vectors [q_indx, kp] = G_pq Returns: - eri: (pkp qkq | rkr sks) + eri: (pkp qkq | rkr sks) """ ikp, ikq, ikr, iks = kpts_indx - delta_G_indx = G_mapping[q_indx, ikr] + delta_g_indx = g_mapping[q_indx, ikr] eri = np.einsum( "pm,qm,mn,rn,sn->pqrs", chi[ikp].conj(), chi[ikq], - zeta[q_indx][delta_G_indx], + zeta[q_indx][delta_g_indx], chi[ikr].conj(), chi[iks], optimize=True, @@ -580,10 +578,9 @@ def kpoint_isdf_double_translation( kpts: npt.NDArray, orbitals: npt.NDArray, grid_points: npt.NDArray, - only_unique_G: bool = True, + only_unique_g: bool = True, ) -> Tuple[npt.NDArray, npt.NDArray, npt.NDArray, npt.NDArray]: - r""" - Build kpoint ISDF-THC tensors. + r"""Build kpoint ISDF-THC tensors. Given the orbitals evaluated on a (dense) real space grid, and a set of interpolating points (indexed by interp_indx) determine the interpolating @@ -595,31 +592,31 @@ def kpoint_isdf_double_translation( the unique G's which satisfiy this expression rather than all 27^2. Args: - df_inst: instance of pyscf.pbc.df.FFTDF object. - interp_indx: array indexing interpolating points determined through - K-Means CVT procedure. Dimension [num_interp_points] - kpts: Array of k-points. - orbitals: orbitals on a grid of shape [num_grid_points, - num_orbitals], note num_orbitals = N_k * m, where m is the number of - orbitals in the unit cell and N_k is the number of k-points. - grid_points: Real space grid. Dimension [num_grid_points, num_dim], - num_dim is 1, 2 or 3 for 1D, 2D, 3D. - only_unique_G: Only build central tensor for unique Gs which satisfy - momentum conservation condition. + df_inst: instance of pyscf.pbc.df.FFTDF object. + interp_indx: array indexing interpolating points determined through + K-Means CVT procedure. Dimension [num_interp_points] + kpts: Array of k-points. + orbitals: orbitals on a grid of shape [num_grid_points, + num_orbitals], note num_orbitals = N_k * m, where m is the number of + orbitals in the unit cell and N_k is the number of k-points. + grid_points: Real space grid. Dimension [num_grid_points, num_dim], + num_dim is 1, 2 or 3 for 1D, 2D, 3D. + only_unique_G: Only build central tensor for unique Gs which satisfy + momentum conservation condition. Returns: - chi: orbitals on interpolating points - zeta: THC central tensor. Dimension is - [num_kpts, 27, 27, num_interp_points, num_interp_points] - if only_unique_G is False otherwise it is of shape [num_kpts, - num_unique[Q], num_unique[Q], 27, num_interp_points, num_interp_points]. - Theta: Matrix of interpolating vectors of dimension [num_grid_points, - num_interp_points] (also called xi_mu(r)), where num_grid_points is the - number of real space grid points and num_interp_points is the number of - interpolating points. Zeta (the - G_mapping: G_mapping maps k-points to the appropriate delta_G index, i.e. - G_mapping[iq, ik] = i_delta_G. the index will map to the appropriate - index in the reduced set of G vectors. + chi: orbitals on interpolating points + zeta: THC central tensor. Dimension is + [num_kpts, 27, 27, num_interp_points, num_interp_points] + if only_unique_G is False otherwise it is of shape [num_kpts, + num_unique[q], num_unique[q], 27, num_interp_points, num_interp_points]. + theta: matrix of interpolating vectors of dimension [num_grid_points, + num_interp_points] (also called xi_mu(r)), where num_grid_points is the + number of real space grid points and num_interp_points is the number of + interpolating points. zeta (the + g_mapping: g_mapping maps k-points to the appropriate delta_g index, i.e. + g_mapping[iq, ik] = i_delta_g. the index will map to the appropriate + index in the reduced set of g vectors. """ num_grid_points = len(grid_points) assert orbitals.shape[0] == num_grid_points @@ -629,32 +626,32 @@ def kpoint_isdf_double_translation( num_interp_points = xi.shape[1] assert xi.shape == (num_grid_points, num_interp_points) ( - G_vectors, - G_mapping, - G_mapping_unique, - delta_Gs_unique, - ) = build_G_vector_mappings_double_translation(df_inst.cell, kpts, + g_vectors, + g_mapping, + g_mapping_unique, + delta_gs_unique, + ) = build_g_vector_mappings_double_translation(df_inst.cell, kpts, momentum_map) - if only_unique_G: - G_mapping = G_mapping_unique - delta_Gs = delta_Gs_unique + if only_unique_g: + g_mapping = g_mapping_unique + delta_gs = delta_gs_unique else: - delta_Gs = [G_vectors] * num_kpts - G_mapping = G_mapping + delta_gs = [g_vectors] * num_kpts + g_mapping = g_mapping zeta = np.zeros((num_kpts,), dtype=object) for iq in range(num_kpts): - num_G = len(delta_Gs[iq]) + num_g = len(delta_gs[iq]) out_array = np.zeros( - (num_G, num_G, num_interp_points, num_interp_points), + (num_g, num_g, num_interp_points, num_interp_points), dtype=np.complex128, ) - for iG, delta_G in enumerate(delta_Gs[iq]): - for iG_prime, delta_G_prime in enumerate(delta_Gs[iq]): - zeta_indx = build_kpoint_zeta(df_inst, kpts[iq], delta_G, - delta_G_prime, grid_points, xi) - out_array[iG, iG_prime] = zeta_indx + for ig, delta_g in enumerate(delta_gs[iq]): + for ig_prime, delta_g_prime in enumerate(delta_gs[iq]): + zeta_indx = build_kpoint_zeta(df_inst, kpts[iq], delta_g, + delta_g_prime, grid_points, xi) + out_array[ig, ig_prime] = zeta_indx zeta[iq] = out_array - return chi, zeta, xi, G_mapping + return chi, zeta, xi, g_mapping def kpoint_isdf_single_translation( @@ -664,8 +661,7 @@ def kpoint_isdf_single_translation( orbitals: npt.NDArray, grid_points: npt.NDArray, ) -> Tuple[npt.NDArray, npt.NDArray, npt.NDArray, npt.NDArray]: - r""" - Build kpoint ISDF-THC tensors. + r"""Build kpoint ISDF-THC tensors. Given the orbitals evaluated on a (dense) real space grid, and a set of interpolating points (indexed by interp_indx) determine the interpolating @@ -677,29 +673,29 @@ def kpoint_isdf_single_translation( the unique G's which satisfiy this expression rather than all 27^2. Args: - df_inst: instance of pyscf.pbc.df.FFTDF object. - interp_indx: array indexing interpolating points determined through - K-Means CVT procedure. Dimension [num_interp_points] - kpts: Array of k-points. - orbitals: orbitals on a grid of shape [num_grid_points, - num_orbitals], note num_orbitals = N_k * m, where m is the number of - orbitals in the unit cell and N_k is the number of k-points. - grid_points: Real space grid. Dimension [num_grid_points, num_dim], - num_dim is 1, 2 or 3 for 1D, 2D, 3D. + df_inst: instance of pyscf.pbc.df.FFTDF object. + interp_indx: array indexing interpolating points determined through + K-Means CVT procedure. Dimension [num_interp_points] + kpts: Array of k-points. + orbitals: orbitals on a grid of shape [num_grid_points, + num_orbitals], note num_orbitals = N_k * m, where m is the number of + orbitals in the unit cell and N_k is the number of k-points. + grid_points: Real space grid. Dimension [num_grid_points, num_dim], + num_dim is 1, 2 or 3 for 1D, 2D, 3D. Returns: - chi: orbitals on interpolating points - zeta: THC central tensor. Dimension is - [num_kpts, 27, 27, num_interp_points, num_interp_points] - if only_unique_G is False otherwise it is of shape [num_kpts, - num_unique[Q], num_unique[Q], 27, num_interp_points, num_interp_points]. - Theta: Matrix of interpolating vectors of dimension [num_grid_points, - num_interp_points] (also called xi_mu(r)), where num_grid_points is the - number of real space grid points and num_interp_points is the number of - interpolating points. Zeta (the - G_mapping: G_mapping maps k-points to the appropriate delta_G index, i.e. - G_mapping[iq, ik] = i_delta_G. the index will map to the appropriate - index in the reduced set of G vectors. + chi: orbitals on interpolating points + zeta: THC central tensor. Dimension is + [num_kpts, 27, 27, num_interp_points, num_interp_points] + if only_unique_G is False otherwise it is of shape [num_kpts, + num_unique[Q], num_unique[Q], 27, num_interp_points, num_interp_points]. + Theta: Matrix of interpolating vectors of dimension [num_grid_points, + num_interp_points] (also called xi_mu(r)), where num_grid_points is the + number of real space grid points and num_interp_points is the number of + interpolating points. Zeta (the + G_mapping: G_mapping maps k-points to the appropriate delta_G index, i.e. + G_mapping[iq, ik] = i_delta_G. the index will map to the appropriate + index in the reduced set of G vectors. """ num_grid_points = len(grid_points) assert orbitals.shape[0] == num_grid_points @@ -715,32 +711,32 @@ def kpoint_isdf_single_translation( ]) transfers = kpts_pq[:, 0] - kpts_pq[:, 1] unique_q, unique_indx, _ = unique(transfers) - _, _, G_map_unique, delta_Gs = build_G_vector_mappings_single_translation( + _, _, g_map_unique, delta_gs = build_g_vector_mappings_single_translation( df_inst.cell, kpts, kpts_pq_indx[unique_indx]) num_q_vectors = len(unique_q) zeta = np.zeros((num_q_vectors,), dtype=object) for iq in range(len(unique_q)): - num_G = len(delta_Gs[iq]) - out_array = np.zeros((num_G, num_interp_points, num_interp_points), + num_g = len(delta_gs[iq]) + out_array = np.zeros((num_g, num_interp_points, num_interp_points), dtype=np.complex128) - for iG, delta_G in enumerate(delta_Gs[iq]): + for ig, delta_g in enumerate(delta_gs[iq]): zeta_indx = build_kpoint_zeta_single_tranlsation( - df_inst, unique_q[iq], delta_G, grid_points, xi) - out_array[iG] = zeta_indx + df_inst, unique_q[iq], delta_g, grid_points, xi) + out_array[ig] = zeta_indx zeta[iq] = out_array - return chi, zeta, xi, G_map_unique + return chi, zeta, xi, g_map_unique def build_isdf_orbital_inputs(mf_inst: scf.RHF) -> npt.NDArray: """Build orbital product inputs from mean field object Args: - mf_inst: pyscf pbc mean-field object. - mf_inst: scf.RHF: + mf_inst: pyscf pbc mean-field object. + mf_inst: scf.RHF: Returns: - cell_periodic_mo: cell periodic part of Bloch orbital on real space grid. - shape: [num_grid_points, num_kpts*num_mo] + cell_periodic_mo: cell periodic part of Bloch orbital on real space grid. + shape: [num_grid_points, num_kpts*num_mo] """ cell = mf_inst.cell @@ -774,13 +770,13 @@ def density_guess( """Select initial centroids based on electronic density. Args: - density: Density on real space grid. - grid_inst: pyscf UniformGrids object. - grid_points: Real space grid points. - num_interp_points: Number of interpolating points. + density: Density on real space grid. + grid_inst: pyscf UniformGrids object. + grid_points: Real space grid points. + num_interp_points: Number of interpolating points. Returns: - grid_points: Grid points sampled using density as a weighting function. + grid_points: Grid points sampled using density as a weighting function. """ norm_factor = np.einsum("R,R->", density, grid_inst.weights).real @@ -804,16 +800,13 @@ def interp_indx_from_qrcp(Z: npt.NDArray, permuted Z^T. Args: - orbitals_on_grid: cell-periodic part of bloch orbitals on real space grid - of shape (num_grid_points, num_kpts*num_mo) - num_interp_pts: integer corresponding to number of interpolating points - to select from full real space grid. - Z: npt.NDArray: - return_diagonal: bool: (Default value = False) + Z: Orbital pair product matrix. + num_interp_pts: integer corresponding to number of interpolating points + to select from full real space grid. + return_diagonal: bool: (Default value = False) Returns: - interp_indx: Index of interpolating points in full real space grid. - + interp_indx: Index of interpolating points in full real space grid. """ Q, R, P = scipy.linalg.qr(Z.T, pivoting=True) @@ -834,16 +827,16 @@ def setup_isdf(mf_inst: scf.RHF, verbose: bool = False """Setup common data for ISDF solution. Args: - mf_inst: pyscf pbc mean-field object. - verbose: Whether to print some information. - mf_inst: scf.RHF: - verbose: bool: (Default value = False) + mf_inst: pyscf pbc mean-field object. + verbose: Whether to print some information. + mf_inst: scf.RHF: + verbose: bool: (Default value = False) Returns: - grid_points: Real space grid points. - cell_periodic_mo: Cell periodic part of MOs on a grid. - [num_grid, num_kpts * num_orb] - bloch_orbitals_mo: MOs on a grid. [num_grid, num_kpts, num_orb] + grid_points: Real space grid points. + cell_periodic_mo: Cell periodic part of MOs on a grid. + [num_grid, num_kpts * num_orb] + bloch_orbitals_mo: MOs on a grid. [num_grid, num_kpts, num_orb] """ assert isinstance(mf_inst.with_df, df.FFTDF), "mf object must use FFTDF" @@ -905,23 +898,22 @@ class KPointTHC: kthc.chi[iks], optimize=True, ) - """ chi: npt.NDArray[np.complex128] zeta: npt.NDArray - G_mapping: npt.NDArray + g_mapping: npt.NDArray xi: Union[npt.NDArray[np.complex128], None] @property def num_interp_points(self) -> int: """Number of interpolating points (THC dimension)""" - return chi.shape[-1] + return self.chi.shape[-1] @property def num_thc_factors(self) -> int: """Number of interpolating points (THC dimension)""" - return chi.shape[-1] + return self.chi.shape[-1] def solve_kmeans_kpisdf( @@ -936,19 +928,19 @@ def solve_kmeans_kpisdf( r"""Solve for k-point THC factors using k-means CVT ISDF procedure. Args: - mf_inst: pyscf pbc mean-field object. - num_interp_points: Number of interpolating points (THC rank M). - max_kmeans_iteration: Max iteration for k-means CVT algorithm. - single_translation: Build THC factors assuming single translation of kp - - kq. If true we build zeta[Q, G], else zeta[Q, G, G']. - use_density_guess: Select initial grid points according to electron - density? Default True. - kmeans_weighting_function: Weighting function to use in k-means CVT. - One of ["density", "orbital_density", "sum_squares"]. - verbose: Whether to print some information. + mf_inst: pyscf pbc mean-field object. + num_interp_points: Number of interpolating points (THC rank M). + max_kmeans_iteration: Max iteration for k-means CVT algorithm. + single_translation: Build THC factors assuming single translation of kp + - kq. If true we build zeta[Q, G], else zeta[Q, G, G']. + use_density_guess: Select initial grid points according to electron + density? Default True. + kmeans_weighting_function: Weighting function to use in k-means CVT. + One of ["density", "orbital_density", "sum_squares"]. + verbose: Whether to print some information. Returns: - solution: THC factors held in KPointTHC object. + solution: THC factors held in KPointTHC object. """ if verbose: print(f" Number of interpolating points: {num_interp_points}") @@ -1024,12 +1016,12 @@ def solve_qrcp_isdf( r"""Solve for k-point THC factors using QRCP ISDF procedure. Args: - mf_inst: pyscf pbc mean-field object. - num_interp_points: Number of interpolating points (THC rank M). - single_translation: Build THC factors assuming single translation of kp - single_translation: Build THC factors assuming single translation of kp - - kq. If true we build zeta[Q, G], else zeta[Q, G, G']. - verbose: Whether to print some information. + mf_inst: pyscf pbc mean-field object. + num_interp_points: Number of interpolating points (THC rank M). + single_translation: Build THC factors assuming single translation of kp + single_translation: Build THC factors assuming single translation of kp + - kq. If true we build zeta[Q, G], else zeta[Q, G, G']. + verbose: Whether to print some information. Returns: solution: THC factors held in KPointTHC object. @@ -1040,11 +1032,11 @@ def solve_qrcp_isdf( # Z_{R, (ki)(k'j)} = u_{ki}(r)* u_{k'j}(r) num_grid_points = len(grid_points) num_orbs = cell_periodic_mo.shape[1] - Z = np.einsum("Ri,Rj->Rij", + pair_products = np.einsum("Ri,Rj->Rij", cell_periodic_mo.conj(), cell_periodic_mo, optimize=True).reshape((num_grid_points, num_orbs**2)) - interp_indx = interp_indx_from_qrcp(Z, num_interp_points) + interp_indx = interp_indx_from_qrcp(pair_products, num_interp_points) # Solve for THC factors. solution = solve_for_thc_factors( mf_inst, @@ -1068,26 +1060,26 @@ def solve_for_thc_factors( r"""Solve for k-point THC factors using interpolating points as input. Args: - mf_inst: pyscf pbc mean-field object. - interp_points_index: Indices of interpolating points found from k-means - CVT or QRCP. - cell_periodic_mo: cell periodic part of Bloch orbital on real space grid. - shape: [num_grid_points, num_kpts*num_mo] - kmeans_weighting_function: Weighting function to use in k-means CVT. - One of ["density", "orbital_density", "sum_squares"]. - grid_points: Real space grid. Dimension [num_grid_points, num_dim], - num_dim is 1, 2 or 3 for 1D, 2D, 3D. - single_translation: Build THC factors assuming single translation of kp - - kq. If true we build zeta[Q, G], else zeta[Q, G, G']. - (Default value = True) - verbose: Whether to print some information. (Default value = True) + mf_inst: pyscf pbc mean-field object. + interp_points_index: Indices of interpolating points found from k-means + CVT or QRCP. + cell_periodic_mo: cell periodic part of Bloch orbital on real space grid. + shape: [num_grid_points, num_kpts*num_mo] + kmeans_weighting_function: Weighting function to use in k-means CVT. + One of ["density", "orbital_density", "sum_squares"]. + grid_points: Real space grid. Dimension [num_grid_points, num_dim], + num_dim is 1, 2 or 3 for 1D, 2D, 3D. + single_translation: Build THC factors assuming single translation of kp + - kq. If true we build zeta[Q, G], else zeta[Q, G, G']. + (Default value = True) + verbose: Whether to print some information. (Default value = True) Returns: - solution: THC factors held in KPointTHC object. + solution: THC factors held in KPointTHC object. """ assert isinstance(mf_inst.with_df, df.FFTDF), "mf object must use FFTDF" if single_translation: - chi, zeta, xi, G_mapping = kpoint_isdf_single_translation( + chi, zeta, xi, g_mapping = kpoint_isdf_single_translation( mf_inst.with_df, interp_points_index, mf_inst.kpts, @@ -1095,7 +1087,7 @@ def solve_for_thc_factors( grid_points, ) else: - chi, zeta, xi, G_mapping = kpoint_isdf_double_translation( + chi, zeta, xi, g_mapping = kpoint_isdf_double_translation( mf_inst.with_df, interp_points_index, mf_inst.kpts, @@ -1110,6 +1102,6 @@ def solve_for_thc_factors( chi = chi.reshape((num_interp_points, num_kpts, num_mo)) chi = chi.transpose((1, 2, 0)) - solution = KPointTHC(chi=chi, zeta=zeta, xi=xi, G_mapping=G_mapping) + solution = KPointTHC(chi=chi, zeta=zeta, xi=xi, g_mapping=g_mapping) return solution diff --git a/src/openfermion/resource_estimates/pbc/thc/utils/isdf_test.py b/src/openfermion/resource_estimates/pbc/thc/utils/isdf_test.py index a3679bd06..056d83b4a 100644 --- a/src/openfermion/resource_estimates/pbc/thc/utils/isdf_test.py +++ b/src/openfermion/resource_estimates/pbc/thc/utils/isdf_test.py @@ -20,13 +20,13 @@ from openfermion.resource_estimates.pbc.thc.utils.kmeans import KMeansCVT from openfermion.resource_estimates.pbc.thc.utils.isdf import ( - inverse_G_map_double_translation, + inverse_g_map_double_translation, build_kpoint_zeta, get_miller, - build_minus_Q_G_mapping, - build_G_vectors, - build_G_vector_mappings_single_translation, - build_G_vector_mappings_double_translation, + build_minus_q_g_mapping, + build_g_vectors, + build_g_vector_mappings_single_translation, + build_g_vector_mappings_double_translation, build_eri_isdf_double_translation, build_eri_isdf_single_translation, solve_kmeans_kpisdf, @@ -218,7 +218,7 @@ def test_G_vector_mapping_double_translation(): G_map, G_unique, _, - ) = build_G_vector_mappings_double_translation(cell, kpts, momentum_map) + ) = build_g_vector_mappings_double_translation(cell, kpts, momentum_map) num_kpts = len(kpts) for iq in range(num_kpts): for ikp in range(num_kpts): @@ -231,7 +231,7 @@ def test_G_vector_mapping_double_translation(): for i, G in enumerate(G_map[iq]): assert unique_G[G_unique[iq][i]] == G - inv_G_map = inverse_G_map_double_translation(cell, kpts, momentum_map) + inv_G_map = inverse_g_map_double_translation(cell, kpts, momentum_map) for iq in range(num_kpts): for ik in range(num_kpts): ix_G_qk = G_map[iq, ik] @@ -274,7 +274,7 @@ def test_G_vector_mapping_single_translation(): _, G_unique, delta_Gs, - ) = build_G_vector_mappings_single_translation(cell, kpts, + ) = build_g_vector_mappings_single_translation(cell, kpts, kpts_pq_indx[unique_indx]) kconserv = get_kconserv(cell, kpts) for ikp in range(num_kpts): @@ -331,7 +331,7 @@ def test_kpoint_isdf_double_translation(): ikq = momentum_map[iq, ikp] for iks in range(num_kpts): ikr = momentum_map[iq, iks] - _ = kpt_thc.G_mapping[iq, iks] + _ = kpt_thc.g_mapping[iq, iks] kpt_pqrs = [kpts[ikp], kpts[ikq], kpts[ikr], kpts[iks]] mos_pqrs = [ mf.mo_coeff[ikp], @@ -347,7 +347,7 @@ def test_kpoint_isdf_double_translation(): kpt_thc.zeta, iq, [ikp, ikq, ikr, iks], - kpt_thc.G_mapping, + kpt_thc.g_mapping, ) assert np.allclose(eri_pqrs, eri_pqrs_isdf) @@ -411,7 +411,7 @@ def test_kpoint_isdf_single_translation(): kpt_thc.zeta, qindx, [ikp, ikq, ikr, iks], - kpt_thc.G_mapping, + kpt_thc.g_mapping, ) assert np.allclose(eri_pqrs, eri_pqrs_isdf) @@ -458,8 +458,8 @@ def test_kpoint_isdf_symmetries(): _, G_unique, delta_Gs, - ) = build_G_vector_mappings_double_translation(cell, kpts, momentum_map) - _, minus_Q_G_map_unique = build_minus_Q_G_mapping(cell, kpts, momentum_map) + ) = build_g_vector_mappings_double_translation(cell, kpts, momentum_map) + _, minus_Q_G_map_unique = build_minus_q_g_mapping(cell, kpts, momentum_map) num_kpts = len(kpts) # Test symmetries from Appendix D of https://arxiv.org/pdf/2302.05531.pdf # Test LHS for sanity too (need to uncomment) @@ -467,74 +467,72 @@ def test_kpoint_isdf_symmetries(): from pyscf.pbc.lib.kpts_helper import conj_mapping minus_k_map = conj_mapping(cell, kpts) - for iq in range(3, num_kpts): - # Get -Q index - minus_iq = minus_k_map[iq] - for ik in range(num_kpts): - ik_minus_q = momentum_map[iq, ik] - iGpq = G_unique[iq, ik] - for ik_prime in range(num_kpts): - iGsr = G_unique[iq, ik_prime] - ik_prime_minus_q = momentum_map[iq, ik_prime] - # Sanity check G mappings - assert np.allclose(kpts[ik] - kpts[ik_minus_q] - kpts[iq], - delta_Gs[iq][iGpq]) - assert np.allclose( - kpts[ik_prime] - kpts[ik_prime_minus_q] - kpts[iq], - delta_Gs[iq][iGsr], - ) - # (pk qk-Q | rk'-Q sk') = (q k-Q p k | sk' rk'-Q)* - ik_prime_minus_q = momentum_map[iq, ik_prime] - # uncomment to check normal eris - # kpt_pqrs = [ik, ik_minus_q, ik_prime_minus_q, ik_prime] - # eri_pqrs = build_eri(mf, kpt_pqrs) - # kpt_pqrs = [ik, ik_minus_q, ik_prime_minus_q, ik_prime] - # kpt_pqrs = [ik_minus_q, ik, ik_prime, ik_prime_minus_q] - # eri_qpsr = build_eri(mf, kpt_pqrs).transpose((1, 0, 3, 2)) - # Sanity check relationship - # assert np.allclose(eri_pqrs, eri_qpsr.conj()) - # Now check how to index into correct G when Q is conjugated - # We want to find (-Q) + G_pq_comp + (Q + Gpq) = 0, - # Q + Gpq = kp - kq = q - # so G_pq_comp = -((-Q) + (Q+Gpq)) - iGpq_comp = minus_Q_G_map_unique[minus_iq, ik] - iGsr_comp = minus_Q_G_map_unique[minus_iq, ik_prime] - # Check zeta symmetry: expect zeta[Q,G1,G2,m,n] = - # zeta[-Q,G1_comp,G2_comp,m, n].conj() - # Build refernce point zeta[Q,G1,G2,m,n] - zeta_ref = kpt_thc.zeta[iq][iGpq, iGsr] - zeta_test = kpt_thc.zeta[minus_iq][iGpq_comp, iGsr_comp] - # F31 (pk qk-Q | rk'-Q sk') = (rk'-Q s k'| pk qk-Q) - assert np.allclose(zeta_ref, zeta_test.conj()) - # Sanity check do literal minus signs (should be complex - # conjugate) - zeta_test = build_kpoint_zeta( - mf.with_df, - -kpts[iq], - -delta_Gs[iq][iGpq], - -delta_Gs[iq][iGsr], - grid_points, - kpt_thc.xi, - ) - assert np.allclose(zeta_ref, zeta_test.conj()) - # (pk qk-Q | rk'-Q sk') = (rk'-Q s k'| pk qk-Q) - # uncomment to check normal eris - # kpt_pqrs = [ik_prime_minus_q, ik_prime, ik, ik_minus_q] - # eri_rspq = build_eri(mf, kpt_pqrs).transpose((2, 3, 0, 1)) - # assert np.allclose(eri_pqrs, eri_rspq) - # Check zeta symmetry: expect zeta[Q,G1,G2,m,n] = - # zeta[-Q,G2_comp,G1_comp,m, n] - zeta_test = kpt_thc.zeta[minus_iq][iGsr_comp, iGpq_comp] - assert np.allclose(zeta_ref, zeta_test.T) - # (pk qk-Q | rk'-Q sk') = (sk' r k'-Q| qk-Q pk) - # uncomment to check normal eris - # kpt_pqrs = [ik_prime, ik_prime_minus_q, ik_minus_q, ik] - # eri_srqp = build_eri(mf, kpt_pqrs).transpose((3, 2, 1, 0)) - # assert np.allclose(eri_pqrs, eri_srqp.conj()) - # Check zeta symmetry: expect zeta[Q,G1,G2,m,n] - # = zeta[Q,G2,G1,n, m].conj() - zeta_test = kpt_thc.zeta[iq][iGsr, iGpq] - assert np.allclose(zeta_ref, zeta_test.conj().T) + iq, ik, ik_prime = np.random.randint(0, num_kpts, 3) + # Get -Q index + minus_iq = minus_k_map[iq] + ik_minus_q = momentum_map[iq, ik] + iGpq = G_unique[iq, ik] + iGsr = G_unique[iq, ik_prime] + ik_prime_minus_q = momentum_map[iq, ik_prime] + # Sanity check G mappings + assert np.allclose(kpts[ik] - kpts[ik_minus_q] - kpts[iq], + delta_Gs[iq][iGpq]) + assert np.allclose( + kpts[ik_prime] - kpts[ik_prime_minus_q] - kpts[iq], + delta_Gs[iq][iGsr], + ) + # (pk qk-Q | rk'-Q sk') = (q k-Q p k | sk' rk'-Q)* + ik_prime_minus_q = momentum_map[iq, ik_prime] + # uncomment to check normal eris + # kpt_pqrs = [ik, ik_minus_q, ik_prime_minus_q, ik_prime] + # eri_pqrs = build_eri(mf, kpt_pqrs) + # kpt_pqrs = [ik, ik_minus_q, ik_prime_minus_q, ik_prime] + # kpt_pqrs = [ik_minus_q, ik, ik_prime, ik_prime_minus_q] + # eri_qpsr = build_eri(mf, kpt_pqrs).transpose((1, 0, 3, 2)) + # Sanity check relationship + # assert np.allclose(eri_pqrs, eri_qpsr.conj()) + # Now check how to index into correct G when Q is conjugated + # We want to find (-Q) + G_pq_comp + (Q + Gpq) = 0, + # Q + Gpq = kp - kq = q + # so G_pq_comp = -((-Q) + (Q+Gpq)) + iGpq_comp = minus_Q_G_map_unique[minus_iq, ik] + iGsr_comp = minus_Q_G_map_unique[minus_iq, ik_prime] + # Check zeta symmetry: expect zeta[Q,G1,G2,m,n] = + # zeta[-Q,G1_comp,G2_comp,m, n].conj() + # Build refernce point zeta[Q,G1,G2,m,n] + zeta_ref = kpt_thc.zeta[iq][iGpq, iGsr] + zeta_test = kpt_thc.zeta[minus_iq][iGpq_comp, iGsr_comp] + # F31 (pk qk-Q | rk'-Q sk') = (rk'-Q s k'| pk qk-Q) + assert np.allclose(zeta_ref, zeta_test.conj()) + # Sanity check do literal minus signs (should be complex + # conjugate) + zeta_test = build_kpoint_zeta( + mf.with_df, + -kpts[iq], + -delta_Gs[iq][iGpq], + -delta_Gs[iq][iGsr], + grid_points, + kpt_thc.xi, + ) + assert np.allclose(zeta_ref, zeta_test.conj()) + # (pk qk-Q | rk'-Q sk') = (rk'-Q s k'| pk qk-Q) + # uncomment to check normal eris + # kpt_pqrs = [ik_prime_minus_q, ik_prime, ik, ik_minus_q] + # eri_rspq = build_eri(mf, kpt_pqrs).transpose((2, 3, 0, 1)) + # assert np.allclose(eri_pqrs, eri_rspq) + # Check zeta symmetry: expect zeta[Q,G1,G2,m,n] = + # zeta[-Q,G2_comp,G1_comp,m, n] + zeta_test = kpt_thc.zeta[minus_iq][iGsr_comp, iGpq_comp] + assert np.allclose(zeta_ref, zeta_test.T) + # (pk qk-Q | rk'-Q sk') = (sk' r k'-Q| qk-Q pk) + # uncomment to check normal eris + # kpt_pqrs = [ik_prime, ik_prime_minus_q, ik_minus_q, ik] + # eri_srqp = build_eri(mf, kpt_pqrs).transpose((3, 2, 1, 0)) + # assert np.allclose(eri_pqrs, eri_srqp.conj()) + # Check zeta symmetry: expect zeta[Q,G1,G2,m,n] + # = zeta[Q,G2,G1,n, m].conj() + zeta_test = kpt_thc.zeta[iq][iGsr, iGpq] + assert np.allclose(zeta_ref, zeta_test.conj().T) def test_symmetry_of_G_maps(): @@ -561,8 +559,8 @@ def test_symmetry_of_G_maps(): G_map, _, delta_Gs, - ) = build_G_vector_mappings_double_translation(cell, kpts, momentum_map) - G_dict, _ = build_G_vectors(cell) + ) = build_g_vector_mappings_double_translation(cell, kpts, momentum_map) + G_dict, _ = build_g_vectors(cell) num_kpts = len(kpts) lattice_vectors = cell.lattice_vectors() from pyscf.pbc.lib.kpts_helper import conj_mapping @@ -591,7 +589,7 @@ def test_symmetry_of_G_maps(): assert iGsr_comp in G_indx_unique # Check minus Q mapping - minus_Q_G_map, minus_Q_G_map_unique = build_minus_Q_G_mapping( + minus_Q_G_map, minus_Q_G_map_unique = build_minus_q_g_mapping( cell, kpts, momentum_map) for iq in range(1, num_kpts): minus_iq = minus_k_map[iq] @@ -652,6 +650,6 @@ def test_isdf_qrcp(): kpt_thc.zeta, iq, [ikp, ikq, ikr, iks], - kpt_thc.G_mapping, + kpt_thc.g_mapping, ) assert np.allclose(eri_pqrs, eri_pqrs_isdf) From 26acc38a460bdb5decc348f238ec8bbb2ffdc8ce Mon Sep 17 00:00:00 2001 From: Fionn Malone Date: Sat, 22 Jul 2023 06:07:54 +0000 Subject: [PATCH 10/19] Remove utils. --- .../pbc/thc/{utils => factorizations}/__init__.py | 0 .../pbc/thc/{utils => factorizations}/gvec_logic.py | 0 .../pbc/thc/{utils => factorizations}/gvec_logic_test.py | 2 +- .../pbc/thc/{utils => factorizations}/isdf.py | 2 +- .../pbc/thc/{utils => factorizations}/isdf_test.py | 4 ++-- .../pbc/thc/{utils => factorizations}/kmeans.py | 0 .../pbc/thc/{utils => factorizations}/kmeans_test.py | 0 .../pbc/thc/{utils => factorizations}/thc_jax.py | 2 +- .../pbc/thc/{utils => factorizations}/thc_jax_test.py | 4 ++-- 9 files changed, 7 insertions(+), 7 deletions(-) rename src/openfermion/resource_estimates/pbc/thc/{utils => factorizations}/__init__.py (100%) rename src/openfermion/resource_estimates/pbc/thc/{utils => factorizations}/gvec_logic.py (100%) rename src/openfermion/resource_estimates/pbc/thc/{utils => factorizations}/gvec_logic_test.py (99%) rename src/openfermion/resource_estimates/pbc/thc/{utils => factorizations}/isdf.py (99%) rename src/openfermion/resource_estimates/pbc/thc/{utils => factorizations}/isdf_test.py (99%) rename src/openfermion/resource_estimates/pbc/thc/{utils => factorizations}/kmeans.py (100%) rename src/openfermion/resource_estimates/pbc/thc/{utils => factorizations}/kmeans_test.py (100%) rename src/openfermion/resource_estimates/pbc/thc/{utils => factorizations}/thc_jax.py (99%) rename src/openfermion/resource_estimates/pbc/thc/{utils => factorizations}/thc_jax_test.py (98%) diff --git a/src/openfermion/resource_estimates/pbc/thc/utils/__init__.py b/src/openfermion/resource_estimates/pbc/thc/factorizations/__init__.py similarity index 100% rename from src/openfermion/resource_estimates/pbc/thc/utils/__init__.py rename to src/openfermion/resource_estimates/pbc/thc/factorizations/__init__.py diff --git a/src/openfermion/resource_estimates/pbc/thc/utils/gvec_logic.py b/src/openfermion/resource_estimates/pbc/thc/factorizations/gvec_logic.py similarity index 100% rename from src/openfermion/resource_estimates/pbc/thc/utils/gvec_logic.py rename to src/openfermion/resource_estimates/pbc/thc/factorizations/gvec_logic.py diff --git a/src/openfermion/resource_estimates/pbc/thc/utils/gvec_logic_test.py b/src/openfermion/resource_estimates/pbc/thc/factorizations/gvec_logic_test.py similarity index 99% rename from src/openfermion/resource_estimates/pbc/thc/utils/gvec_logic_test.py rename to src/openfermion/resource_estimates/pbc/thc/factorizations/gvec_logic_test.py index 548331287..0c82751f0 100644 --- a/src/openfermion/resource_estimates/pbc/thc/utils/gvec_logic_test.py +++ b/src/openfermion/resource_estimates/pbc/thc/factorizations/gvec_logic_test.py @@ -12,7 +12,7 @@ # limitations under the License. import itertools import numpy as np -from openfermion.resource_estimates.pbc.thc.utils.gvec_logic import ( +from openfermion.resource_estimates.pbc.thc.factorizations.gvec_logic import ( get_miller_indices, get_delta_kp_kq_q, build_transfer_map, diff --git a/src/openfermion/resource_estimates/pbc/thc/utils/isdf.py b/src/openfermion/resource_estimates/pbc/thc/factorizations/isdf.py similarity index 99% rename from src/openfermion/resource_estimates/pbc/thc/utils/isdf.py rename to src/openfermion/resource_estimates/pbc/thc/factorizations/isdf.py index 1b19015ef..ad22fcdc8 100644 --- a/src/openfermion/resource_estimates/pbc/thc/utils/isdf.py +++ b/src/openfermion/resource_estimates/pbc/thc/factorizations/isdf.py @@ -37,7 +37,7 @@ from pyscf.pbc.dft.gen_grid import UniformGrids from pyscf.pbc.lib.kpts_helper import conj_mapping, get_kconserv, unique -from openfermion.resource_estimates.pbc.thc.utils.kmeans import KMeansCVT +from openfermion.resource_estimates.pbc.thc.factorizations.kmeans import KMeansCVT from openfermion.resource_estimates.pbc.utils.hamiltonian_utils import ( build_momentum_transfer_mapping,) diff --git a/src/openfermion/resource_estimates/pbc/thc/utils/isdf_test.py b/src/openfermion/resource_estimates/pbc/thc/factorizations/isdf_test.py similarity index 99% rename from src/openfermion/resource_estimates/pbc/thc/utils/isdf_test.py rename to src/openfermion/resource_estimates/pbc/thc/factorizations/isdf_test.py index 056d83b4a..50c9f1c8f 100644 --- a/src/openfermion/resource_estimates/pbc/thc/utils/isdf_test.py +++ b/src/openfermion/resource_estimates/pbc/thc/factorizations/isdf_test.py @@ -18,8 +18,8 @@ from pyscf.pbc.tools import pyscf_ase from pyscf.pbc.lib.kpts_helper import unique, get_kconserv, member -from openfermion.resource_estimates.pbc.thc.utils.kmeans import KMeansCVT -from openfermion.resource_estimates.pbc.thc.utils.isdf import ( +from openfermion.resource_estimates.pbc.thc.factorizations.kmeans import KMeansCVT +from openfermion.resource_estimates.pbc.thc.factorizations.isdf import ( inverse_g_map_double_translation, build_kpoint_zeta, get_miller, diff --git a/src/openfermion/resource_estimates/pbc/thc/utils/kmeans.py b/src/openfermion/resource_estimates/pbc/thc/factorizations/kmeans.py similarity index 100% rename from src/openfermion/resource_estimates/pbc/thc/utils/kmeans.py rename to src/openfermion/resource_estimates/pbc/thc/factorizations/kmeans.py diff --git a/src/openfermion/resource_estimates/pbc/thc/utils/kmeans_test.py b/src/openfermion/resource_estimates/pbc/thc/factorizations/kmeans_test.py similarity index 100% rename from src/openfermion/resource_estimates/pbc/thc/utils/kmeans_test.py rename to src/openfermion/resource_estimates/pbc/thc/factorizations/kmeans_test.py diff --git a/src/openfermion/resource_estimates/pbc/thc/utils/thc_jax.py b/src/openfermion/resource_estimates/pbc/thc/factorizations/thc_jax.py similarity index 99% rename from src/openfermion/resource_estimates/pbc/thc/utils/thc_jax.py rename to src/openfermion/resource_estimates/pbc/thc/factorizations/thc_jax.py index 7451b4002..0434384df 100644 --- a/src/openfermion/resource_estimates/pbc/thc/utils/thc_jax.py +++ b/src/openfermion/resource_estimates/pbc/thc/factorizations/thc_jax.py @@ -43,7 +43,7 @@ import jax.typing as jnpt from openfermion.resource_estimates.thc.utils import adagrad -from openfermion.resource_estimates.pbc.thc.utils.isdf import ( +from openfermion.resource_estimates.pbc.thc.factorizations.isdf import ( KPointTHC, solve_kmeans_kpisdf, ) diff --git a/src/openfermion/resource_estimates/pbc/thc/utils/thc_jax_test.py b/src/openfermion/resource_estimates/pbc/thc/factorizations/thc_jax_test.py similarity index 98% rename from src/openfermion/resource_estimates/pbc/thc/utils/thc_jax_test.py rename to src/openfermion/resource_estimates/pbc/thc/factorizations/thc_jax_test.py index 2055710c9..c9f2f737a 100644 --- a/src/openfermion/resource_estimates/pbc/thc/utils/thc_jax_test.py +++ b/src/openfermion/resource_estimates/pbc/thc/factorizations/thc_jax_test.py @@ -21,11 +21,11 @@ from openfermion.resource_estimates.thc.utils.thc_factorization import ( thc_objective_regularized as thc_obj_mol,) -from openfermion.resource_estimates.pbc.thc.utils.isdf import ( +from openfermion.resource_estimates.pbc.thc.factorizations.isdf import ( solve_kmeans_kpisdf,) from openfermion.resource_estimates.pbc.utils.hamiltonian_utils import ( cholesky_from_df_ints,) -from openfermion.resource_estimates.pbc.thc.utils.thc_jax import ( +from openfermion.resource_estimates.pbc.thc.factorizations.thc_jax import ( adagrad_opt_kpthc_batched, get_zeta_size, kpoint_thc_via_isdf, From 3a0b9546b8e11cb97b818e9e05febea0b2553fbc Mon Sep 17 00:00:00 2001 From: Fionn Malone Date: Sat, 22 Jul 2023 06:20:30 +0000 Subject: [PATCH 11/19] No more utils. --- .../pbc/hamiltonian/__init__.py | 25 +++++++++++++++++++ .../hamiltonian.py} | 0 .../hamiltonian_test.py} | 4 +-- .../pbc/{utils => testing}/__init__.py | 1 + .../test_utils.py => testing/test_systems.py} | 0 .../pbc/thc/factorizations/isdf.py | 2 +- .../pbc/thc/factorizations/isdf_test.py | 2 +- .../pbc/thc/factorizations/kmeans_test.py | 2 +- .../pbc/thc/factorizations/thc_jax.py | 2 +- .../pbc/thc/factorizations/thc_jax_test.py | 7 +++--- 10 files changed, 35 insertions(+), 10 deletions(-) create mode 100644 src/openfermion/resource_estimates/pbc/hamiltonian/__init__.py rename src/openfermion/resource_estimates/pbc/{utils/hamiltonian_utils.py => hamiltonian/hamiltonian.py} (100%) rename src/openfermion/resource_estimates/pbc/{utils/hamiltonian_utils_test.py => hamiltonian/hamiltonian_test.py} (95%) rename src/openfermion/resource_estimates/pbc/{utils => testing}/__init__.py (94%) rename src/openfermion/resource_estimates/pbc/{utils/test_utils.py => testing/test_systems.py} (100%) diff --git a/src/openfermion/resource_estimates/pbc/hamiltonian/__init__.py b/src/openfermion/resource_estimates/pbc/hamiltonian/__init__.py new file mode 100644 index 000000000..c5eedd173 --- /dev/null +++ b/src/openfermion/resource_estimates/pbc/hamiltonian/__init__.py @@ -0,0 +1,25 @@ +# coverage: ignore +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import pytest + +try: + import pyscf +except (ImportError, ModuleNotFoundError) as err: + pytest.skip(f"Need pyscf for PBC resource estimates {err}", + allow_module_level=True) + +from .hamiltonian import ( + cholesky_from_df_ints, + build_hamiltonian, + build_momentum_transfer_mapping + ) \ No newline at end of file diff --git a/src/openfermion/resource_estimates/pbc/utils/hamiltonian_utils.py b/src/openfermion/resource_estimates/pbc/hamiltonian/hamiltonian.py similarity index 100% rename from src/openfermion/resource_estimates/pbc/utils/hamiltonian_utils.py rename to src/openfermion/resource_estimates/pbc/hamiltonian/hamiltonian.py diff --git a/src/openfermion/resource_estimates/pbc/utils/hamiltonian_utils_test.py b/src/openfermion/resource_estimates/pbc/hamiltonian/hamiltonian_test.py similarity index 95% rename from src/openfermion/resource_estimates/pbc/utils/hamiltonian_utils_test.py rename to src/openfermion/resource_estimates/pbc/hamiltonian/hamiltonian_test.py index dc28f60b0..57fa0f6be 100644 --- a/src/openfermion/resource_estimates/pbc/utils/hamiltonian_utils_test.py +++ b/src/openfermion/resource_estimates/pbc/hamiltonian/hamiltonian_test.py @@ -17,11 +17,11 @@ from pyscf.lib import chkfile import pytest -from openfermion.resource_estimates.pbc.utils.hamiltonian_utils import ( +from openfermion.resource_estimates.pbc.hamiltonian import ( build_hamiltonian, cholesky_from_df_ints, ) -from openfermion.resource_estimates.pbc.utils.test_utils import ( +from openfermion.resource_estimates.pbc.testing import ( make_diamond_113_szv,) diff --git a/src/openfermion/resource_estimates/pbc/utils/__init__.py b/src/openfermion/resource_estimates/pbc/testing/__init__.py similarity index 94% rename from src/openfermion/resource_estimates/pbc/utils/__init__.py rename to src/openfermion/resource_estimates/pbc/testing/__init__.py index 1f62ad09e..c005c1480 100644 --- a/src/openfermion/resource_estimates/pbc/utils/__init__.py +++ b/src/openfermion/resource_estimates/pbc/testing/__init__.py @@ -17,3 +17,4 @@ except (ImportError, ModuleNotFoundError) as err: pytest.skip(f"Need pyscf for PBC resource estimates {err}", allow_module_level=True) +from .test_systems import make_diamond_113_szv diff --git a/src/openfermion/resource_estimates/pbc/utils/test_utils.py b/src/openfermion/resource_estimates/pbc/testing/test_systems.py similarity index 100% rename from src/openfermion/resource_estimates/pbc/utils/test_utils.py rename to src/openfermion/resource_estimates/pbc/testing/test_systems.py diff --git a/src/openfermion/resource_estimates/pbc/thc/factorizations/isdf.py b/src/openfermion/resource_estimates/pbc/thc/factorizations/isdf.py index ad22fcdc8..442ddcdd6 100644 --- a/src/openfermion/resource_estimates/pbc/thc/factorizations/isdf.py +++ b/src/openfermion/resource_estimates/pbc/thc/factorizations/isdf.py @@ -38,7 +38,7 @@ from pyscf.pbc.lib.kpts_helper import conj_mapping, get_kconserv, unique from openfermion.resource_estimates.pbc.thc.factorizations.kmeans import KMeansCVT -from openfermion.resource_estimates.pbc.utils.hamiltonian_utils import ( +from openfermion.resource_estimates.pbc.hamiltonian import ( build_momentum_transfer_mapping,) diff --git a/src/openfermion/resource_estimates/pbc/thc/factorizations/isdf_test.py b/src/openfermion/resource_estimates/pbc/thc/factorizations/isdf_test.py index 50c9f1c8f..f7c82a09d 100644 --- a/src/openfermion/resource_estimates/pbc/thc/factorizations/isdf_test.py +++ b/src/openfermion/resource_estimates/pbc/thc/factorizations/isdf_test.py @@ -33,7 +33,7 @@ solve_qrcp_isdf, supercell_isdf, ) -from openfermion.resource_estimates.pbc.utils.hamiltonian_utils import ( +from openfermion.resource_estimates.pbc.hamiltonian import ( build_momentum_transfer_mapping,) diff --git a/src/openfermion/resource_estimates/pbc/thc/factorizations/kmeans_test.py b/src/openfermion/resource_estimates/pbc/thc/factorizations/kmeans_test.py index 67f9c762c..c3c6b65df 100644 --- a/src/openfermion/resource_estimates/pbc/thc/factorizations/kmeans_test.py +++ b/src/openfermion/resource_estimates/pbc/thc/factorizations/kmeans_test.py @@ -12,7 +12,7 @@ # limitations under the License. import numpy as np -from openfermion.resource_estimates.pbc.thc.utils.kmeans import KMeansCVT +from openfermion.resource_estimates.pbc.thc.factorizations.kmeans import KMeansCVT def gaussian(dx, sigma): diff --git a/src/openfermion/resource_estimates/pbc/thc/factorizations/thc_jax.py b/src/openfermion/resource_estimates/pbc/thc/factorizations/thc_jax.py index 0434384df..42394438c 100644 --- a/src/openfermion/resource_estimates/pbc/thc/factorizations/thc_jax.py +++ b/src/openfermion/resource_estimates/pbc/thc/factorizations/thc_jax.py @@ -47,7 +47,7 @@ KPointTHC, solve_kmeans_kpisdf, ) -from openfermion.resource_estimates.pbc.utils.hamiltonian_utils import ( +from openfermion.resource_estimates.pbc.hamiltonian import ( build_momentum_transfer_mapping,) diff --git a/src/openfermion/resource_estimates/pbc/thc/factorizations/thc_jax_test.py b/src/openfermion/resource_estimates/pbc/thc/factorizations/thc_jax_test.py index c9f2f737a..16053a7b5 100644 --- a/src/openfermion/resource_estimates/pbc/thc/factorizations/thc_jax_test.py +++ b/src/openfermion/resource_estimates/pbc/thc/factorizations/thc_jax_test.py @@ -23,8 +23,9 @@ from openfermion.resource_estimates.pbc.thc.factorizations.isdf import ( solve_kmeans_kpisdf,) -from openfermion.resource_estimates.pbc.utils.hamiltonian_utils import ( - cholesky_from_df_ints,) +from openfermion.resource_estimates.pbc.hamiltonian import ( + cholesky_from_df_ints, +build_momentum_transfer_mapping) from openfermion.resource_estimates.pbc.thc.factorizations.thc_jax import ( adagrad_opt_kpthc_batched, get_zeta_size, @@ -38,8 +39,6 @@ thc_objective_regularized_batched, unpack_thc_factors, ) -from openfermion.resource_estimates.pbc.utils.hamiltonian_utils import ( - build_momentum_transfer_mapping,) def test_kpoint_thc_reg_gamma(): From 91b17119ac48e9d50595d104fd6972e7cf23d77d Mon Sep 17 00:00:00 2001 From: Fionn Malone Date: Sat, 22 Jul 2023 06:25:25 +0000 Subject: [PATCH 12/19] More review comments. --- .../resource_estimates/pbc/thc/factorizations/kmeans.py | 5 +++++ .../resource_estimates/pbc/thc/factorizations/thc_jax.py | 2 -- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/openfermion/resource_estimates/pbc/thc/factorizations/kmeans.py b/src/openfermion/resource_estimates/pbc/thc/factorizations/kmeans.py index 06753c353..b6e434e84 100644 --- a/src/openfermion/resource_estimates/pbc/thc/factorizations/kmeans.py +++ b/src/openfermion/resource_estimates/pbc/thc/factorizations/kmeans.py @@ -12,6 +12,11 @@ # limitations under the License. import numpy as np import numpy.typing as npt +"""Module for performing K-Means CVT algorithm to find interpolating points. + +Provides centroidal Veronoi tesselation of the grid of real space points +weighted by the electron density using a K-Means classification. +""" class KMeansCVT(object): diff --git a/src/openfermion/resource_estimates/pbc/thc/factorizations/thc_jax.py b/src/openfermion/resource_estimates/pbc/thc/factorizations/thc_jax.py index 42394438c..903fbc7c4 100644 --- a/src/openfermion/resource_estimates/pbc/thc/factorizations/thc_jax.py +++ b/src/openfermion/resource_estimates/pbc/thc/factorizations/thc_jax.py @@ -529,7 +529,6 @@ def lbfgsb_opt_kpthc_l2reg( jnp.array(chol), penalty_param, )) - # print() res = minimize( thc_objective_regularized, initial_guess, @@ -550,7 +549,6 @@ def lbfgsb_opt_kpthc_l2reg( }, ) - # print(res) params = np.array(res.x) loss = thc_objective_regularized( jnp.array(res.x), From f5c8646784520478e1e60fd6aaaf99cf5e3a1bcf Mon Sep 17 00:00:00 2001 From: Fionn Malone Date: Sat, 22 Jul 2023 06:39:08 +0000 Subject: [PATCH 13/19] Fix formatting. --- .../pbc/hamiltonian/__init__.py | 7 +-- .../pbc/thc/factorizations/gvec_logic.py | 5 +- .../pbc/thc/factorizations/isdf.py | 58 ++++++++++--------- .../pbc/thc/factorizations/isdf_test.py | 5 +- .../pbc/thc/factorizations/kmeans_test.py | 3 +- .../pbc/thc/factorizations/thc_jax_test.py | 3 +- 6 files changed, 42 insertions(+), 39 deletions(-) diff --git a/src/openfermion/resource_estimates/pbc/hamiltonian/__init__.py b/src/openfermion/resource_estimates/pbc/hamiltonian/__init__.py index c5eedd173..689be1b9f 100644 --- a/src/openfermion/resource_estimates/pbc/hamiltonian/__init__.py +++ b/src/openfermion/resource_estimates/pbc/hamiltonian/__init__.py @@ -18,8 +18,5 @@ pytest.skip(f"Need pyscf for PBC resource estimates {err}", allow_module_level=True) -from .hamiltonian import ( - cholesky_from_df_ints, - build_hamiltonian, - build_momentum_transfer_mapping - ) \ No newline at end of file +from .hamiltonian import (cholesky_from_df_ints, build_hamiltonian, + build_momentum_transfer_mapping) \ No newline at end of file diff --git a/src/openfermion/resource_estimates/pbc/thc/factorizations/gvec_logic.py b/src/openfermion/resource_estimates/pbc/thc/factorizations/gvec_logic.py index 123338bfd..b9f1db422 100644 --- a/src/openfermion/resource_estimates/pbc/thc/factorizations/gvec_logic.py +++ b/src/openfermion/resource_estimates/pbc/thc/factorizations/gvec_logic.py @@ -1,3 +1,4 @@ +# coverage: ignore # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at @@ -36,8 +37,8 @@ def get_miller_indices(kmesh): Assumes a non gamma centered non-1stBZ Monkorhst-Pack mesh Args: - kmesh: A length 3 1-D iteratble with the number of k-points in the x,y,z direction - [Nk_x, NK_y, NK_z] where NK_x/y/z are positive integers + kmesh: A length 3 1-D iteratble with the number of k-points in the x,y,z + direction [Nk_x, NK_y, NK_z] where NK_x/y/z are positive integers Returns: np.array 2D that is prod([Nk_x, NK_y, NK_z]) diff --git a/src/openfermion/resource_estimates/pbc/thc/factorizations/isdf.py b/src/openfermion/resource_estimates/pbc/thc/factorizations/isdf.py index 442ddcdd6..804c26b7d 100644 --- a/src/openfermion/resource_estimates/pbc/thc/factorizations/isdf.py +++ b/src/openfermion/resource_estimates/pbc/thc/factorizations/isdf.py @@ -37,7 +37,8 @@ from pyscf.pbc.dft.gen_grid import UniformGrids from pyscf.pbc.lib.kpts_helper import conj_mapping, get_kconserv, unique -from openfermion.resource_estimates.pbc.thc.factorizations.kmeans import KMeansCVT +from openfermion.resource_estimates.pbc.thc.factorizations.kmeans import ( + KMeansCVT) from openfermion.resource_estimates.pbc.hamiltonian import ( build_momentum_transfer_mapping,) @@ -142,10 +143,10 @@ def supercell_isdf( Returns: tuple: (chi, zeta, Theta): orbitals on interpolating - points, zeta (central tensor), and matrix of interpolating vectors Theta - of dimension [num_grid_points, num_interp_points] (also called - xi_mu(r)), where num_grid_points is the number of real space grid points - and num_interp_points is the number of interpolating points. + points, zeta (central tensor), and matrix of interpolating vectors + Theta of dimension [num_grid_points, num_interp_points] (also called + xi_mu(r)), where num_grid_points is the number of real space grid + points and num_interp_points is the number of interpolating points. """ cell = mydf.cell @@ -373,9 +374,9 @@ def build_minus_q_g_mapping(cell: gto.Cell, kpts: npt.NDArray, momentum_map[iq, ikp] = ikq. Returns: - minus_q_mapping: [indx_minus_q, k]-th element yields index for g satisfying (*) - above, where indx_minus_q is given by indx_minus_q = minus_k[q], and - minus_k = conj_mapping(cell, kpts). + minus_q_mapping: [indx_minus_q, k]-th element yields index for g + satisfying (*) above, where indx_minus_q is given by indx_minus_q = + minus_k[q], and minus_k = conj_mapping(cell, kpts). minus_q_mapping_unique: indexes the appropriate g vector given by delta_gs[indx_minus_q][indx] = g, where indx = minus_q_mapping_unique[indx_minus_q, k], and deltags is built by @@ -428,9 +429,9 @@ def build_g_vector_mappings_single_translation( Returns: G_vectors: list of all 27 G-vectors - Gpqr_mapping: [iq, kr]-th element yields indx_Gpqr, where Gpqr = kpts[ikp] - - kpts[ikq] + kpts[ikr] - kpts[iks], i.e. returns index to G_vectors - (consistent with G_vectors) satisfying this condition. + Gpqr_mapping: [iq, kr]-th element yields indx_Gpqr, where Gpqr = + kpts[ikp] - kpts[ikq] + kpts[ikr] - kpts[iks], i.e. returns index to + G_vectors (consistent with G_vectors) satisfying this condition. Gpqr_mapping_unique: provides mapping to unique G_vector index. Delta_gs: provides compressed lists of unique G vectors. @@ -609,12 +610,13 @@ def kpoint_isdf_double_translation( zeta: THC central tensor. Dimension is [num_kpts, 27, 27, num_interp_points, num_interp_points] if only_unique_G is False otherwise it is of shape [num_kpts, - num_unique[q], num_unique[q], 27, num_interp_points, num_interp_points]. + num_unique[q], num_unique[q], 27, num_interp_points, + num_interp_points]. theta: matrix of interpolating vectors of dimension [num_grid_points, - num_interp_points] (also called xi_mu(r)), where num_grid_points is the - number of real space grid points and num_interp_points is the number of - interpolating points. zeta (the - g_mapping: g_mapping maps k-points to the appropriate delta_g index, i.e. + num_interp_points] (also called xi_mu(r)), where num_grid_points is + the number of real space grid points and num_interp_points is the + number of interpolating points. zeta (the + g_mapping: g_mapping maps k-points to the appropriate delta_g index, i.e g_mapping[iq, ik] = i_delta_g. the index will map to the appropriate index in the reduced set of g vectors. """ @@ -688,12 +690,13 @@ def kpoint_isdf_single_translation( zeta: THC central tensor. Dimension is [num_kpts, 27, 27, num_interp_points, num_interp_points] if only_unique_G is False otherwise it is of shape [num_kpts, - num_unique[Q], num_unique[Q], 27, num_interp_points, num_interp_points]. + num_unique[Q], num_unique[Q], 27, num_interp_points, + num_interp_points]. Theta: Matrix of interpolating vectors of dimension [num_grid_points, - num_interp_points] (also called xi_mu(r)), where num_grid_points is the - number of real space grid points and num_interp_points is the number of - interpolating points. Zeta (the - G_mapping: G_mapping maps k-points to the appropriate delta_G index, i.e. + num_interp_points] (also called xi_mu(r)), where num_grid_points is + the number of real space grid points and num_interp_points is the + number of interpolating points. Zeta (the + G_mapping: G_mapping maps k-points to the appropriate delta_G index, i.e G_mapping[iq, ik] = i_delta_G. the index will map to the appropriate index in the reduced set of G vectors. """ @@ -735,8 +738,8 @@ def build_isdf_orbital_inputs(mf_inst: scf.RHF) -> npt.NDArray: mf_inst: scf.RHF: Returns: - cell_periodic_mo: cell periodic part of Bloch orbital on real space grid. - shape: [num_grid_points, num_kpts*num_mo] + cell_periodic_mo: cell periodic part of Bloch orbital on real space grid + of shape [num_grid_points, num_kpts*num_mo] """ cell = mf_inst.cell @@ -1033,9 +1036,10 @@ def solve_qrcp_isdf( num_grid_points = len(grid_points) num_orbs = cell_periodic_mo.shape[1] pair_products = np.einsum("Ri,Rj->Rij", - cell_periodic_mo.conj(), - cell_periodic_mo, - optimize=True).reshape((num_grid_points, num_orbs**2)) + cell_periodic_mo.conj(), + cell_periodic_mo, + optimize=True).reshape( + (num_grid_points, num_orbs**2)) interp_indx = interp_indx_from_qrcp(pair_products, num_interp_points) # Solve for THC factors. solution = solve_for_thc_factors( @@ -1063,7 +1067,7 @@ def solve_for_thc_factors( mf_inst: pyscf pbc mean-field object. interp_points_index: Indices of interpolating points found from k-means CVT or QRCP. - cell_periodic_mo: cell periodic part of Bloch orbital on real space grid. + cell_periodic_mo: cell periodic part of Bloch orbital on real space grid shape: [num_grid_points, num_kpts*num_mo] kmeans_weighting_function: Weighting function to use in k-means CVT. One of ["density", "orbital_density", "sum_squares"]. diff --git a/src/openfermion/resource_estimates/pbc/thc/factorizations/isdf_test.py b/src/openfermion/resource_estimates/pbc/thc/factorizations/isdf_test.py index f7c82a09d..5cac9a241 100644 --- a/src/openfermion/resource_estimates/pbc/thc/factorizations/isdf_test.py +++ b/src/openfermion/resource_estimates/pbc/thc/factorizations/isdf_test.py @@ -18,7 +18,8 @@ from pyscf.pbc.tools import pyscf_ase from pyscf.pbc.lib.kpts_helper import unique, get_kconserv, member -from openfermion.resource_estimates.pbc.thc.factorizations.kmeans import KMeansCVT +from openfermion.resource_estimates.pbc.thc.factorizations.kmeans import ( + KMeansCVT) from openfermion.resource_estimates.pbc.thc.factorizations.isdf import ( inverse_g_map_double_translation, build_kpoint_zeta, @@ -476,7 +477,7 @@ def test_kpoint_isdf_symmetries(): ik_prime_minus_q = momentum_map[iq, ik_prime] # Sanity check G mappings assert np.allclose(kpts[ik] - kpts[ik_minus_q] - kpts[iq], - delta_Gs[iq][iGpq]) + delta_Gs[iq][iGpq]) assert np.allclose( kpts[ik_prime] - kpts[ik_prime_minus_q] - kpts[iq], delta_Gs[iq][iGsr], diff --git a/src/openfermion/resource_estimates/pbc/thc/factorizations/kmeans_test.py b/src/openfermion/resource_estimates/pbc/thc/factorizations/kmeans_test.py index c3c6b65df..3b2fb4a1b 100644 --- a/src/openfermion/resource_estimates/pbc/thc/factorizations/kmeans_test.py +++ b/src/openfermion/resource_estimates/pbc/thc/factorizations/kmeans_test.py @@ -12,7 +12,8 @@ # limitations under the License. import numpy as np -from openfermion.resource_estimates.pbc.thc.factorizations.kmeans import KMeansCVT +from openfermion.resource_estimates.pbc.thc.factorizations.kmeans import ( + KMeansCVT) def gaussian(dx, sigma): diff --git a/src/openfermion/resource_estimates/pbc/thc/factorizations/thc_jax_test.py b/src/openfermion/resource_estimates/pbc/thc/factorizations/thc_jax_test.py index 16053a7b5..b8fa8a0ee 100644 --- a/src/openfermion/resource_estimates/pbc/thc/factorizations/thc_jax_test.py +++ b/src/openfermion/resource_estimates/pbc/thc/factorizations/thc_jax_test.py @@ -24,8 +24,7 @@ from openfermion.resource_estimates.pbc.thc.factorizations.isdf import ( solve_kmeans_kpisdf,) from openfermion.resource_estimates.pbc.hamiltonian import ( - cholesky_from_df_ints, -build_momentum_transfer_mapping) + cholesky_from_df_ints, build_momentum_transfer_mapping) from openfermion.resource_estimates.pbc.thc.factorizations.thc_jax import ( adagrad_opt_kpthc_batched, get_zeta_size, From dc995a3088e8b11d58706667724112ecd95905b5 Mon Sep 17 00:00:00 2001 From: Fionn Malone Date: Sat, 22 Jul 2023 06:48:24 +0000 Subject: [PATCH 14/19] Formatting + add map to gvec_logic. --- .../factorizations/{gvec_logic.py => gvec_map_logic.py} | 8 ++++---- .../{gvec_logic_test.py => gvec_map_logic_test.py} | 2 +- .../resource_estimates/pbc/thc/factorizations/isdf.py | 8 ++++---- 3 files changed, 9 insertions(+), 9 deletions(-) rename src/openfermion/resource_estimates/pbc/thc/factorizations/{gvec_logic.py => gvec_map_logic.py} (99%) rename src/openfermion/resource_estimates/pbc/thc/factorizations/{gvec_logic_test.py => gvec_map_logic_test.py} (99%) diff --git a/src/openfermion/resource_estimates/pbc/thc/factorizations/gvec_logic.py b/src/openfermion/resource_estimates/pbc/thc/factorizations/gvec_map_logic.py similarity index 99% rename from src/openfermion/resource_estimates/pbc/thc/factorizations/gvec_logic.py rename to src/openfermion/resource_estimates/pbc/thc/factorizations/gvec_map_logic.py index b9f1db422..17bb72b1b 100644 --- a/src/openfermion/resource_estimates/pbc/thc/factorizations/gvec_logic.py +++ b/src/openfermion/resource_estimates/pbc/thc/factorizations/gvec_map_logic.py @@ -24,7 +24,7 @@ than vectors given by the literal sum in (*). This module tests out various mappings between k-points and reciprocal lattice -vectors in terms of these integer values. +vectors in terms of these integer values. """ import itertools import numpy as np @@ -61,7 +61,7 @@ def get_miller_indices(kmesh): def get_delta_kp_kq_q(int_scaled_kpts): """Generate kp - kq - Q = S for kp, kq, and Q. - + The difference of the three integers is stored as a four tensor D_{kp, kq, Q} = S. where the dimension of D is (nkpts, nkpts, nkpts, 3). The last dimension stores the x,y,z components of S. @@ -81,7 +81,7 @@ def get_delta_kp_kq_q(int_scaled_kpts): def build_transfer_map(kmesh, scaled_kpts): """Define mapping momentum_transfer_map[Q][k1] = k2. - + Where k1 - k2 + G = Q. Here k1, k2, Q are all tuples of integers [[0, Nkx-1], [0, Nky-1], [0, Nkz-1]] and G is [{0, Nkx}, {0, Nky}, {0, Nkz}]. @@ -162,7 +162,7 @@ def build_G_vectors(kmesh): def build_gpq_mapping(kmesh, int_scaled_kpts): """Build map for kp - kq = Q + G - + Here G is [{0, -Nkx}, {0, -Nky}, {0, -Nkz}]. G will be 0 or Nkz because kp - kq takes on values between [-Nka + 1, Nka - 1] in each component. diff --git a/src/openfermion/resource_estimates/pbc/thc/factorizations/gvec_logic_test.py b/src/openfermion/resource_estimates/pbc/thc/factorizations/gvec_map_logic_test.py similarity index 99% rename from src/openfermion/resource_estimates/pbc/thc/factorizations/gvec_logic_test.py rename to src/openfermion/resource_estimates/pbc/thc/factorizations/gvec_map_logic_test.py index 0c82751f0..7f18dcd84 100644 --- a/src/openfermion/resource_estimates/pbc/thc/factorizations/gvec_logic_test.py +++ b/src/openfermion/resource_estimates/pbc/thc/factorizations/gvec_map_logic_test.py @@ -12,7 +12,7 @@ # limitations under the License. import itertools import numpy as np -from openfermion.resource_estimates.pbc.thc.factorizations.gvec_logic import ( +from openfermion.resource_estimates.pbc.thc.factorizations.gvec_map_logic import ( get_miller_indices, get_delta_kp_kq_q, build_transfer_map, diff --git a/src/openfermion/resource_estimates/pbc/thc/factorizations/isdf.py b/src/openfermion/resource_estimates/pbc/thc/factorizations/isdf.py index 804c26b7d..48b67afaa 100644 --- a/src/openfermion/resource_estimates/pbc/thc/factorizations/isdf.py +++ b/src/openfermion/resource_estimates/pbc/thc/factorizations/isdf.py @@ -175,7 +175,7 @@ def build_kpoint_zeta( xi_mu: npt.NDArray, ) -> npt.NDArray: """Build k-point THC zeta (central tensor). - + Built for for given q, delta_g, delta_g_prime. Args: @@ -363,8 +363,8 @@ def get_miller(lattice_vectors: npt.NDArray, g: npt.NDArray) -> npt.NDArray: def build_minus_q_g_mapping(cell: gto.Cell, kpts: npt.NDArray, momentum_map: npt.NDArray) -> npt.NDArray: - """Build conjugat g map - + """Build conjugat g map + Mapping satisfies (-Q) + G + (Q + Gpq) = 0 (*) Args: @@ -803,7 +803,7 @@ def interp_indx_from_qrcp(Z: npt.NDArray, permuted Z^T. Args: - Z: Orbital pair product matrix. + Z: Orbital pair product matrix. num_interp_pts: integer corresponding to number of interpolating points to select from full real space grid. return_diagonal: bool: (Default value = False) From 9c0cdfb738b496c3e5ab710059fd80e7f445013b Mon Sep 17 00:00:00 2001 From: Fionn Malone Date: Sat, 22 Jul 2023 07:05:21 +0000 Subject: [PATCH 15/19] Fix import issues. --- .../resource_estimates/pbc/thc/__init__.py | 19 +++++++++++++++++++ .../thc/factorizations/gvec_map_logic_test.py | 3 ++- 2 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 src/openfermion/resource_estimates/pbc/thc/__init__.py diff --git a/src/openfermion/resource_estimates/pbc/thc/__init__.py b/src/openfermion/resource_estimates/pbc/thc/__init__.py new file mode 100644 index 000000000..1f62ad09e --- /dev/null +++ b/src/openfermion/resource_estimates/pbc/thc/__init__.py @@ -0,0 +1,19 @@ +# coverage: ignore +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import pytest + +try: + import pyscf +except (ImportError, ModuleNotFoundError) as err: + pytest.skip(f"Need pyscf for PBC resource estimates {err}", + allow_module_level=True) diff --git a/src/openfermion/resource_estimates/pbc/thc/factorizations/gvec_map_logic_test.py b/src/openfermion/resource_estimates/pbc/thc/factorizations/gvec_map_logic_test.py index 7f18dcd84..29fbb78ac 100644 --- a/src/openfermion/resource_estimates/pbc/thc/factorizations/gvec_map_logic_test.py +++ b/src/openfermion/resource_estimates/pbc/thc/factorizations/gvec_map_logic_test.py @@ -12,7 +12,8 @@ # limitations under the License. import itertools import numpy as np -from openfermion.resource_estimates.pbc.thc.factorizations.gvec_map_logic import ( +from openfermion.resource_estimates.\ + pbc.thc.factorizations.gvec_map_logic import ( get_miller_indices, get_delta_kp_kq_q, build_transfer_map, From a73e6d57031b5b0bb37e0e46a93740879c41e999 Mon Sep 17 00:00:00 2001 From: Fionn Malone Date: Mon, 7 Aug 2023 05:13:15 +0000 Subject: [PATCH 16/19] Mark slow tests and catch imports. --- .../resource_estimates/pbc/__init__.py | 7 -- .../pbc/hamiltonian/__init__.py | 8 +- .../pbc/hamiltonian/hamiltonian_test.py | 21 +++-- .../pbc/testing/__init__.py | 7 -- .../resource_estimates/pbc/thc/__init__.py | 7 -- .../pbc/thc/factorizations/__init__.py | 8 -- .../thc/factorizations/gvec_map_logic_test.py | 32 +++++-- .../pbc/thc/factorizations/isdf_test.py | 71 ++++++++++------ .../pbc/thc/factorizations/thc_jax_test.py | 83 ++++++++++--------- 9 files changed, 125 insertions(+), 119 deletions(-) diff --git a/src/openfermion/resource_estimates/pbc/__init__.py b/src/openfermion/resource_estimates/pbc/__init__.py index 1f62ad09e..d0d41a5f5 100644 --- a/src/openfermion/resource_estimates/pbc/__init__.py +++ b/src/openfermion/resource_estimates/pbc/__init__.py @@ -10,10 +10,3 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -import pytest - -try: - import pyscf -except (ImportError, ModuleNotFoundError) as err: - pytest.skip(f"Need pyscf for PBC resource estimates {err}", - allow_module_level=True) diff --git a/src/openfermion/resource_estimates/pbc/hamiltonian/__init__.py b/src/openfermion/resource_estimates/pbc/hamiltonian/__init__.py index 689be1b9f..bb63502e7 100644 --- a/src/openfermion/resource_estimates/pbc/hamiltonian/__init__.py +++ b/src/openfermion/resource_estimates/pbc/hamiltonian/__init__.py @@ -12,11 +12,5 @@ # limitations under the License. import pytest -try: - import pyscf -except (ImportError, ModuleNotFoundError) as err: - pytest.skip(f"Need pyscf for PBC resource estimates {err}", - allow_module_level=True) - from .hamiltonian import (cholesky_from_df_ints, build_hamiltonian, - build_momentum_transfer_mapping) \ No newline at end of file + build_momentum_transfer_mapping) diff --git a/src/openfermion/resource_estimates/pbc/hamiltonian/hamiltonian_test.py b/src/openfermion/resource_estimates/pbc/hamiltonian/hamiltonian_test.py index 57fa0f6be..245ef3dd4 100644 --- a/src/openfermion/resource_estimates/pbc/hamiltonian/hamiltonian_test.py +++ b/src/openfermion/resource_estimates/pbc/hamiltonian/hamiltonian_test.py @@ -11,20 +11,21 @@ # See the License for the specific language governing permissions and # limitations under the License. import itertools -import numpy as np -from pyscf.pbc import gto, scf, mp, cc -from pyscf.lib import chkfile +import numpy as np import pytest +from pyscf.pbc import cc, mp + +from openfermion.resource_estimates import HAVE_DEPS_FOR_RESOURCE_ESTIMATES -from openfermion.resource_estimates.pbc.hamiltonian import ( - build_hamiltonian, - cholesky_from_df_ints, -) -from openfermion.resource_estimates.pbc.testing import ( - make_diamond_113_szv,) +if HAVE_DEPS_FOR_RESOURCE_ESTIMATES: + from openfermion.resource_estimates.pbc.hamiltonian import ( + build_hamiltonian, cholesky_from_df_ints) + from openfermion.resource_estimates.pbc.testing import make_diamond_113_szv +@pytest.mark.skipif(not HAVE_DEPS_FOR_RESOURCE_ESTIMATES, + reason='pyscf and/or jax not installed.') def test_build_hamiltonian(): mf = make_diamond_113_szv() nmo = mf.mo_coeff[0].shape[-1] @@ -36,6 +37,8 @@ def test_build_hamiltonian(): assert chol[0, 0].shape == (naux, nmo, nmo) +@pytest.mark.skipif(not HAVE_DEPS_FOR_RESOURCE_ESTIMATES, + reason='pyscf and/or jax not installed.') def test_pyscf_chol_from_df(): mf = make_diamond_113_szv() mymp = mp.KMP2(mf) diff --git a/src/openfermion/resource_estimates/pbc/testing/__init__.py b/src/openfermion/resource_estimates/pbc/testing/__init__.py index c005c1480..b957ba9a3 100644 --- a/src/openfermion/resource_estimates/pbc/testing/__init__.py +++ b/src/openfermion/resource_estimates/pbc/testing/__init__.py @@ -10,11 +10,4 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -import pytest - -try: - import pyscf -except (ImportError, ModuleNotFoundError) as err: - pytest.skip(f"Need pyscf for PBC resource estimates {err}", - allow_module_level=True) from .test_systems import make_diamond_113_szv diff --git a/src/openfermion/resource_estimates/pbc/thc/__init__.py b/src/openfermion/resource_estimates/pbc/thc/__init__.py index 1f62ad09e..d0d41a5f5 100644 --- a/src/openfermion/resource_estimates/pbc/thc/__init__.py +++ b/src/openfermion/resource_estimates/pbc/thc/__init__.py @@ -10,10 +10,3 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -import pytest - -try: - import pyscf -except (ImportError, ModuleNotFoundError) as err: - pytest.skip(f"Need pyscf for PBC resource estimates {err}", - allow_module_level=True) diff --git a/src/openfermion/resource_estimates/pbc/thc/factorizations/__init__.py b/src/openfermion/resource_estimates/pbc/thc/factorizations/__init__.py index ccef0d8cb..d0d41a5f5 100644 --- a/src/openfermion/resource_estimates/pbc/thc/factorizations/__init__.py +++ b/src/openfermion/resource_estimates/pbc/thc/factorizations/__init__.py @@ -10,11 +10,3 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -import pytest - -try: - import jax - import pyscf -except (ImportError, ModuleNotFoundError) as err: - pytest.skip(f"Need pyscf and jax for PBC resource estimates {err}", - allow_module_level=True) diff --git a/src/openfermion/resource_estimates/pbc/thc/factorizations/gvec_map_logic_test.py b/src/openfermion/resource_estimates/pbc/thc/factorizations/gvec_map_logic_test.py index 29fbb78ac..373fde253 100644 --- a/src/openfermion/resource_estimates/pbc/thc/factorizations/gvec_map_logic_test.py +++ b/src/openfermion/resource_estimates/pbc/thc/factorizations/gvec_map_logic_test.py @@ -11,18 +11,20 @@ # See the License for the specific language governing permissions and # limitations under the License. import itertools + import numpy as np -from openfermion.resource_estimates.\ - pbc.thc.factorizations.gvec_map_logic import ( - get_miller_indices, - get_delta_kp_kq_q, - build_transfer_map, - build_G_vectors, - build_gpq_mapping, - build_conjugate_map, -) +import pytest + +from openfermion.resource_estimates import HAVE_DEPS_FOR_RESOURCE_ESTIMATES + +if HAVE_DEPS_FOR_RESOURCE_ESTIMATES: + from openfermion.resource_estimates.pbc.thc.factorizations.gvec_map_logic import ( + build_conjugate_map, build_G_vectors, build_gpq_mapping, + build_transfer_map, get_delta_kp_kq_q, get_miller_indices) +@pytest.mark.skipif(not HAVE_DEPS_FOR_RESOURCE_ESTIMATES, + reason='pyscf and/or jax not installed.') def test_get_miller_indices(): kmesh = [3, 1, 1] int_scaled_kpts = get_miller_indices(kmesh) @@ -36,6 +38,8 @@ def test_get_miller_indices(): assert np.allclose(int_scaled_kpts[:, 1], [0, 1, 0, 1, 0, 1]) +@pytest.mark.skipif(not HAVE_DEPS_FOR_RESOURCE_ESTIMATES, + reason='pyscf and/or jax not installed.') def test_get_delta_k1_k2_Q(): kmesh = [3, 2, 1] nkpts = np.prod(kmesh) @@ -49,6 +53,8 @@ def test_get_delta_k1_k2_Q(): ) +@pytest.mark.skipif(not HAVE_DEPS_FOR_RESOURCE_ESTIMATES, + reason='pyscf and/or jax not installed.') def test_transfer_map(): kmesh = [3, 1, 1] nkpts = np.prod(kmesh) @@ -115,6 +121,8 @@ def test_transfer_map(): assert np.allclose(transfer_map, true_transfer_map) +@pytest.mark.skipif(not HAVE_DEPS_FOR_RESOURCE_ESTIMATES, + reason='pyscf and/or jax not installed.') def test_build_Gvectors(): kmesh = [3, 2, 1] g_dict = build_G_vectors(kmesh) @@ -125,6 +133,8 @@ def test_build_Gvectors(): indx += 1 +@pytest.mark.skipif(not HAVE_DEPS_FOR_RESOURCE_ESTIMATES, + reason='pyscf and/or jax not installed.') def test_gpq_mapping(): kmesh = [3, 2, 1] nkpts = np.prod(kmesh) @@ -145,6 +155,8 @@ def test_gpq_mapping(): assert g_val[2] in [0, -kmesh[2]] +@pytest.mark.skipif(not HAVE_DEPS_FOR_RESOURCE_ESTIMATES, + reason='pyscf and/or jax not installed.') def test_build_conjugate_map(): kmesh = [4, 3, 3] nkpts = np.prod(kmesh) @@ -159,6 +171,8 @@ def test_build_conjugate_map(): assert gval[2] in [0, kmesh[2]] +@pytest.mark.skipif(not HAVE_DEPS_FOR_RESOURCE_ESTIMATES, + reason='pyscf and/or jax not installed.') def test_compliment_g(): # setup kmesh = [4, 1, 1] diff --git a/src/openfermion/resource_estimates/pbc/thc/factorizations/isdf_test.py b/src/openfermion/resource_estimates/pbc/thc/factorizations/isdf_test.py index 5cac9a241..4b6c0a423 100644 --- a/src/openfermion/resource_estimates/pbc/thc/factorizations/isdf_test.py +++ b/src/openfermion/resource_estimates/pbc/thc/factorizations/isdf_test.py @@ -10,34 +10,33 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -from ase.build import bulk import numpy as np - -from pyscf.pbc import gto, scf -from pyscf.pbc.dft import numint -from pyscf.pbc.tools import pyscf_ase -from pyscf.pbc.lib.kpts_helper import unique, get_kconserv, member - -from openfermion.resource_estimates.pbc.thc.factorizations.kmeans import ( - KMeansCVT) -from openfermion.resource_estimates.pbc.thc.factorizations.isdf import ( - inverse_g_map_double_translation, - build_kpoint_zeta, - get_miller, - build_minus_q_g_mapping, - build_g_vectors, - build_g_vector_mappings_single_translation, - build_g_vector_mappings_double_translation, - build_eri_isdf_double_translation, - build_eri_isdf_single_translation, - solve_kmeans_kpisdf, - solve_qrcp_isdf, - supercell_isdf, -) -from openfermion.resource_estimates.pbc.hamiltonian import ( - build_momentum_transfer_mapping,) - - +import pytest + +from openfermion.resource_estimates import HAVE_DEPS_FOR_RESOURCE_ESTIMATES + +if HAVE_DEPS_FOR_RESOURCE_ESTIMATES: + from ase.build import bulk + from pyscf.pbc import gto, scf + from pyscf.pbc.dft import numint + from pyscf.pbc.lib.kpts_helper import get_kconserv, member, unique + from pyscf.pbc.tools import pyscf_ase + + from openfermion.resource_estimates.pbc.hamiltonian import \ + build_momentum_transfer_mapping + from openfermion.resource_estimates.pbc.thc.factorizations.isdf import ( + build_eri_isdf_double_translation, build_eri_isdf_single_translation, + build_g_vector_mappings_double_translation, + build_g_vector_mappings_single_translation, build_g_vectors, + build_kpoint_zeta, build_minus_q_g_mapping, get_miller, + inverse_g_map_double_translation, solve_kmeans_kpisdf, solve_qrcp_isdf, + supercell_isdf) + from openfermion.resource_estimates.pbc.thc.factorizations.kmeans import \ + KMeansCVT + + +@pytest.mark.skipif(not HAVE_DEPS_FOR_RESOURCE_ESTIMATES, + reason='pyscf and/or jax not installed.') def test_supercell_isdf_gamma(): cell = gto.Cell() cell.atom = """ @@ -111,6 +110,8 @@ def test_supercell_isdf_gamma(): assert np.allclose(eri_thc, eri_ref) +@pytest.mark.skipif(not HAVE_DEPS_FOR_RESOURCE_ESTIMATES, + reason='pyscf and/or jax not installed.') def test_supercell_isdf_complex(): cell = gto.Cell() cell.atom = """ @@ -198,6 +199,8 @@ def test_supercell_isdf_complex(): assert np.allclose(eri_thc, eri_ref) +@pytest.mark.skipif(not HAVE_DEPS_FOR_RESOURCE_ESTIMATES, + reason='pyscf and/or jax not installed.') def test_G_vector_mapping_double_translation(): ase_atom = bulk("AlN", "wurtzite", a=3.11, c=4.98) cell = gto.Cell() @@ -239,6 +242,8 @@ def test_G_vector_mapping_double_translation(): assert ik in inv_G_map[iq, ix_G_qk] +@pytest.mark.skipif(not HAVE_DEPS_FOR_RESOURCE_ESTIMATES, + reason='pyscf and/or jax not installed.') def test_G_vector_mapping_single_translation(): cell = gto.Cell() cell.atom = """ @@ -294,6 +299,8 @@ def test_G_vector_mapping_single_translation(): assert np.allclose(delta_G_expected, delta_G) +@pytest.mark.skipif(not HAVE_DEPS_FOR_RESOURCE_ESTIMATES, + reason='pyscf and/or jax not installed.') def test_kpoint_isdf_double_translation(): cell = gto.Cell() cell.atom = """ @@ -353,6 +360,8 @@ def test_kpoint_isdf_double_translation(): assert np.allclose(eri_pqrs, eri_pqrs_isdf) +@pytest.mark.skipif(not HAVE_DEPS_FOR_RESOURCE_ESTIMATES, + reason='pyscf and/or jax not installed.') def test_kpoint_isdf_single_translation(): cell = gto.Cell() cell.atom = """ @@ -417,12 +426,16 @@ def test_kpoint_isdf_single_translation(): assert np.allclose(eri_pqrs, eri_pqrs_isdf) + def get_complement(miller_indx, kmesh): complement = ~miller_indx complement[np.where(np.array(kmesh) == 1)] = 0 return complement +@pytest.mark.skipif(not HAVE_DEPS_FOR_RESOURCE_ESTIMATES, + reason='pyscf and/or jax not installed.') +@pytest.mark.slow def test_kpoint_isdf_symmetries(): cell = gto.Cell() cell.atom = """ @@ -536,6 +549,8 @@ def test_kpoint_isdf_symmetries(): assert np.allclose(zeta_ref, zeta_test.conj().T) +@pytest.mark.skipif(not HAVE_DEPS_FOR_RESOURCE_ESTIMATES, + reason='pyscf and/or jax not installed.') def test_symmetry_of_G_maps(): cell = gto.Cell() cell.atom = """ @@ -604,6 +619,8 @@ def test_symmetry_of_G_maps(): assert np.allclose(Gpq_comp, Gpq_comp_from_map) +@pytest.mark.skipif(not HAVE_DEPS_FOR_RESOURCE_ESTIMATES, + reason='pyscf and/or jax not installed.') def test_isdf_qrcp(): cell = gto.Cell() cell.atom = """ diff --git a/src/openfermion/resource_estimates/pbc/thc/factorizations/thc_jax_test.py b/src/openfermion/resource_estimates/pbc/thc/factorizations/thc_jax_test.py index b8fa8a0ee..9089afcc4 100644 --- a/src/openfermion/resource_estimates/pbc/thc/factorizations/thc_jax_test.py +++ b/src/openfermion/resource_estimates/pbc/thc/factorizations/thc_jax_test.py @@ -10,36 +10,37 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -import jax -import jax.numpy as jnp import numpy as np import pytest -from pyscf.pbc import gto, mp, scf -from openfermion.resource_estimates.thc.utils.thc_factorization import ( - lbfgsb_opt_thc_l2reg,) -from openfermion.resource_estimates.thc.utils.thc_factorization import ( - thc_objective_regularized as thc_obj_mol,) +from openfermion.resource_estimates import HAVE_DEPS_FOR_RESOURCE_ESTIMATES -from openfermion.resource_estimates.pbc.thc.factorizations.isdf import ( - solve_kmeans_kpisdf,) -from openfermion.resource_estimates.pbc.hamiltonian import ( - cholesky_from_df_ints, build_momentum_transfer_mapping) -from openfermion.resource_estimates.pbc.thc.factorizations.thc_jax import ( - adagrad_opt_kpthc_batched, - get_zeta_size, - kpoint_thc_via_isdf, - lbfgsb_opt_kpthc_l2reg, - lbfgsb_opt_kpthc_l2reg_batched, - make_contiguous_cholesky, - pack_thc_factors, - prepare_batched_data_indx_arrays, - thc_objective_regularized, - thc_objective_regularized_batched, - unpack_thc_factors, -) +if HAVE_DEPS_FOR_RESOURCE_ESTIMATES: + import jax + import jax.numpy as jnp + from pyscf.pbc import gto, mp, scf + from openfermion.resource_estimates.pbc.hamiltonian import ( + build_hamiltonian, build_momentum_transfer_mapping, + cholesky_from_df_ints) + from openfermion.resource_estimates.pbc.testing import make_diamond_113_szv + from openfermion.resource_estimates.pbc.thc.factorizations.isdf import \ + solve_kmeans_kpisdf + from openfermion.resource_estimates.pbc.thc.factorizations.thc_jax import ( + adagrad_opt_kpthc_batched, get_zeta_size, kpoint_thc_via_isdf, + lbfgsb_opt_kpthc_l2reg, lbfgsb_opt_kpthc_l2reg_batched, + make_contiguous_cholesky, pack_thc_factors, + prepare_batched_data_indx_arrays, thc_objective_regularized, + thc_objective_regularized_batched, unpack_thc_factors) + from openfermion.resource_estimates.thc.utils.thc_factorization import \ + lbfgsb_opt_thc_l2reg + from openfermion.resource_estimates.thc.utils.thc_factorization import \ + thc_objective_regularized as thc_obj_mol + +@pytest.mark.skipif(not HAVE_DEPS_FOR_RESOURCE_ESTIMATES, + reason='pyscf and/or jax not installed.') +@pytest.mark.slow def test_kpoint_thc_reg_gamma(): cell = gto.Cell() cell.atom = """ @@ -67,7 +68,7 @@ def test_kpoint_thc_reg_gamma(): kpt_thc = solve_kmeans_kpisdf(mf, num_interp_points, single_translation=False) - chi, zeta, G_mapping = kpt_thc.chi, kpt_thc.zeta, kpt_thc.G_mapping + chi, zeta, g_mapping = kpt_thc.chi, kpt_thc.zeta, kpt_thc.g_mapping momentum_map = build_momentum_transfer_mapping(cell, kpts) buffer = np.zeros(2 * (chi.size + get_zeta_size(zeta)), dtype=np.float64) pack_thc_factors(chi, zeta, buffer) @@ -108,7 +109,7 @@ def test_kpoint_thc_reg_gamma(): chi, zeta, momentum_map, - G_mapping, + g_mapping, jnp.array(Luv_cont), maxiter=10, penalty_param=None, @@ -127,13 +128,16 @@ def test_kpoint_thc_reg_gamma(): num_mo, num_interp_points, momentum_map, - G_mapping, + g_mapping, Luv_cont, 1e-3, ) assert mol_obj - gam_obj < 1e-12 +@pytest.mark.skipif(not HAVE_DEPS_FOR_RESOURCE_ESTIMATES, + reason='pyscf and/or jax not installed.') +@pytest.mark.slow def test_kpoint_thc_reg_batched(): cell = gto.Cell() cell.atom = """ @@ -162,7 +166,7 @@ def test_kpoint_thc_reg_batched(): num_interp_points, single_translation=False, verbose=False) - chi, zeta, G_mapping = kpt_thc.chi, kpt_thc.zeta, kpt_thc.G_mapping + chi, zeta, g_mapping = kpt_thc.chi, kpt_thc.zeta, kpt_thc.g_mapping rsmf = scf.KRHF(mf.cell, mf.kpts).rs_density_fit() rsmf.mo_occ = mf.mo_occ rsmf.mo_coeff = mf.mo_coeff @@ -190,19 +194,19 @@ def test_kpoint_thc_reg_batched(): num_mo, num_interp_points, momentum_map, - G_mapping, + g_mapping, Luv_cont, penalty, ) # # Test gradient is the same - indx_arrays = prepare_batched_data_indx_arrays(momentum_map, G_mapping) + indx_arrays = prepare_batched_data_indx_arrays(momentum_map, g_mapping) batch_size = num_kpts**2 obj_batched = thc_objective_regularized_batched( buffer, num_mo, num_interp_points, momentum_map, - G_mapping, + g_mapping, Luv_cont, indx_arrays, batch_size, @@ -215,7 +219,7 @@ def test_kpoint_thc_reg_batched(): num_mo, num_interp_points, momentum_map, - G_mapping, + g_mapping, Luv_cont, penalty, ) @@ -226,7 +230,7 @@ def test_kpoint_thc_reg_batched(): num_mo, num_interp_points, momentum_map, - G_mapping, + g_mapping, Luv_cont, indx_arrays, batch_size, @@ -237,7 +241,7 @@ def test_kpoint_thc_reg_batched(): chi, zeta, momentum_map, - G_mapping, + g_mapping, jnp.array(Luv_cont), maxiter=2, penalty_param=1e-3, @@ -247,7 +251,7 @@ def test_kpoint_thc_reg_batched(): chi, zeta, momentum_map, - G_mapping, + g_mapping, jnp.array(Luv_cont), maxiter=2, penalty_param=1e-3, @@ -259,7 +263,7 @@ def test_kpoint_thc_reg_batched(): chi, zeta, momentum_map, - G_mapping, + g_mapping, jnp.array(Luv_cont), batch_size=batch_size, maxiter=2, @@ -271,7 +275,7 @@ def test_kpoint_thc_reg_batched(): chi, zeta, momentum_map, - G_mapping, + g_mapping, jnp.array(Luv_cont), maxiter=2, batch_size=1, @@ -282,7 +286,7 @@ def test_kpoint_thc_reg_batched(): chi, zeta, momentum_map, - G_mapping, + g_mapping, jnp.array(Luv_cont), batch_size=batch_size, maxiter=2, @@ -290,6 +294,9 @@ def test_kpoint_thc_reg_batched(): assert np.allclose(ada_param, ada_param_diff_batch) +@pytest.mark.skipif(not HAVE_DEPS_FOR_RESOURCE_ESTIMATES, + reason='pyscf and/or jax not installed.') +@pytest.mark.slow def test_kpoint_thc_helper(): cell = gto.Cell() cell.atom = """ From 6baeaf51d12ff16b6bbf166710121aa165745e0c Mon Sep 17 00:00:00 2001 From: Fionn Malone Date: Mon, 7 Aug 2023 05:19:14 +0000 Subject: [PATCH 17/19] Fix checks. --- .../pbc/thc/factorizations/gvec_map_logic_test.py | 2 +- .../resource_estimates/pbc/thc/factorizations/isdf_test.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/openfermion/resource_estimates/pbc/thc/factorizations/gvec_map_logic_test.py b/src/openfermion/resource_estimates/pbc/thc/factorizations/gvec_map_logic_test.py index 373fde253..6643ad354 100644 --- a/src/openfermion/resource_estimates/pbc/thc/factorizations/gvec_map_logic_test.py +++ b/src/openfermion/resource_estimates/pbc/thc/factorizations/gvec_map_logic_test.py @@ -18,7 +18,7 @@ from openfermion.resource_estimates import HAVE_DEPS_FOR_RESOURCE_ESTIMATES if HAVE_DEPS_FOR_RESOURCE_ESTIMATES: - from openfermion.resource_estimates.pbc.thc.factorizations.gvec_map_logic import ( + from openfermion.resource_estimates.pbc.thc.factorizations.gvec_map_logic import ( # pylint: disable=line-too-long build_conjugate_map, build_G_vectors, build_gpq_mapping, build_transfer_map, get_delta_kp_kq_q, get_miller_indices) diff --git a/src/openfermion/resource_estimates/pbc/thc/factorizations/isdf_test.py b/src/openfermion/resource_estimates/pbc/thc/factorizations/isdf_test.py index 4b6c0a423..3cfd81b08 100644 --- a/src/openfermion/resource_estimates/pbc/thc/factorizations/isdf_test.py +++ b/src/openfermion/resource_estimates/pbc/thc/factorizations/isdf_test.py @@ -426,7 +426,6 @@ def test_kpoint_isdf_single_translation(): assert np.allclose(eri_pqrs, eri_pqrs_isdf) - def get_complement(miller_indx, kmesh): complement = ~miller_indx complement[np.where(np.array(kmesh) == 1)] = 0 From 322bd459c97c263d2af643d21a547d23b77035ff Mon Sep 17 00:00:00 2001 From: Fionn Malone Date: Mon, 7 Aug 2023 16:44:27 +0000 Subject: [PATCH 18/19] Fix test failures. --- dev_tools/requirements/deps/resource_estimates.txt | 1 + .../resource_estimates/pbc/hamiltonian/__init__.py | 9 ++++++--- .../pbc/hamiltonian/hamiltonian_test.py | 3 ++- .../resource_estimates/pbc/testing/__init__.py | 5 ++++- .../pbc/testing/{test_systems.py => systems.py} | 0 5 files changed, 13 insertions(+), 5 deletions(-) rename src/openfermion/resource_estimates/pbc/testing/{test_systems.py => systems.py} (100%) diff --git a/dev_tools/requirements/deps/resource_estimates.txt b/dev_tools/requirements/deps/resource_estimates.txt index 2a580db7c..e48b771e9 100644 --- a/dev_tools/requirements/deps/resource_estimates.txt +++ b/dev_tools/requirements/deps/resource_estimates.txt @@ -1,3 +1,4 @@ pyscf jax jaxlib +ase \ No newline at end of file diff --git a/src/openfermion/resource_estimates/pbc/hamiltonian/__init__.py b/src/openfermion/resource_estimates/pbc/hamiltonian/__init__.py index bb63502e7..f0d52f8e4 100644 --- a/src/openfermion/resource_estimates/pbc/hamiltonian/__init__.py +++ b/src/openfermion/resource_estimates/pbc/hamiltonian/__init__.py @@ -10,7 +10,10 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -import pytest -from .hamiltonian import (cholesky_from_df_ints, build_hamiltonian, - build_momentum_transfer_mapping) +from openfermion.resource_estimates import HAVE_DEPS_FOR_RESOURCE_ESTIMATES + +if HAVE_DEPS_FOR_RESOURCE_ESTIMATES: + from .hamiltonian import (build_hamiltonian, + build_momentum_transfer_mapping, + cholesky_from_df_ints) diff --git a/src/openfermion/resource_estimates/pbc/hamiltonian/hamiltonian_test.py b/src/openfermion/resource_estimates/pbc/hamiltonian/hamiltonian_test.py index 245ef3dd4..3dbc2a096 100644 --- a/src/openfermion/resource_estimates/pbc/hamiltonian/hamiltonian_test.py +++ b/src/openfermion/resource_estimates/pbc/hamiltonian/hamiltonian_test.py @@ -14,11 +14,12 @@ import numpy as np import pytest -from pyscf.pbc import cc, mp from openfermion.resource_estimates import HAVE_DEPS_FOR_RESOURCE_ESTIMATES if HAVE_DEPS_FOR_RESOURCE_ESTIMATES: + from pyscf.pbc import cc, mp + from openfermion.resource_estimates.pbc.hamiltonian import ( build_hamiltonian, cholesky_from_df_ints) from openfermion.resource_estimates.pbc.testing import make_diamond_113_szv diff --git a/src/openfermion/resource_estimates/pbc/testing/__init__.py b/src/openfermion/resource_estimates/pbc/testing/__init__.py index b957ba9a3..87c50f02b 100644 --- a/src/openfermion/resource_estimates/pbc/testing/__init__.py +++ b/src/openfermion/resource_estimates/pbc/testing/__init__.py @@ -10,4 +10,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -from .test_systems import make_diamond_113_szv +from openfermion.resource_estimates import HAVE_DEPS_FOR_RESOURCE_ESTIMATES + +if HAVE_DEPS_FOR_RESOURCE_ESTIMATES: + from .systems import make_diamond_113_szv diff --git a/src/openfermion/resource_estimates/pbc/testing/test_systems.py b/src/openfermion/resource_estimates/pbc/testing/systems.py similarity index 100% rename from src/openfermion/resource_estimates/pbc/testing/test_systems.py rename to src/openfermion/resource_estimates/pbc/testing/systems.py From d04fc376def72b455723bcef1a085e9c7cc01d6d Mon Sep 17 00:00:00 2001 From: Fionn Malone Date: Mon, 7 Aug 2023 17:00:51 +0000 Subject: [PATCH 19/19] Add ase to resources requirements. --- dev_tools/requirements/resource_estimates.env.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/dev_tools/requirements/resource_estimates.env.txt b/dev_tools/requirements/resource_estimates.env.txt index 68b7b494d..692199097 100644 --- a/dev_tools/requirements/resource_estimates.env.txt +++ b/dev_tools/requirements/resource_estimates.env.txt @@ -4,6 +4,8 @@ # # pip-compile --output-file=resource_estimates.env.txt deps/resource_estimates.txt pytest.env.txt # +ase==3.22.1 + # via -r deps/resource_estimates.txt attrs==23.1.0 # via # -r pytest.env.txt @@ -83,6 +85,7 @@ kiwisolver==1.4.4 matplotlib==3.7.1 # via # -r pytest.env.txt + # ase # cirq-core ml-dtypes==0.2.0 # via @@ -101,6 +104,7 @@ networkx==2.8.8 numpy==1.23.5 # via # -r pytest.env.txt + # ase # cirq-core # contourpy # h5py @@ -174,6 +178,7 @@ requests==2.31.0 scipy==1.9.3 # via # -r pytest.env.txt + # ase # cirq-core # jax # jaxlib