def test_adc1_linear_solve(self): conv_tol = 1e-9 matrix = adcc.AdcMatrix("adc1", cache.refstate["h2o_sto3g"]) rhs = adcc.guess_zero(matrix) rhs["s"].set_random() guess = rhs.copy() guess["s"].set_random() res = conjugate_gradient(matrix, rhs, guess, callback=cgprint, conv_tol=conv_tol) residual = matrix @ res.solution - rhs assert np.sqrt(residual @ residual) < conv_tol
def test_functionality(self): ground_state = adcc.LazyMp(cache.refstate["h2o_sto3g"]) matrix = adcc.AdcMatrix("adc2", ground_state) vectors = [adcc.guess_zero(matrix) for i in range(2)] for vec in vectors: vec.set_random() v, w = vectors with pytest.raises(AttributeError): v.pph with pytest.raises(AttributeError): v.pph = w.ph # setattr with expression z = adcc.zeros_like(v) z.ph = v.ph + w.ph z -= w np.testing.assert_allclose(v.ph.to_ndarray(), z.ph.to_ndarray())
def template_matmul(self, case): shift = -0.3 matrix, shifted = self.construct_matrices(case, shift) vec = adcc.guess_zero(matrix) vec.set_random() ores = matrix @ vec sres = shifted @ vec assert ores.ph.describe_symmetry() == sres.ph.describe_symmetry() assert ores.pphh.describe_symmetry() == sres.pphh.describe_symmetry() diff_s = sres.ph - ores.ph - shift * vec.ph diff_d = sres.pphh - ores.pphh - shift * vec.pphh assert np.max(np.abs(diff_s.to_ndarray())) < 1e-12 assert np.max(np.abs(diff_d.to_ndarray())) < 1e-12
def template_singles_view(self, method): if "cvs" in method: reference_state = cache.refstate_cvs["h2o_sto3g"] shape = (8, 8) spaces_s = ["o2", "v1"] else: reference_state = cache.refstate["h2o_sto3g"] shape = (40, 40) spaces_s = ["o1", "v1"] matrix = adcc.AdcMatrix(method, adcc.LazyMp(reference_state)) view = adcc.AdcBlockView(matrix, "s") assert view.ndim == 2 assert view.is_core_valence_separated == ("cvs" in method) assert view.shape == shape assert len(view) == shape[0] assert view.blocks == ["s"] assert view.has_block("s") assert not view.has_block("d") assert not view.has_block("t") assert view.block_spaces("s") == spaces_s assert view.reference_state == reference_state assert view.mospaces == reference_state.mospaces assert isinstance(view.timer, adcc.timings.Timer) assert view.to_cpp() == matrix.to_cpp() # Check diagonal diff = matrix.diagonal("s") - view.diagonal("s") assert diff.dot(diff) < 1e-12 # Check @ (one vector and multiple vectors) invec = adcc.guess_zero(matrix) invec["s"].set_random() # "d" left as zero invec_singles = adcc.AmplitudeVector(invec["s"]) ref = matrix @ invec res = view @ invec_singles diff = res["s"] - ref["s"] assert diff.dot(diff) < 1e-12 res = view @ [invec_singles, invec_singles, invec_singles] diff = [res[i]["s"] - ref["s"] for i in range(3)] assert max(d.dot(d) for d in diff) < 1e-12
def template_singles_view(self, method): if "cvs" in method: reference_state = cache.refstate_cvs["h2o_sto3g"] shape = (8, 8) spaces_ph = ["o2", "v1"] else: reference_state = cache.refstate["h2o_sto3g"] shape = (40, 40) spaces_ph = ["o1", "v1"] matrix = adcc.AdcMatrix(method, adcc.LazyMp(reference_state)) view = matrix.block_view("ph_ph") assert view.ndim == 2 assert view.is_core_valence_separated == ("cvs" in method) assert view.shape == shape assert len(view) == shape[0] assert view.axis_blocks == ["ph"] assert sorted(view.axis_spaces.keys()) == view.axis_blocks assert sorted(view.axis_lengths.keys()) == view.axis_blocks assert view.axis_spaces["ph"] == spaces_ph assert view.axis_lengths["ph"] == shape[0] assert view.reference_state == reference_state assert view.mospaces == reference_state.mospaces assert isinstance(view.timer, adcc.timings.Timer) # Check diagonal diff = matrix.diagonal().ph - view.diagonal().ph assert diff.dot(diff) < 1e-12 # Check @ (one vector and multiple vectors) invec = adcc.guess_zero(matrix) invec.ph.set_random() # "d" left as zero invec_singles = adcc.AmplitudeVector(ph=invec.ph) ref = matrix @ invec res = view @ invec_singles diff = res.ph - ref.ph assert diff.dot(diff) < 1e-12 res = view @ [invec_singles, invec_singles, invec_singles] diff = [res[i].ph - ref.ph for i in range(3)] assert max(d.dot(d) for d in diff) < 1e-12
def test_matvec_adc2(self): ground_state = adcc.LazyMp(cache.refstate["h2o_sto3g"]) matrix = adcc.AdcMatrix("adc2", ground_state) vectors = [adcc.guess_zero(matrix) for i in range(3)] for vec in vectors: vec.set_random() v, w, x = vectors # Compute references: refv = matrix.matvec(v) refw = matrix.matvec(w) refx = matrix.matvec(x) # @ operator (1 vector) resv = matrix @ v diffv = refv - resv assert diffv.ph.dot(diffv.ph) < 1e-12 assert diffv.pphh.dot(diffv.pphh) < 1e-12 # @ operator (multiple vectors) resv, resw, resx = matrix @ [v, w, x] diffs = [refv - resv, refw - resw, refx - resx] for i in range(3): assert diffs[i].ph.dot(diffs[i].ph) < 1e-12 assert diffs[i].pphh.dot(diffs[i].pphh) < 1e-12 # compute matvec resv = matrix.matvec(v) diffv = refv - resv assert diffv.ph.dot(diffv.ph) < 1e-12 assert diffv.pphh.dot(diffv.pphh) < 1e-12 resv = matrix.rmatvec(v) diffv = refv - resv assert diffv.ph.dot(diffv.ph) < 1e-12 assert diffv.pphh.dot(diffv.pphh) < 1e-12 # Test apply resv.ph = matrix.block_apply("ph_ph", v.ph) resv.ph += matrix.block_apply("ph_pphh", v.pphh) refv = matrix.matvec(v) diffv = resv.ph - refv.ph assert diffv.dot(diffv) < 1e-12
def test_matvec_adc2(self): ground_state = adcc.LazyMp(cache.refstate["h2o_sto3g"]) matrix = adcc.AdcMatrix("adc2", ground_state) cppmatrix = libadcc.AdcMatrix("adc2", ground_state) vectors = [adcc.guess_zero(matrix) for i in range(3)] for vec in vectors: vec["s"].set_random() vec["d"].set_random() v, w, x = vectors # Compute references: refv = adcc.empty_like(v) refw = adcc.empty_like(w) refx = adcc.empty_like(x) cppmatrix.compute_matvec(v.to_cpp(), refv.to_cpp()) cppmatrix.compute_matvec(w.to_cpp(), refw.to_cpp()) cppmatrix.compute_matvec(x.to_cpp(), refx.to_cpp()) # @ operator (1 vector) resv = matrix @ v diffv = refv - resv assert diffv["s"].dot(diffv["s"]) < 1e-12 assert diffv["d"].dot(diffv["d"]) < 1e-12 # @ operator (multiple vectors) resv, resw, resx = matrix @ [v, w, x] diffs = [refv - resv, refw - resw, refx - resx] for i in range(3): assert diffs[i]["s"].dot(diffs[i]["s"]) < 1e-12 assert diffs[i]["d"].dot(diffs[i]["d"]) < 1e-12 # compute matvec matrix.compute_matvec(v, resv) diffv = refv - resv assert diffv["s"].dot(diffv["s"]) < 1e-12 assert diffv["d"].dot(diffv["d"]) < 1e-12 matrix.compute_apply("ss", v["s"], resv["s"]) cppmatrix.compute_apply("ss", v["s"], refv["s"]) diffv = resv["s"] - refv["s"] assert diffv.dot(diffv) < 1e-12
def make_mock_adc_state(refstate, matmethod, kind, reference): ground_state = LazyMp(refstate, CacheAllPolicy()) matrix = AdcMatrix(matmethod, ground_state) # Number of full state results n_full = len(reference[kind]["eigenvectors_singles"]) state = AdcMockState(matrix) state.method = matrix.method state.ground_state = ground_state state.reference_state = refstate state.kind = kind state.eigenvalues = reference[kind]["eigenvalues"][:n_full] spin_change = 0 if refstate.restricted and kind == "singlet": symm = "symmetric" elif refstate.restricted and kind == "triplet": symm = "antisymmetric" elif kind in ["state", "spin_flip"]: symm = "none" else: raise ValueError("Unknown kind: {}".format(kind)) state.eigenvectors = [ guess_zero(matrix, irrep="A", spin_change=spin_change, spin_block_symmetrisation=symm) for i in range(n_full) ] has_doubles = "eigenvectors_doubles" in reference[kind] vec_singles = reference[kind]["eigenvectors_singles"] vec_doubles = reference[kind].get("eigenvectors_doubles", None) for i, evec in enumerate(state.eigenvectors): evec["s"].set_from_ndarray(vec_singles[i]) if has_doubles: evec["d"].set_from_ndarray(vec_doubles[i], 1e-14) return ExcitedStates(state)
def to_ndarray(self, out=None): """ Return the ADC matrix object as a dense numpy array. Converts the sparse internal representation of the ADC matrix to a dense matrix and return as a numpy array. Notes ----- This method is only intended to be used for debugging and visualisation purposes as it involves computing a large amount of matrix-vector products and the returned array consumes a considerable amount of memory. The resulting matrix has no spin symmetry imposed, which means that its eigenspectrum may contain non-physical excitations (e.g. with linear combinations of α->β and α->α components in the excitation vector). This function has not been sufficiently tested to be considered stable. """ # TODO Update to ph / pphh # TODO Still uses deprecated functions import tqdm from adcc import guess_zero # Get zero amplitude of the appropriate symmetry # (TODO: Only true for C1, where there is only a single irrep) ampl_zero = guess_zero(self) assert self.mospaces.point_group == "C1" # Build the shape of the returned array # Since the basis of the doubles block is not the unit vectors # this *not* equal to the shape of the AdcMatrix object basis = {b: self.dense_basis(b) for b in self.axis_blocks} mat_len = sum(len(basis[b]) for b in basis) if out is None: out = np.zeros((mat_len, mat_len)) else: if out.shape != (mat_len, mat_len): raise ValueError("Output array has shape ({0:}, {1:}), but " "shape ({2:}, {2:}) is required." "".format(*out.shape, mat_len)) out[:] = 0 # Zero all data in out. # Check for the cases actually implemented if any(b not in ("ph", "pphh") for b in self.axis_blocks): raise NotImplementedError("Blocks other than ph and pphh " "not implemented") if "ph" not in self.axis_blocks: raise NotImplementedError("Block 'ph' needs to be present") # Extract singles-singles block (contiguous) assert "ph" in self.axis_blocks n_orbs_ph = [self.mospaces.n_orbs(sp) for sp in self.axis_spaces["ph"]] n_ph = np.prod(n_orbs_ph) assert len(basis["ph"]) == n_ph view_ss = out[:n_ph, :n_ph].reshape(*n_orbs_ph, *n_orbs_ph) for i in range(n_orbs_ph[0]): for a in range(n_orbs_ph[1]): ampl = ampl_zero.copy() ampl.ph[i, a] = 1 view_ss[:, :, i, a] = (self @ ampl).ph.to_ndarray() # Extract singles-doubles and doubles-doubles block if "pphh" in self.axis_blocks: assert self.axis_blocks == ["ph", "pphh"] view_sd = out[:n_ph, n_ph:].reshape(*n_orbs_ph, len(basis["pphh"])) view_dd = out[n_ph:, n_ph:] for j, bas1 in tqdm.tqdm(enumerate(basis["pphh"]), total=len(basis["pphh"])): ampl = ampl_zero.copy() for idx, val in bas1: ampl.pphh[idx] = val ret_ampl = self @ ampl view_sd[:, :, j] = ret_ampl.ph.to_ndarray() for i, bas2 in enumerate(basis["pphh"]): view_dd[i, j] = sum(val * ret_ampl.pphh[idx] for idx, val in bas2) out[n_ph:, :n_ph] = np.transpose(out[:n_ph, n_ph:]) return out
def guess_random(matrix, n_guesses): guess = adcc.guess_zero(matrix, spin_block_symmetrisation="antisymmetric") guess["s"].set_random() guess["d"].set_random() return [guess]
def test_extra_term(self): ground_state = adcc.LazyMp(cache.refstate["h2o_sto3g"]) matrix_adc1 = adcc.AdcMatrix("adc1", ground_state) with pytest.raises(TypeError): matrix_adc1 += 42 matrix = adcc.AdcMatrix("adc2", ground_state) with pytest.raises(TypeError): adcc.AdcMatrix("adc2", ground_state, diagonal_precomputed=42) with pytest.raises(ValueError): adcc.AdcMatrix("adc2", ground_state, diagonal_precomputed=matrix.diagonal() + 42) with pytest.raises(TypeError): AdcExtraTerm(matrix, "fail") with pytest.raises(TypeError): AdcExtraTerm(matrix, {"fail": "not_callable"}) shift = -0.3 shifted = AdcMatrixShifted(matrix, shift) # TODO: need to use AmplitudeVector to differentiate between # diagonals for ph and pphh # if we just pass numbers, i.e., shift # we get 2*shift on the diagonal ones = matrix.diagonal().ones_like() def __shift_ph(hf, mp, intermediates): def apply(invec): return adcc.AmplitudeVector(ph=shift * invec.ph) diag = adcc.AmplitudeVector(ph=shift * ones.ph) return AdcBlock(apply, diag) def __shift_pphh(hf, mp, intermediates): def apply(invec): return adcc.AmplitudeVector(pphh=shift * invec.pphh) diag = adcc.AmplitudeVector(pphh=shift * ones.pphh) return AdcBlock(apply, diag) extra = AdcExtraTerm(matrix, { 'ph_ph': __shift_ph, 'pphh_pphh': __shift_pphh }) # cannot add to 'pphh_pphh' in ADC(1) matrix with pytest.raises(ValueError): matrix_adc1 += extra shifted_2 = matrix + extra shifted_3 = extra + matrix for manual in [shifted_2, shifted_3]: assert_allclose(shifted.diagonal().ph.to_ndarray(), manual.diagonal().ph.to_ndarray(), atol=1e-12) assert_allclose(shifted.diagonal().pphh.to_ndarray(), manual.diagonal().pphh.to_ndarray(), atol=1e-12) vec = adcc.guess_zero(matrix) vec.set_random() ref = shifted @ vec ret = manual @ vec diff_s = ref.ph - ret.ph diff_d = ref.pphh - ret.pphh assert np.max(np.abs(diff_s.to_ndarray())) < 1e-12 assert np.max(np.abs(diff_d.to_ndarray())) < 1e-12