Skip to content

Commit

Permalink
Merge pull request #66 from ValeevGroup/conner_tensorwick
Browse files Browse the repository at this point in the history
Conner tensorwick
  • Loading branch information
evaleev authored Oct 12, 2022
2 parents b10867c + a0630dc commit efa695d
Show file tree
Hide file tree
Showing 87 changed files with 7,221 additions and 3,864 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/cmake.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ jobs:
-DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
-DBUILD_SHARED_LIBS=OFF
-DMPIEXEC_PREFLAGS='--bind-to;none;--allow-run-as-root'
-DCMAKE_PREFIX_PATH='/usr/local/Cellar/boost@1.76/1.76.0_1'
-DCMAKE_PREFIX_PATH='/usr/local/opt/boost@1.76'
-DSEQUANT_EVAL_TESTS=ON
steps:
- uses: actions/checkout@v2
Expand Down
75 changes: 47 additions & 28 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -206,8 +206,8 @@ add_library(SeQuant
SeQuant/core/bliss.hpp
SeQuant/core/timer.hpp
SeQuant/core/binary_node.hpp
SeQuant/core/clone.hpp
SeQuant/core/clone.cpp
SeQuant/core/clone_packed.hpp
SeQuant/core/clone_packed.cpp
SeQuant/core/eval_seq.hpp
SeQuant/core/eval_expr.hpp
SeQuant/core/eval_expr.cpp
Expand Down Expand Up @@ -241,10 +241,9 @@ add_library(SeQuant
SeQuant/domain/mbpt/spin.cpp
SeQuant/domain/mbpt/sr/sr.cpp
SeQuant/domain/mbpt/sr/sr.hpp
SeQuant/domain/transcorrelated/antisymmetrizer.hpp
SeQuant/domain/transcorrelated/simplifications.h
SeQuant/domain/transcorrelated/three_body_decomp.hpp
SeQuant/domain/eqs/single_ref_uccf12.h)
SeQuant/domain/mbpt/antisymmetrizer.hpp
SeQuant/domain/mbpt/rdm.hpp
)

target_link_libraries(SeQuant PUBLIC range-v3::range-v3 Boost::regex Boost::boost SeQuant-bliss Threads::Threads)
if (SEQUANT_HAS_EXECUTION_HEADER_STANDALONE OR SEQUANT_HAS_EXECUTION_HEADER_WITH_TBB)
Expand Down Expand Up @@ -301,7 +300,9 @@ if (BUILD_TESTING)
tests/unit/test_optimize.cpp
tests/unit/test_token_sequant.cpp
tests/unit/test_rpn.cpp
tests/unit/test_clone.cpp
tests/unit/test_clone_packed.cpp
tests/unit/test_cache_manager.cpp
tests/unit/test_runtime.cpp
)

if (TARGET tiledarray)
Expand All @@ -313,7 +314,7 @@ if (BUILD_TESTING)

set(utests_deps SeQuant)

set(unit_test_executable unit_tests)
set(unit_test_executable unit_tests-sequant)
add_executable(${unit_test_executable} EXCLUDE_FROM_ALL
tests/unit/test_main.cpp
tests/unit/catch.hpp
Expand All @@ -333,19 +334,38 @@ if (BUILD_TESTING)
PROPERTIES DEPENDS sequant/unit/build
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/tests/unit)

####### Tests ########
####### Integration tests aka "examples" ########

# Single-Reference Coupled-Cluster equation generation (spin-orbital)
# Single-Reference Coupled-Cluster equation generation
# (spin-orbital)
set(example0 srcc)
add_executable(${example0} EXCLUDE_FROM_ALL
examples/${example0}/${example0}.cpp)
find_package(Eigen3 3.0 REQUIRED NO_MODULE)
target_link_libraries(${example0} SeQuant Eigen3::Eigen ${TBB_LIBRARIES})
target_link_libraries(${example0} SeQuant)

