LBANN  0.103.0
LivermoreBigArtificialNeuralNetworkToolkit
operator_layer_impl.hpp
Go to the documentation of this file.
1 // Copyright (c) 2014-2023, Lawrence Livermore National Security, LLC.
3 // Produced at the Lawrence Livermore National Laboratory.
4 // Written by the LBANN Research Team (B. Van Essen, et al.) listed in
5 // the CONTRIBUTORS file. <lbann-dev@llnl.gov>
6 //
7 // LLNL-CODE-697807.
8 // All rights reserved.
9 //
10 // This file is part of LBANN: Livermore Big Artificial Neural Network
11 // Toolkit. For details, see http://software.llnl.gov/LBANN or
12 // https://github.com/LLNL/LBANN.
13 //
14 // Licensed under the Apache License, Version 2.0 (the "Licensee"); you
15 // may not use this file except in compliance with the License. You may
16 // obtain a copy of the License at:
17 //
18 // http://www.apache.org/licenses/LICENSE-2.0
19 //
20 // Unless required by applicable law or agreed to in writing, software
21 // distributed under the License is distributed on an "AS IS" BASIS,
22 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
23 // implied. See the License for the specific language governing
24 // permissions and limitations under the license.
26 #ifndef LBANN_LAYERS_OPERATOR_LAYER_IMPL_HPP_INCLUDED
27 #define LBANN_LAYERS_OPERATOR_LAYER_IMPL_HPP_INCLUDED
28 
31 
36 
37 #include "lbann/proto/layers.pb.h"
38 #include <cereal/types/base_class.hpp>
39 #include <memory>
40 
41 namespace lbann {
42 
43 template <typename InputT, typename OutputT, data_layout Layout, El::Device D>
45  OperatorPtr op)
46  : DataTypeLayer(&comm)
47 {
48  LBANN_ASSERT(op);
49  m_ops.reserve(1);
50  m_ops.emplace_back(std::move(op));
51  this->m_expected_num_parent_layers = -1; // No limit on parents
52 }
53 
54 template <typename InputT, typename OutputT, data_layout Layout, El::Device D>
56  lbann_comm& comm,
57  std::vector<OperatorPtr> operators)
58  : DataTypeLayer(&comm), m_ops{std::move(operators)}
59 {
60  LBANN_ASSERT(m_ops.size() == 1UL); // For starters.
61  LBANN_ASSERT(m_ops[0]);
62  this->m_expected_num_parent_layers = -1; // No limit on parents
63 }
64 
65 template <typename InputT, typename OutputT, data_layout Layout, El::Device D>
67  OperatorLayer const& other)
68  : DataTypeLayer(other), m_ops{clone_ops(other.m_ops)}
69 {}
70 
71 template <typename InputT, typename OutputT, data_layout Layout, El::Device D>
73  OperatorLayer const& other) -> OperatorLayer&
74 {
75  // This is self-assignment safe
77  m_ops = clone_ops(other.m_ops);
78  return *this;
79 }
80 
81 template <typename InputT, typename OutputT, data_layout Layout, El::Device D>
83 {
85 }
86 
87 template <typename InputT, typename OutputT, data_layout Layout, El::Device D>
89 {
90  return "operator";
91 }
92 
93 template <typename InputT, typename OutputT, data_layout Layout, El::Device D>
95 {
96  return Layout;
97 }
98 
99 template <typename InputT, typename OutputT, data_layout Layout, El::Device D>
102 {
103  return D;
104 }
105 
106 template <typename InputT, typename OutputT, data_layout Layout, El::Device D>
108 {
109  return true;
110 }
111 
112 template <typename InputT, typename OutputT, data_layout Layout, El::Device D>
114 {
115  // Find the union of all internal operators
116  int result = ERROR_SIGNALS;
117  for (const auto& op : m_ops) {
118  result |= op->get_backprop_requirements();
119  }
120  return result;
121 }
122 
123 template <typename InputT, typename OutputT, data_layout Layout, El::Device D>
125 {
126  return m_ops[0]->fp_compute(this->get_inputs(), this->get_outputs());
127 }
128 
129 template <typename InputT, typename OutputT, data_layout Layout, El::Device D>
131 {
132  return m_ops[0]->bp_compute(this->get_inputs(),
133  this->get_grad_wrt_outputs(),
134  this->get_grad_wrt_inputs());
135 }
136 
137 template <typename InputT, typename OutputT, data_layout Layout, El::Device D>
139 {
140  auto desc = DataTypeLayer::get_description();
141  for (auto const& op : m_ops)
142  desc.add(op->get_description());
143  return desc;
144 }
145 
146 template <typename InputT, typename OutputT, data_layout Layout, El::Device D>
147 template <typename ArchiveT>
149 {
150  ar(cereal::base_class<DataTypeLayer>(this), m_ops);
151 }
152 
153 template <typename InputT, typename OutputT, data_layout Layout, El::Device D>
155  : DataTypeLayer(nullptr)
156 {
157  m_ops.reserve(1);
158 }
159 
160 template <typename InputT, typename OutputT, data_layout Layout, El::Device D>
162  std::vector<OperatorPtr> const& ops) -> std::vector<OperatorPtr>
163 {
164  std::vector<OperatorPtr> out;
165  out.reserve(ops.size());
166  for (auto const& x : ops) {
167  out.emplace_back(x->clone());
168  }
169  return out;
170 }
171 
172 template <typename InputT, typename OutputT, data_layout Layout, El::Device D>
173 std::vector<size_t>
175 {
176  return std::vector<size_t>{cbegin(in), cend(in)};
177 }
178 
179 // WARNING: The next 4 functions all assume the minibatch dim is the
180 // width of the matrix.
181 
182 template <typename InputT, typename OutputT, data_layout Layout, El::Device D>
183 std::vector<utils::ConstDistTensorView<InputT, D>>
185 {
186  auto n_parents = this->get_num_parents();
187  std::vector<utils::ConstDistTensorView<InputT, D>> out;
188  out.reserve(n_parents);
189  for (int p = 0; p < n_parents; ++p) {
190  auto const& prev_acts = this->get_prev_activations(p);
191  out.emplace_back(prev_acts,
192  splice_dims(prev_acts.Width(), this->get_input_dims(p)));
193  }
194  return out;
195 }
196 
197 template <typename InputT, typename OutputT, data_layout Layout, El::Device D>
198 std::vector<utils::DistTensorView<OutputT, D>>
200 {
201  auto n_children = this->get_num_children();
202  std::vector<utils::DistTensorView<OutputT, D>> out;
203  out.reserve(n_children);
204  for (int c = 0; c < n_children; ++c) {
205  auto& acts = this->get_activations(c);
206  out.emplace_back(acts, splice_dims(acts.Width(), this->get_output_dims(c)));
207  }
208  return out;
209 }
210 
211 template <typename InputT, typename OutputT, data_layout Layout, El::Device D>
212 std::vector<utils::ConstDistTensorView<OutputT, D>>
214 {
215  auto n_children = this->get_num_children();
216  std::vector<utils::ConstDistTensorView<OutputT, D>> out;
217  out.reserve(n_children);
218  for (int c = 0; c < n_children; ++c) {
219  auto const& prev_sigs = this->get_prev_error_signals(c);
220  out.emplace_back(prev_sigs,
221  splice_dims(prev_sigs.Width(), this->get_output_dims(c)));
222  }
223  return out;
224 }
225 
226 template <typename InputT, typename OutputT, data_layout Layout, El::Device D>
227 std::vector<utils::DistTensorView<InputT, D>>
229 {
230  auto n_parents = this->get_num_parents();
231  std::vector<utils::DistTensorView<InputT, D>> out;
232  out.reserve(n_parents);
233  for (int p = 0; p < n_parents; ++p) {
234  auto& error_sigs = this->get_error_signals(p);
235  out.emplace_back(error_sigs,
236  splice_dims(error_sigs.Width(), this->get_input_dims(p)));
237  }
238  return out;
239 }
240 
241 } // namespace lbann
242 
243 template <typename InputT,
244  typename OutputT,
245  lbann::data_layout Layout,
246  El::Device D>
248  lbann_data::Layer const& msg)
249  -> std::unique_ptr<Layer>
250 {
253  using OperatorPtr = std::unique_ptr<OperatorType>;
254 
255  LBANN_ASSERT(comm); // Sanity check
256 
257  // Build up the list of operators for this layer.
258  auto const& params = msg.operator_layer();
259 
260  auto const num_ops = params.ops_size();
261  std::vector<OperatorPtr> ops;
262  ops.reserve(num_ops);
263  for (int ii = 0; ii < num_ops; ++ii) {
264 #ifdef LBANN_DEBUG
265  LBANN_ASSERT(msg.datatype() == params.ops(ii).input_datatype());
266  LBANN_ASSERT(msg.datatype() == params.ops(ii).output_datatype());
267 #endif
268  ops.emplace_back(
269  proto::construct_operator<InputT, OutputT, D>(params.ops(ii)));
270  }
271  return std::make_unique<LayerType>(*comm, std::move(ops));
272 }
273 
274 #ifndef LBANN_INSTANTIATE_OPERATOR_LAYER
275 namespace lbann {
276 
277 #define PROTO_DEVICE(T, D) \
278  extern template class OperatorLayer<T, T, data_layout::DATA_PARALLEL, D>; \
279  extern template class OperatorLayer<T, T, data_layout::MODEL_PARALLEL, D>; \
280  extern template std::unique_ptr<Layer> \
281  build_operator_layer_from_pbuf<T, T, data_layout::DATA_PARALLEL, D>( \
282  lbann_comm*, \
283  lbann_data::Layer const&); \
284  extern template std::unique_ptr<Layer> \
285  build_operator_layer_from_pbuf<T, T, data_layout::MODEL_PARALLEL, D>( \
286  lbann_comm*, \
287  lbann_data::Layer const&)
288 
290 
291 } // namespace lbann
292 #endif // LBANN_INSTANTIATE_OPERATOR_LAYER
293 #endif // LBANN_LAYERS_OPERATOR_LAYER_IMPL_HPP_INCLUDED
std::vector< size_t > splice_dims(ArgTs &&... args)
std::string get_type() const final
Get the layer type&#39;s name.
std::vector< utils::ConstDistTensorView< OutputT, D > > get_grad_wrt_outputs() const
std::vector< OperatorPtr > m_ops
static std::vector< size_t > fix_type(std::vector< int > const &in)
El::Device get_device_allocation() const final
Get the device allocation for the data tensors. We assume that the decice allocation of the previous ...
int get_num_parents() const noexcept
Get number of parent layers.
Definition: layer.hpp:574
OperatorLayer & operator=(OperatorLayer const &other)
Copy assignment.
description get_description() const final
Human-readable description.
std::vector< int > get_input_dims(size_t input_index=0) const
Get input tensor dimensions.
Generates nicely formatted description messages.
Definition: description.hpp:49
virtual description get_description() const
Human-readable description.
constexpr El::Device Device
OutputAbsDistMatrixType & get_prev_error_signals(int child_index=0)
InputAbsDistMatrixType & get_prev_activations(int parent_index=0)
#define LBANN_ASSERT(cond)
Definition: exception.hpp:97
const OutputAbsDistMatrixType & get_activations(const Layer &child) const override
std::unique_ptr< Layer > build_operator_layer_from_pbuf(lbann_comm *, lbann_data::Layer const &)
std::vector< utils::ConstDistTensorView< InputT, D > > get_inputs() const
int get_num_children() const noexcept
Get number of child layers.
Definition: layer.hpp:576
void bp_compute() final
Compute objective funciton gradients. Called by the &#39;back_prop&#39; function. Given the input...
static std::vector< OperatorPtr > clone_ops(std::vector< OperatorPtr > const &ops)
OperatorLayer * copy() const final
Polymorphic copy.
int get_backprop_requirements() const final
Returns the necessary tensors for computing backpropagation.
data_layout
Data layout that is optimized for different modes of parallelism.
Definition: base.hpp:218
void fp_compute() final
Apply layer operation. Called by the &#39;forward_prop&#39; function. Given the input tensors, the output tensors are populated with computed values.
data_layout get_data_layout() const final
Get data layout of the data tensors. We assume that the data layouts of the previous activations...
Neural network tensor operation.
Definition: operator.hpp:85
std::vector< utils::DistTensorView< OutputT, D > > get_outputs()
bool can_run_inplace() const final
If True, the computation can run in-place (feeding each input activations tensor as the corresponding...
std::unique_ptr< OperatorType > OperatorPtr
std::vector< int > get_output_dims(size_t output_index=0) const
Get output tensor dimensions.
Layer composed of one or more operator objects.
std::vector< utils::DistTensorView< InputT, D > > get_grad_wrt_inputs()
int m_expected_num_parent_layers
Definition: layer.hpp:838
data_type_layer & operator=(data_type_layer &&other)=default
const InputAbsDistMatrixType & get_error_signals(const Layer &parent) const override