Line data Source code
1 : #include <sstream> 2 : #include <algorithm> 3 : #include "TypeCasts.hpp" 4 : 5 : #include "Layer.h" 6 : 7 : namespace elsa::ml 8 : { 9 : template <typename data_t> 10 9 : Layer<data_t>::Layer(LayerType layerType, const std::string& name, 11 : int requiredNumberOfDimensions, int allowedNumberOfInputs, 12 : bool isTrainable) 13 : : requiredNumberOfDimensions_(requiredNumberOfDimensions), 14 : allowedNumberOfInputs_(allowedNumberOfInputs), 15 : layerType_(layerType), 16 : numberOfTrainableParameters_(0), 17 9 : isTrainable_(isTrainable) 18 : { 19 9 : globalIndex_ = staticGlobalIndex_++; 20 : 21 : // If no name is set we use a (lowercase) string representation of the 22 : // layer-type together with the layer's global index 23 9 : if (name == "") { 24 10 : std::stringstream ss; 25 5 : std::string typeString = detail::getEnumMemberAsString(layerType); 26 5 : std::transform(std::begin(typeString), std::end(typeString), std::begin(typeString), 27 27 : [](unsigned char c) { return std::tolower(c); }); 28 5 : ss << typeString << "_" << globalIndex_; 29 5 : name_ = ss.str(); 30 : } else { 31 4 : name_ = name; 32 : } 33 : 34 9 : if (this->isTrainable()) 35 3 : staticGlobalIndex_++; 36 9 : } 37 : 38 : template <typename data_t> 39 0 : Layer<data_t>::Layer(const Layer& other) 40 0 : : globalIndex_(other.globalIndex_), 41 0 : layerType_(other.layerType_), 42 0 : name_(other.name_), 43 0 : outputDescriptor_(other.outputDescriptor_->clone()) 44 : { 45 0 : for (std::size_t i = 0; i < other.inputDescriptors_.size(); ++i) 46 0 : inputDescriptors_.push_back(other.inputDescriptors_[i]->clone()); 47 0 : } 48 : 49 : template <typename data_t> 50 0 : Layer<data_t>& Layer<data_t>::operator=(const Layer<data_t>& other) 51 : { 52 0 : if (this != &other) { 53 0 : globalIndex_ = other.globalIndex_; 54 0 : layerType_ = other.layerType_; 55 0 : name_ = other.name_; 56 0 : outputDescriptor_ = other.outputDescriptor_->clone(); 57 0 : for (std::size_t i = 0; i < other.inputDescriptors_.size(); ++i) 58 0 : inputDescriptors_.push_back(other.inputDescriptors_[i]->clone()); 59 : } 60 0 : return *this; 61 : } 62 : 63 : template <typename data_t> 64 1 : LayerType Layer<data_t>::getLayerType() const 65 : { 66 1 : return layerType_; 67 : } 68 : 69 : template <typename data_t> 70 4 : std::string Layer<data_t>::getName() const 71 : { 72 4 : return name_; 73 : } 74 : 75 : template <typename data_t> 76 9 : void Layer<data_t>::setInputDescriptor(const VolumeDescriptor& inputDescriptor) 77 : { 78 9 : Layer<data_t>::checkNumberOfInputDimensions(inputDescriptor); 79 8 : inputDescriptors_.push_back(inputDescriptor.clone()); 80 8 : } 81 : 82 : template <typename data_t> 83 2 : void Layer<data_t>::setInput(Layer<data_t>* layer) 84 : { 85 : // Get graph from global state 86 2 : auto& graph = detail::State<data_t>::getGraph(); 87 : // Add edge from layer to this and fill the graph nodes 88 2 : graph.addEdge(layer->getGlobalIndex(), globalIndex_); 89 2 : graph.setData(layer->getGlobalIndex(), layer); 90 2 : graph.setData(globalIndex_, this); 91 : 92 : // Note that we don't set input descriptors here since we want to allow delaying the 93 : // input specifications. We'll set all input descriptors later when building the model 94 : // by traversing the graph 95 2 : } 96 : 97 : template <typename data_t> 98 0 : void Layer<data_t>::setInput(std::initializer_list<Layer*> layers) 99 : { 100 0 : for (auto&& l : layers) 101 0 : setInput(l); 102 0 : } 103 : 104 : template <typename data_t> 105 7 : VolumeDescriptor Layer<data_t>::getInputDescriptor(index_t index) const 106 : { 107 7 : if (inputDescriptors_.size() <= static_cast<std::size_t>(index)) 108 0 : throw std::logic_error("No input descriptor at given index"); 109 : 110 7 : if (!inputDescriptors_[asUnsigned(index)]) 111 0 : throw std::logic_error("Input descriptor not set"); 112 : 113 7 : return downcast_safe<VolumeDescriptor>(*inputDescriptors_[asUnsigned(index)]); 114 : } 115 : 116 : template <typename data_t> 117 0 : index_t Layer<data_t>::getNumberOfInputs() const 118 : { 119 0 : return static_cast<index_t>(inputDescriptors_.size()); 120 : } 121 : 122 : template <typename data_t> 123 7 : VolumeDescriptor Layer<data_t>::getOutputDescriptor() const 124 : { 125 7 : if (!outputDescriptor_) 126 0 : throw std::logic_error("Output descriptor not set"); 127 : 128 7 : return downcast_safe<VolumeDescriptor>(*outputDescriptor_); 129 : } 130 : 131 : template <typename data_t> 132 4 : index_t Layer<data_t>::getGlobalIndex() const 133 : { 134 4 : return globalIndex_; 135 : } 136 : 137 : template <typename data_t> 138 2 : void Layer<data_t>::computeOutputDescriptor() 139 : { 140 : // This default implementation requires a single input descriptor 141 2 : if (inputDescriptors_.size() != 1 || !inputDescriptors_.front()) 142 : throw std::logic_error("Cannot compute output descriptor since it depends on an input " 143 0 : "descriptor that has not been set"); 144 2 : outputDescriptor_ = inputDescriptors_.front()->clone(); 145 2 : } 146 : 147 : template <typename data_t> 148 9 : void Layer<data_t>::checkNumberOfInputDimensions(const VolumeDescriptor& inputDescriptor) const 149 : { 150 18 : if (requiredNumberOfDimensions_ != Layer<data_t>::AnyNumberOfInputDimensions 151 9 : && requiredNumberOfDimensions_ != inputDescriptor.getNumberOfDimensions()) { 152 2 : std::stringstream what; 153 1 : what << "Expected an input descriptor with " << requiredNumberOfDimensions_ 154 1 : << " dimension but got one with " << inputDescriptor.getNumberOfDimensions() 155 1 : << "."; 156 1 : throw std::invalid_argument(what.str()); 157 : } 158 8 : } 159 : 160 : template <typename data_t> 161 9 : bool Layer<data_t>::isTrainable() const 162 : { 163 9 : return isTrainable_; 164 : } 165 : 166 : template <typename data_t> 167 0 : bool Layer<data_t>::canMerge() const 168 : { 169 0 : return false; 170 : } 171 : 172 : template <typename data_t> 173 2 : index_t Layer<data_t>::getNumberOfTrainableParameters() const 174 : { 175 2 : return numberOfTrainableParameters_; 176 : } 177 : 178 : template class Layer<float>; 179 : } // namespace elsa::ml