set(example1 uccf12)
# Single-Reference closed-shell Coupled-Cluster equation
# generation (fast spin-traced)
set(example1 stcc)
add_executable(${example1} EXCLUDE_FROM_ALL
examples/${example1}/${example1}.cpp)
target_link_libraries(${example1} SeQuant ${TBB_LIBRARIES})
find_package(Eigen3 3.0 REQUIRED NO_MODULE)
target_link_libraries(${example1} SeQuant Eigen3::Eigen)

# Single-Reference closed-shell Coupled-Cluster equation
# generation (rigorous spin-traced)
set(example2 stcc_rigorous)
add_executable(${example2} EXCLUDE_FROM_ALL
examples/${example2}/${example2}.cpp)
find_package(Eigen3 3.0 REQUIRED NO_MODULE)
target_link_libraries(${example2} SeQuant Eigen3::Eigen)

# Single-Reference open-shell equation generation (spin-traced)
set(example3 osstcc)
add_executable(${example3} EXCLUDE_FROM_ALL
examples/${example3}/${example3}.cpp)
target_link_libraries(${example3} SeQuant)

# uccf12 example moved to MPQC

set(example_eval_src
examples/eval/eval_utils.hpp
Expand All @@ -360,37 +380,36 @@ if (BUILD_TESTING)
)

