Commit 9a2ebd03 authored by Dmitry I. Lyakh's avatar Dmitry I. Lyakh
Browse files

Implemented multi-state eigensolver via Optimizer.

parent dbb55657
Loading
Loading
Loading
Loading
+30 −19
Original line number Diff line number Diff line
ISSUES:

- Automatically generated unique tensor names have local scope,
  thus the corresponding tensors with automatically generated
  names will have their names differ accross different processes.
  The only way to ensure proper tensor correspondence in global
  tensor operations is to make sure all participating processes
  execute the same algorithm in a consistent fashion, like SIMD.
  That is, the order of tensor operations across all participating
  processes must be consistent such that every encountered global
  tensor operation will receive the same tensor operand irrespective
  of the difference in the locally generated tensor name. Special
  care needs to be taken in iterating over associative tensor containers,
  to ensure that the keys are consistent accross all participating
  processes. For example, automatically generated tensor names
  cannot serve as keys in an iteration procedure since they are
  inconsistent accross different processes whereas tensor Ids
  can serve as keys since they are normally consistent accross
  different processes because the container was built with the
  same structure accross all processes.

BUGS:

- 32-bit integer MPI message chunking issue in the backend.

- Fix the bug(s) in the tensor order reduction mechanism in the backend.
- Fix the bug(s) in the tensor order reduction mechanism in the TalshExecutor backend.


FEATURES:
@@ -14,42 +35,32 @@ FEATURES:
  4. Limit the Host buffer size to be similar to the combined devices buffer size;
  5. Introduce manual knobs to keep tensors on GPU;


- Implement tensor operator builders.


- TensorExpansion: Constructor that converts a TensorOperator
  into a TensorExpansion.


- TensorNetwork: Subnetwork replacement method:
  Contract replaced tensors, then replace the contracted
  tensor with a new tensor (sub)network.

- Implement SAVE/LOAD API for TensorExpansion.

- Introduce parallelization over tensor networks within a tensor expansion.
- Implement TensorNetwork slice computing Generator.

- Implement b-D procedure.

- Implement constrained optimization for isometric tensors.
- Implement conjugate gradient optimization procedure.

- Implement DIIS convergence accelerator.

- Tensor network bond dimension adaptivity in solvers.


- Implement parameterized non-linear optimization:
  dTensorFunctional/dParameter = dTensorFunctional/dTensor * dTensor/dParameter


- Implement conjugate gradient optimization procedure.

- Implement constrained optimization for isometric tensors.

- Fix index slicing logic (switch to two passes):
  Pass 1: Compute the throughout volume (TH) for all indices;
          TH(index) = Sum of tensor volumes for all tensors containing the index.
  Pass 2: Slice indices with maximal throughout volume;

- Implement parameterized non-linear optimization:
  dTensorFunctional/dParameter = dTensorFunctional/dTensor * dTensor/dParameter

- Introduce DAG nodes/dependencies with multiple output operands.


- Implement the guided k-way partitioning algorithm.
+12 −3
Original line number Diff line number Diff line
/** ExaTN::Numerics: General client header (free function API)
REVISION: 2021/10/19
REVISION: 2021/10/30

Copyright (C) 2018-2021 Dmitry I. Lyakh (Liakh)
Copyright (C) 2018-2021 Oak Ridge National Laboratory (UT-Battelle) **/
@@ -476,6 +476,14 @@ inline bool initTensorsRndSync(TensorNetwork & tensor_network) //inout: tensor n
 {return numericalServer->initTensorsRndSync(tensor_network);}


/** Initializes all input tensors in a given tensor network expansion to a random value. **/
inline bool initTensorsRnd(TensorExpansion & tensor_expansion)     //inout: tensor network expansion
 {return numericalServer->initTensorsRnd(tensor_expansion);}

inline bool initTensorsRndSync(TensorExpansion & tensor_expansion) //inout: tensor network expansion
 {return numericalServer->initTensorsRndSync(tensor_expansion);}


/** Initializes special tensors present in the tensor network. **/
inline bool initTensorsSpecial(TensorNetwork & tensor_network)     //inout: tensor network
 {return numericalServer->initTensorsSpecial(tensor_network);}
@@ -971,7 +979,7 @@ inline bool balanceNormalizeNorm2Sync(const ProcessGroup & process_group, //in:

/** Duplicates a given tensor network as a new tensor network copy.
    The name of the tensor network copy will be prepended with an underscore
    whereas all copies of the input tensors will be renamed with their unique hashes. **/
    whereas all copies of the input tensors will be renamed with their unique (local) hashes. **/
inline std::shared_ptr<TensorNetwork> duplicateSync(const TensorNetwork & network) //in: tensor network
 {return numericalServer->duplicateSync(network);}

@@ -981,7 +989,8 @@ inline std::shared_ptr<TensorNetwork> duplicateSync(const ProcessGroup & process


/** Duplicates a given tensor network expansion as a new tensor network expansion copy.
    The new tensor network expansion copy will have no name. **/
    The name of the tensor network expnasion copy will be prepended with an underscore
    whereas all copies of the input tensors will be renamed with their unique (local) hashes. **/
inline std::shared_ptr<TensorExpansion> duplicateSync(const TensorExpansion & expansion) //in: tensor expansion
 {return numericalServer->duplicateSync(expansion);}

+90 −11
Original line number Diff line number Diff line
/** ExaTN::Numerics: Numerical server
REVISION: 2021/10/19
REVISION: 2021/10/30

Copyright (C) 2018-2021 Dmitry I. Lyakh (Liakh)
Copyright (C) 2018-2021 Oak Ridge National Laboratory (UT-Battelle) **/
@@ -8,6 +8,7 @@ Copyright (C) 2018-2021 Oak Ridge National Laboratory (UT-Battelle) **/
#include "tensor_range.hpp"
#include "timers.hpp"

#include <unordered_set>
#include <complex>
#include <vector>
#include <stack>
@@ -1564,11 +1565,13 @@ bool NumServer::initTensorRndSync(const std::string & name)
bool NumServer::initTensorsRnd(TensorNetwork & tensor_network)
{
 bool success = true;
 for(auto tens = tensor_network.begin(); tens != tensor_network.end(); ++tens){
 std::unordered_set<std::string> tensor_names;
 for(auto tens = tensor_network.cbegin(); tens != tensor_network.cend(); ++tens){
  auto tensor = tens->second.getTensor();
  const auto & tens_name = tensor->getName();
  if(tens->first != 0){ //input tensor
   if(tensorAllocated(tens_name)){
    //auto res = tensor_names.emplace(tens_name);
    success = initTensorRnd(tens_name);
   }else{
    success = false;
@@ -1578,17 +1581,81 @@ bool NumServer::initTensorsRnd(TensorNetwork & tensor_network)
  }
  if(!success) break;
 }
 if(success){
  for(const auto & tens_name: tensor_names){
   success = initTensorRnd(tens_name); if(!success) break;
  }
 }
 return success;
}

bool NumServer::initTensorsRndSync(TensorNetwork & tensor_network)
{
 bool success = true;
 for(auto tens = tensor_network.begin(); tens != tensor_network.end(); ++tens){
 std::unordered_set<std::string> tensor_names;
 for(auto tens = tensor_network.cbegin(); tens != tensor_network.cend(); ++tens){
  auto tensor = tens->second.getTensor();
  const auto & tens_name = tensor->getName();
  if(tens->first != 0){ //input tensor
   if(tensorAllocated(tens_name)){
    //auto res = tensor_names.emplace(tens_name);
    success = initTensorRndSync(tens_name);
   }else{
    success = false;
   }
  }else{ //output tensor
   if(tensorAllocated(tens_name)) success = initTensorSync(tens_name,0.0);
  }
  if(!success) break;
 }
 if(success){
  for(const auto & tens_name: tensor_names){
   success = initTensorRndSync(tens_name); if(!success) break;
  }
 }
 return success;
}

bool NumServer::initTensorsRnd(TensorExpansion & tensor_expansion)
{
 bool success = true;
 std::unordered_set<std::string> tensor_names;
 for(auto tensor_network = tensor_expansion.cbegin(); tensor_network != tensor_expansion.cend(); ++tensor_network){
  for(auto tens = tensor_network->network->cbegin(); tens != tensor_network->network->cend(); ++tens){
   auto tensor = tens->second.getTensor();
   const auto & tens_name = tensor->getName();
   if(tens->first != 0){ //input tensor
    if(tensorAllocated(tens_name)){
     //auto res = tensor_names.emplace(tens_name);
     success = initTensorRnd(tens_name);
    }else{
     success = false;
    }
   }else{ //output tensor
    if(tensorAllocated(tens_name)) success = initTensor(tens_name,0.0);
   }
   if(!success) break;
  }
 }
 if(success){
  for(const auto & tens_name: tensor_names){
   success = initTensorRnd(tens_name); if(!success) break;
  }
 }
 return success;
}

bool NumServer::initTensorsRndSync(TensorExpansion & tensor_expansion)
{
 bool success = true;
 std::unordered_set<std::string> tensor_names;
 for(auto tensor_network = tensor_expansion.cbegin(); tensor_network != tensor_expansion.cend(); ++tensor_network){
  for(auto tens = tensor_network->network->cbegin(); tens != tensor_network->network->cend(); ++tens){
   auto tensor = tens->second.getTensor();
   const auto & tens_name = tensor->getName();
   if(tens->first != 0){ //input tensor
    if(tensorAllocated(tens_name)){
     //auto res = tensor_names.emplace(tens_name);
     success = initTensorRndSync(tens_name);
    }else{
     success = false;
@@ -1598,6 +1665,12 @@ bool NumServer::initTensorsRndSync(TensorNetwork & tensor_network)
   }
   if(!success) break;
  }
 }
 if(success){
  for(const auto & tens_name: tensor_names){
   success = initTensorRndSync(tens_name); if(!success) break;
  }
 }
 return success;
}

@@ -3241,23 +3314,27 @@ std::shared_ptr<TensorNetwork> NumServer::duplicateSync(const ProcessGroup & pro
{
 unsigned int local_rank; //local process rank within the process group
 if(!process_group.rankIsIn(process_rank_,&local_rank)) return std::shared_ptr<TensorNetwork>(nullptr); //process is not in the group: Do nothing
 bool success = true;
 const auto tens_elem_type = network.getTensorElementType();
 auto network_copy = makeSharedTensorNetwork(network,true); assert(network_copy);
 network_copy->rename("_"+network.getName());
 std::unordered_map<std::string,std::shared_ptr<Tensor>> tensor_copies;
 for(auto tensor = network_copy->begin(); tensor != network_copy->end(); ++tensor){ //replace input tensors by their copies
  if(tensor->first != 0){
   const auto & tensor_name = tensor->second.getName();
   const auto & tensor_name = tensor->second.getName(); //original tensor name
   auto iter = tensor_copies.find(tensor_name);
   if(iter == tensor_copies.end()){
    auto res = tensor_copies.emplace(std::make_pair(tensor_name,makeSharedTensor(*(tensor->second.getTensor()))));
    assert(res.second);
    iter = res.first;
    iter->second->rename();
    iter->second->rename(); //new (automatically generated) tensor name
    success = createTensor(iter->second,tens_elem_type); assert(success);
    success = copyTensor(iter->second->getName(),tensor_name); assert(success);
   }
   tensor->second.replaceStoredTensor(iter->second);
  }
 }
 network_copy->rename("_" + network.getName());
 auto success = createTensorsSync(process_group,*network_copy,network.getTensorElementType()); assert(success);
 success = sync(process_group); assert(success);
 return network_copy;
}

@@ -3271,11 +3348,13 @@ std::shared_ptr<TensorExpansion> NumServer::duplicateSync(const ProcessGroup & p
{
 unsigned int local_rank; //local process rank within the process group
 if(!process_group.rankIsIn(process_rank_,&local_rank)) return std::shared_ptr<TensorExpansion>(nullptr); //process is not in the group: Do nothing
 auto expansion_copy = makeSharedTensorExpansion(expansion.isKet()); assert(expansion_copy);
 auto expansion_copy = makeSharedTensorExpansion("_"+expansion.getName(),expansion.isKet());
 assert(expansion_copy);
 for(auto component = expansion.cbegin(); component != expansion.cend(); ++component){
  auto success = expansion_copy->appendComponent(duplicateSync(process_group,*(component->network)),
                                                 component->coefficient);
  if(!success) return std::shared_ptr<TensorExpansion>(nullptr);
  auto dup_network = duplicateSync(process_group,*(component->network));
  assert(dup_network);
  auto success = expansion_copy->appendComponent(dup_network,component->coefficient);
  assert(success);
 }
 return expansion_copy;
}
+9 −3
Original line number Diff line number Diff line
/** ExaTN::Numerics: Numerical server
REVISION: 2021/10/19
REVISION: 2021/10/30

Copyright (C) 2018-2021 Dmitry I. Lyakh (Liakh)
Copyright (C) 2018-2021 Oak Ridge National Laboratory (UT-Battelle) **/
@@ -664,6 +664,11 @@ public:

 bool initTensorsRndSync(TensorNetwork & tensor_network); //inout: tensor network

 /** Initializes all input tensors in a given tensor network expansion to a random value. **/
 bool initTensorsRnd(TensorExpansion & tensor_expansion);     //inout: tensor network expansion

 bool initTensorsRndSync(TensorExpansion & tensor_expansion); //inout: tensor network expansion

 /** Initializes special tensors present in the tensor network. **/
 bool initTensorsSpecial(TensorNetwork & tensor_network);     //inout: tensor network

@@ -962,14 +967,15 @@ public:

 /** Duplicates a given tensor network as a new tensor network copy.
     The name of the tensor network copy will be prepended with an underscore
     whereas all copies of the input tensors will be renamed with their unique hashes. **/
     whereas all copies of the input tensors will be renamed with their unique (local) hashes. **/
 std::shared_ptr<TensorNetwork> duplicateSync(const TensorNetwork & network); //in: tensor network

 std::shared_ptr<TensorNetwork> duplicateSync(const ProcessGroup & process_group, //in: chosen group of MPI processes
                                              const TensorNetwork & network);     //in: tensor network

 /** Duplicates a given tensor network expansion as a new tensor network expansion copy.
     The new tensor network expansion copy will have no name. **/
     The name of the tensor network expansion copy will be prepended with an underscore
     whereas all copies of the input tensors will be renamed with their unique (local) hashes. **/
 std::shared_ptr<TensorExpansion> duplicateSync(const TensorExpansion & expansion); //in: tensor expansion

 std::shared_ptr<TensorExpansion> duplicateSync(const ProcessGroup & process_group, //in: chosen group of MPI processes
+66 −1
Original line number Diff line number Diff line
/** ExaTN:: Variational optimizer of a closed symmetric tensor network expansion functional
REVISION: 2021/10/22
REVISION: 2021/10/30

Copyright (C) 2018-2021 Dmitry I. Lyakh (Liakh)
Copyright (C) 2018-2021 Oak Ridge National Laboratory (UT-Battelle) **/
@@ -74,12 +74,28 @@ std::shared_ptr<TensorExpansion> TensorNetworkOptimizer::getSolution(std::comple
}


std::shared_ptr<TensorExpansion> TensorNetworkOptimizer::getSolution(unsigned int root_id,
                                                                     std::complex<double> * average_expect_val) const
{
 assert(root_id < eigenvalues_.size());
 if(average_expect_val != nullptr) *average_expect_val = eigenvalues_[root_id];
 return eigenvectors_[root_id];
}


std::complex<double> TensorNetworkOptimizer::getExpectationValue() const
{
 return average_expect_val_;
}


std::complex<double> TensorNetworkOptimizer::getExpectationValue(unsigned int root_id) const
{
 assert(root_id < eigenvalues_.size());
 return eigenvalues_[root_id];
}


bool TensorNetworkOptimizer::optimize()
{
 return optimize(exatn::getDefaultProcessGroup());
@@ -92,6 +108,54 @@ bool TensorNetworkOptimizer::optimize(const ProcessGroup & process_group)
}


bool TensorNetworkOptimizer::optimize(unsigned int num_roots)
{
 return optimize(exatn::getDefaultProcessGroup(),num_roots);
}


bool TensorNetworkOptimizer::optimize(const ProcessGroup & process_group,
                                      unsigned int num_roots)
{
 bool success = true;
 auto original_operator = tensor_operator_;
 for(unsigned int root_id = 0; root_id < num_roots; ++root_id){
  success = initTensorsRndSync(*vector_expansion_); assert(success);
  bool synced = sync(process_group); assert(synced);
  success = optimize(process_group);
  synced = sync(process_group); assert(synced);
  if(!success) break;
  const auto expect_val = getExpectationValue();
  eigenvalues_.emplace_back(expect_val);
  auto solution_vector = duplicateSync(process_group,*vector_expansion_);
  assert(solution_vector);
  success = normalizeNorm2Sync(*solution_vector,1.0); assert(success);
  eigenvectors_.emplace_back(solution_vector);
  for(auto ket_net = solution_vector->begin(); ket_net != solution_vector->end(); ++ket_net){
   ket_net->network->markOptimizableNoTensors();
  }
  const auto num_legs = solution_vector->getRank();
  std::vector<std::pair<unsigned int, unsigned int>> ket_pairing(num_legs);
  for(unsigned int i = 0; i < num_legs; ++i) ket_pairing[i] = std::make_pair(i,i);
  std::vector<std::pair<unsigned int, unsigned int>> bra_pairing(num_legs);
  for(unsigned int i = 0; i < num_legs; ++i) bra_pairing[i] = std::make_pair(i,i);
  auto projector = makeSharedTensorOperator("EigenProjector" + std::to_string(root_id));
  for(auto ket_net = solution_vector->cbegin(); ket_net != solution_vector->cend(); ++ket_net){
   for(auto bra_net = solution_vector->cbegin(); bra_net != solution_vector->cend(); ++bra_net){
    success = projector->appendComponent(ket_net->network,bra_net->network,ket_pairing,bra_pairing,
                                         (-expect_val) * std::conj(ket_net->coefficient) * (bra_net->coefficient));
    assert(success);
   }
  }
  auto proj_hamiltonian = combineTensorOperators(*tensor_operator_,*projector);
  assert(proj_hamiltonian);
  tensor_operator_ = proj_hamiltonian;
 }
 tensor_operator_ = original_operator;
 return success;
}


bool TensorNetworkOptimizer::optimize_sd(const ProcessGroup & process_group)
{
 constexpr bool NORMALIZE_WITH_METRICS = true;  //whether to normalize tensor network factors with metrics or not
@@ -160,6 +224,7 @@ bool TensorNetworkOptimizer::optimize_sd(const ProcessGroup & process_group)
 }

 //Prepare derivative environments for all optimizable tensors in the vector expansion:
 environments_.clear();
 std::unordered_set<std::string> tensor_names;
 // Loop over the tensor networks constituting the tensor network vector expansion:
 for(auto network = vector_expansion_->cbegin(); network != vector_expansion_->cend(); ++network){
Loading