// Copyright (c) 2018 Graphcore Ltd. All rights reserved.
/*
 * THIS IS AN AUTOGENERATED FILE, DO NOT EDIT DIRECTLY
 *
 * To regenerate this file run the gen_operators.py script
 */

#include "popart/builder.gen.hpp"

#include <cstdint>
#include <cstdlib>
#include <limits>
#include <map>
#include <onnx/onnx_pb.h>

#include "builder_helper.hpp"
#include "builder_impl.hpp"
#include "builderdebuginfo.hpp"
#include "filereader.hpp"
#include "onnxutil.hpp"
#include "popart/builder.hpp"
#include "popart/datatype.hpp"
#include "popart/error.hpp"
#include "popart/logging.hpp"
#include "popart/operators.hpp"
#include "popart/tensordebuginfo.hpp"
#include "popart/tensorinfo.hpp"
#include "popart/vendored/any.hpp"
#include "poparttracepoint.hpp"

namespace popart {
class ConstVoidData;

TensorId AiOnnxOpset6::abs(const std::vector<TensorId> &args,
                           const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Abs_6, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset6::add(const std::vector<TensorId> &args,
                           nonstd::optional<int64_t> axis,
                           int64_t broadcast,
                           const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (axis != nonstd::optional<int64_t>()) {
    attributes["axis"] = *axis;
  }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["broadcast"] = broadcast; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Add_6, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset6::logical_and(const std::vector<TensorId> &args,
                                   nonstd::optional<int64_t> axis,
                                   int64_t broadcast,
                                   const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (axis != nonstd::optional<int64_t>()) {
    attributes["axis"] = *axis;
  }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["broadcast"] = broadcast; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::And_1, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset6::argmax(const std::vector<TensorId> &args,
                              int64_t axis,
                              int64_t keepdims,
                              const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["axis"] = axis; }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["keepdims"] = keepdims; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::ArgMax_1, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset6::argmin(const std::vector<TensorId> &args,
                              int64_t axis,
                              int64_t keepdims,
                              const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["axis"] = axis; }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["keepdims"] = keepdims; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::ArgMin_1, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset6::averagepool(const std::vector<TensorId> &args,
                                   const std::vector<int64_t> &kernel_shape,
                                   const std::vector<int64_t> &pads,
                                   const std::vector<int64_t> &strides,
                                   const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  attributes["kernel_shape"] = kernel_shape;
  if (!pads.empty()) {
    attributes["pads"] = pads;
  }
  if (!strides.empty()) {
    attributes["strides"] = strides;
  }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::AveragePool_1,
      getOpsetVersion(),
      args,
      attributes,
      {di},
      [this](const std::vector<TensorId> &inputs_,
             std::map<std::string, popart::any> attributes_) {
        verify_AiOnnxOpset6_AveragePool_1(this->impl, inputs_, attributes_);
      });
  di.setOutputs(outputs);
  return outputs[0];
}

std::vector<TensorId>
AiOnnxOpset6::batchnormalization(const std::vector<TensorId> &args,
                                 unsigned num_outputs,
                                 float epsilon,
                                 int64_t is_test,
                                 float momentum,
                                 int64_t spatial,
                                 const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (epsilon != 1e-05f) {
    attributes["epsilon"] = epsilon;
  }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["is_test"] = is_test; }
  if (momentum != 0.9f) {
    attributes["momentum"] = momentum;
  }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["spatial"] = spatial; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(Onnx::Operators::BatchNormalization_6,
                          getOpsetVersion(),
                          args,
                          num_outputs,
                          attributes,
                          {di});
  di.setOutputs(outputs);
  return outputs;
}

TensorId AiOnnxOpset6::cast(const std::vector<TensorId> &args,
                            const std::string &to,
                            const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  // Special case where we cast from DataType to int
  DataType toDataType = dataTypeFromString(to);
  attributes["to"]    = static_cast<int>(onnxutil::getTPDataType(toDataType));
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Cast_6, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset6::ceil(const std::vector<TensorId> &args,
                            const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Ceil_6, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset6::clip(const std::vector<TensorId> &args,
                            float max,
                            float min,
                            const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (max != 3.4028234663852886e+38f) {
    attributes["max"] = max;
  }
  if (min != -3.4028234663852886e+38f) {
    attributes["min"] = min;
  }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Clip_6, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset6::concat(const std::vector<TensorId> &args,
                              int64_t axis,
                              const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  attributes["axis"] = axis;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Concat_4, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset6::constant(const ConstVoidData &value,
                                const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  attributes["value"] = value;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, {}, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Constant_1, getOpsetVersion(), {}, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset6::conv(const std::vector<TensorId> &args,
                            const std::vector<int64_t> &dilations,
                            int64_t group,
                            const std::vector<int64_t> &kernel_shape,
                            const std::vector<int64_t> &pads,
                            const std::vector<int64_t> &strides,
                            const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (!dilations.empty()) {
    attributes["dilations"] = dilations;
  }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["group"] = group; }
  if (!kernel_shape.empty()) {
    attributes["kernel_shape"] = kernel_shape;
  }
  if (!pads.empty()) {
    attributes["pads"] = pads;
  }
  if (!strides.empty()) {
    attributes["strides"] = strides;
  }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs =
      impl->op(Onnx::Operators::Conv_1,
               getOpsetVersion(),
               args,
               attributes,
               {di},
               [this](const std::vector<TensorId> &inputs_,
                      std::map<std::string, popart::any> attributes_) {
                 verify_AiOnnxOpset6_Conv_1(this->impl, inputs_, attributes_);
               });
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset6::convtranspose(const std::vector<TensorId> &args,
                                     const std::vector<int64_t> &dilations,
                                     int64_t group,
                                     const std::vector<int64_t> &kernel_shape,
                                     const std::vector<int64_t> &output_padding,
                                     const std::vector<int64_t> &output_shape,
                                     const std::vector<int64_t> &pads,
                                     const std::vector<int64_t> &strides,
                                     const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (!dilations.empty()) {
    attributes["dilations"] = dilations;
  }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["group"] = group; }
  if (!kernel_shape.empty()) {
    attributes["kernel_shape"] = kernel_shape;
  }
  if (!output_padding.empty()) {
    attributes["output_padding"] = output_padding;
  }
  if (!output_shape.empty()) {
    attributes["output_shape"] = output_shape;
  }
  if (!pads.empty()) {
    attributes["pads"] = pads;
  }
  if (!strides.empty()) {
    attributes["strides"] = strides;
  }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(Onnx::Operators::ConvTranspose_1,
                          getOpsetVersion(),
                          args,
                          attributes,
                          {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset6::depthtospace(const std::vector<TensorId> &args,
                                    int64_t blocksize,
                                    const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  attributes["blocksize"] = blocksize;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(Onnx::Operators::DepthToSpace_1,
                          getOpsetVersion(),
                          args,
                          attributes,
                          {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset6::div(const std::vector<TensorId> &args,
                           nonstd::optional<int64_t> axis,
                           int64_t broadcast,
                           const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (axis != nonstd::optional<int64_t>()) {
    attributes["axis"] = *axis;
  }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["broadcast"] = broadcast; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Div_6, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

std::vector<TensorId>
AiOnnxOpset6::dropout(const std::vector<TensorId> &args,
                      unsigned num_outputs,
                      int64_t is_test,
                      float ratio,
                      const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["is_test"] = is_test; }
  if (ratio != 0.5f) {
    attributes["ratio"] = ratio;
  }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(Onnx::Operators::Dropout_6,
                          getOpsetVersion(),
                          args,
                          num_outputs,
                          attributes,
                          {di});
  di.setOutputs(outputs);
  return outputs;
}

TensorId AiOnnxOpset6::elu(const std::vector<TensorId> &args,
                           float alpha,
                           const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (alpha != 1.0f) {
    attributes["alpha"] = alpha;
  }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Elu_6, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset6::equal(const std::vector<TensorId> &args,
                             nonstd::optional<int64_t> axis,
                             int64_t broadcast,
                             const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (axis != nonstd::optional<int64_t>()) {
    attributes["axis"] = *axis;
  }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["broadcast"] = broadcast; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Equal_1, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset6::exp(const std::vector<TensorId> &args,
                           const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Exp_6, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset6::flatten(const std::vector<TensorId> &args,
                               int64_t axis,
                               const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["axis"] = axis; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Flatten_1, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset6::floor(const std::vector<TensorId> &args,
                             const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Floor_6, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

std::vector<TensorId>
AiOnnxOpset6::gru(const std::vector<TensorId> &args,
                  unsigned num_outputs,
                  const std::vector<float> &activation_alpha,
                  const std::vector<float> &activation_beta,
                  const std::vector<std::string> &activations,
                  nonstd::optional<float> clip,
                  const std::string &direction,
                  nonstd::optional<int64_t> hidden_size,
                  int64_t linear_before_reset,
                  int64_t output_sequence,
                  const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (!activation_alpha.empty()) {
    attributes["activation_alpha"] = activation_alpha;
  }
  if (!activation_beta.empty()) {
    attributes["activation_beta"] = activation_beta;
  }
  if (!activations.empty()) {
    attributes["activations"] = activations;
  }
  if (clip != nonstd::optional<float>()) {
    attributes["clip"] = *clip;
  }
  if (direction != "forward") {
    attributes["direction"] = direction;
  }
  if (hidden_size != nonstd::optional<int64_t>()) {
    attributes["hidden_size"] = *hidden_size;
  }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["linear_before_reset"] = linear_before_reset; }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["output_sequence"] = output_sequence; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(Onnx::Operators::GRU_3,
                          getOpsetVersion(),
                          args,
                          num_outputs,
                          attributes,
                          {di});
  di.setOutputs(outputs);
  return outputs;
}

TensorId AiOnnxOpset6::gather(const std::vector<TensorId> &args,
                              int64_t axis,
                              const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["axis"] = axis; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Gather_1, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset6::gemm(const std::vector<TensorId> &args,
                            float alpha,
                            float beta,
                            int64_t broadcast,
                            int64_t transA,
                            int64_t transB,
                            const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (alpha != 1.0f) {
    attributes["alpha"] = alpha;
  }
  if (beta != 1.0f) {
    attributes["beta"] = beta;
  }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["broadcast"] = broadcast; }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["transA"] = transA; }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["transB"] = transB; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Gemm_6, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId
AiOnnxOpset6::globalaveragepool(const std::vector<TensorId> &args,
                                const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(Onnx::Operators::GlobalAveragePool_1,
                          getOpsetVersion(),
                          args,
                          attributes,
                          {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset6::globallppool(const std::vector<TensorId> &args,
                                    int64_t p,
                                    const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["p"] = p; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(Onnx::Operators::GlobalLpPool_2,
                          getOpsetVersion(),
                          args,
                          attributes,
                          {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset6::globalmaxpool(const std::vector<TensorId> &args,
                                     const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(Onnx::Operators::GlobalMaxPool_1,
                          getOpsetVersion(),
                          args,
                          attributes,
                          {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset6::greater(const std::vector<TensorId> &args,
                               nonstd::optional<int64_t> axis,
                               int64_t broadcast,
                               const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (axis != nonstd::optional<int64_t>()) {
    attributes["axis"] = *axis;
  }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["broadcast"] = broadcast; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Greater_1, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset6::hardsigmoid(const std::vector<TensorId> &args,
                                   float alpha,
                                   float beta,
                                   const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (alpha != 0.2f) {
    attributes["alpha"] = alpha;
  }
  if (beta != 0.5f) {
    attributes["beta"] = beta;
  }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(Onnx::Operators::HardSigmoid_6,
                          getOpsetVersion(),
                          args,
                          attributes,
                          {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset6::hardmax(const std::vector<TensorId> &args,
                               int64_t axis,
                               const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["axis"] = axis; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Hardmax_1, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset6::identity(const std::vector<TensorId> &args,
                                const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Identity_1, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

std::vector<TensorId>
AiOnnxOpset6::logical_if(const std::vector<TensorId> &args,
                         unsigned num_outputs,
                         const Builder &else_branch,
                         const Builder &then_branch,
                         const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  // Special case where we convert from a Builder object to an
  // onnx::GraphProto object so as not to expose the onnx class
  // at the API level
  attributes["else_branch"] =
      io::getModelFromString(else_branch.getModelProto()).graph();
  // Special case where we convert from a Builder object to an
  // onnx::GraphProto object so as not to expose the onnx class
  // at the API level
  attributes["then_branch"] =
      io::getModelFromString(then_branch.getModelProto()).graph();
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(Onnx::Operators::If_1,
                          getOpsetVersion(),
                          args,
                          num_outputs,
                          attributes,
                          {di});
  di.setOutputs(outputs);
  return outputs;
}

TensorId
AiOnnxOpset6::instancenormalization(const std::vector<TensorId> &args,
                                    float epsilon,
                                    const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (epsilon != 1e-05f) {
    attributes["epsilon"] = epsilon;
  }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(Onnx::Operators::InstanceNormalization_6,
                          getOpsetVersion(),
                          args,
                          attributes,
                          {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset6::lrn(const std::vector<TensorId> &args,
                           int64_t size,
                           float alpha,
                           float beta,
                           float bias,
                           const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (alpha != 0.0001f) {
    attributes["alpha"] = alpha;
  }
  if (beta != 0.75f) {
    attributes["beta"] = beta;
  }
  if (bias != 1.0f) {
    attributes["bias"] = bias;
  }
  attributes["size"] = size;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::LRN_1, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

std::vector<TensorId>
AiOnnxOpset6::lstm(const std::vector<TensorId> &args,
                   unsigned num_outputs,
                   const std::vector<float> &activation_alpha,
                   const std::vector<float> &activation_beta,
                   const std::vector<std::string> &activations,
                   nonstd::optional<float> clip,
                   const std::string &direction,
                   nonstd::optional<int64_t> hidden_size,
                   int64_t input_forget,
                   int64_t output_sequence,
                   const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (!activation_alpha.empty()) {
    attributes["activation_alpha"] = activation_alpha;
  }
  if (!activation_beta.empty()) {
    attributes["activation_beta"] = activation_beta;
  }
  if (!activations.empty()) {
    attributes["activations"] = activations;
  }
  if (clip != nonstd::optional<float>()) {
    attributes["clip"] = *clip;
  }
  if (direction != "forward") {
    attributes["direction"] = direction;
  }
  if (hidden_size != nonstd::optional<int64_t>()) {
    attributes["hidden_size"] = *hidden_size;
  }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["input_forget"] = input_forget; }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["output_sequence"] = output_sequence; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(Onnx::Operators::LSTM_1,
                          getOpsetVersion(),
                          args,
                          num_outputs,
                          attributes,
                          {di});
  di.setOutputs(outputs);
  return outputs;
}

TensorId AiOnnxOpset6::leakyrelu(const std::vector<TensorId> &args,
                                 float alpha,
                                 const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (alpha != 0.01f) {
    attributes["alpha"] = alpha;
  }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::LeakyRelu_6, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset6::less(const std::vector<TensorId> &args,
                            nonstd::optional<int64_t> axis,
                            int64_t broadcast,
                            const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (axis != nonstd::optional<int64_t>()) {
    attributes["axis"] = *axis;
  }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["broadcast"] = broadcast; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Less_1, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset6::log(const std::vector<TensorId> &args,
                           const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Log_6, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset6::logsoftmax(const std::vector<TensorId> &args,
                                  int64_t axis,
                                  const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["axis"] = axis; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::LogSoftmax_1, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

std::vector<TensorId>
AiOnnxOpset6::loop(const std::vector<TensorId> &args,
                   unsigned num_outputs,
                   const Builder &body,
                   const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  // Special case where we convert from a Builder object to an
  // onnx::GraphProto object so as not to expose the onnx class
  // at the API level
  attributes["body"] = io::getModelFromString(body.getModelProto()).graph();
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(Onnx::Operators::Loop_1,
                          getOpsetVersion(),
                          args,
                          num_outputs,
                          attributes,
                          {di});
  di.setOutputs(outputs);
  return outputs;
}

TensorId
AiOnnxOpset6::lpnormalization(const std::vector<TensorId> &args,
                              int64_t axis,
                              int64_t p,
                              const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["axis"] = axis; }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["p"] = p; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(Onnx::Operators::LpNormalization_1,
                          getOpsetVersion(),
                          args,
                          attributes,
                          {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset6::lppool(const std::vector<TensorId> &args,
                              const std::vector<int64_t> &kernel_shape,
                              int64_t p,
                              const std::vector<int64_t> &pads,
                              const std::vector<int64_t> &strides,
                              const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  attributes["kernel_shape"] = kernel_shape;
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["p"] = p; }
  if (!pads.empty()) {
    attributes["pads"] = pads;
  }
  if (!strides.empty()) {
    attributes["strides"] = strides;
  }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::LpPool_2, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset6::matmul(const std::vector<TensorId> &args,
                              const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::MatMul_1, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset6::max(const std::vector<TensorId> &args,
                           const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Max_6, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset6::maxpool(const std::vector<TensorId> &args,
                               const std::vector<int64_t> &kernel_shape,
                               const std::vector<int64_t> &pads,
                               const std::vector<int64_t> &strides,
                               const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  attributes["kernel_shape"] = kernel_shape;
  if (!pads.empty()) {
    attributes["pads"] = pads;
  }
  if (!strides.empty()) {
    attributes["strides"] = strides;
  }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::MaxPool_1,
      getOpsetVersion(),
      args,
      attributes,
      {di},
      [this](const std::vector<TensorId> &inputs_,
             std::map<std::string, popart::any> attributes_) {
        verify_AiOnnxOpset6_MaxPool_1(this->impl, inputs_, attributes_);
      });
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset6::maxroipool(const std::vector<TensorId> &args,
                                  const std::vector<int64_t> &pooled_shape,
                                  float spatial_scale,
                                  const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  attributes["pooled_shape"] = pooled_shape;
  if (spatial_scale != 1.0f) {
    attributes["spatial_scale"] = spatial_scale;
  }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::MaxRoiPool_1, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset6::mean(const std::vector<TensorId> &args,
                            const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Mean_6, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset6::min(const std::vector<TensorId> &args,
                           const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Min_6, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset6::mul(const std::vector<TensorId> &args,
                           nonstd::optional<int64_t> axis,
                           int64_t broadcast,
                           const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (axis != nonstd::optional<int64_t>()) {
    attributes["axis"] = *axis;
  }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["broadcast"] = broadcast; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Mul_6, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset6::neg(const std::vector<TensorId> &args,
                           const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Neg_6, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset6::logical_not(const std::vector<TensorId> &args,
                                   const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Not_1, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset6::logical_or(const std::vector<TensorId> &args,
                                  nonstd::optional<int64_t> axis,
                                  int64_t broadcast,
                                  const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (axis != nonstd::optional<int64_t>()) {
    attributes["axis"] = *axis;
  }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["broadcast"] = broadcast; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Or_1, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset6::prelu(const std::vector<TensorId> &args,
                             const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::PRelu_6, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset6::pad(const std::vector<TensorId> &args,
                           const std::vector<int64_t> &pads,
                           const std::string &mode,
                           float value,
                           const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (mode != "constant") {
    attributes["mode"] = mode;
  }
  attributes["pads"] = pads;
  if (value != 0.0f) {
    attributes["value"] = value;
  }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs =
      impl->op(Onnx::Operators::Pad_2,
               getOpsetVersion(),
               args,
               attributes,
               {di},
               [this](const std::vector<TensorId> &inputs_,
                      std::map<std::string, popart::any> attributes_) {
                 verify_AiOnnxOpset6_Pad_2(this->impl, inputs_, attributes_);
               });
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset6::pow(const std::vector<TensorId> &args,
                           nonstd::optional<int64_t> axis,
                           int64_t broadcast,
                           const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (axis != nonstd::optional<int64_t>()) {
    attributes["axis"] = *axis;
  }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["broadcast"] = broadcast; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Pow_1, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

std::vector<TensorId>
AiOnnxOpset6::rnn(const std::vector<TensorId> &args,
                  unsigned num_outputs,
                  const std::vector<float> &activation_alpha,
                  const std::vector<float> &activation_beta,
                  const std::vector<std::string> &activations,
                  nonstd::optional<float> clip,
                  const std::string &direction,
                  nonstd::optional<int64_t> hidden_size,
                  int64_t output_sequence,
                  const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (!activation_alpha.empty()) {
    attributes["activation_alpha"] = activation_alpha;
  }
  if (!activation_beta.empty()) {
    attributes["activation_beta"] = activation_beta;
  }
  if (!activations.empty()) {
    attributes["activations"] = activations;
  }
  if (clip != nonstd::optional<float>()) {
    attributes["clip"] = *clip;
  }
  if (direction != "forward") {
    attributes["direction"] = direction;
  }
  if (hidden_size != nonstd::optional<int64_t>()) {
    attributes["hidden_size"] = *hidden_size;
  }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["output_sequence"] = output_sequence; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(Onnx::Operators::RNN_1,
                          getOpsetVersion(),
                          args,
                          num_outputs,
                          attributes,
                          {di});
  di.setOutputs(outputs);
  return outputs;
}

TensorId AiOnnxOpset6::randomnormal(const std::vector<int64_t> &shape,
                                    int64_t dtype,
                                    float mean,
                                    float scale,
                                    nonstd::optional<float> seed,
                                    const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["dtype"] = dtype; }
  if (mean != 0.0f) {
    attributes["mean"] = mean;
  }
  if (scale != 1.0f) {
    attributes["scale"] = scale;
  }
  if (seed != nonstd::optional<float>()) {
    attributes["seed"] = *seed;
  }
  attributes["shape"] = shape;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, {}, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::RandomNormal_1, getOpsetVersion(), {}, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId
AiOnnxOpset6::randomnormallike(const std::vector<TensorId> &args,
                               nonstd::optional<int64_t> dtype,
                               float mean,
                               float scale,
                               nonstd::optional<float> seed,
                               const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (dtype != nonstd::optional<int64_t>()) {
    attributes["dtype"] = *dtype;
  }
  if (mean != 0.0f) {
    attributes["mean"] = mean;
  }
  if (scale != 1.0f) {
    attributes["scale"] = scale;
  }
  if (seed != nonstd::optional<float>()) {
    attributes["seed"] = *seed;
  }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(Onnx::Operators::RandomNormalLike_1,
                          getOpsetVersion(),
                          args,
                          attributes,
                          {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset6::randomuniform(const std::vector<int64_t> &shape,
                                     int64_t dtype,
                                     float high,
                                     float low,
                                     nonstd::optional<float> seed,
                                     const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["dtype"] = dtype; }
  if (high != 1.0f) {
    attributes["high"] = high;
  }
  if (low != 0.0f) {
    attributes["low"] = low;
  }
  if (seed != nonstd::optional<float>()) {
    attributes["seed"] = *seed;
  }
  attributes["shape"] = shape;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, {}, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(Onnx::Operators::RandomUniform_1,
                          getOpsetVersion(),
                          {},
                          attributes,
                          {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId
AiOnnxOpset6::randomuniformlike(const std::vector<TensorId> &args,
                                nonstd::optional<int64_t> dtype,
                                float high,
                                float low,
                                nonstd::optional<float> seed,
                                const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (dtype != nonstd::optional<int64_t>()) {
    attributes["dtype"] = *dtype;
  }
  if (high != 1.0f) {
    attributes["high"] = high;
  }
  if (low != 0.0f) {
    attributes["low"] = low;
  }
  if (seed != nonstd::optional<float>()) {
    attributes["seed"] = *seed;
  }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(Onnx::Operators::RandomUniformLike_1,
                          getOpsetVersion(),
                          args,
                          attributes,
                          {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset6::reciprocal(const std::vector<TensorId> &args,
                                  const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Reciprocal_6, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset6::reducel1(const std::vector<TensorId> &args,
                                nonstd::optional<std::vector<int64_t>> axes,
                                int64_t keepdims,
                                const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (axes != nonstd::optional<std::vector<int64_t>>()) {
    attributes["axes"] = *axes;
  }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["keepdims"] = keepdims; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::ReduceL1_1, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset6::reducel2(const std::vector<TensorId> &args,
                                nonstd::optional<std::vector<int64_t>> axes,
                                int64_t keepdims,
                                const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (axes != nonstd::optional<std::vector<int64_t>>()) {
    attributes["axes"] = *axes;
  }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["keepdims"] = keepdims; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::ReduceL2_1, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset6::reducelogsum(const std::vector<TensorId> &args,
                                    nonstd::optional<std::vector<int64_t>> axes,
                                    int64_t keepdims,
                                    const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (axes != nonstd::optional<std::vector<int64_t>>()) {
    attributes["axes"] = *axes;
  }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["keepdims"] = keepdims; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(Onnx::Operators::ReduceLogSum_1,
                          getOpsetVersion(),
                          args,
                          attributes,
                          {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId
AiOnnxOpset6::reducelogsumexp(const std::vector<TensorId> &args,
                              nonstd::optional<std::vector<int64_t>> axes,
                              int64_t keepdims,
                              const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (axes != nonstd::optional<std::vector<int64_t>>()) {
    attributes["axes"] = *axes;
  }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["keepdims"] = keepdims; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(Onnx::Operators::ReduceLogSumExp_1,
                          getOpsetVersion(),
                          args,
                          attributes,
                          {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset6::reducemax(const std::vector<TensorId> &args,
                                 nonstd::optional<std::vector<int64_t>> axes,
                                 int64_t keepdims,
                                 const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (axes != nonstd::optional<std::vector<int64_t>>()) {
    attributes["axes"] = *axes;
  }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["keepdims"] = keepdims; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::ReduceMax_1, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset6::reducemean(const std::vector<TensorId> &args,
                                  nonstd::optional<std::vector<int64_t>> axes,
                                  int64_t keepdims,
                                  const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (axes != nonstd::optional<std::vector<int64_t>>()) {
    attributes["axes"] = *axes;
  }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["keepdims"] = keepdims; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::ReduceMean_1, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset6::reducemin(const std::vector<TensorId> &args,
                                 nonstd::optional<std::vector<int64_t>> axes,
                                 int64_t keepdims,
                                 const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (axes != nonstd::optional<std::vector<int64_t>>()) {
    attributes["axes"] = *axes;
  }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["keepdims"] = keepdims; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::ReduceMin_1, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset6::reduceprod(const std::vector<TensorId> &args,
                                  nonstd::optional<std::vector<int64_t>> axes,
                                  int64_t keepdims,
                                  const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (axes != nonstd::optional<std::vector<int64_t>>()) {
    attributes["axes"] = *axes;
  }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["keepdims"] = keepdims; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::ReduceProd_1, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset6::reducesum(const std::vector<TensorId> &args,
                                 nonstd::optional<std::vector<int64_t>> axes,
                                 int64_t keepdims,
                                 const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (axes != nonstd::optional<std::vector<int64_t>>()) {
    attributes["axes"] = *axes;
  }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["keepdims"] = keepdims; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::ReduceSum_1, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId
AiOnnxOpset6::reducesumsquare(const std::vector<TensorId> &args,
                              nonstd::optional<std::vector<int64_t>> axes,
                              int64_t keepdims,
                              const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (axes != nonstd::optional<std::vector<int64_t>>()) {
    attributes["axes"] = *axes;
  }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["keepdims"] = keepdims; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(Onnx::Operators::ReduceSumSquare_1,
                          getOpsetVersion(),
                          args,
                          attributes,
                          {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset6::relu(const std::vector<TensorId> &args,
                            const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Relu_6, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset6::reshape(const std::vector<TensorId> &args,
                               const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Reshape_5, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset6::selu(const std::vector<TensorId> &args,
                            float alpha,
                            float gamma,
                            const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (alpha != 1.67326f) {
    attributes["alpha"] = alpha;
  }
  if (gamma != 1.0507f) {
    attributes["gamma"] = gamma;
  }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Selu_6, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset6::shape(const std::vector<TensorId> &args,
                             const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Shape_1, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset6::sigmoid(const std::vector<TensorId> &args,
                               const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Sigmoid_6, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset6::size(const std::vector<TensorId> &args,
                            const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Size_1, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset6::slice(const std::vector<TensorId> &args,
                             const std::vector<int64_t> &ends,
                             const std::vector<int64_t> &starts,
                             const std::vector<int64_t> &axes,
                             const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (!axes.empty()) {
    attributes["axes"] = axes;
  }
  attributes["ends"]   = ends;
  attributes["starts"] = starts;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Slice_1, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset6::softmax(const std::vector<TensorId> &args,
                               int64_t axis,
                               const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["axis"] = axis; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Softmax_1, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset6::softplus(const std::vector<TensorId> &args,
                                const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Softplus_1, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset6::softsign(const std::vector<TensorId> &args,
                                const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Softsign_1, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset6::spacetodepth(const std::vector<TensorId> &args,
                                    int64_t blocksize,
                                    const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  attributes["blocksize"] = blocksize;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(Onnx::Operators::SpaceToDepth_1,
                          getOpsetVersion(),
                          args,
                          attributes,
                          {di});
  di.setOutputs(outputs);
  return outputs[0];
}

std::vector<TensorId>
AiOnnxOpset6::split(const std::vector<TensorId> &args,
                    unsigned num_outputs,
                    int64_t axis,
                    const std::vector<int64_t> &split,
                    const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["axis"] = axis; }
  if (!split.empty()) {
    attributes["split"] = split;
  }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(Onnx::Operators::Split_2,
                          getOpsetVersion(),
                          args,
                          num_outputs,
                          attributes,
                          {di});
  di.setOutputs(outputs);
  return outputs;
}

TensorId AiOnnxOpset6::sqrt(const std::vector<TensorId> &args,
                            const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Sqrt_6, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset6::squeeze(const std::vector<TensorId> &args,
                               const std::vector<int64_t> &axes,
                               const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (!axes.empty()) {
    attributes["axes"] = axes;
  }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Squeeze_1, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset6::sub(const std::vector<TensorId> &args,
                           nonstd::optional<int64_t> axis,
                           int64_t broadcast,
                           const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (axis != nonstd::optional<int64_t>()) {
    attributes["axis"] = *axis;
  }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["broadcast"] = broadcast; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Sub_6, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset6::sum(const std::vector<TensorId> &args,
                           const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Sum_6, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset6::tanh(const std::vector<TensorId> &args,
                            const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Tanh_6, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset6::tile(const std::vector<TensorId> &args,
                            const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Tile_6, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

std::vector<TensorId>
AiOnnxOpset6::topk(const std::vector<TensorId> &args,
                   int64_t k,
                   int64_t axis,
                   const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["axis"] = axis; }
  attributes["k"] = k;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::TopK_1, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs;
}

TensorId AiOnnxOpset6::transpose(const std::vector<TensorId> &args,
                                 const std::vector<int64_t> &perm,
                                 const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (!perm.empty()) {
    attributes["perm"] = perm;
  }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Transpose_1, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset6::unsqueeze(const std::vector<TensorId> &args,
                                 const std::vector<int64_t> &axes,
                                 const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  attributes["axes"] = axes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Unsqueeze_1, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset6::upsample(const std::vector<TensorId> &args,
                                float height_scale,
                                float width_scale,
                                const std::string &mode,
                                const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  attributes["height_scale"] = height_scale;
  if (mode != "nearest") {
    attributes["mode"] = mode;
  }
  attributes["width_scale"] = width_scale;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Upsample_1, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset6::logical_xor(const std::vector<TensorId> &args,
                                   nonstd::optional<int64_t> axis,
                                   int64_t broadcast,
                                   const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (axis != nonstd::optional<int64_t>()) {
    attributes["axis"] = *axis;
  }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["broadcast"] = broadcast; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Xor_1, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset7::acos(const std::vector<TensorId> &args,
                            const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Acos_7, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset7::add(const std::vector<TensorId> &args,
                           const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Add_7, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset7::logical_and(const std::vector<TensorId> &args,
                                   const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::And_7, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset7::asin(const std::vector<TensorId> &args,
                            const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Asin_7, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset7::atan(const std::vector<TensorId> &args,
                            const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Atan_7, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset7::averagepool(const std::vector<TensorId> &args,
                                   const std::vector<int64_t> &kernel_shape,
                                   int64_t count_include_pad,
                                   const std::vector<int64_t> &pads,
                                   const std::vector<int64_t> &strides,
                                   const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["count_include_pad"] = count_include_pad; }
  attributes["kernel_shape"] = kernel_shape;
  if (!pads.empty()) {
    attributes["pads"] = pads;
  }
  if (!strides.empty()) {
    attributes["strides"] = strides;
  }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::AveragePool_7,
      getOpsetVersion(),
      args,
      attributes,
      {di},
      [this](const std::vector<TensorId> &inputs_,
             std::map<std::string, popart::any> attributes_) {
        verify_AiOnnxOpset7_AveragePool_7(this->impl, inputs_, attributes_);
      });
  di.setOutputs(outputs);
  return outputs[0];
}

std::vector<TensorId>
AiOnnxOpset7::batchnormalization(const std::vector<TensorId> &args,
                                 unsigned num_outputs,
                                 float epsilon,
                                 float momentum,
                                 int64_t spatial,
                                 const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (epsilon != 1e-05f) {
    attributes["epsilon"] = epsilon;
  }
  if (momentum != 0.9f) {
    attributes["momentum"] = momentum;
  }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["spatial"] = spatial; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(Onnx::Operators::BatchNormalization_7,
                          getOpsetVersion(),
                          args,
                          num_outputs,
                          attributes,
                          {di});
  di.setOutputs(outputs);
  return outputs;
}

TensorId AiOnnxOpset7::cos(const std::vector<TensorId> &args,
                           const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Cos_7, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset7::div(const std::vector<TensorId> &args,
                           const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Div_7, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

std::vector<TensorId>
AiOnnxOpset7::dropout(const std::vector<TensorId> &args,
                      unsigned num_outputs,
                      float ratio,
                      const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (ratio != 0.5f) {
    attributes["ratio"] = ratio;
  }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(Onnx::Operators::Dropout_7,
                          getOpsetVersion(),
                          args,
                          num_outputs,
                          attributes,
                          {di});
  di.setOutputs(outputs);
  return outputs;
}

TensorId AiOnnxOpset7::equal(const std::vector<TensorId> &args,
                             const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Equal_7, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

std::vector<TensorId>
AiOnnxOpset7::gru(const std::vector<TensorId> &args,
                  unsigned num_outputs,
                  const std::vector<float> &activation_alpha,
                  const std::vector<float> &activation_beta,
                  const std::vector<std::string> &activations,
                  nonstd::optional<float> clip,
                  const std::string &direction,
                  nonstd::optional<int64_t> hidden_size,
                  int64_t linear_before_reset,
                  const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (!activation_alpha.empty()) {
    attributes["activation_alpha"] = activation_alpha;
  }
  if (!activation_beta.empty()) {
    attributes["activation_beta"] = activation_beta;
  }
  if (!activations.empty()) {
    attributes["activations"] = activations;
  }
  if (clip != nonstd::optional<float>()) {
    attributes["clip"] = *clip;
  }
  if (direction != "forward") {
    attributes["direction"] = direction;
  }
  if (hidden_size != nonstd::optional<int64_t>()) {
    attributes["hidden_size"] = *hidden_size;
  }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["linear_before_reset"] = linear_before_reset; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(Onnx::Operators::GRU_7,
                          getOpsetVersion(),
                          args,
                          num_outputs,
                          attributes,
                          {di});
  di.setOutputs(outputs);
  return outputs;
}

TensorId AiOnnxOpset7::gemm(const std::vector<TensorId> &args,
                            float alpha,
                            float beta,
                            int64_t transA,
                            int64_t transB,
                            const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (alpha != 1.0f) {
    attributes["alpha"] = alpha;
  }
  if (beta != 1.0f) {
    attributes["beta"] = beta;
  }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["transA"] = transA; }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["transB"] = transB; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Gemm_7, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset7::greater(const std::vector<TensorId> &args,
                               const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Greater_7, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

std::vector<TensorId>
AiOnnxOpset7::lstm(const std::vector<TensorId> &args,
                   unsigned num_outputs,
                   const std::vector<float> &activation_alpha,
                   const std::vector<float> &activation_beta,
                   const std::vector<std::string> &activations,
                   nonstd::optional<float> clip,
                   const std::string &direction,
                   nonstd::optional<int64_t> hidden_size,
                   int64_t input_forget,
                   const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (!activation_alpha.empty()) {
    attributes["activation_alpha"] = activation_alpha;
  }
  if (!activation_beta.empty()) {
    attributes["activation_beta"] = activation_beta;
  }
  if (!activations.empty()) {
    attributes["activations"] = activations;
  }
  if (clip != nonstd::optional<float>()) {
    attributes["clip"] = *clip;
  }
  if (direction != "forward") {
    attributes["direction"] = direction;
  }
  if (hidden_size != nonstd::optional<int64_t>()) {
    attributes["hidden_size"] = *hidden_size;
  }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["input_forget"] = input_forget; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(Onnx::Operators::LSTM_7,
                          getOpsetVersion(),
                          args,
                          num_outputs,
                          attributes,
                          {di});
  di.setOutputs(outputs);
  return outputs;
}

TensorId AiOnnxOpset7::less(const std::vector<TensorId> &args,
                            const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Less_7, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset7::mul(const std::vector<TensorId> &args,
                           const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Mul_7, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset7::multinomial(const std::vector<TensorId> &args,
                                   int64_t dtype,
                                   int64_t sample_size,
                                   nonstd::optional<float> seed,
                                   const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["dtype"] = dtype; }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["sample_size"] = sample_size; }
  if (seed != nonstd::optional<float>()) {
    attributes["seed"] = *seed;
  }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(Onnx::Operators::Multinomial_7,
                          getOpsetVersion(),
                          args,
                          attributes,
                          {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset7::logical_or(const std::vector<TensorId> &args,
                                  const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Or_7, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset7::prelu(const std::vector<TensorId> &args,
                             const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::PRelu_7, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset7::pow(const std::vector<TensorId> &args,
                           const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Pow_7, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

std::vector<TensorId>
AiOnnxOpset7::rnn(const std::vector<TensorId> &args,
                  unsigned num_outputs,
                  const std::vector<float> &activation_alpha,
                  const std::vector<float> &activation_beta,
                  const std::vector<std::string> &activations,
                  nonstd::optional<float> clip,
                  const std::string &direction,
                  nonstd::optional<int64_t> hidden_size,
                  const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (!activation_alpha.empty()) {
    attributes["activation_alpha"] = activation_alpha;
  }
  if (!activation_beta.empty()) {
    attributes["activation_beta"] = activation_beta;
  }
  if (!activations.empty()) {
    attributes["activations"] = activations;
  }
  if (clip != nonstd::optional<float>()) {
    attributes["clip"] = *clip;
  }
  if (direction != "forward") {
    attributes["direction"] = direction;
  }
  if (hidden_size != nonstd::optional<int64_t>()) {
    attributes["hidden_size"] = *hidden_size;
  }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(Onnx::Operators::RNN_7,
                          getOpsetVersion(),
                          args,
                          num_outputs,
                          attributes,
                          {di});
  di.setOutputs(outputs);
  return outputs;
}

TensorId AiOnnxOpset7::sin(const std::vector<TensorId> &args,
                           const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Sin_7, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset7::sub(const std::vector<TensorId> &args,
                           const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Sub_7, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset7::tan(const std::vector<TensorId> &args,
                           const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Tan_7, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset7::upsample(const std::vector<TensorId> &args,
                                const std::vector<float> &scales,
                                const std::string &mode,
                                const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (mode != "nearest") {
    attributes["mode"] = mode;
  }
  attributes["scales"] = scales;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Upsample_7, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset7::logical_xor(const std::vector<TensorId> &args,
                                   const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Xor_7, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset8::expand(const std::vector<TensorId> &args,
                              const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Expand_8, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset8::max(const std::vector<TensorId> &args,
                           const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Max_8, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

std::vector<TensorId>
AiOnnxOpset8::maxpool(const std::vector<TensorId> &args,
                      unsigned num_outputs,
                      const std::vector<int64_t> &kernel_shape,
                      const std::vector<int64_t> &pads,
                      int64_t storage_order,
                      const std::vector<int64_t> &strides,
                      const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  attributes["kernel_shape"] = kernel_shape;
  if (!pads.empty()) {
    attributes["pads"] = pads;
  }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["storage_order"] = storage_order; }
  if (!strides.empty()) {
    attributes["strides"] = strides;
  }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::MaxPool_8,
      getOpsetVersion(),
      args,
      num_outputs,
      attributes,
      {di},
      [this](const std::vector<TensorId> &inputs_,
             std::map<std::string, popart::any> attributes_) {
        verify_AiOnnxOpset8_MaxPool_8(this->impl, inputs_, attributes_);
      });
  di.setOutputs(outputs);
  return outputs;
}

TensorId AiOnnxOpset8::mean(const std::vector<TensorId> &args,
                            const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Mean_8, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset8::min(const std::vector<TensorId> &args,
                           const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Min_8, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

std::vector<TensorId>
AiOnnxOpset8::scan(const std::vector<TensorId> &args,
                   unsigned num_outputs,
                   const Builder &body,
                   int64_t num_scan_inputs,
                   const std::vector<int64_t> &directions,
                   const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  // Special case where we convert from a Builder object to an
  // onnx::GraphProto object so as not to expose the onnx class
  // at the API level
  attributes["body"] = io::getModelFromString(body.getModelProto()).graph();
  if (!directions.empty()) {
    attributes["directions"] = directions;
  }
  attributes["num_scan_inputs"] = num_scan_inputs;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(Onnx::Operators::Scan_8,
                          getOpsetVersion(),
                          args,
                          num_outputs,
                          attributes,
                          {di});
  di.setOutputs(outputs);
  return outputs;
}

TensorId AiOnnxOpset8::sum(const std::vector<TensorId> &args,
                           const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Sum_8, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset9::acosh(const std::vector<TensorId> &args,
                             const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Acosh_9, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset9::asinh(const std::vector<TensorId> &args,
                             const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Asinh_9, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset9::atanh(const std::vector<TensorId> &args,
                             const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Atanh_9, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

std::vector<TensorId>
AiOnnxOpset9::batchnormalization(const std::vector<TensorId> &args,
                                 unsigned num_outputs,
                                 float epsilon,
                                 float momentum,
                                 const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (epsilon != 1e-05f) {
    attributes["epsilon"] = epsilon;
  }
  if (momentum != 0.9f) {
    attributes["momentum"] = momentum;
  }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(Onnx::Operators::BatchNormalization_9,
                          getOpsetVersion(),
                          args,
                          num_outputs,
                          attributes,
                          {di});
  di.setOutputs(outputs);
  return outputs;
}

TensorId AiOnnxOpset9::cast(const std::vector<TensorId> &args,
                            const std::string &to,
                            const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  // Special case where we cast from DataType to int
  DataType toDataType = dataTypeFromString(to);
  attributes["to"]    = static_cast<int>(onnxutil::getTPDataType(toDataType));
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Cast_9, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset9::compress(const std::vector<TensorId> &args,
                                nonstd::optional<int64_t> axis,
                                const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (axis != nonstd::optional<int64_t>()) {
    attributes["axis"] = *axis;
  }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Compress_9, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset9::constant(const ConstVoidData &value,
                                const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  attributes["value"] = value;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, {}, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Constant_9, getOpsetVersion(), {}, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId
AiOnnxOpset9::constantofshape(const std::vector<TensorId> &args,
                              const ConstVoidData &value,
                              const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  attributes["value"] = value;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(Onnx::Operators::ConstantOfShape_9,
                          getOpsetVersion(),
                          args,
                          attributes,
                          {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset9::cosh(const std::vector<TensorId> &args,
                            const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Cosh_9, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset9::erf(const std::vector<TensorId> &args,
                           const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Erf_9, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset9::eyelike(const std::vector<TensorId> &args,
                               nonstd::optional<int64_t> dtype,
                               int64_t k,
                               const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (dtype != nonstd::optional<int64_t>()) {
    attributes["dtype"] = *dtype;
  }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["k"] = k; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::EyeLike_9, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset9::flatten(const std::vector<TensorId> &args,
                               int64_t axis,
                               const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["axis"] = axis; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Flatten_9, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset9::gemm(const std::vector<TensorId> &args,
                            float alpha,
                            float beta,
                            int64_t transA,
                            int64_t transB,
                            const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (alpha != 1.0f) {
    attributes["alpha"] = alpha;
  }
  if (beta != 1.0f) {
    attributes["beta"] = beta;
  }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["transA"] = transA; }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["transB"] = transB; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Gemm_9, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset9::greater(const std::vector<TensorId> &args,
                               const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Greater_9, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset9::isnan(const std::vector<TensorId> &args,
                             const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::IsNaN_9, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset9::less(const std::vector<TensorId> &args,
                            const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Less_9, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset9::matmul(const std::vector<TensorId> &args,
                              const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::MatMul_9, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset9::maxunpool(const std::vector<TensorId> &args,
                                 const std::vector<int64_t> &kernel_shape,
                                 const std::vector<int64_t> &pads,
                                 const std::vector<int64_t> &strides,
                                 const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  attributes["kernel_shape"] = kernel_shape;
  if (!pads.empty()) {
    attributes["pads"] = pads;
  }
  if (!strides.empty()) {
    attributes["strides"] = strides;
  }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::MaxUnpool_9, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset9::meanvariancenormalization(
    const std::vector<TensorId> &args,
    const std::vector<int64_t> &axes,
    const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (!axes.empty()) {
    attributes["axes"] = axes;
  }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(Onnx::Operators::MeanVarianceNormalization_9,
                          getOpsetVersion(),
                          args,
                          attributes,
                          {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset9::nonzero(const std::vector<TensorId> &args,
                               const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::NonZero_9, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset9::onehot(const std::vector<TensorId> &args,
                              int64_t axis,
                              const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["axis"] = axis; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::OneHot_9, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset9::prelu(const std::vector<TensorId> &args,
                             const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::PRelu_9, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

std::vector<TensorId>
AiOnnxOpset9::scan(const std::vector<TensorId> &args,
                   unsigned num_outputs,
                   const Builder &body,
                   int64_t num_scan_inputs,
                   const std::vector<int64_t> &scan_input_axes,
                   const std::vector<int64_t> &scan_input_directions,
                   const std::vector<int64_t> &scan_output_axes,
                   const std::vector<int64_t> &scan_output_directions,
                   const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  // Special case where we convert from a Builder object to an
  // onnx::GraphProto object so as not to expose the onnx class
  // at the API level
  attributes["body"] = io::getModelFromString(body.getModelProto()).graph();
  attributes["num_scan_inputs"] = num_scan_inputs;
  if (!scan_input_axes.empty()) {
    attributes["scan_input_axes"] = scan_input_axes;
  }
  if (!scan_input_directions.empty()) {
    attributes["scan_input_directions"] = scan_input_directions;
  }
  if (!scan_output_axes.empty()) {
    attributes["scan_output_axes"] = scan_output_axes;
  }
  if (!scan_output_directions.empty()) {
    attributes["scan_output_directions"] = scan_output_directions;
  }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(Onnx::Operators::Scan_9,
                          getOpsetVersion(),
                          args,
                          num_outputs,
                          attributes,
                          {di});
  di.setOutputs(outputs);
  return outputs;
}

TensorId AiOnnxOpset9::scatter(const std::vector<TensorId> &args,
                               int64_t axis,
                               const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["axis"] = axis; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Scatter_9, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset9::shrink(const std::vector<TensorId> &args,
                              float bias,
                              float lambd,
                              const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (bias != 0.0f) {
    attributes["bias"] = bias;
  }
  if (lambd != 0.5f) {
    attributes["lambd"] = lambd;
  }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Shrink_9, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset9::sign(const std::vector<TensorId> &args,
                            const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Sign_9, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset9::sinh(const std::vector<TensorId> &args,
                            const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Sinh_9, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId
AiOnnxOpset9::tfidfvectorizer(const std::vector<TensorId> &args,
                              int64_t max_gram_length,
                              int64_t max_skip_count,
                              int64_t min_gram_length,
                              const std::string &mode,
                              const std::vector<int64_t> &ngram_counts,
                              const std::vector<int64_t> &ngram_indexes,
                              const std::vector<int64_t> &pool_int64s,
                              const std::vector<std::string> &pool_strings,
                              const std::vector<float> &weights,
                              const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  attributes["max_gram_length"] = max_gram_length;
  attributes["max_skip_count"]  = max_skip_count;
  attributes["min_gram_length"] = min_gram_length;
  attributes["mode"]            = mode;
  attributes["ngram_counts"]    = ngram_counts;
  attributes["ngram_indexes"]   = ngram_indexes;
  if (!pool_int64s.empty()) {
    attributes["pool_int64s"] = pool_int64s;
  }
  if (!pool_strings.empty()) {
    attributes["pool_strings"] = pool_strings;
  }
  if (!weights.empty()) {
    attributes["weights"] = weights;
  }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(Onnx::Operators::TfIdfVectorizer_9,
                          getOpsetVersion(),
                          args,
                          attributes,
                          {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset9::upsample(const std::vector<TensorId> &args,
                                const std::string &mode,
                                const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (mode != "nearest") {
    attributes["mode"] = mode;
  }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Upsample_9, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset9::where(const std::vector<TensorId> &args,
                             const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Where_9, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset10::averagepool(const std::vector<TensorId> &args,
                                    const std::vector<int64_t> &kernel_shape,
                                    int64_t ceil_mode,
                                    int64_t count_include_pad,
                                    const std::vector<int64_t> &pads,
                                    const std::vector<int64_t> &strides,
                                    const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["ceil_mode"] = ceil_mode; }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["count_include_pad"] = count_include_pad; }
  attributes["kernel_shape"] = kernel_shape;
  if (!pads.empty()) {
    attributes["pads"] = pads;
  }
  if (!strides.empty()) {
    attributes["strides"] = strides;
  }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::AveragePool_10,
      getOpsetVersion(),
      args,
      attributes,
      {di},
      [this](const std::vector<TensorId> &inputs_,
             std::map<std::string, popart::any> attributes_) {
        verify_AiOnnxOpset10_AveragePool_10(this->impl, inputs_, attributes_);
      });
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset10::convinteger(const std::vector<TensorId> &args,
                                    const std::vector<int64_t> &dilations,
                                    int64_t group,
                                    const std::vector<int64_t> &kernel_shape,
                                    const std::vector<int64_t> &pads,
                                    const std::vector<int64_t> &strides,
                                    const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (!dilations.empty()) {
    attributes["dilations"] = dilations;
  }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["group"] = group; }
  if (!kernel_shape.empty()) {
    attributes["kernel_shape"] = kernel_shape;
  }
  if (!pads.empty()) {
    attributes["pads"] = pads;
  }
  if (!strides.empty()) {
    attributes["strides"] = strides;
  }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(Onnx::Operators::ConvInteger_10,
                          getOpsetVersion(),
                          args,
                          attributes,
                          {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId
AiOnnxOpset10::dequantizelinear(const std::vector<TensorId> &args,
                                const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(Onnx::Operators::DequantizeLinear_10,
                          getOpsetVersion(),
                          args,
                          attributes,
                          {di});
  di.setOutputs(outputs);
  return outputs[0];
}

std::vector<TensorId>
AiOnnxOpset10::dropout(const std::vector<TensorId> &args,
                       unsigned num_outputs,
                       float ratio,
                       const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (ratio != 0.5f) {
    attributes["ratio"] = ratio;
  }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(Onnx::Operators::Dropout_10,
                          getOpsetVersion(),
                          args,
                          num_outputs,
                          attributes,
                          {di});
  di.setOutputs(outputs);
  return outputs;
}

TensorId AiOnnxOpset10::isinf(const std::vector<TensorId> &args,
                              int64_t detect_negative,
                              int64_t detect_positive,
                              const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["detect_negative"] = detect_negative; }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["detect_positive"] = detect_positive; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::IsInf_10, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId
AiOnnxOpset10::matmulinteger(const std::vector<TensorId> &args,
                             const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(Onnx::Operators::MatMulInteger_10,
                          getOpsetVersion(),
                          args,
                          attributes,
                          {di});
  di.setOutputs(outputs);
  return outputs[0];
}

std::vector<TensorId>
AiOnnxOpset10::maxpool(const std::vector<TensorId> &args,
                       unsigned num_outputs,
                       const std::vector<int64_t> &kernel_shape,
                       int64_t ceil_mode,
                       const std::vector<int64_t> &dilations,
                       const std::vector<int64_t> &pads,
                       int64_t storage_order,
                       const std::vector<int64_t> &strides,
                       const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["ceil_mode"] = ceil_mode; }
  if (!dilations.empty()) {
    attributes["dilations"] = dilations;
  }
  attributes["kernel_shape"] = kernel_shape;
  if (!pads.empty()) {
    attributes["pads"] = pads;
  }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["storage_order"] = storage_order; }
  if (!strides.empty()) {
    attributes["strides"] = strides;
  }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::MaxPool_10,
      getOpsetVersion(),
      args,
      num_outputs,
      attributes,
      {di},
      [this](const std::vector<TensorId> &inputs_,
             std::map<std::string, popart::any> attributes_) {
        verify_AiOnnxOpset10_MaxPool_10(this->impl, inputs_, attributes_);
      });
  di.setOutputs(outputs);
  return outputs;
}

TensorId AiOnnxOpset10::mod(const std::vector<TensorId> &args,
                            int64_t fmod,
                            const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["fmod"] = fmod; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Mod_10, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId
AiOnnxOpset10::nonmaxsuppression(const std::vector<TensorId> &args,
                                 int64_t center_point_box,
                                 const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["center_point_box"] = center_point_box; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(Onnx::Operators::NonMaxSuppression_10,
                          getOpsetVersion(),
                          args,
                          attributes,
                          {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset10::qlinearconv(const std::vector<TensorId> &args,
                                    const std::vector<int64_t> &dilations,
                                    int64_t group,
                                    const std::vector<int64_t> &kernel_shape,
                                    const std::vector<int64_t> &pads,
                                    const std::vector<int64_t> &strides,
                                    const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (!dilations.empty()) {
    attributes["dilations"] = dilations;
  }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["group"] = group; }
  if (!kernel_shape.empty()) {
    attributes["kernel_shape"] = kernel_shape;
  }
  if (!pads.empty()) {
    attributes["pads"] = pads;
  }
  if (!strides.empty()) {
    attributes["strides"] = strides;
  }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(Onnx::Operators::QLinearConv_10,
                          getOpsetVersion(),
                          args,
                          attributes,
                          {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId
AiOnnxOpset10::qlinearmatmul(const std::vector<TensorId> &args,
                             const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(Onnx::Operators::QLinearMatMul_10,
                          getOpsetVersion(),
                          args,
                          attributes,
                          {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId
AiOnnxOpset10::quantizelinear(const std::vector<TensorId> &args,
                              const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(Onnx::Operators::QuantizeLinear_10,
                          getOpsetVersion(),
                          args,
                          attributes,
                          {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset10::resize(const std::vector<TensorId> &args,
                               const std::string &mode,
                               const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (mode != "nearest") {
    attributes["mode"] = mode;
  }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Resize_10, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId
AiOnnxOpset10::reversesequence(const std::vector<TensorId> &args,
                               int64_t batch_axis,
                               int64_t time_axis,
                               const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["batch_axis"] = batch_axis; }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["time_axis"] = time_axis; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(Onnx::Operators::ReverseSequence_10,
                          getOpsetVersion(),
                          args,
                          attributes,
                          {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset10::roialign(const std::vector<TensorId> &args,
                                 const std::string &mode,
                                 int64_t output_height,
                                 int64_t output_width,
                                 int64_t sampling_ratio,
                                 float spatial_scale,
                                 const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (mode != "avg") {
    attributes["mode"] = mode;
  }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["output_height"] = output_height; }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["output_width"] = output_width; }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["sampling_ratio"] = sampling_ratio; }
  if (spatial_scale != 1.0f) {
    attributes["spatial_scale"] = spatial_scale;
  }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::RoiAlign_10, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset10::slice(const std::vector<TensorId> &args,
                              const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Slice_10, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId
AiOnnxOpset10::stringnormalizer(const std::vector<TensorId> &args,
                                const std::string &case_change_action,
                                int64_t is_case_sensitive,
                                nonstd::optional<std::string> locale,
                                const std::vector<std::string> &stopwords,
                                const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (case_change_action != "NONE") {
    attributes["case_change_action"] = case_change_action;
  }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["is_case_sensitive"] = is_case_sensitive; }
  if (locale != std::string()) {
    attributes["locale"] = *locale;
  }
  if (!stopwords.empty()) {
    attributes["stopwords"] = stopwords;
  }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(Onnx::Operators::StringNormalizer_10,
                          getOpsetVersion(),
                          args,
                          attributes,
                          {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId
AiOnnxOpset10::thresholdedrelu(const std::vector<TensorId> &args,
                               float alpha,
                               const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (alpha != 1.0f) {
    attributes["alpha"] = alpha;
  }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(Onnx::Operators::ThresholdedRelu_10,
                          getOpsetVersion(),
                          args,
                          attributes,
                          {di});
  di.setOutputs(outputs);
  return outputs[0];
}

std::vector<TensorId>
AiOnnxOpset10::topk(const std::vector<TensorId> &args,
                    int64_t axis,
                    const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["axis"] = axis; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::TopK_10, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs;
}

TensorId AiOnnxOpset10::upsample(const std::vector<TensorId> &args,
                                 const std::string &mode,
                                 const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (mode != "nearest") {
    attributes["mode"] = mode;
  }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Upsample_10, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset11::argmax(const std::vector<TensorId> &args,
                               int64_t axis,
                               int64_t keepdims,
                               const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["axis"] = axis; }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["keepdims"] = keepdims; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::ArgMax_11, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset11::argmin(const std::vector<TensorId> &args,
                               int64_t axis,
                               int64_t keepdims,
                               const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["axis"] = axis; }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["keepdims"] = keepdims; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::ArgMin_11, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset11::averagepool(const std::vector<TensorId> &args,
                                    const std::vector<int64_t> &kernel_shape,
                                    int64_t ceil_mode,
                                    int64_t count_include_pad,
                                    const std::vector<int64_t> &pads,
                                    const std::vector<int64_t> &strides,
                                    const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["ceil_mode"] = ceil_mode; }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["count_include_pad"] = count_include_pad; }
  attributes["kernel_shape"] = kernel_shape;
  if (!pads.empty()) {
    attributes["pads"] = pads;
  }
  if (!strides.empty()) {
    attributes["strides"] = strides;
  }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::AveragePool_11,
      getOpsetVersion(),
      args,
      attributes,
      {di},
      [this](const std::vector<TensorId> &inputs_,
             std::map<std::string, popart::any> attributes_) {
        verify_AiOnnxOpset11_AveragePool_11(this->impl, inputs_, attributes_);
      });
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset11::bitshift(const std::vector<TensorId> &args,
                                 const std::string &direction,
                                 const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  attributes["direction"] = direction;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::BitShift_11, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset11::clip(const std::vector<TensorId> &args,
                             const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Clip_11, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset11::compress(const std::vector<TensorId> &args,
                                 nonstd::optional<int64_t> axis,
                                 const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (axis != nonstd::optional<int64_t>()) {
    attributes["axis"] = *axis;
  }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Compress_11, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset11::concat(const std::vector<TensorId> &args,
                               int64_t axis,
                               const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  attributes["axis"] = axis;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Concat_11, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId
AiOnnxOpset11::concatfromsequence(const std::vector<TensorId> &args,
                                  int64_t axis,
                                  int64_t new_axis,
                                  const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  attributes["axis"] = axis;
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["new_axis"] = new_axis; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(Onnx::Operators::ConcatFromSequence_11,
                          getOpsetVersion(),
                          args,
                          attributes,
                          {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset11::constant(const ConstVoidData &value,
                                 bool is_value_sparse,
                                 const DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (is_value_sparse) {
    throw error(
        "Attributes of type `sparse_tensor' are currently not supported.");
  } else {
    attributes["value"] = value;
  }

  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, {}, attributes);
  attributes.insert({sDebugInfoId, di.getId()});

  auto outputs = impl->op(
      Onnx::Operators::Constant_11, getOpsetVersion(), {}, attributes, {di});
  di.setOutputs(outputs);
  return outputs.at(0);
}

TensorId AiOnnxOpset11::conv(const std::vector<TensorId> &args,
                             const std::vector<int64_t> &dilations,
                             int64_t group,
                             const std::vector<int64_t> &kernel_shape,
                             const std::vector<int64_t> &pads,
                             const std::vector<int64_t> &strides,
                             const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (!dilations.empty()) {
    attributes["dilations"] = dilations;
  }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["group"] = group; }
  if (!kernel_shape.empty()) {
    attributes["kernel_shape"] = kernel_shape;
  }
  if (!pads.empty()) {
    attributes["pads"] = pads;
  }
  if (!strides.empty()) {
    attributes["strides"] = strides;
  }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs =
      impl->op(Onnx::Operators::Conv_11,
               getOpsetVersion(),
               args,
               attributes,
               {di},
               [this](const std::vector<TensorId> &inputs_,
                      std::map<std::string, popart::any> attributes_) {
                 verify_AiOnnxOpset11_Conv_11(this->impl, inputs_, attributes_);
               });
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId
AiOnnxOpset11::convtranspose(const std::vector<TensorId> &args,
                             const std::vector<int64_t> &dilations,
                             int64_t group,
                             const std::vector<int64_t> &kernel_shape,
                             const std::vector<int64_t> &output_padding,
                             const std::vector<int64_t> &output_shape,
                             const std::vector<int64_t> &pads,
                             const std::vector<int64_t> &strides,
                             const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (!dilations.empty()) {
    attributes["dilations"] = dilations;
  }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["group"] = group; }
  if (!kernel_shape.empty()) {
    attributes["kernel_shape"] = kernel_shape;
  }
  if (!output_padding.empty()) {
    attributes["output_padding"] = output_padding;
  }
  if (!output_shape.empty()) {
    attributes["output_shape"] = output_shape;
  }
  if (!pads.empty()) {
    attributes["pads"] = pads;
  }
  if (!strides.empty()) {
    attributes["strides"] = strides;
  }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(Onnx::Operators::ConvTranspose_11,
                          getOpsetVersion(),
                          args,
                          attributes,
                          {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset11::cumsum(const std::vector<TensorId> &args,
                               int64_t exclusive,
                               int64_t reverse,
                               const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["exclusive"] = exclusive; }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["reverse"] = reverse; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::CumSum_11, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset11::depthtospace(const std::vector<TensorId> &args,
                                     int64_t blocksize,
                                     const std::string &mode,
                                     const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  attributes["blocksize"] = blocksize;
  if (mode != "DCR") {
    attributes["mode"] = mode;
  }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(Onnx::Operators::DepthToSpace_11,
                          getOpsetVersion(),
                          args,
                          attributes,
                          {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset11::det(const std::vector<TensorId> &args,
                            const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Det_11, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

std::vector<TensorId>
AiOnnxOpset11::dynamicquantizelinear(const std::vector<TensorId> &args,
                                     const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(Onnx::Operators::DynamicQuantizeLinear_11,
                          getOpsetVersion(),
                          args,
                          attributes,
                          {di});
  di.setOutputs(outputs);
  return outputs;
}

TensorId AiOnnxOpset11::equal(const std::vector<TensorId> &args,
                              const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Equal_11, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset11::flatten(const std::vector<TensorId> &args,
                                int64_t axis,
                                const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["axis"] = axis; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Flatten_11, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset11::gather(const std::vector<TensorId> &args,
                               int64_t axis,
                               const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["axis"] = axis; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Gather_11, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId
AiOnnxOpset11::gatherelements(const std::vector<TensorId> &args,
                              int64_t axis,
                              const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["axis"] = axis; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(Onnx::Operators::GatherElements_11,
                          getOpsetVersion(),
                          args,
                          attributes,
                          {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset11::gathernd(const std::vector<TensorId> &args,
                                 const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::GatherND_11, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset11::gemm(const std::vector<TensorId> &args,
                             float alpha,
                             float beta,
                             int64_t transA,
                             int64_t transB,
                             const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (alpha != 1.0f) {
    attributes["alpha"] = alpha;
  }
  if (beta != 1.0f) {
    attributes["beta"] = beta;
  }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["transA"] = transA; }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["transB"] = transB; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Gemm_11, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset11::hardmax(const std::vector<TensorId> &args,
                                int64_t axis,
                                const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["axis"] = axis; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Hardmax_11, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

std::vector<TensorId>
AiOnnxOpset11::logical_if(const std::vector<TensorId> &args,
                          unsigned num_outputs,
                          const Builder &else_branch,
                          const Builder &then_branch,
                          const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  // Special case where we convert from a Builder object to an
  // onnx::GraphProto object so as not to expose the onnx class
  // at the API level
  attributes["else_branch"] =
      io::getModelFromString(else_branch.getModelProto()).graph();
  // Special case where we convert from a Builder object to an
  // onnx::GraphProto object so as not to expose the onnx class
  // at the API level
  attributes["then_branch"] =
      io::getModelFromString(then_branch.getModelProto()).graph();
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(Onnx::Operators::If_11,
                          getOpsetVersion(),
                          args,
                          num_outputs,
                          attributes,
                          {di});
  di.setOutputs(outputs);
  return outputs;
}

TensorId AiOnnxOpset11::logsoftmax(const std::vector<TensorId> &args,
                                   int64_t axis,
                                   const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["axis"] = axis; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(Onnx::Operators::LogSoftmax_11,
                          getOpsetVersion(),
                          args,
                          attributes,
                          {di});
  di.setOutputs(outputs);
  return outputs[0];
}

std::vector<TensorId>
AiOnnxOpset11::loop(const std::vector<TensorId> &args,
                    unsigned num_outputs,
                    const Builder &body,
                    const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  // Special case where we convert from a Builder object to an
  // onnx::GraphProto object so as not to expose the onnx class
  // at the API level
  attributes["body"] = io::getModelFromString(body.getModelProto()).graph();
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(Onnx::Operators::Loop_11,
                          getOpsetVersion(),
                          args,
                          num_outputs,
                          attributes,
                          {di});
  di.setOutputs(outputs);
  return outputs;
}

TensorId AiOnnxOpset11::lppool(const std::vector<TensorId> &args,
                               const std::vector<int64_t> &kernel_shape,
                               int64_t p,
                               const std::vector<int64_t> &pads,
                               const std::vector<int64_t> &strides,
                               const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  attributes["kernel_shape"] = kernel_shape;
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["p"] = p; }
  if (!pads.empty()) {
    attributes["pads"] = pads;
  }
  if (!strides.empty()) {
    attributes["strides"] = strides;
  }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::LpPool_11, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

std::vector<TensorId>
AiOnnxOpset11::maxpool(const std::vector<TensorId> &args,
                       unsigned num_outputs,
                       const std::vector<int64_t> &kernel_shape,
                       int64_t ceil_mode,
                       const std::vector<int64_t> &dilations,
                       const std::vector<int64_t> &pads,
                       int64_t storage_order,
                       const std::vector<int64_t> &strides,
                       const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["ceil_mode"] = ceil_mode; }
  if (!dilations.empty()) {
    attributes["dilations"] = dilations;
  }
  attributes["kernel_shape"] = kernel_shape;
  if (!pads.empty()) {
    attributes["pads"] = pads;
  }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["storage_order"] = storage_order; }
  if (!strides.empty()) {
    attributes["strides"] = strides;
  }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::MaxPool_11,
      getOpsetVersion(),
      args,
      num_outputs,
      attributes,
      {di},
      [this](const std::vector<TensorId> &inputs_,
             std::map<std::string, popart::any> attributes_) {
        verify_AiOnnxOpset11_MaxPool_11(this->impl, inputs_, attributes_);
      });
  di.setOutputs(outputs);
  return outputs;
}

TensorId AiOnnxOpset11::maxunpool(const std::vector<TensorId> &args,
                                  const std::vector<int64_t> &kernel_shape,
                                  const std::vector<int64_t> &pads,
                                  const std::vector<int64_t> &strides,
                                  const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  attributes["kernel_shape"] = kernel_shape;
  if (!pads.empty()) {
    attributes["pads"] = pads;
  }
  if (!strides.empty()) {
    attributes["strides"] = strides;
  }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::MaxUnpool_11, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId
AiOnnxOpset11::nonmaxsuppression(const std::vector<TensorId> &args,
                                 int64_t center_point_box,
                                 const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["center_point_box"] = center_point_box; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(Onnx::Operators::NonMaxSuppression_11,
                          getOpsetVersion(),
                          args,
                          attributes,
                          {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset11::onehot(const std::vector<TensorId> &args,
                               int64_t axis,
                               const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["axis"] = axis; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::OneHot_11, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset11::pad(const std::vector<TensorId> &args,
                            const std::string &mode,
                            const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (mode != "constant") {
    attributes["mode"] = mode;
  }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Pad_11, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset11::range(const std::vector<TensorId> &args,
                              const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Range_11, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset11::reducel1(const std::vector<TensorId> &args,
                                 nonstd::optional<std::vector<int64_t>> axes,
                                 int64_t keepdims,
                                 const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (axes != nonstd::optional<std::vector<int64_t>>()) {
    attributes["axes"] = *axes;
  }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["keepdims"] = keepdims; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::ReduceL1_11, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset11::reducel2(const std::vector<TensorId> &args,
                                 nonstd::optional<std::vector<int64_t>> axes,
                                 int64_t keepdims,
                                 const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (axes != nonstd::optional<std::vector<int64_t>>()) {
    attributes["axes"] = *axes;
  }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["keepdims"] = keepdims; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::ReduceL2_11, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId
AiOnnxOpset11::reducelogsum(const std::vector<TensorId> &args,
                            nonstd::optional<std::vector<int64_t>> axes,
                            int64_t keepdims,
                            const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (axes != nonstd::optional<std::vector<int64_t>>()) {
    attributes["axes"] = *axes;
  }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["keepdims"] = keepdims; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(Onnx::Operators::ReduceLogSum_11,
                          getOpsetVersion(),
                          args,
                          attributes,
                          {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId
AiOnnxOpset11::reducelogsumexp(const std::vector<TensorId> &args,
                               nonstd::optional<std::vector<int64_t>> axes,
                               int64_t keepdims,
                               const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (axes != nonstd::optional<std::vector<int64_t>>()) {
    attributes["axes"] = *axes;
  }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["keepdims"] = keepdims; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(Onnx::Operators::ReduceLogSumExp_11,
                          getOpsetVersion(),
                          args,
                          attributes,
                          {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset11::reducemax(const std::vector<TensorId> &args,
                                  nonstd::optional<std::vector<int64_t>> axes,
                                  int64_t keepdims,
                                  const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (axes != nonstd::optional<std::vector<int64_t>>()) {
    attributes["axes"] = *axes;
  }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["keepdims"] = keepdims; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::ReduceMax_11, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset11::reducemean(const std::vector<TensorId> &args,
                                   nonstd::optional<std::vector<int64_t>> axes,
                                   int64_t keepdims,
                                   const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (axes != nonstd::optional<std::vector<int64_t>>()) {
    attributes["axes"] = *axes;
  }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["keepdims"] = keepdims; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(Onnx::Operators::ReduceMean_11,
                          getOpsetVersion(),
                          args,
                          attributes,
                          {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset11::reducemin(const std::vector<TensorId> &args,
                                  nonstd::optional<std::vector<int64_t>> axes,
                                  int64_t keepdims,
                                  const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (axes != nonstd::optional<std::vector<int64_t>>()) {
    attributes["axes"] = *axes;
  }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["keepdims"] = keepdims; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::ReduceMin_11, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset11::reduceprod(const std::vector<TensorId> &args,
                                   nonstd::optional<std::vector<int64_t>> axes,
                                   int64_t keepdims,
                                   const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (axes != nonstd::optional<std::vector<int64_t>>()) {
    attributes["axes"] = *axes;
  }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["keepdims"] = keepdims; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(Onnx::Operators::ReduceProd_11,
                          getOpsetVersion(),
                          args,
                          attributes,
                          {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset11::reducesum(const std::vector<TensorId> &args,
                                  nonstd::optional<std::vector<int64_t>> axes,
                                  int64_t keepdims,
                                  const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (axes != nonstd::optional<std::vector<int64_t>>()) {
    attributes["axes"] = *axes;
  }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["keepdims"] = keepdims; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::ReduceSum_11, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId
AiOnnxOpset11::reducesumsquare(const std::vector<TensorId> &args,
                               nonstd::optional<std::vector<int64_t>> axes,
                               int64_t keepdims,
                               const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (axes != nonstd::optional<std::vector<int64_t>>()) {
    attributes["axes"] = *axes;
  }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["keepdims"] = keepdims; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(Onnx::Operators::ReduceSumSquare_11,
                          getOpsetVersion(),
                          args,
                          attributes,
                          {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId
AiOnnxOpset11::resize(const std::vector<TensorId> &args,
                      const std::string &coordinate_transformation_mode,
                      float cubic_coeff_a,
                      int64_t exclude_outside,
                      float extrapolation_value,
                      const std::string &mode,
                      const std::string &nearest_mode,
                      const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (coordinate_transformation_mode != "half_pixel") {
    attributes["coordinate_transformation_mode"] =
        coordinate_transformation_mode;
  }
  if (cubic_coeff_a != -0.75f) {
    attributes["cubic_coeff_a"] = cubic_coeff_a;
  }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["exclude_outside"] = exclude_outside; }
  if (extrapolation_value != 0.0f) {
    attributes["extrapolation_value"] = extrapolation_value;
  }
  if (mode != "nearest") {
    attributes["mode"] = mode;
  }
  if (nearest_mode != "round_prefer_floor") {
    attributes["nearest_mode"] = nearest_mode;
  }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Resize_11, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset11::round(const std::vector<TensorId> &args,
                              const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Round_11, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

std::vector<TensorId>
AiOnnxOpset11::scan(const std::vector<TensorId> &args,
                    unsigned num_outputs,
                    const Builder &body,
                    int64_t num_scan_inputs,
                    const std::vector<int64_t> &scan_input_axes,
                    const std::vector<int64_t> &scan_input_directions,
                    const std::vector<int64_t> &scan_output_axes,
                    const std::vector<int64_t> &scan_output_directions,
                    const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  // Special case where we convert from a Builder object to an
  // onnx::GraphProto object so as not to expose the onnx class
  // at the API level
  attributes["body"] = io::getModelFromString(body.getModelProto()).graph();
  attributes["num_scan_inputs"] = num_scan_inputs;
  if (!scan_input_axes.empty()) {
    attributes["scan_input_axes"] = scan_input_axes;
  }
  if (!scan_input_directions.empty()) {
    attributes["scan_input_directions"] = scan_input_directions;
  }
  if (!scan_output_axes.empty()) {
    attributes["scan_output_axes"] = scan_output_axes;
  }
  if (!scan_output_directions.empty()) {
    attributes["scan_output_directions"] = scan_output_directions;
  }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(Onnx::Operators::Scan_11,
                          getOpsetVersion(),
                          args,
                          num_outputs,
                          attributes,
                          {di});
  di.setOutputs(outputs);
  return outputs;
}

TensorId AiOnnxOpset11::scatter(const std::vector<TensorId> &args,
                                int64_t axis,
                                const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["axis"] = axis; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Scatter_11, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId
AiOnnxOpset11::scatterelements(const std::vector<TensorId> &args,
                               int64_t axis,
                               const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["axis"] = axis; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(Onnx::Operators::ScatterElements_11,
                          getOpsetVersion(),
                          args,
                          attributes,
                          {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset11::scatternd(const std::vector<TensorId> &args,
                                  const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::ScatterND_11, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset11::sequenceat(const std::vector<TensorId> &args,
                                   const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(Onnx::Operators::SequenceAt_11,
                          getOpsetVersion(),
                          args,
                          attributes,
                          {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId
AiOnnxOpset11::sequenceconstruct(const std::vector<TensorId> &args,
                                 const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(Onnx::Operators::SequenceConstruct_11,
                          getOpsetVersion(),
                          args,
                          attributes,
                          {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId
AiOnnxOpset11::sequenceempty(nonstd::optional<int64_t> dtype,
                             const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (dtype != nonstd::optional<int64_t>()) {
    attributes["dtype"] = *dtype;
  }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, {}, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(Onnx::Operators::SequenceEmpty_11,
                          getOpsetVersion(),
                          {},
                          attributes,
                          {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId
AiOnnxOpset11::sequenceerase(const std::vector<TensorId> &args,
                             const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(Onnx::Operators::SequenceErase_11,
                          getOpsetVersion(),
                          args,
                          attributes,
                          {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId
AiOnnxOpset11::sequenceinsert(const std::vector<TensorId> &args,
                              const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(Onnx::Operators::SequenceInsert_11,
                          getOpsetVersion(),
                          args,
                          attributes,
                          {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId
AiOnnxOpset11::sequencelength(const std::vector<TensorId> &args,
                              const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(Onnx::Operators::SequenceLength_11,
                          getOpsetVersion(),
                          args,
                          attributes,
                          {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset11::slice(const std::vector<TensorId> &args,
                              const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Slice_11, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset11::softmax(const std::vector<TensorId> &args,
                                int64_t axis,
                                const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["axis"] = axis; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Softmax_11, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

std::vector<TensorId>
AiOnnxOpset11::split(const std::vector<TensorId> &args,
                     unsigned num_outputs,
                     int64_t axis,
                     const std::vector<int64_t> &split,
                     const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["axis"] = axis; }
  if (!split.empty()) {
    attributes["split"] = split;
  }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(Onnx::Operators::Split_11,
                          getOpsetVersion(),
                          args,
                          num_outputs,
                          attributes,
                          {di});
  di.setOutputs(outputs);
  return outputs;
}

TensorId
AiOnnxOpset11::splittosequence(const std::vector<TensorId> &args,
                               int64_t axis,
                               int64_t keepdims,
                               const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["axis"] = axis; }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["keepdims"] = keepdims; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(Onnx::Operators::SplitToSequence_11,
                          getOpsetVersion(),
                          args,
                          attributes,
                          {di});
  di.setOutputs(outputs);
  return outputs[0];
}

TensorId AiOnnxOpset11::squeeze(const std::vector<TensorId> &args,
                                const std::vector<int64_t> &axes,
                                const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (!axes.empty()) {
    attributes["axes"] = axes;
  }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Squeeze_11, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

std::vector<TensorId>
AiOnnxOpset11::topk(const std::vector<TensorId> &args,
                    int64_t axis,
                    int64_t largest,
                    int64_t sorted,
                    const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["axis"] = axis; }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["largest"] = largest; }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["sorted"] = sorted; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::TopK_11, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs;
}

std::vector<TensorId>
AiOnnxOpset11::unique(const std::vector<TensorId> &args,
                      unsigned num_outputs,
                      nonstd::optional<int64_t> axis,
                      int64_t sorted,
                      const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  if (axis != nonstd::optional<int64_t>()) {
    attributes["axis"] = *axis;
  }
  // Workaround Onnx not applying default values during type/shape inference
  { attributes["sorted"] = sorted; }
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(Onnx::Operators::Unique_11,
                          getOpsetVersion(),
                          args,
                          num_outputs,
                          attributes,
                          {di});
  di.setOutputs(outputs);
  return outputs;
}

TensorId AiOnnxOpset11::unsqueeze(const std::vector<TensorId> &args,
                                  const std::vector<int64_t> &axes,
                                  const popart::DebugContext &debugContext) {
  std::map<std::string, popart::any> attributes;
  attributes["axes"] = axes;
  BuilderDebugInfo di(debugContext, __POPART_FUNCTION_NAME__, args, attributes);
  attributes.insert({sDebugInfoId, di.getId()});
  auto outputs = impl->op(
      Onnx::Operators::Unsqueeze_11, getOpsetVersion(), args, attributes, {di});
  di.setOutputs(outputs);
  return outputs[0];
}

} // namespace popart