if (TARGET tiledarray)
set(example2 eval_ta)
add_executable(${example2} EXCLUDE_FROM_ALL
set(example5 eval_ta)
add_executable(${example5} EXCLUDE_FROM_ALL
${example_eval_src}
SeQuant/domain/eval/eval_ta.hpp
examples/eval/ta/data_world_ta.hpp
examples/eval/ta/scf_ta.hpp
examples/eval/ta/main.cpp)
target_link_libraries(${example2} SeQuant ${TBB_LIBRARIES} tiledarray)
target_link_libraries(${example5} SeQuant tiledarray)
endif (TARGET tiledarray)

set(example4 antisymmetrizer_test)
add_executable(${example4} EXCLUDE_FROM_ALL
examples/${example4}/${example4}.cpp)
target_link_libraries(${example4} SeQuant ${TBB_LIBRARIES})
target_link_directories(${example4} PUBLIC ${TBB_LIBRARIES})
target_compile_definitions(${example4} PRIVATE SEQUANT_HAS_TILEDARRAY)
set(example6 antisymmetrizer_test)
add_executable(${example6} EXCLUDE_FROM_ALL
examples/${example6}/${example6}.cpp)
target_link_libraries(${example6} SeQuant)
target_compile_definitions(${example6} PRIVATE SEQUANT_HAS_TILEDARRAY)

if (BTAS_SOURCE_DIR)
set(example3 eval_btas)
add_executable(${example3} EXCLUDE_FROM_ALL
set(example7 eval_btas)
add_executable(${example7} EXCLUDE_FROM_ALL
${example_eval_src}
SeQuant/domain/eval/eval_btas.hpp
examples/eval/btas/data_world_btas.hpp
examples/eval/btas/scf_btas.hpp
examples/eval/btas/main.cpp)
target_include_directories(${example3} PUBLIC ${BTAS_SOURCE_DIR} ${Boost_INCLUDE_DIRS})
target_link_libraries(${example3} SeQuant ${TBB_LIBRARIES})
target_include_directories(${example7} PUBLIC ${BTAS_SOURCE_DIR} ${Boost_INCLUDE_DIRS})
target_link_libraries(${example7} SeQuant)
endif (BTAS_SOURCE_DIR)

# add tests for running examples
set(lastexample 4)
set(lastexample 8)
foreach (i RANGE ${lastexample})
if (TARGET ${example${i}})
add_test(sequant/example/${example${i}}/build "${CMAKE_COMMAND}"
Expand Down
189 changes: 189 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,195 @@ tensor algebra expressions.

See file INSTALL.md .

# Getting started

## Build harness
To use SeQuant from within an existing codebase that has a CMake harness (the only case considered here) it should be sufficient to do this:
```cmake
find_package(SeQuant CONFIG REQUIRED)
target_link_libraries(your_executable_or_library_target PUBLIC SeQuant::SeQuant)
```
It is often desirable to build SeQuant from source within a standalone codebase; this case be done using the FetchContent CMake module as follows:
```cmake
find_package(SeQuant CONFIG)
if (NOT TARGET SeQuant::SeQuant)
cmake_minimum_required(VERSION 3.14.0) # for FetchContent_MakeAvailable
include(FetchContent)
FetchContent_Declare(sequant
GIT_REPOSITORY https://github.com/ValeevGroup/SeQuant2.git
)
FetchContent_MakeAvailable(sequant)
endif()
target_link_libraries(your_executable_or_library_target PUBLIC SeQuant::SeQuant)
```

## Using

### Getting Started

To get started let's use SeQuant to apply Wick's theorem to a simple product of elementary (creation and annihilation)
fermionic operators:

![a_{p_3} a_{p_4} a^\dagger_{p_1} a^\dagger_{p_2}](doc/images/tut-expr1.svg).

This is achieved by the following SeQuant program:

```c++
#include <SeQuant/core/sequant.hpp>
#include <SeQuant/core/op.hpp>
#include <SeQuant/core/wick.hpp>

int main() {
using namespace sequant;

IndexSpace sp;
Index p1(L"p_1", sp), p2(L"p_2", sp), p3(L"p_3", sp), p4(L"p_4", sp);

auto cp1 = fcrex(p1), cp2 = fcrex(p2);
auto ap3 = fannx(p3), ap4 = fannx(p4);

std::wcout << to_latex(ap3 * ap4 * cp1 * cp2) << " = "
<< to_latex(FWickTheorem{ap3 * ap4 * cp1 * cp2}
.set_external_indices(std::array{p1, p2, p3, p4})
.full_contractions(false)
.compute())
<< std::endl;

return 0;
}
```

N.B. All _core_ user-facing SeQuant code lives in C++ namespace `sequant`, from now on we will assume this namespace has been imported via `using namespace sequant` and will omit the explicit namespace qualification for SeQuant components.

Running this program should produce a LaTeX expression for this formula:

![{{a^{}_{{p_3}}}{a^{}_{{p_4}}}{a^{{p_1}}_{}}{a^{{p_2}}_{}}} = { \bigl( - {{a^{{p_1}{p_2}}_{{p_3}{p_4}}}} - {{s^{{p_1}}_{{p_3}}}{s^{{p_2}}_{{p_4}}}} + {{s^{{p_1}}_{{p_3}}}{a^{{p_2}}_{{p_4}}}} - {{s^{{p_1}}_{{p_4}}}{a^{{p_2}}_{{p_3}}}} + {{s^{{p_2}}_{{p_3}}}{s^{{p_1}}_{{p_4}}}} - {{s^{{p_2}}_{{p_3}}}{a^{{p_1}}_{{p_4}}}} + {{s^{{p_2}}_{{p_4}}}{a^{{p_1}}_{{p_3}}}}\bigr) }](doc/images/tut-expr1-result1.svg)

, where the tensor notation is used to denote elementary and composite _normal_ operators,

![a^p \equiv \, & a_p^\dagger \\ a^{p_1 p_2 \dots p_c}_{q_1 q_2 \dots q_a} \equiv \, & a_{p_1}^\dagger a_{p_2}^\dagger \dots a_{p_c}^\dagger a_{q_a} \dots a_{q_2} a_{q_1}](doc/images/tut-notation-eq1.svg)

, and

![s^p_q \equiv \langle q | p \rangle](doc/images/tut-notation-eq2.svg)

denotes 1-particle state inner products (overlaps). Wick's theorem can of course be applied to directly to products of normal composite operators, e.g,

```c++
auto nop1 = ex<FNOperator>(std::array{p1, p2}, std::array{p3, p4});
auto nop2 = ex<FNOperator>(std::array{p5}, std::array{p6, p7});

std::wcout << to_latex(nop1 * nop2) << " = "
<< to_latex(FWickTheorem{nop1 * nop2}
.set_external_indices(
std::array{p1, p2, p3, p4, p5, p6, p7})
.full_contractions(false)
.compute())
<< std::endl;
```
produces
![{{a^{{p_1}{p_2}}_{{p_3}{p_4}}}{a^{\textvisiblespace\,{p_5}}_{{p_6}{p_7}}}} = { \bigl({a^{\textvisiblespace\,{p_1}{p_2}{p_5}}_{{p_3}{p_4}{p_6}{p_7}}} - {{s^{{p_5}}_{{p_4}}}{a^{\textvisiblespace\,{p_1}{p_2}}_{{p_3}{p_6}{p_7}}}} + {{s^{{p_5}}_{{p_3}}}{a^{\textvisiblespace\,{p_1}{p_2}}_{{p_4}{p_6}{p_7}}}}\bigr) }](doc/images/tut-expr2-result1.svg)
, where `␣` is used in number-nonconserving operators to point out the empty "slots".
Of course, same manipulations can be used for bosons:
![{{b^{{p_1}{p_2}}_{{p_3}{p_4}}}{b^{{p_5}{p_6}}_{\textvisiblespace\,{p_7}}}} = { \bigl({b^{{p_5}{p_1}{p_2}{p_6}}_{\textvisiblespace\,{p_3}{p_4}{p_7}}} + {{s^{{p_6}}_{{p_3}}}{b^{{p_5}{p_2}{p_1}}_{\textvisiblespace\,{p_4}{p_7}}}} + {{s^{{p_5}}_{{p_3}}}{b^{{p_1}{p_2}{p_6}}_{\textvisiblespace\,{p_4}{p_7}}}} + {{s^{{p_6}}_{{p_4}}}{b^{{p_5}{p_1}{p_2}}_{\textvisiblespace\,{p_3}{p_7}}}} + {{s^{{p_5}}_{{p_4}}}{b^{{p_2}{p_1}{p_6}}_{\textvisiblespace\,{p_3}{p_7}}}} + {{s^{{p_5}}_{{p_3}}}{s^{{p_6}}_{{p_4}}}{b^{{p_1}{p_2}}_{\textvisiblespace\,{p_7}}}} + {{s^{{p_6}}_{{p_3}}}{s^{{p_5}}_{{p_4}}}{b^{{p_2}{p_1}}_{\textvisiblespace\,{p_7}}}}\bigr) }](doc/images/tut-expr3-result1.svg)
, where `b` denotes normal bosonic operators constructed analogously with the normal fermionic operators `a`, is obtained via
```c++
auto nop3 = ex<BNOperator>(std::array{p1, p2}, std::array{p3, p4});
auto nop4 = ex<BNOperator>(std::array{p5, p6}, std::array{p7});
std::wcout << to_latex(nop3 * nop4) << " = "
<< to_latex(BWickTheorem{nop3 * nop4}
.set_external_indices(
std::array{p1, p2, p3, p4, p5, p6, p7})
.full_contractions(false)
.compute())
<< std::endl;
```

### Quasiparticles

In most cases we are interested using SeQuant to manipulate expressions involving operators in normal order relative to a vacuum state with a finite number of particles, rather than with respect to the genuine vacuum with zero particles. To make such composition easier SeQuant expressions depend on SeQuant _context_, which specifies things like the vacuum type, whether the single-particle (SP) basis is orthonormal, etc. The above SeQuant program used the default context, which assumes the genuine vacuum. The active context can be examined by calling `get_default_context()`, changed via `set_default_context()`, and reset to the default via `reset_default_context()`:

```c++
#include <SeQuant/core/sequant.hpp>

int main() {
using namespace sequant;

// the default is to use genuine vacuum
assert(get_default_context().vacuum() == Vacuum::Physical);
// now set the context to a single product of SP states
set_default_context(SeQuant{Vacuum::SingleProduct, IndexSpaceMetric::Unit, BraKetSymmetry::symm});
assert(get_default_context().vacuum() == Vacuum::SingleProduct);
// reset the context back to the default
reset_default_context();
assert(get_default_context().vacuum() == Vacuum::Physical);

return 0;
}
```

However, to deal with the single-product vacuum it is necessary to declare the `IndexSpace` objects that will represent SP states included (_occupied_) in the vacuum state and excluded from (_unoccupied_ in) the vacuum state. Since there is no convention for labeling such states SeQuant demands such choices to be specified explicitly, e.g., by declaring

```c++
IndexSpace::register_instance(L"y", IndexSpace::occupied);
IndexSpace::register_instance(L"z", IndexSpace::complete_unoccupied);
```
we can evaluate Wick's theorem in single-product normal order:
```c++
auto cp1 = fcrex(p1), cp2 = fcrex(p2);
auto ap3 = fannx(p3), ap4 = fannx(p4);
std::wcout << to_latex(ap3 * cp1 * ap4 * cp2) << " = "
<< to_latex(FWickTheorem{ap3 * cp1 * ap4 * cp2}
.set_external_indices(std::array{p1, p2, p3, p4})
.full_contractions(false)
.compute())
<< std::endl;
```

produces

![{{\tilde{a}_{{p_3}}}{\tilde{a}^{{p_1}}}{\tilde{a}_{{p_4}}}{\tilde{a}^{{p_2}}}} = { \bigl({\tilde{a}^{{p_1}{p_2}}_{{p_3}{p_4}}} - {{s^{{p_1}}_{{z_1}}}{s^{{z_1}}_{{p_3}}}{\tilde{a}^{{p_2}}_{{p_4}}}} - {{s^{{p_2}}_{{z_1}}}{s^{{z_1}}_{{p_4}}}{\tilde{a}^{{p_1}}_{{p_3}}}} - {{s^{{p_1}}_{{y_1}}}{s^{{y_1}}_{{p_4}}}{\tilde{a}^{{p_2}}_{{p_3}}}} + {{s^{{p_2}}_{{z_1}}}{s^{{z_1}}_{{p_3}}}{\tilde{a}^{{p_1}}_{{p_4}}}} + {{s^{{p_1}}_{{z_1}}}{s^{{p_2}}_{{z_2}}}{s^{{z_1}}_{{p_3}}}{s^{{z_2}}_{{p_4}}}} + {{s^{{p_1}}_{{y_1}}}{s^{{p_2}}_{{z_1}}}{s^{{z_1}}_{{p_3}}}{s^{{y_1}}_{{p_4}}}}\bigr) }](doc/images/tut-expr4-result1.svg)

. Note that:
- the tilde in `ã` denotes normal order with respect to single-product vacuum, and
- Einstein summation convention is implied, i.e., indices that appear twice in a given product are summed over.

Registering spaces has more benefits than being able to deal with non-genuine vacuum; it can also simplify composition. By registering an index space we associate it with a given base index. This allows to subsequently map index labels to the registered spaces in constructors of indices and operators:

```c++
Index y21(L"y_21"); // <- represents IndexSpace::occupied
Index z1(L"z_1"); // <- represents IndexSpace::complete_unoccupied
auto op_oo_oo = ex<FNOperator>(WstrList{L"y_1", L"y_2"}, WstrList{L"y_3", L"y_4"});
```
To simplify index registration we provide support for a particular convention that the SeQuant developers prefer that we call the "Quantum Chemistry in Fock Space" (QCiFS), named after the [series of articles](http://doi.org/10.1063/1.444231) by Werner Kutzelnigg that introduced its essential elements. It can be loaded in 1 line:
```c++
#include <SeQuant/core/sequant.hpp>
#include <SeQuant/domain/mbpt/convention.hpp>
int main() {
// load the QCiFS convention
mbpt::set_default_convention(); // =mbpt::set_default_convention(mbpt::Convention::QCiFS)
Index i1(L"i_1"); // active occupied
Index a1(L"a_1"); // active unoccupied
Index p1(L"p_1"); // any state in computational basis
// etc.
}
```

# Developers

`SeQuant` is developed by the Valeev group at Virginia Tech.
Loading

0 comments on commit efa695d

Please sign in to comment.