Skip to content

Commit

Permalink
use CompositeFactory with vectors of functions
Browse files Browse the repository at this point in the history
  • Loading branch information
fbischoff committed Dec 20, 2023
1 parent a634f04 commit 299cd3d
Show file tree
Hide file tree
Showing 6 changed files with 209 additions and 92 deletions.
2 changes: 1 addition & 1 deletion src/madness/chem/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ if(BUILD_TESTING)
SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
# The list of unit test source files
set(CHEM_TEST_SOURCES_SHORT test_pointgroupsymmetry.cc test_masks_and_boxes.cc
test_qc.cc test_MolecularOrbitals.cc test_BSHApply.cc)
test_qc.cc test_MolecularOrbitals.cc test_BSHApply.cc test_projector.cc)
set(CHEM_TEST_SOURCES_LONG test_localizer.cc test_ccpairfunction.cc)
if (LIBXC_FOUND)
list(APPEND CHEM_TEST_SOURCES_SHORT test_dft.cc )
Expand Down
114 changes: 49 additions & 65 deletions src/madness/chem/projector.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,18 +108,21 @@ namespace madness {
/// @param[in] f the 6D function to be projected
/// @param[in] the particle that is projected (1 or 2)
/// @return the projected function
real_function_6d operator()(const real_function_6d& f, const size_t particle) const {
real_function_6d result = real_factory_6d(f.world());
MADNESS_ASSERT(particle == 1 or particle == 2);
template<std::size_t KDIM>
typename std::enable_if<KDIM==2*NDIM, Function<T,KDIM> >::type
operator()(const Function<T,KDIM>& f, size_t particle1=-1) const {
Function<T,KDIM> result = FunctionFactory<T,KDIM>(f.world());
if (particle1==-1) particle1=particle;
MADNESS_ASSERT(particle1 == 1 or particle1 == 2);
for (size_t i = 0; i < mo_ket_.size(); i++) {
real_function_3d tmp1 = mo_ket_[i];
real_function_3d tmp2 = f.project_out(mo_bra_[i], particle - 1);
real_function_6d tmp12;
if (particle == 1) {
tmp12 = CompositeFactory<double, 6, 3>(f.world()).particle1(copy(tmp1)).particle2(copy(tmp2));
Function<T,NDIM> tmp1 = mo_ket_[i];
Function<T,NDIM> tmp2 = f.project_out(mo_bra_[i], particle - 1);
Function<T,KDIM> tmp12;
if (particle1 == 1) {
tmp12 = CompositeFactory<T, KDIM, NDIM>(f.world()).particle1(copy(tmp1)).particle2(copy(tmp2));
tmp12.fill_tree();
} else {
tmp12 = CompositeFactory<double, 6, 3>(f.world()).particle1(copy(tmp2)).particle2(copy(tmp1));
tmp12 = CompositeFactory<T, KDIM, NDIM>(f.world()).particle1(copy(tmp2)).particle2(copy(tmp1));
tmp12.fill_tree();
}
result += tmp12;
Expand All @@ -128,7 +131,8 @@ namespace madness {
}

template<typename argT>
argT operator()(const argT& argument) const {
typename std::enable_if<!std::is_same<argT,Function<T,2*NDIM> >::value, argT>::type
operator()(const argT& argument) const {
return madness::apply(*this,argument);
}

Expand Down Expand Up @@ -275,78 +279,58 @@ namespace madness {
// imprecise for lower accuracies
// return (f-O1(f)-O2(f)+O1(O2(f))).truncate().reduce_rank();

const double thresh=FunctionDefaults<2*NDIM>::get_thresh();
const double tight_thresh=FunctionDefaults<2*NDIM>::get_thresh()*0.1;

// Eq. (A9): g_kl = < k(1) l(2) | f(1,2) >
// note no (kl) symmetry here!
reconstruct(world,bra1_,false);
reconstruct(world,bra2_,true);
Tensor<double> g_kl(bra1_.size(),bra2_.size());
for (size_t k=0; k<bra1_.size(); ++k) {
for (size_t l=0; l<bra2_.size(); ++l) {
Function<T,2*NDIM> kl=CompositeFactory<T,2*NDIM,NDIM>(world)
.particle1(copy(bra1_[k])).particle2(copy(bra2_[l]));
.particle1(bra1_[k]).particle2(bra2_[l]);
g_kl(k,l)=inner(f,kl);
}
}
// if (world.rank()==0) {print(g_kl);};

// Eq. (A12)
// project out the mainly first particle: O1 (1 - 1/2 O2)
Function<T,2*NDIM> r1=FunctionFactory<T,2*NDIM>(world).thresh(tight_thresh);
std::vector<Function<T,NDIM>> h2(bra1_.size());
std::vector<Function<T,NDIM>> h1(ket2_.size());
reconstruct(world,bra1_,false);
reconstruct(world,bra2_,true);
for (size_t k=0; k<bra1_.size(); ++k) {

// project out the mainly first particle: O1 (1 - 1/2 O2): first term
// Eq. (A10)
Function<T,NDIM> h2=f.project_out(bra1_[k],0);

// Eq. (A12)
for (size_t l=0; l<ket2_.size(); ++l) {
h2-=0.5*g_kl(k,l)*ket2_[l];
}

// Eq. (A7), second term rhs
// the hartree product tends to be inaccurate; tighten threshold
FunctionDefaults<2*NDIM>::set_thresh(tight_thresh);
r1=(r1+hartree_product(ket1_[k],h2));
FunctionDefaults<2*NDIM>::set_thresh(thresh);
r1.set_thresh(thresh);
r1.print_size("r1"+stringify(k));
}
h2[k]=f.project_out(bra1_[k],0);

// project out the mainly second particle: O2 (1 - 1/2 O1): first term
// Eq. (A11)
std::size_t l=k;
h1[l]=f.project_out(bra2_[l],1);

// project out the mainly second particle: O2 (1 - 1/2 O1)
Function<T,2*NDIM> r2=FunctionFactory<T,2*NDIM>(world).thresh(tight_thresh);
for (size_t l=0; l<ket2_.size(); ++l) {

// Eq. (A11)
Function<T,NDIM> h1=f.project_out(bra2_[l],1);

// Eq. (A13)
for (size_t k=0; k<ket1_.size(); ++k) {
h1-=0.5*g_kl(k,l)*ket1_[k]; // ordering g(k,l) is correct
}

// Eq. (A7), third term rhs
// the hartree product tends to be inaccurate; tighten threshold
FunctionDefaults<2*NDIM>::set_thresh(tight_thresh);
r2=(r2+hartree_product(h1,ket2_[l]));
r2.set_thresh(thresh);
FunctionDefaults<2*NDIM>::set_thresh(thresh);
r2.print_size("r2"+stringify(l));
}
FunctionDefaults<2*NDIM>::set_thresh(tight_thresh);
Function<T,2*NDIM> result=(f-r1-r2).truncate().reduce_rank();
FunctionDefaults<2*NDIM>::set_thresh(thresh);

// // for debugging purposes only: check orthogonality
// for (size_t k=0; k<hf->nocc(); ++k) {
// for (size_t l=0; l<hf->nocc(); ++l) {
// real_function_6d kl=CompositeFactory<double,6,3>(world)
// .particle1(copy(O1_mos[k])).particle2(copy(O2_mos[l]));
// g_kl(k,l)=inner(result,kl);
// }
// }
// if (world.rank()==0) {print(g_kl);};

return result;

// Transforms a vector of functions according to new[i] = sum[j] old[j]*c[j,i]
// project out the mainly first particle: O1 (1 - 1/2 O2): second term
// Eq. (A12), (A13)
h2-=transform(world,ket2_,0.5*transpose(g_kl),false); // ordering g(k,l) is correct
h1-=transform(world,ket1_,0.5*g_kl,true); // ordering g(k,l) is correct
// aka
// for (size_t l=0; l<ket2_.size(); ++l) {
// h2[k]-=0.5*g_kl(k,l)*ket2_[l];
// }

change_tree_state(h1,reconstructed,false);
change_tree_state(h2,reconstructed,false);
change_tree_state(ket1_,reconstructed,false);
change_tree_state(ket2_,reconstructed,false);
world.gop.fence();
Function<T,2*NDIM> result=copy(f);
result-=hartree_product(ket1_,h2);
result-=hartree_product(h1,ket2_);
result.truncate();
return result;
}

private:
Expand Down
131 changes: 131 additions & 0 deletions src/madness/chem/test_projector.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
//
// Created by Florian Bischoff on 12/19/23.
//

#include <madness/mra/mra.h>
#include <madness/chem/projector.h>
#include <madness/world/test_utilities.h>


using namespace madness;

template<typename T, std::size_t NDIM>
int test_Q12_projector(World& world) {
test_output t1("testing Q12 projector for dimension "+std::to_string(NDIM));
t1.set_cout_to_terminal();
constexpr std::size_t LDIM=NDIM/2;
static_assert(NDIM==LDIM*2);
double thresh=FunctionDefaults<NDIM>::get_thresh();
FunctionDefaults<NDIM>::set_tensor_type(TT_2D);
FunctionDefaults<LDIM>::set_tensor_type(TT_FULL);

auto g1=[](const Vector<double,LDIM>& r){return exp(-inner(r,r));};
auto g2=[](const Vector<double,LDIM>& r){return exp(-2.0*inner(r,r));};
auto g_hidim=[](const Vector<double,NDIM>& r){return exp(-3.0*inner(r,r));};

// set up an orthonormal basis for projection
std::vector<Function<T,LDIM>> vphi;
vphi.push_back(FunctionFactory<T,LDIM>(world).functor(g1));
vphi.push_back(FunctionFactory<T,LDIM>(world).functor(g2));
vphi=orthonormalize_symmetric(vphi);
// auto S=matrix_inner(world,vphi,vphi);
// print("overlap");
// print(S);

StrongOrthogonalityProjector<T,LDIM> SO(world);
SO.set_spaces(vphi);

Function<T,NDIM> f=FunctionFactory<T,NDIM>(world).functor(g_hidim);
double fnorm=f.norm2();
print("fnorm",fnorm);

// check that the projector is indeed a projector
Function<T,NDIM> f1=SO(f);
double sonorm=f1.norm2();
print("Q12(f) norm",sonorm);
double refnorm=0.0028346312885398958; // according to mathematica
print("err0",fabs(sonorm-refnorm));
t1.checkpoint(fabs(sonorm-refnorm)<thresh,"SO projector is correct");

Function<T,NDIM> f2=SO(f1);
double err=(f1-f2).norm2()/f.norm2();
print("err",err);
t1.checkpoint(fabs(err)<thresh,"SO projector is a projector");


// check projector being consistent
// SO(f) = f - O1(f) - O2(f) + O1O2(f)
Projector<T,LDIM> O1(vphi);
Projector<T,LDIM> O2(vphi);
O1.set_particle(1);
O2.set_particle(2);
Function<T,NDIM> f3=f-O1(f)-O2(f)+O1(O2(f));
double err1=(f1-f3).norm2()/f.norm2();
print("err1",err1);
t1.checkpoint(fabs(err1)<thresh*3.0,"SO projector is consistent with 1-O1-O2+O1O2");


return t1.end();
}

int main(int argc, char**argv) {

World& world=initialize(argc,argv);
commandlineparser parser(argc,argv);

// the parameters
double L=40;
long k=5;
double thresh=1.e-4;

if (parser.key_exists("k")) k=atoi(parser.value("k").c_str());
if (parser.key_exists("thresh")) k=atof(parser.value("thresh").c_str());
print("k, thresh", k, thresh);

srand(time(nullptr));
startup(world,argc,argv);

FunctionDefaults<1>::set_thresh(thresh);
FunctionDefaults<2>::set_thresh(thresh);
FunctionDefaults<3>::set_thresh(thresh);
FunctionDefaults<4>::set_thresh(thresh);
FunctionDefaults<5>::set_thresh(thresh);
FunctionDefaults<6>::set_thresh(thresh);

FunctionDefaults<1>::set_cubic_cell(-L/2,L/2);
FunctionDefaults<2>::set_cubic_cell(-L/2,L/2);
FunctionDefaults<3>::set_cubic_cell(-L/2,L/2);
FunctionDefaults<4>::set_cubic_cell(-L/2,L/2);
FunctionDefaults<5>::set_cubic_cell(-L/2,L/2);
FunctionDefaults<6>::set_cubic_cell(-L/2,L/2);

FunctionDefaults<1>::set_k(k);
FunctionDefaults<2>::set_k(k);
FunctionDefaults<3>::set_k(k);
FunctionDefaults<4>::set_k(k);
FunctionDefaults<5>::set_k(k);
FunctionDefaults<6>::set_k(k);

print("entering testsuite for 6-dimensional functions\n");
print("k ",k);
print("thresh ",thresh);
print("boxsize ",L);
print("tensor type: ", FunctionDefaults<6>::get_tensor_type());
print("");


int error=0;
{
error+=test_Q12_projector<double,2>(world);
error+=test_Q12_projector<double,4>(world);
error+=test_Q12_projector<double,6>(world);

}


world.gop.fence();
finalize();

return error;
}

10 changes: 6 additions & 4 deletions src/madness/mra/funcimpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -720,7 +720,8 @@ namespace madness {
accumulate_op(FunctionImpl<T,NDIM>* f) : impl(f) {}
accumulate_op(const accumulate_op& other) = default;
void operator()(const Key<NDIM>& key, const coeffT& coeff, const bool& is_leaf) const {
impl->get_coeffs().task(key, &nodeT::accumulate, coeff, impl->get_coeffs(), key, impl->get_tensor_args());
if (coeff.has_data())
impl->get_coeffs().task(key, &nodeT::accumulate, coeff, impl->get_coeffs(), key, impl->get_tensor_args());
}
template <typename Archive> void serialize (Archive& ar) {
ar & impl;
Expand Down Expand Up @@ -5003,7 +5004,7 @@ template<size_t NDIM>
/// forces fence
double finalize_apply();

/// after summing upwe need to do some cleanup;
/// after summing up we need to do some cleanup;

/// forces fence
void finalize_sum();
Expand Down Expand Up @@ -6484,11 +6485,12 @@ template<size_t NDIM>
std::vector<Slice> other_s(fcoeff.get_svdtensor().dim_per_vector(otherdim)+1,_);

// do the actual contraction
std::vector<long> shape(LDIM,k);
for (int r=0; r<fcoeff.rank(); ++r) {
s[0]=Slice(r,r);
other_s[0]=Slice(r,r);
const tensorT contracted_tensor=fcoeff.get_svdtensor().ref_vector(dim)(s).reshape(k,k,k);
const tensorT other_tensor=fcoeff.get_svdtensor().ref_vector(otherdim)(other_s).reshape(k,k,k);
const tensorT contracted_tensor=fcoeff.get_svdtensor().ref_vector(dim)(s).reshape(shape);
const tensorT other_tensor=fcoeff.get_svdtensor().ref_vector(otherdim)(other_s).reshape(shape);
const double ovlp= gtensor.trace_conj(contracted_tensor);
const double fac=ovlp * fcoeff.get_svdtensor().weights(r);
final+=fac*other_tensor;
Expand Down
Loading

0 comments on commit 299cd3d

Please sign in to comment.