/*!
 *  Copyright (c) 2016 by Contributors
 * \file nnvm/op_attr_types.h
 * \brief Data structures that can appear in operator attributes.
 */
#ifndef NNVM_OP_ATTR_TYPES_H_
#define NNVM_OP_ATTR_TYPES_H_

#include <vector>
#include <string>
#include <utility>
#include <functional>
#include <unordered_map>
#include "base.h"
#include "node.h"
#include "tuple.h"
#include "layout.h"

namespace nnvm {

// These types are optional attributes in each operator.
// Each attribute can be required by some passes.

/*!
 * \brief Return list of input arguments names of each operator.
 *
 * \param attrs The attributes of the node.
 * \return list of inputs
 * \note Register under "FListInputNames", default return {"data"}.
 *
 *  FListInputNames enables automatic variable creation for missing arguments.
 */
using FListInputNames = std::function<std::vector<std::string> (const NodeAttrs& attrs)>;

/*!
 * \brief Return number of visible outputs by the user.
 *
 * \param attrs The attributes of the node.
 *
 * \note Register under "FNumVisibleOutputs", default not registered.
 *  This can be used to hide certain output from the user,
 *  but the additional outputs can be used to pass information from
 *  forward to gradient pass.
 */
using FNumVisibleOutputs = std::function<uint32_t (const NodeAttrs& attrs)>;

/*!
 * \brief Return list of output arguments names of each operator.
 *
 * \param attrs The attributes of the node.
 * \return list of inputs
 * \note Register under "FListOutputNames", default return {"outputs"}.
 *
 *  FListOutputNames customized naming for operator outputs.
 */
using FListOutputNames = std::function<std::vector<std::string> (const NodeAttrs& attrs)>;

/*!
 * \brief Check whether operator will mutate k-th input.
 * \param attrs The attributes of the node.
 * \return list of input indices it mutates.
 *
 * \note Register under "FMutateInputs", default return false
 * FMutateInputs enables mutation order handling correctly.
 */
using FMutateInputs = std::function<std::vector<uint32_t> (const NodeAttrs& attrs)>;

/*!
 * \brief Inference function of certain type.
 * \tparam AttrType The type of the attribute to be infered.
 * \return whether all attributes are inferred.
 */
template<typename AttrType>
using FInferNodeEntryAttr = std::function<bool (const NodeAttrs& attrs,
                                                std::vector<AttrType> *in_attrs,
                                                std::vector<AttrType> *out_attrs)>;

/*!
 * \brief Get attribute dictionary from node.
 *
 * \param attrs The attributes of the node.
 * \return The attribute dict.
 * \note Register under "FUpdateAttrDict"
 */
using FGetAttrDict = std::function<
  std::unordered_map<std::string, std::string>
  (const NodeAttrs& attrs)>;

/*!
 * \brief Shape inference function.
 *  Update the shapes given the input shape information.
 *  TShape.ndim() == 0 means the shape is still unknown.
 *
 * \note Register under "FInferShape",
 *  by default do not update any shapes.
 *
 *  FInferShape is needed by shape inference
 */
using FInferShape = FInferNodeEntryAttr<TShape>;

/*!
 * \brief Type inference function.
 *  Update the type given the known type information.
 *
 * \note Register under "FInferType",
 *  by default set all the output types to 0.
 */
using FInferType = FInferNodeEntryAttr<int>;

/*!
 * \brief Whether this op is an explicit backward operator,
 * If TIsBackward is true:
 *   - The first control_deps of the node points to the corresponding forward operator.
 *
 * \note Register under "TIsBackward"
 * This enables easier shape/type inference for backward operators.
 */
using TIsBackward = bool;

/*!
 * \brief Get possible inplace options.
 *  This function enables optimization to reuse memory of inputs in output.
 * \param attrs The attributes of the node
 * \return list of pair of that maps input->output,
 *   indicating possible in place operations.
 *
 * \note Register under "FInplaceOption", by default no inplace can happen.
 */
using FInplaceOption = std::function<
  std::vector<std::pair<int, int> > (const NodeAttrs& attrs)>;

/*!
 * \brief Get if the inplace option is an identity
 *  This function enables inplace optimization even when input reference count
 *  is greater than one.
 * \param attrs The attributes of the node
 * \return list of bool indicating whether corresponding pair from FInplaceOption
 *         is an identity
 *
 * \note Register under "FInplaceIdentity", by default no identities.
 */
using FInplaceIdentity = std::function<std::vector<bool> (const NodeAttrs& attrs)>;

/*!
 * \brief Get list of inputs in the op whose content are actually not used by the operator
 *  These are dummy input that can be used for example in zeros_like, ones_like.
 *
 * \param attrs The attributes of the node
 * \return list input index that are not used by the operator.
 *
 * \note Register under "FIgnoreInputs".
 */
using FIgnoreInputs = std::function<
  std::vector<uint32_t> (const NodeAttrs& attrs)>;

/*!
 * \brief Get the gradient node of the op node
 *  This function generates the backward graph of the node
 * \param nodeptr The node to take gradient
 * \param out_grads Gradient of current node's outputs
 * \return gradients of the inputs
 *
 * \note Register under "FGradient"
 */
using FGradient = std::function<std::vector<NodeEntry>(
    const NodePtr& nodeptr,
    const std::vector<NodeEntry>& out_grads)>;

/*!
 * \brief Set the attributes of input variable.
 *  Usually used for setting initialization or weight decay.
 *  \param attrs The attributes of this node.
 *  \param var the input variable
 *  \param index index of var in all inputs
 */
using FSetInputVarAttrOnCompose = std::function<void(
    const NodeAttrs& attrs,
    NodePtr var,
    const int index)>;

/*!
 * \brief Infer & correct function of node layout. See \p Layout for layout convention
 * \param attrs The attribute of the node.
 * \param ilayouts Given the input layouts produced by ancestor nodes,
 *                 it should be filled by layouts that the node requests.
 *                 If the requested layout is different from what ancestor produces,
 *                 a __layout_transform__ operator will be inserted automatically.
 * \param last_ilayouts The input layouts requested by the node
 *                      at the last infer pass (if any).
 *                      This can be useful when an operator wants to keep
 *                      the input layout the same as the original one.
 *                      For example, after the pass of AlterOpLayout,
 *                      transpose(input, axis=[1, 2, 3, 0]) may receive an input of NCHW16c layout,
 *                      with which it cannot calculate with axis=[1, 2, 3, 0].
 *                      Last input layouts allow it to know what the layout it originally inferred,
 *                      i.e., the layout in the imported model.
 * \param olayouts Inferred output layouts.
 * \return success flag.
 */
using FCorrectLayout = std::function<bool(
    const NodeAttrs& attrs,
    std::vector<Layout> *ilayouts,
    const std::vector<Layout> *last_ilayouts,
    std::vector<Layout> *olayouts)>;

/*!
 * \brief Get a list of inputs that represent graphs instead of data.
 * Normally, input symbols are considered as data to the operator. However,
 * control flow operators and high-order functions need to interpret symbols
 * as graphs.
 * \param attrs The attributes of this node.
 * \return a list of input index that are interpreted as symbols by the operator.
 *
 * \note Register under "FInputGraph".
 */
using FInputGraph = std::function<std::vector<uint32_t>(const NodeAttrs& attrs)>;

}  // namespace nnvm

#endif  // NNVM_OP_ATTR_TYPES_H_
