Program Listing for File parameters.h

Return to documentation for file (src/graph/parameters.h)

#pragma once

#include <fstream>
#include <map>
#include <unordered_set>

#include "common/definitions.h"
#include "graph/chainable.h"
#include "tensors/tensor_allocator.h"

namespace marian {

// @TODO: Currently an ExpressionGraph only supports one Parameters object and
// the type of parameters has to be the inside on Parameters object. This limits
// parameter types to a single chosen type, e.g. only fp32 or only fp16. This should
// be extended to allow multiple sets of parameters.
// The reason here is to be able to efficiently compute updates of whole parameter
// sets of one type.
class Parameters {
protected:
  Type acceptedElementType_; // this parameter object only takes paramters of this type

  std::vector<Expr> params_;
  std::map<std::string, Expr> named_;

  Ptr<TensorAllocator> vals_;
  Ptr<TensorAllocator> grads_;

  size_t totalCapacity(Ptr<TensorAllocator> alloc) {
    size_t sum = 0;
    for(auto p : params_) {
      sum += alloc->capacity(p->shape(), p->value_type());
    }
    return sum;
  }

public:
  Parameters(Type acceptedType) : acceptedElementType_(acceptedType) {
    LOG(debug, "Created parameter object of type {}", acceptedElementType_);
  }

  virtual ~Parameters() {
    LOG(debug, "Destroyed parameter object of type {}", acceptedElementType_);
  }

  auto begin() -> decltype(params_.begin()) { return params_.begin(); }

  auto end() -> decltype(params_.begin()) { return params_.end(); }

  auto getMap() -> decltype(named_)& { return named_; }

  Expr get(const std::string& name) {
    auto it = named_.find(name);
    if(it != named_.end()) {
      return it->second;
    } else {
      return Expr();
    }
  }

  size_t size() { return params_.size(); }

  void add(Expr p, const std::string& name) {
    LOG(debug, "Adding parameter {} to parameter object of type {}", name, acceptedElementType_);

    ABORT_IF(named_.count(name), "Parameter '{}' already exists", name);
    ABORT_IF(p->value_type() != acceptedElementType_,
             "Requested parameter type ({}) is different from chosen parameter type ({})",
             p->value_type(), acceptedElementType_);
    params_.push_back(p);
    named_[name] = p;
  }

  virtual void init(Ptr<Backend> backend) {
    vals_ = New<TensorAllocator>(backend);
    grads_ = New<TensorAllocator>(backend);
  }

  virtual void init(Ptr<Backend> backend, Ptr<Device> device) {
    vals_ = New<TensorAllocator>(backend, device);
    grads_ = New<TensorAllocator>(backend, device);
  }

  virtual void allocateForward() {
    if(!params_.empty() && vals_->size() == 0) {
      vals_->reserveExact(totalCapacity(vals_));

      // sort parameters by name before allocation to make sure the memory layout after allocation is always the same
      std::sort(params_.begin(), params_.end(), [](Expr n1, Expr n2){ return n1->name() < n2->name(); });

      for(auto p : params_) {
        if(!p->val()) {
          vals_->allocate(p->val(), p->shape(), p->value_type());
        }
      }
    }
  }

  virtual void allocateBackward() {
    if(!params_.empty() && grads_->size() == 0) {

      // sort parameters by name before allocation to make sure the memory layout after allocation is always the same
      std::sort(params_.begin(), params_.end(), [](Expr n1, Expr n2){ return n1->name() < n2->name(); });

      grads_->reserveExact(totalCapacity(grads_));
      for(auto p : params_)
        if(!p->grad())
          grads_->allocate(p->grad(), p->shape(), p->value_type());
    }
  }

  virtual void set_zero_adjoint() { grads()->set(0.f); }

  virtual Tensor vals() { return vals_->asTensor(acceptedElementType_); }

  virtual Tensor grads() { return grads_->asTensor(acceptedElementType_); }

  virtual void clear() {
    params_.clear();
    named_.clear();

    vals_->clear();
    grads_->clear();
  }
};

class MappedParameters : public Parameters {
private:
  Ptr<Backend> backend_;

public:
  MappedParameters(Type acceptedElementType) : Parameters(acceptedElementType) {
    LOG(debug, "Created mapped parameter object of type {}", acceptedElementType);
  }

  virtual void init(Ptr<Backend> backend) override { backend_ = backend; }
  virtual void init(Ptr<Backend> backend, Ptr<Device>) override { init(backend); }

  virtual void allocateForward() override {
    if(!params_.empty()) {
      for(auto p : params_) {
        if(!p->val()) {
          p->val() = TensorBase::New(nullptr, p->shape(), p->value_type(), backend_);
        }
      }
    }
  }

  virtual void allocateBackward() override {
    ABORT("Not implemented for memory-mapped parameters");
  }

  virtual void set_zero_adjoint() override {
    ABORT("Not implemented for memory-mapped parameters");
  }

  virtual Tensor vals() override {
    ABORT("Not implemented for memory-mapped parameters");
  }

  virtual Tensor grads() override {
    ABORT("Not implemented for memory-mapped parameters");
  }

  virtual void clear() override {
    params_.clear();
    named_.clear();
  }
};

}  // namespace marian