Commit a4902ccc authored by Dmitry I. Lyakh's avatar Dmitry I. Lyakh

Almost finished TensorNetwork evaluator in NumServer. Things to finish:

1. TensorNetwork::getOperationList required generation of the symbolic contraction pattern.
2. Node executor should automatically create non-existing tensors and set them to zero.
parent 8db39908
Pipeline #70609 passed with stage
in 4 minutes and 49 seconds
/** ExaTN::Numerics: Numerical server
REVISION: 2019/09/03
REVISION: 2019/09/09
Copyright (C) 2018-2019 Dmitry I. Lyakh (Liakh)
Copyright (C) 2018-2019 Oak Ridge National Laboratory (UT-Battelle) **/
......@@ -162,7 +162,10 @@ void NumServer::submit(std::shared_ptr<TensorOperation> operation)
void NumServer::submit(TensorNetwork & network)
{
assert(false);//`Finish
auto & op_list = network.getOperationList();
for(auto op = op_list.begin(); op != op_list.end(); ++op){
tensor_rt_->submit(*op);
}
return;
}
......@@ -184,8 +187,7 @@ bool NumServer::sync(TensorOperation & operation, bool wait)
bool NumServer::sync(TensorNetwork & network, bool wait)
{
assert(false);//`Finish
return false;
return sync(*(network.getTensor(0)),wait);
}
} //namespace exatn
......@@ -36,8 +36,11 @@ public:
/** Determines the pseudo-optimal tensor contraction sequence required for
evaluating a given tensor network. The unique intermediate tensor id's are generated
by the provided intermediate number generator (each invocation returns a new id).
The latter can be conveniently passed as a lambda closure. **/
by the provided intermediate number generator (each invocation returns a new tensor id).
The latter can be conveniently passed as a lambda closure. The returned double value
is an estimate of the total flop count associated with the determined contraction sequence.
The tensor network must have at least two input tensors in order to get a single contraction.
No contraction sequence is generated for tensor networks with a single input tensor. **/
virtual double determineContractionSequence(const TensorNetwork & network,
std::list<ContrTriple> & contr_seq,
std::function<unsigned int ()> intermediate_num_generator) = 0;
......
......@@ -17,7 +17,7 @@ double ContractionSeqOptimizerDummy::determineContractionSequence(const TensorNe
{
contr_seq.clear();
double flops = 0.0;
const auto num_tensors = network.getNumTensors();
const auto num_tensors = network.getNumTensors(); //number of input tensors
if(num_tensors > 1){
TensorNetwork net(network);
unsigned int ids[num_tensors], i = 0;
......
......@@ -847,27 +847,67 @@ double TensorNetwork::getContractionCost(unsigned int left_id, unsigned int righ
}
std::list<std::shared_ptr<TensorOperation>> TensorNetwork::getOperationList(const std::string & contr_seq_opt_name)
std::list<std::shared_ptr<TensorOperation>> & TensorNetwork::getOperationList(const std::string & contr_seq_opt_name)
{
std::list<std::shared_ptr<TensorOperation>> ops;
auto iter = optimizers.find(contr_seq_opt_name);
if(iter == optimizers.end()){ //not cached
auto & optimizer_factory = *(ContractionSeqOptimizerFactory::get());
auto optimizer = optimizer_factory.createContractionSeqOptimizer(contr_seq_opt_name);
if(optimizer){
auto res = optimizers.emplace(std::make_pair(contr_seq_opt_name,
if(operations_.empty()){
//Get the tensor contraction sequence optimizer:
auto iter = optimizers.find(contr_seq_opt_name);
if(iter == optimizers.end()){ //not cached
auto & optimizer_factory = *(ContractionSeqOptimizerFactory::get());
auto optimizer = optimizer_factory.createContractionSeqOptimizer(contr_seq_opt_name);
if(optimizer){
auto res = optimizers.emplace(std::make_pair(contr_seq_opt_name,
std::shared_ptr<ContractionSeqOptimizer>(std::move(optimizer))));
assert(res.second);
iter = res.first;
}else{
std::cout << "#ERROR(TensorNetwork::getOperationList): Invalid request: " <<
"Tensor contraction sequence optimizer" << contr_seq_opt_name << "has not been registered before!" << std::endl;
assert(false);
assert(res.second);
iter = res.first;
}else{
std::cout << "#ERROR(TensorNetwork::getOperationList): Invalid request: " <<
"Tensor contraction sequence optimizer" << contr_seq_opt_name << "has not been registered before!" << std::endl;
assert(false);
}
}
//Determine the pseudo-optimal sequence of tensor contractions:
double flops = determineContractionSequence(*(iter->second));
//Generate the list of operations:
auto & tensor_op_factory = *(TensorOpFactory::get());
if(this->getNumTensors() > 1){ //two or more input tensors: One or more contractions
TensorNetwork net(*this);
unsigned int num_contractions = contraction_seq_.size();
for(auto contr = contraction_seq_.cbegin(); contr != contraction_seq_.cend(); ++contr){
auto tensor1 = net.getTensor(contr->left_id);
auto tensor2 = net.getTensor(contr->right_id);
//`Get index pattern for tensor contraction
auto merged = net.mergeTensors(contr->left_id,contr->right_id,contr->result_id);
assert(merged);
auto tensor0 = net.getTensor(contr->result_id);
auto op = tensor_op_factory.createTensorOp(TensorOpCode::CONTRACT);
op->setTensorOperand(tensor0);
op->setTensorOperand(tensor1);
op->setTensorOperand(tensor2);
op->setIndexPattern("`Replace with generated index pattern");
assert(op->isSet());
operations_.emplace_back(std::shared_ptr<TensorOperation>(std::move(op)));
}
}else{ //one input tensor: Single addition
std::shared_ptr<Tensor> tensor0(nullptr);
std::shared_ptr<Tensor> tensor1(nullptr);
for(auto iter = this->begin(); iter != this->end(); ++iter){
if(iter->first == 0){
tensor0 = this->getTensor(iter->first);
}else{
tensor1 = this->getTensor(iter->first);
}
}
auto op = tensor_op_factory.createTensorOp(TensorOpCode::ADD);
op->setTensorOperand(tensor0);
op->setTensorOperand(tensor1);
//`Get index pattern for tensor addition
op->setIndexPattern("`Replace with generated index pattern");
assert(op->isSet());
operations_.emplace_back(std::shared_ptr<TensorOperation>(std::move(op)));
}
}
double flops = determineContractionSequence(*(iter->second));
//`Finish: Copy tensor network, contract according to the sequence, record each contraction as operation
return ops;
return operations_;
}
} //namespace numerics
......
......@@ -213,7 +213,7 @@ public:
bool adjust_cost = false); //in: whether or not to adjust the flops cost due to arithmetic intensity
/** Returns the list of tensor operations required for evaluating the tensor network. **/
std::list<std::shared_ptr<TensorOperation>> getOperationList(const std::string & contr_seq_opt_name = "dummy");
std::list<std::shared_ptr<TensorOperation>> & getOperationList(const std::string & contr_seq_opt_name = "dummy");
protected:
......@@ -237,7 +237,9 @@ protected:
void invalidateContractionSequence();
/** Determines a pseudo-optimal tensor contraction sequence required for evaluating the tensor network.
Returns an estimate of the total flop count required by the returned contraction sequence. **/
Returns an estimate of the total flop count required by the returned contraction sequence.
The tensor network must contain at least two input tensors in order to generate a single contraction.
No contraction sequence is generated for tensor networks consisting of a single input tensor. **/
double determineContractionSequence(ContractionSeqOptimizer & contr_seq_optimizer);
private:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment