<commit_before>// Copyright 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "myelin/kernel/generic.h"

#include <algorithm>
#include <vector>

#include "myelin/compute.h"

namespace sling {
namespace myelin {

// array.cc
void RegisterArrayKernels(Library *library);

// generic-math.cc
void RegisterGenericMath(Library *library);

// generic-matmul.cc
void RegisterGenericMatMul(Library *library);

// generic-operators.cc
void RegisterGenericOperators(Library *library);

// If the |variable| is an int32 constant scalar, sets |data| to the constant
// value and returns true.
static bool TryGetInt32Data(const Flow::Variable &variable, int *data) {
  if (variable.type != DT_INT32) return false;
  if (variable.rank() != 0) return false;
  if (variable.data == nullptr) return false;
  *data = *reinterpret_cast<const int32 *>(variable.data);
  return true;
}

// As above, but for vectors.
static bool TryGetInt32Data(const Flow::Variable &variable,
                            std::vector<int> *data) {
  if (variable.type != DT_INT32) return false;
  if (variable.rank() != 1) return false;
  if (variable.data == nullptr) return false;
  const int32 *array = reinterpret_cast<const int32 *>(variable.data);
  data->assign(array, array + variable.dim(0));
  return true;
}

// Rename operations with aliases.
class RenameTransformer : public Transformer {
 public:
  bool Transform(Flow *flow) override {
    // Rename BiasAdd to Add.
    int renames = 0;
    for (Flow::Operation *op : flow->ops()) {
      if (op->type == "BiasAdd") {
        op->type = "Add";
        renames++;
      }
    }

    return renames > 0;
  }
};

// Remove identity ops.
class IdentityTransformer : public Transformer {
 public:
  bool Transform(Flow *flow) override {
    // Eliminate no-ops.
    std::vector<Flow::Operation *> noops;
    for (Flow::Operation *op : flow->ops()) {
      if (op->type == "Identity" ||
          op->type == "Const" ||
          op->type == "Variable" ||
          op->type == "VariableV2" ||
          op->type == "Placeholder" ||
          op->type == "Enter") {
        noops.push_back(op);
      } else if (op->type == "Reshape") {
        // Eliminate reshaping if input and output shapes are equal.
        if (op->indegree() == 2 && op->outdegree() == 1) {
          Flow::Variable *in = op->inputs[0];
          Flow::Variable *out = op->outputs[0];
          if (!in->shape.undefined() && !in->shape.partial() &&
              !out->shape.undefined() && !out->shape.partial() &&
              in->shape == out->shape &&in->type == out->type) {
            Flow::Variable *shape = op->inputs[1];
            op->RemoveInput(shape);
            noops.push_back(op);
          }
        }
      } else if (op->type == "Concat" || op->type == "ConcatV2") {
        // Eliminate concatenations with only one input.
        int n = op->GetAttr("N", 0);
        if (n == 1) {
          Flow::Variable *axis = op->inputs[n];
          op->RemoveInput(axis);
          noops.push_back(op);
        }
      }
    }

    // Remove no-ops from the flow and eliminate the intermediate variables.
    for (Flow::Operation *op : noops) {
      flow->Eliminate(op);
    }

    return !noops.empty();
  }
};

// Combine ops.
class CombineTransformer : public Transformer {
 public:
  bool Transform(Flow *flow) override {
    int combines = 0;
    while (Combine(flow, "MatMul", "Add", "MatMulAdd") ||
           Combine(flow, "MatMul", "Relu", "MatMulRelu") ||
           Combine(flow, "MatMulAdd", "Relu", "MatMulAddRelu")) {
      combines++;
    }
    return combines > 0;
  }

  // Try to find combinations and replace them with a combined op.
  bool Combine(Flow *flow, const string &first, const string &second,
               const string &combined) {
    // Find operations that can be combined.
    for (Flow::Operation *op : flow->ops()) {
      if (op->type != first) continue;
      if (op->outputs.size() != 1) continue;
      Flow::Variable *var = op->outputs[0];
      if (var->consumers.size() != 1) continue;
      if (var->consumers[0]->type != second) continue;
      if (var->consumers[0]->task != op->task) continue;
      if (var->out) continue;
      if (var->shape.undefined()) continue;
      if (op->indegree() >= 1) {
        // Only combine for vector inputs.
        Flow::Variable *input = op->inputs[0];
        if (input->rank() == 2 && input->dim(0) > 1) continue;
      }

      flow->Fuse(op, var->consumers[0], combined);
      return true;
    }
    return false;
  }
};

// Flattens nested concatenations, if possible.  E.g.,
// tf.concat([a, tf.concat([b, c], 1), d], 1) = tf.concat([a, b, c, d], 1)
class FlattenConcatTransformer : public Transformer {
 public:
  bool Transform(Flow *flow) override {
    bool transformed = false;
    while (TryFlattenOnce(flow)) transformed = true;
    return transformed;
  }

 private:
  // Returns true if the |operation| is a concatenation.
  static bool IsConcat(const Flow::Operation &operation) {
    if (operation.type != "ConcatV2") return false;
    if (!operation.HasAttr("N")) return false;
    const int num_to_concat = operation.GetAttr("N", -1);
    CHECK_GT(num_to_concat, 0);
    if (operation.indegree() != num_to_concat + 1) return false;
    if (operation.outdegree() != 1) return false;
    return true;
  }

  // Flattens one nested concatenation and returns true, if possible.
  static bool TryFlattenOnce(Flow *flow) {
    // Search for a parent and child concat, where both have the same axis and
    // the result of the child concat is only used by the parent concat.
    for (Flow::Operation *child : flow->ops()) {
      if (!IsConcat(*child)) continue;

      // The child should have only one consumer, the parent.
      Flow::Variable *child_result = child->outputs[0];
      if (child_result->consumers.size() != 1) continue;
      Flow::Operation *parent = child_result->consumers[0];
      if (!IsConcat(*parent)) continue;

      // The axes (i.e., final inputs) should match.
      int parent_axis = 0, child_axis = 0;
      if (!TryGetInt32Data(*parent->inputs.back(), &parent_axis)) continue;
      if (!TryGetInt32Data(*child->inputs.back(), &child_axis)) continue;
      if (parent_axis != child_axis) continue;

      // The child axis will be pruned, so it should have no other dependencies.
      if (child->inputs.back()->consumers.size() != 1) continue;
      if (child->inputs.back()->producer != nullptr) continue;

      Flatten(flow, parent, child);
      return true;
    }

    return false;
  }

  // Flattens the |child| concatenation into the |parent| concatenation by
  // replacing the |child| with the inputs it concatenates.
  static void Flatten(Flow *flow, Flow::Operation *parent,
                      Flow::Operation *child) {
    VLOG(1) << "Flattening " << child->type << " (" << child->name << ") into "
            << parent->type << " (" << parent->name << ")";

    // Find the index of the child among the parent's inputs.  This is where the
    // child's inputs should be inserted.
    Flow::Variable *child_result = child->outputs[0];
    const int child_index =
        std::find(parent->inputs.begin(), parent->inputs.end(), child_result) -
        parent->inputs.begin();
    CHECK_LT(child_index, parent->inputs.size())
        << "parent=" << parent->name << " child=" << child->name;

    // Discard the child's axis; it is redundant with the parent's axis.
    Flow::Variable *child_axis = child->inputs.back();
    child->RemoveInput(child_axis);
    flow->DeleteVariable(child_axis);

    // Discard the child's result; it will be replaced with the child's inputs.
    child->RemoveOutput(child_result);
    parent->RemoveInput(child_result);
    flow->DeleteVariable(child_result);

    // Move the child's inputs to the parent.
    while (!child->inputs.empty()) {
      Flow::Variable *input = child->inputs.back();  // iterate back to front
      child->MoveInput(input, parent);

      // MoveInput() appends to the parent's input list, so pop and reinsert it
      // at the proper location.  Since we iterate the child's inputs backwards,
      // it suffices to repeatedly insert at the same index.
      CHECK_EQ(input, parent->inputs.back());
      parent->inputs.pop_back();
      parent->inputs.insert(parent->inputs.begin() + child_index, input);
    }

    flow->DeleteOperation(child);
    parent->SetAttr("N", static_cast<int>(parent->inputs.size() - 1));
  }
};

// Type inference for standard ops.
class StandardTyper : public Typer {
 public:
  bool InferTypes(Flow::Operation *op) override {
    // Infer shape for matrix multiplication.
    if (op->type == "MatMul" ||
        op->type == "MatMulAdd" ||
        op->type == "MatMulRelu" ||
        op->type == "MatMulAddRelu") {
      if (op->indegree() >= 2 && op->outdegree() == 1) {
        Flow::Variable *a = op->inputs[0];
        Flow::Variable *b = op->inputs[1];
        Flow::Variable *c = op->outputs[0];

        if (c->type == DT_INVALID) c->type = a->type;

        // Matrix multiplied by matrix.
        if (a->rank() == 2 && b->rank() == 2 && a->dim(1) == b->dim(0)) {
          c->shape.assign(a->dim(0), b->dim(1));
          return true;
        }
      }
    }

    // Infer shape for element-wise operation.
    if (op->type == "Add" ||
        op->type == "BiasAdd" ||
        op->type == "Mul" ||
        op->type == "Sub" ||
        op->type == "Tanh" ||
        op->type == "Sigmoid" ||
        op->type == "Relu" ||
        op->type == "Calculate") {
      if (op->indegree() > 0 && op->outdegree() > 0) {
        // Determine output rank.
        Shape shape;
        int rank = 0;
        for (Flow::Variable *in : op->inputs) {
          if (in->rank() > rank) rank = in->rank();
        }
        shape.fill(rank, 1);

        // Determine output shape based on broadcast semantics.
        for (Flow::Variable *in : op->inputs) {
          int depth = rank - in->rank();
          for (int d = 0; d < in->rank(); ++d) {
            if (shape.dim(d + depth) < in->dim(d)) {
              shape.set(d + depth, in->dim(d));
            }
          }
        }

        // Set shape for outputs.
        for (Flow::Variable *out : op->outputs) {
          out->shape = shape;
        }

        if (op->outputs[0]->type == DT_INVALID) {
          op->outputs[0]->type = op->inputs[0]->type;
        }
        return true;
      }
    }

    // Infer shape for concat operation.
    if (op->type == "ConcatV2") {
      int n = op->GetAttr("N", 0);
      if (n > op->indegree()) return false;
      int axis = 0;
      if (op->indegree() == n + 1) {
        Flow::Variable *a = op->inputs[n];
        TryGetInt32Data(*a, &axis);
      }

      if (n > 0 && op->outdegree() == 1) {
        Flow::Variable *result = op->outputs[0];
        Shape concat = op->inputs[0]->shape;
        bool compatible = true;
        for (int i = 1; i < n; ++i) {
          Flow::Variable *input = op->inputs[i];
          if (input->shape.rank() == concat.rank()) {
            for (int d = 0; d < concat.rank(); ++d) {
              if (d == axis) {
                concat.set(d, concat.dim(d) + input->shape.dim(d));
              } else if (concat.dim(d) != input->shape.dim(d)) {
                compatible = false;
              }
            }
          } else {
            compatible = false;
          }
        }
        if (compatible) {
          result->shape = concat;
          return true;
        }
      }
    }

    // Infer shape for reshaping operation.
    if (op->type == "Reshape") {
      if (op->indegree() == 2 && op->outdegree() == 1) {
        Flow::Variable *shape = op->inputs[1];
        Flow::Variable *result = op->outputs[0];
        std::vector<int> dims;
        if (TryGetInt32Data(*shape, &dims)) {
          result->shape.clear();
          for (int dim : dims) {
            // Unspecified dimensions (-1) typically correspond to to the input
            // sequence length.  Since Myelin cells process one input element at
            // a time, -1 can be replaced with 1.
            result->shape.add(dim == -1 ? 1 : dim);
          }
          if (op->outputs[0]->type == DT_INVALID) {
            op->outputs[0]->type = op->inputs[0]->type;
          }
          return true;
        }
      }
    }

    // Infer shape for gather operation.
    if (op->type == "Gather") {
      // For the 2-arg form tf.gather(params, indices):
      //   result.type = params.dtype.
      //   result.shape = indices.shape + params.shape[1:].
      // https://www.tensorflow.org/api_docs/python/tf/gather
      // Note that there is also a 3-arg form tf.gather(params, indices, axis).
      if (op->indegree() == 2 && op->outdegree() == 1) {
        Flow::Variable *params = op->inputs[0];
        Flow::Variable *indices = op->inputs[1];
        Flow::Variable *result = op->outputs[0];
        result->type = params->type;
        result->shape = indices->shape;
        for (int i = 1; i < params->shape.rank(); ++i) {
          result->shape.add(params->shape.dim(i));
        }
        return true;
      }
    }

    return false;
  }
};

// Register generic library.
void RegisterGenericLibrary(Library *library) {
  // Register transformations.
  library->RegisterTransformer(new RenameTransformer());
  library->RegisterTransformer(new IdentityTransformer());
  library->RegisterTransformer(new CombineTransformer());
  library->RegisterTransformer(new FlattenConcatTransformer());

  // Register type inference.
  library->RegisterTyper(new StandardTyper());

  // Register kernels.
  RegisterArrayKernels(library);
  RegisterGenericMath(library);
  RegisterGenericMatMul(library);
  RegisterGenericOperators(library);
}

}  // namespace myelin
}  // namespace sling
<commit_msg>Addresses review comments.<commit_after>// Copyright 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "myelin/kernel/generic.h"

#include <algorithm>
#include <vector>

#include "myelin/compute.h"

namespace sling {
namespace myelin {

// array.cc
void RegisterArrayKernels(Library *library);

// generic-math.cc
void RegisterGenericMath(Library *library);

// generic-matmul.cc
void RegisterGenericMatMul(Library *library);

// generic-operators.cc
void RegisterGenericOperators(Library *library);

// If the variable is an int32 constant scalar, sets data to the constant value
// and returns true.
static bool TryGetInt32Data(const Flow::Variable &variable, int *data) {
  if (variable.type != DT_INT32) return false;
  if (variable.rank() != 0) return false;
  if (variable.data == nullptr) return false;
  *data = *reinterpret_cast<const int32 *>(variable.data);
  return true;
}

// As above, but for vectors.
static bool TryGetInt32Data(const Flow::Variable &variable,
                            std::vector<int> *data) {
  if (variable.type != DT_INT32) return false;
  if (variable.rank() != 1) return false;
  if (variable.data == nullptr) return false;
  const int32 *array = reinterpret_cast<const int32 *>(variable.data);
  data->assign(array, array + variable.dim(0));
  return true;
}

// Rename operations with aliases.
class RenameTransformer : public Transformer {
 public:
  bool Transform(Flow *flow) override {
    // Rename BiasAdd to Add.
    int renames = 0;
    for (Flow::Operation *op : flow->ops()) {
      if (op->type == "BiasAdd") {
        op->type = "Add";
        renames++;
      }
    }

    return renames > 0;
  }
};

// Remove identity ops.
class IdentityTransformer : public Transformer {
 public:
  bool Transform(Flow *flow) override {
    // Eliminate no-ops.
    std::vector<Flow::Operation *> noops;
    for (Flow::Operation *op : flow->ops()) {
      if (op->type == "Identity" ||
          op->type == "Const" ||
          op->type == "Variable" ||
          op->type == "VariableV2" ||
          op->type == "Placeholder" ||
          op->type == "Enter") {
        noops.push_back(op);
      } else if (op->type == "Reshape") {
        // Eliminate reshaping if input and output shapes are equal.
        if (op->indegree() == 2 && op->outdegree() == 1) {
          Flow::Variable *in = op->inputs[0];
          Flow::Variable *out = op->outputs[0];
          if (!in->shape.undefined() && !in->shape.partial() &&
              !out->shape.undefined() && !out->shape.partial() &&
              in->shape == out->shape &&in->type == out->type) {
            Flow::Variable *shape = op->inputs[1];
            op->RemoveInput(shape);
            noops.push_back(op);
          }
        }
      } else if (op->type == "Concat" || op->type == "ConcatV2") {
        // Eliminate concatenations with only one input.
        int n = op->GetAttr("N", 0);
        if (n == 1) {
          Flow::Variable *axis = op->inputs[n];
          op->RemoveInput(axis);
          noops.push_back(op);
        }
      }
    }

    // Remove no-ops from the flow and eliminate the intermediate variables.
    for (Flow::Operation *op : noops) {
      flow->Eliminate(op);
    }

    return !noops.empty();
  }
};

// Combine ops.
class CombineTransformer : public Transformer {
 public:
  bool Transform(Flow *flow) override {
    int combines = 0;
    while (Combine(flow, "MatMul", "Add", "MatMulAdd") ||
           Combine(flow, "MatMul", "Relu", "MatMulRelu") ||
           Combine(flow, "MatMulAdd", "Relu", "MatMulAddRelu")) {
      combines++;
    }
    return combines > 0;
  }

  // Try to find combinations and replace them with a combined op.
  bool Combine(Flow *flow, const string &first, const string &second,
               const string &combined) {
    // Find operations that can be combined.
    for (Flow::Operation *op : flow->ops()) {
      if (op->type != first) continue;
      if (op->outputs.size() != 1) continue;
      Flow::Variable *var = op->outputs[0];
      if (var->consumers.size() != 1) continue;
      if (var->consumers[0]->type != second) continue;
      if (var->consumers[0]->task != op->task) continue;
      if (var->out) continue;
      if (var->shape.undefined()) continue;
      if (op->indegree() >= 1) {
        // Only combine for vector inputs.
        Flow::Variable *input = op->inputs[0];
        if (input->rank() == 2 && input->dim(0) > 1) continue;
      }

      flow->Fuse(op, var->consumers[0], combined);
      return true;
    }
    return false;
  }
};

// Flattens nested concatenations, if possible.  E.g.,
// tf.concat([a, tf.concat([b, c], 1), d], 1) = tf.concat([a, b, c, d], 1)
class FlattenConcatTransformer : public Transformer {
 public:
  bool Transform(Flow *flow) override {
    bool transformed = false;
    while (TryFlattenOnce(flow)) transformed = true;
    return transformed;
  }

 private:
  // Returns true if the operation is a concatenation.
  static bool IsConcat(const Flow::Operation &operation) {
    if (operation.type != "ConcatV2") return false;
    if (!operation.HasAttr("N")) return false;
    const int num_to_concat = operation.GetAttr("N", -1);
    if (num_to_concat <= 0) return false;
    if (operation.indegree() != num_to_concat + 1) return false;
    if (operation.outdegree() != 1) return false;
    return true;
  }

  // Flattens one nested concatenation and returns true, if possible.
  static bool TryFlattenOnce(Flow *flow) {
    // Search for a parent and child concat, where both have the same axis and
    // the result of the child concat is only used by the parent concat.
    for (Flow::Operation *child : flow->ops()) {
      if (!IsConcat(*child)) continue;

      // The child should have only one consumer, the parent.
      Flow::Variable *child_result = child->outputs[0];
      if (child_result->consumers.size() != 1) continue;
      Flow::Operation *parent = child_result->consumers[0];
      if (!IsConcat(*parent)) continue;

      // The axes (i.e., final inputs) should match.
      int parent_axis = 0, child_axis = 0;
      if (!TryGetInt32Data(*parent->inputs.back(), &parent_axis)) continue;
      if (!TryGetInt32Data(*child->inputs.back(), &child_axis)) continue;
      if (parent_axis != child_axis) continue;

      // The child axis will be pruned, so it should have no other dependencies.
      if (child->inputs.back()->consumers.size() != 1) continue;
      if (child->inputs.back()->producer != nullptr) continue;

      Flatten(flow, parent, child);
      return true;
    }

    return false;
  }

  // Flattens the child concatenation into the parent concatenation by replacing
  // the child with the inputs it concatenates.
  static void Flatten(Flow *flow, Flow::Operation *parent,
                      Flow::Operation *child) {
    VLOG(9) << "Flattening " << child->type << " (" << child->name << ") into "
            << parent->type << " (" << parent->name << ")";

    // Find the index of the child among the parent's inputs.  This is where the
    // child's inputs should be inserted.
    Flow::Variable *child_result = child->outputs[0];
    const int child_index =
        std::find(parent->inputs.begin(), parent->inputs.end(), child_result) -
        parent->inputs.begin();
    CHECK_LT(child_index, parent->inputs.size())
        << "parent=" << parent->name << " child=" << child->name;

    // Discard the child's axis; it is redundant with the parent's axis.
    Flow::Variable *child_axis = child->inputs.back();
    child->RemoveInput(child_axis);
    flow->DeleteVariable(child_axis);

    // Discard the child's result; it will be replaced with the child's inputs.
    child->RemoveOutput(child_result);
    parent->RemoveInput(child_result);
    flow->DeleteVariable(child_result);

    // Move the child's inputs to the parent.
    while (!child->inputs.empty()) {
      Flow::Variable *input = child->inputs.back();  // iterate back to front
      child->MoveInput(input, parent);

      // MoveInput() appends to the parent's input list, so pop and reinsert it
      // at the proper location.  Since we iterate the child's inputs backwards,
      // it suffices to repeatedly insert at the same index.
      CHECK_EQ(input, parent->inputs.back());
      parent->inputs.pop_back();
      parent->inputs.insert(parent->inputs.begin() + child_index, input);
    }

    flow->DeleteOperation(child);
    parent->SetAttr("N", static_cast<int>(parent->inputs.size() - 1));
  }
};

// Type inference for standard ops.
class StandardTyper : public Typer {
 public:
  bool InferTypes(Flow::Operation *op) override {
    // Infer shape for matrix multiplication.
    if (op->type == "MatMul" ||
        op->type == "MatMulAdd" ||
        op->type == "MatMulRelu" ||
        op->type == "MatMulAddRelu") {
      if (op->indegree() >= 2 && op->outdegree() == 1) {
        Flow::Variable *a = op->inputs[0];
        Flow::Variable *b = op->inputs[1];
        Flow::Variable *c = op->outputs[0];

        if (c->type == DT_INVALID) c->type = a->type;

        // Matrix multiplied by matrix.
        if (a->rank() == 2 && b->rank() == 2 && a->dim(1) == b->dim(0)) {
          c->shape.assign(a->dim(0), b->dim(1));
          return true;
        }
      }
    }

    // Infer shape for element-wise operation.
    if (op->type == "Add" ||
        op->type == "BiasAdd" ||
        op->type == "Mul" ||
        op->type == "Sub" ||
        op->type == "Tanh" ||
        op->type == "Sigmoid" ||
        op->type == "Relu" ||
        op->type == "Calculate") {
      if (op->indegree() > 0 && op->outdegree() > 0) {
        // Determine output rank.
        Shape shape;
        int rank = 0;
        for (Flow::Variable *in : op->inputs) {
          if (in->rank() > rank) rank = in->rank();
        }
        shape.fill(rank, 1);

        // Determine output shape based on broadcast semantics.
        for (Flow::Variable *in : op->inputs) {
          int depth = rank - in->rank();
          for (int d = 0; d < in->rank(); ++d) {
            if (shape.dim(d + depth) < in->dim(d)) {
              shape.set(d + depth, in->dim(d));
            }
          }
        }

        // Set shape for outputs.
        for (Flow::Variable *out : op->outputs) {
          out->shape = shape;
        }

        if (op->outputs[0]->type == DT_INVALID) {
          op->outputs[0]->type = op->inputs[0]->type;
        }
        return true;
      }
    }

    // Infer shape for concat operation.
    if (op->type == "ConcatV2") {
      int n = op->GetAttr("N", 0);
      if (n > op->indegree()) return false;
      int axis = 0;
      if (op->indegree() == n + 1) {
        Flow::Variable *a = op->inputs[n];
        TryGetInt32Data(*a, &axis);
      }

      if (n > 0 && op->outdegree() == 1) {
        Flow::Variable *result = op->outputs[0];
        Shape concat = op->inputs[0]->shape;
        bool compatible = true;
        for (int i = 1; i < n; ++i) {
          Flow::Variable *input = op->inputs[i];
          if (input->shape.rank() == concat.rank()) {
            for (int d = 0; d < concat.rank(); ++d) {
              if (d == axis) {
                concat.set(d, concat.dim(d) + input->shape.dim(d));
              } else if (concat.dim(d) != input->shape.dim(d)) {
                compatible = false;
              }
            }
          } else {
            compatible = false;
          }
        }
        if (compatible) {
          result->shape = concat;
          return true;
        }
      }
    }

    // Infer shape for reshaping operation.
    if (op->type == "Reshape") {
      if (op->indegree() == 2 && op->outdegree() == 1) {
        Flow::Variable *shape = op->inputs[1];
        Flow::Variable *result = op->outputs[0];
        std::vector<int> dims;
        if (TryGetInt32Data(*shape, &dims)) {
          result->shape.clear();
          for (int dim : dims) {
            // Unspecified dimensions (-1) typically correspond to to the input
            // sequence length.  Since Myelin cells process one input element at
            // a time, -1 can be replaced with 1.
            result->shape.add(dim == -1 ? 1 : dim);
          }
          if (op->outputs[0]->type == DT_INVALID) {
            op->outputs[0]->type = op->inputs[0]->type;
          }
          return true;
        }
      }
    }

    // Infer shape for gather operation.
    if (op->type == "Gather") {
      // For the 2-arg form tf.gather(params, indices):
      //   result.type = params.dtype.
      //   result.shape = indices.shape + params.shape[1:].
      // https://www.tensorflow.org/api_docs/python/tf/gather
      // Note that there is also a 3-arg form tf.gather(params, indices, axis).
      if (op->indegree() == 2 && op->outdegree() == 1) {
        Flow::Variable *params = op->inputs[0];
        Flow::Variable *indices = op->inputs[1];
        Flow::Variable *result = op->outputs[0];
        result->type = params->type;
        result->shape = indices->shape;
        for (int i = 1; i < params->shape.rank(); ++i) {
          result->shape.add(params->shape.dim(i));
        }
        return true;
      }
    }

    return false;
  }
};

// Register generic library.
void RegisterGenericLibrary(Library *library) {
  // Register transformations.
  library->RegisterTransformer(new RenameTransformer());
  library->RegisterTransformer(new IdentityTransformer());
  library->RegisterTransformer(new CombineTransformer());
  library->RegisterTransformer(new FlattenConcatTransformer());

  // Register type inference.
  library->RegisterTyper(new StandardTyper());

  // Register kernels.
  RegisterArrayKernels(library);
  RegisterGenericMath(library);
  RegisterGenericMatMul(library);
  RegisterGenericOperators(library);
}

}  // namespace myelin
}  // namespace sling
<|endoftext|>


<commit_before>// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License

#ifndef __PROCESS_PID_HPP__
#define __PROCESS_PID_HPP__

#include <stdint.h>

#include <iosfwd>
#include <string>

#include <boost/functional/hash.hpp>

#include <process/address.hpp>

#include <stout/ip.hpp>

namespace process {

// Forward declaration to break cyclic dependencies.
class ProcessBase;

/**
 * An "untyped" `PID`, used to encapsulate the process ID for
 * lower-layer abstractions (eg, when receiving incoming requests)
 * in the dispatching mechanism.
 *
 * @see process::PID
 */
struct UPID
{
  UPID() = default;

  UPID(const UPID& that) = default;

  UPID(UPID&& that) = default;

  UPID(const char* id_, const net::IP& ip_, uint16_t port_)
    : id(id_), address(ip_, port_) { resolve(); }

  UPID(const char* id_, const network::inet::Address& address_)
    : id(id_), address(address_) { resolve(); }

  UPID(const std::string& id_, const net::IP& ip_, uint16_t port_)
    : id(id_), address(ip_, port_) { resolve(); }

  UPID(const std::string& id_, const network::inet::Address& address_)
    : id(id_), address(address_) { resolve(); }

  /*implicit*/ UPID(const char* s);

  /*implicit*/ UPID(const std::string& s);

  /*implicit*/ UPID(const ProcessBase& process);

  UPID& operator=(const UPID& that) = default;

  UPID& operator=(UPID&& that) = default;

  operator std::string() const;

  operator bool() const
  {
    return id != "" && !address.ip.isAny() && address.port != 0;
  }

  bool operator!() const // NOLINT(whitespace/operators)
  {
    return id == "" && address.ip.isAny() && address.port == 0;
  }

  bool operator<(const UPID& that) const
  {
    if (address == that.address) {
      return id < that.id;
    } else {
      return address < that.address;
    }
  }

  bool operator==(const UPID& that) const
  {
    return (id == that.id && address == that.address);
  }

  bool operator!=(const UPID& that) const
  {
    return !(*this == that);
  }

  // Attempts to resolve and cache a weak pointer to the ProcessBase
  // to which this UPID refers.
  void resolve();

  // TODO(benh): store all of the members of UPID behind a
  // copy-on-write implementation because UPID is often copied but
  // rarely written which means we could optimize performance by not
  // making so many copies.

  // A copy-on-write string for performance.
  //
  // TODO(benh): Factor this out into a generic copy-on-write string.
  struct ID
  {
    static const std::string EMPTY;

    ID() = default;

    ID(const std::string& s)
      : id(std::make_shared<std::string>(s)) {}

    ID(std::string&& s)
      : id(std::make_shared<std::string>(std::move(s))) {}

    ID& operator=(std::string&& that)
    {
      id = std::make_shared<std::string>(std::move(that));
      return *this;
    }

    bool operator==(const std::string& that) const
    {
      if (!id) {
        return EMPTY == that;
      }
      return *id == that;
    }

    bool operator==(const char* that) const
    {
      if (!id) {
        return EMPTY == that;
      }
      return *id == that;
    }

    bool operator!=(const std::string& that) const
    {
      return !(*this == that);
    }

    bool operator<(const std::string& that) const
    {
      if (!id) {
        return EMPTY < that;
      }
      return *id < that;
    }

    operator const std::string&() const
    {
      if (!id) {
        return EMPTY;
      }
      return *id;
    }

  private:
    std::shared_ptr<std::string> id;
  } id;

  // TODO(asridharan): Ideally, the following `address` field should be of
  // type `network::Address` so that the default address of the PID
  // could be a unix domain socket or an IPv4/v6 address. This change
  // however is disruptive at this point and should be done after we have
  // introduced support for unix domain and IPv6 sockets into
  // `libprocess`.
  network::inet::Address address = network::inet4::Address::ANY_ANY();

  // TODO(asridharan): Currently we are introducing only an `Optional`
  // IPv6 address in the following `addresses` structure. This will
  // help us initiate some basic IPv6 support for the
  // `DockerContainerizer`.  However, going forward, once we start
  // supporting unix domain sockets and IPv4/IPv6 socket in
  // `libprocess` we will add the following fields to this structure.
  //
  // Option<network::unix::Address> unix;
  // Option<network::inet4::Address> v4;
  //
  // With the introduction of the above fields `libprocess` PID will
  // be able to support unix, IPv4 and IPv6 sockets simultaneously.
  struct
  {
    Option<network::inet6::Address> v6;
  } addresses = {None()};

protected:
  friend class ProcessBase;
  friend class ProcessManager;

  // A weak pointer to the actual process used to optimize enqueuing
  // events without having to go through a shared lock in the
  // `ProcessManager`. This is `None` if someone creates a UPID and
  // doesn't call `resolve()` or if `resolve()` doesn't find a valid
  // process (i.e., the process hasn't started or has terminated).
  Option<std::weak_ptr<ProcessBase*>> reference = None();
};

inline std::ostream& operator<<(std::ostream& stream, const UPID::ID& id)
{
  const std::string& s = id;
  return stream << s;
}

inline bool operator==(const std::string& s, const UPID::ID& id)
{
  return id == s;
}

inline bool operator!=(const std::string& s, const UPID::ID& id)
{
  return !(s == id);
}

inline std::string operator+(const UPID::ID& id, const std::string& s)
{
  return (const std::string&) id + s;
}

inline std::string operator+(const UPID::ID& id, std::string&& s)
{
  return (const std::string&) id + std::move(s);
}

inline std::string operator+(const std::string& s, const UPID::ID& id)
{
  return s + (const std::string&) id;
}

inline std::string operator+(std::string&& s, const UPID::ID& id)
{
  return std::move(s) + (const std::string&) id;
}

/**
 * A "process identifier" used to uniquely identify a process when
 * dispatching messages.
 *
 * Typed with the actual process class's type, which must be
 * derived from `process::ProcessBase`.
 *
 * Use it like this:
 *
 *    using namespace process;
 *
 *    class SimpleProcess : public Process<SimpleProcess>
 *    {
 *       // ...
 *    };
 *
 *
 *    SimpleProcess process;
 *    PID<SimpleProcess> pid = spawn(process);
 *
 *    // ...
 *
 *    dispatchpid, &SimpleProcess::method, "argument");
 *
 * @see process::ProcessBase
 */
template <typename T = ProcessBase>
struct PID : UPID
{
  // Need to declare PID<U> as a friend in order to write `reference`.
  template <typename U>
  friend class PID;

  PID() : UPID() {}

  /*implicit*/ PID(const T* t) : UPID(static_cast<const ProcessBase&>(*t)) {}
  /*implicit*/ PID(const T& t) : UPID(static_cast<const ProcessBase&>(t)) {}

  template <typename Base>
  operator PID<Base>() const
  {
    // Only allow upcasts!
    T* t = nullptr;
    Base* base = t;
    (void)base; // Eliminate unused base warning.
    PID<Base> pid;
    pid.id = id;
    pid.address = address;
    pid.addresses = addresses;
    pid.reference = reference;
    return pid;
  }
};

// Outputing UPIDs and generating UPIDs using streams.
std::ostream& operator<<(std::ostream&, const UPID&);
std::istream& operator>>(std::istream&, UPID&);

} // namespace process {

namespace std {

template <>
struct hash<process::UPID>
{
  typedef size_t result_type;

  typedef process::UPID argument_type;

  result_type operator()(const argument_type& upid) const
  {
    size_t seed = 0;
    boost::hash_combine(seed, (const std::string&) upid.id);
    boost::hash_combine(seed, std::hash<net::IP>()(upid.address.ip));
    boost::hash_combine(seed, upid.address.port);
    return seed;
  }
};

} // namespace std {

#endif // __PROCESS_PID_HPP__
<commit_msg>Fixed compiler error.<commit_after>// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License

#ifndef __PROCESS_PID_HPP__
#define __PROCESS_PID_HPP__

#include <stdint.h>

#include <iosfwd>
#include <string>

#include <boost/functional/hash.hpp>

#include <process/address.hpp>

#include <stout/ip.hpp>

namespace process {

// Forward declaration to break cyclic dependencies.
class ProcessBase;

/**
 * An "untyped" `PID`, used to encapsulate the process ID for
 * lower-layer abstractions (eg, when receiving incoming requests)
 * in the dispatching mechanism.
 *
 * @see process::PID
 */
struct UPID
{
  UPID() = default;

  UPID(const UPID& that) = default;

  UPID(UPID&& that) = default;

  UPID(const char* id_, const net::IP& ip_, uint16_t port_)
    : id(id_), address(ip_, port_) { resolve(); }

  UPID(const char* id_, const network::inet::Address& address_)
    : id(id_), address(address_) { resolve(); }

  UPID(const std::string& id_, const net::IP& ip_, uint16_t port_)
    : id(id_), address(ip_, port_) { resolve(); }

  UPID(const std::string& id_, const network::inet::Address& address_)
    : id(id_), address(address_) { resolve(); }

  /*implicit*/ UPID(const char* s);

  /*implicit*/ UPID(const std::string& s);

  /*implicit*/ UPID(const ProcessBase& process);

  UPID& operator=(const UPID& that) = default;

  UPID& operator=(UPID&& that) = default;

  operator std::string() const;

  operator bool() const
  {
    return id != "" && !address.ip.isAny() && address.port != 0;
  }

  bool operator!() const // NOLINT(whitespace/operators)
  {
    return id == "" && address.ip.isAny() && address.port == 0;
  }

  bool operator<(const UPID& that) const
  {
    if (address == that.address) {
      return id < that.id;
    } else {
      return address < that.address;
    }
  }

  bool operator==(const UPID& that) const
  {
    return (id == that.id && address == that.address);
  }

  bool operator!=(const UPID& that) const
  {
    return !(*this == that);
  }

  // Attempts to resolve and cache a weak pointer to the ProcessBase
  // to which this UPID refers.
  void resolve();

  // TODO(benh): store all of the members of UPID behind a
  // copy-on-write implementation because UPID is often copied but
  // rarely written which means we could optimize performance by not
  // making so many copies.

  // A copy-on-write string for performance.
  //
  // TODO(benh): Factor this out into a generic copy-on-write string.
  struct ID
  {
    static const std::string EMPTY;

    ID() = default;

    ID(const std::string& s)
      : id(std::make_shared<std::string>(s)) {}

    ID(std::string&& s)
      : id(std::make_shared<std::string>(std::move(s))) {}

    ID& operator=(std::string&& that)
    {
      id = std::make_shared<std::string>(std::move(that));
      return *this;
    }

    bool operator==(const std::string& that) const
    {
      if (!id) {
        return EMPTY == that;
      }
      return *id == that;
    }

    bool operator==(const char* that) const
    {
      if (!id) {
        return EMPTY == that;
      }
      return *id == that;
    }

    bool operator!=(const std::string& that) const
    {
      return !(*this == that);
    }

    bool operator<(const std::string& that) const
    {
      if (!id) {
        return EMPTY < that;
      }
      return *id < that;
    }

    operator const std::string&() const
    {
      if (!id) {
        return EMPTY;
      }
      return *id;
    }

  private:
    std::shared_ptr<std::string> id;
  } id;

  // TODO(asridharan): Ideally, the following `address` field should be of
  // type `network::Address` so that the default address of the PID
  // could be a unix domain socket or an IPv4/v6 address. This change
  // however is disruptive at this point and should be done after we have
  // introduced support for unix domain and IPv6 sockets into
  // `libprocess`.
  network::inet::Address address = network::inet4::Address::ANY_ANY();

  // TODO(asridharan): Currently we are introducing only an `Optional`
  // IPv6 address in the following `addresses` structure. This will
  // help us initiate some basic IPv6 support for the
  // `DockerContainerizer`.  However, going forward, once we start
  // supporting unix domain sockets and IPv4/IPv6 socket in
  // `libprocess` we will add the following fields to this structure.
  //
  // Option<network::unix::Address> unix;
  // Option<network::inet4::Address> v4;
  //
  // With the introduction of the above fields `libprocess` PID will
  // be able to support unix, IPv4 and IPv6 sockets simultaneously.
  struct
  {
    Option<network::inet6::Address> v6;
  } addresses = {None()};

protected:
  friend class ProcessBase;
  friend class ProcessManager;

  // A weak pointer to the actual process used to optimize enqueuing
  // events without having to go through a shared lock in the
  // `ProcessManager`. This is `None` if someone creates a UPID and
  // doesn't call `resolve()` or if `resolve()` doesn't find a valid
  // process (i.e., the process hasn't started or has terminated).
  Option<std::weak_ptr<ProcessBase*>> reference = None();
};

inline std::ostream& operator<<(std::ostream& stream, const UPID::ID& id)
{
  const std::string& s = id;
  return stream << s;
}

inline bool operator==(const std::string& s, const UPID::ID& id)
{
  return id == s;
}

inline bool operator!=(const std::string& s, const UPID::ID& id)
{
  return !(s == id);
}

inline std::string operator+(const UPID::ID& id, const std::string& s)
{
  return (const std::string&) id + s;
}

inline std::string operator+(const UPID::ID& id, std::string&& s)
{
  return (const std::string&) id + std::move(s);
}

inline std::string operator+(const std::string& s, const UPID::ID& id)
{
  return s + (const std::string&) id;
}

inline std::string operator+(std::string&& s, const UPID::ID& id)
{
  return std::move(s) + (const std::string&) id;
}

/**
 * A "process identifier" used to uniquely identify a process when
 * dispatching messages.
 *
 * Typed with the actual process class's type, which must be
 * derived from `process::ProcessBase`.
 *
 * Use it like this:
 *
 *    using namespace process;
 *
 *    class SimpleProcess : public Process<SimpleProcess>
 *    {
 *       // ...
 *    };
 *
 *
 *    SimpleProcess process;
 *    PID<SimpleProcess> pid = spawn(process);
 *
 *    // ...
 *
 *    dispatchpid, &SimpleProcess::method, "argument");
 *
 * @see process::ProcessBase
 */
template <typename T = ProcessBase>
struct PID : UPID
{
  // Need to declare PID<U> as a friend in order to write `reference`.
  template <typename U>
  friend struct PID;

  PID() : UPID() {}

  /*implicit*/ PID(const T* t) : UPID(static_cast<const ProcessBase&>(*t)) {}
  /*implicit*/ PID(const T& t) : UPID(static_cast<const ProcessBase&>(t)) {}

  template <typename Base>
  operator PID<Base>() const
  {
    // Only allow upcasts!
    T* t = nullptr;
    Base* base = t;
    (void)base; // Eliminate unused base warning.
    PID<Base> pid;
    pid.id = id;
    pid.address = address;
    pid.addresses = addresses;
    pid.reference = reference;
    return pid;
  }
};

// Outputing UPIDs and generating UPIDs using streams.
std::ostream& operator<<(std::ostream&, const UPID&);
std::istream& operator>>(std::istream&, UPID&);

} // namespace process {

namespace std {

template <>
struct hash<process::UPID>
{
  typedef size_t result_type;

  typedef process::UPID argument_type;

  result_type operator()(const argument_type& upid) const
  {
    size_t seed = 0;
    boost::hash_combine(seed, (const std::string&) upid.id);
    boost::hash_combine(seed, std::hash<net::IP>()(upid.address.ip));
    boost::hash_combine(seed, upid.address.port);
    return seed;
  }
};

} // namespace std {

#endif // __PROCESS_PID_HPP__
<|endoftext|>


<commit_before>// $Id$
// Copyright (C) 2005, 2006, 2008 by the deal.II authors
//
// Computes measure, center and barycenter on a variety of cells

#include "../tests.h"
#include <grid/tria.h>
#include <grid/tria_iterator.h>
#include <grid/tria_accessor.h>
#include <grid/grid_generator.h>
#include <fstream>
#include <iomanip>

#define PRECISION 5

char fname[50];

template<int dim>
void create_triangulation(const unsigned int,
			  Triangulation<dim> &)
{
  Assert(false, ExcNotImplemented());
}

template<>
void create_triangulation(const unsigned int case_no,
			  Triangulation<2> &tria)
{
  switch (case_no)
    {
      case 0:
	    GridGenerator::hyper_cube(tria, 1., 3.);
	    break;
      case 1:
      {
	GridGenerator::hyper_cube(tria, 1., 3.);
	Point<2> &v0=tria.begin_active()->vertex(0);
	v0(0) = 0.;
	Point<2> &v2=tria.begin_active()->vertex(3);
	v2(0) = 5.;
	v2(1) = 4.;
//	    exact_areas.push_back(7.);
	break;
      }
      default:
	    Assert(false, ExcNotImplemented());
    };
}

template<>
void create_triangulation(const unsigned int case_no,
			  Triangulation<3> &tria)
{
  switch (case_no)
    {
      case 0:
	    GridGenerator::hyper_cube(tria, 1., 3.);
	    break;
      case 1:
      {
	GridGenerator::hyper_cube(tria, 1., 3.);
	Point<3> &v0=tria.begin_active()->vertex(0);
	v0(0) = 0.;
	break;
      }
      default:
	    Assert(false, ExcNotImplemented());
    };
}

template<int dim>
void test()
{
  Triangulation<dim> tria;
  for (unsigned int case_no=0; case_no<2; ++case_no)
    {
      create_triangulation(case_no, tria);
      deallog << "dim" << dim << ":case" << case_no << ":measure="
	      << tria.begin_active()->measure() << std::endl;
      deallog << "dim" << dim << ":case" << case_no << ":center="
	      << tria.begin_active()->center() << std::endl;
      deallog << "dim" << dim << ":case" << case_no << ":barycenter="
	      << tria.begin_active()->barycenter() << std::endl;
      tria.clear();
    }
}

int main()
{
  std::ofstream logfile ("measure_et_al/output");
  deallog << std::setprecision (PRECISION);
  deallog.attach(logfile);
  deallog.depth_console(0);
  deallog.threshold_double(1.e-10);

  test<2>();
  test<3>();
}

<commit_msg>Remove apparently unused variable.<commit_after>// $Id$
// Copyright (C) 2005, 2006, 2008, 2010 by the deal.II authors
//
// Computes measure, center and barycenter on a variety of cells

#include "../tests.h"
#include <grid/tria.h>
#include <grid/tria_iterator.h>
#include <grid/tria_accessor.h>
#include <grid/grid_generator.h>
#include <fstream>
#include <iomanip>

#define PRECISION 5

template<int dim>
void create_triangulation(const unsigned int,
			  Triangulation<dim> &)
{
  Assert(false, ExcNotImplemented());
}

template<>
void create_triangulation(const unsigned int case_no,
			  Triangulation<2> &tria)
{
  switch (case_no)
    {
      case 0:
	    GridGenerator::hyper_cube(tria, 1., 3.);
	    break;
      case 1:
      {
	GridGenerator::hyper_cube(tria, 1., 3.);
	Point<2> &v0=tria.begin_active()->vertex(0);
	v0(0) = 0.;
	Point<2> &v2=tria.begin_active()->vertex(3);
	v2(0) = 5.;
	v2(1) = 4.;
//	    exact_areas.push_back(7.);
	break;
      }
      default:
	    Assert(false, ExcNotImplemented());
    };
}

template<>
void create_triangulation(const unsigned int case_no,
			  Triangulation<3> &tria)
{
  switch (case_no)
    {
      case 0:
	    GridGenerator::hyper_cube(tria, 1., 3.);
	    break;
      case 1:
      {
	GridGenerator::hyper_cube(tria, 1., 3.);
	Point<3> &v0=tria.begin_active()->vertex(0);
	v0(0) = 0.;
	break;
      }
      default:
	    Assert(false, ExcNotImplemented());
    };
}

template<int dim>
void test()
{
  Triangulation<dim> tria;
  for (unsigned int case_no=0; case_no<2; ++case_no)
    {
      create_triangulation(case_no, tria);
      deallog << "dim" << dim << ":case" << case_no << ":measure="
	      << tria.begin_active()->measure() << std::endl;
      deallog << "dim" << dim << ":case" << case_no << ":center="
	      << tria.begin_active()->center() << std::endl;
      deallog << "dim" << dim << ":case" << case_no << ":barycenter="
	      << tria.begin_active()->barycenter() << std::endl;
      tria.clear();
    }
}

int main()
{
  std::ofstream logfile ("measure_et_al/output");
  deallog << std::setprecision (PRECISION);
  deallog.attach(logfile);
  deallog.depth_console(0);
  deallog.threshold_double(1.e-10);

  test<2>();
  test<3>();
}

<|endoftext|>


<commit_before>#include <iostream>
#include <string>
#include "simplestack.h"

using namespace std;

bool isPair(StackElement *position)
{
	if (position && next(position) && getValue(position) == ')' && getValue(next(position)) == '(')
	{
		return true;
	}
	if (position && next(position) && getValue(position) == '}' && getValue(next(position)) == '{')
	{
		return true;
	}
	if (position && next(position) && getValue(position) == ']' && getValue(next(position)) == '[')
	{
		return true;
	}
	return false;
}

bool removePair(StackElement *position)
{
	if (position && (isPair(next(position))))
	{
		remove(position);
		remove(position);
		return true;
	}
	return false;
}

bool lastCheck(StackElement *head)
{
	if (!head)
	{
		return true;
	}
	if (!next(head))
	{
		return false;
	}
	if (getValue(head) == ')' && getValue(next(head)) == '(')
	{
		return true;
	}
	if (getValue(head) == '}' && getValue(next(head)) == '{')
	{
		return true;
	}
	if (getValue(head) == ']' && getValue(next(head)) == '[')
	{
		return true;
	}
	return false;
}

bool searchForPairs(Stack *stack)
{
	auto head = getHead(stack);
	printReversed_r(stack);
	auto iterator = head;
	if ((!head) || (!next(head)) || (!next(next(head))))
	{
		return lastCheck(head);
	}
	while (iterator && (!isPair(next(iterator))))
	{
		if (iterator)
		{
			iterator = next(iterator);
		}
	}
	if (removePair(iterator))
	{
		return searchForPairs(stack);
	}
	return false;
}

Stack* stringToStack(string str)
{
	auto stack = createStack();
	for (int i = 0; i < str.size(); ++i)
	{
		if (str[i] == '(')
		{
			pushback(stack, '(');
		}
		if (str[i] == ')')
		{
			pushback(stack, ')');
		}
		if (str[i] == '{')
		{
			pushback(stack, '{');
		}
		if (str[i] == '}')
		{
			pushback(stack, '}');
		}
		if (str[i] == '[')
		{
			pushback(stack, '[');
		}
		if (str[i] == ']')
		{
			pushback(stack, ']');
		}
	}
	return stack;
}

bool test1()
{
	string str1 = "{{[]}}()";
	string str2 = "((}}";
	auto stack1 = stringToStack(str1);
	auto stack2 = stringToStack(str2);
	bool testResult = searchForPairs(stack1) && !searchForPairs(stack2);
	deleteStack(stack1);
	deleteStack(stack2);
	return testResult;
}

int main()
{
	string str = "";
	cin >> str;
	auto stack = stringToStack(str);
	printReversed_r(stack);
	cout << searchForPairs(stack) << endl;
	deleteStack(stack);
	cout << "launching test: " << test1() << endl;
	return 0;
}<commit_msg>исправлена визуализация<commit_after>#include <iostream>
#include <string>
#include "simplestack.h"

using namespace std;

bool isPair(StackElement *position)
{
	if (position && next(position) && getValue(position) == ')' && getValue(next(position)) == '(')
	{
		return true;
	}
	if (position && next(position) && getValue(position) == '}' && getValue(next(position)) == '{')
	{
		return true;
	}
	if (position && next(position) && getValue(position) == ']' && getValue(next(position)) == '[')
	{
		return true;
	}
	return false;
}

bool removePair(StackElement *position)
{
	if (position && (isPair(next(position))))
	{
		remove(position);
		remove(position);
		return true;
	}
	return false;
}

bool lastCheck(StackElement *head)
{
	if (!head)
	{
		return true;
	}
	if (!next(head))
	{
		return false;
	}
	if (getValue(head) == ')' && getValue(next(head)) == '(')
	{
		return true;
	}
	if (getValue(head) == '}' && getValue(next(head)) == '{')
	{
		return true;
	}
	if (getValue(head) == ']' && getValue(next(head)) == '[')
	{
		return true;
	}
	return false;
}

bool searchForPairs(Stack *stack)
{
	auto head = getHead(stack);
	printReversed_r(stack);
	auto iterator = head;
	if ((!head) || (!next(head)) || (!next(next(head))))
	{
		return lastCheck(head);
	}
	while (iterator && (!isPair(next(iterator))))
	{
		if (iterator)
		{
			iterator = next(iterator);
		}
	}
	if (removePair(iterator))
	{
		return searchForPairs(stack);
	}
	return false;
}

Stack* stringToStack(string str)
{
	auto stack = createStack();
	for (int i = 0; i < str.size(); ++i)
	{
		if (str[i] == '(')
		{
			pushback(stack, '(');
		}
		if (str[i] == ')')
		{
			pushback(stack, ')');
		}
		if (str[i] == '{')
		{
			pushback(stack, '{');
		}
		if (str[i] == '}')
		{
			pushback(stack, '}');
		}
		if (str[i] == '[')
		{
			pushback(stack, '[');
		}
		if (str[i] == ']')
		{
			pushback(stack, ']');
		}
	}
	return stack;
}

bool test1()
{
	string str1 = "{{[]}}()";
	string str2 = "((}}";
	auto stack1 = stringToStack(str1);
	auto stack2 = stringToStack(str2);
	bool testResult = searchForPairs(stack1) && !searchForPairs(stack2);
	deleteStack(stack1);
	deleteStack(stack2);
	return testResult;
}

int main()
{
	string str = "";
	cin >> str;
	auto stack = stringToStack(str);
	printReversed_r(stack);
	cout << searchForPairs(stack) << endl;
	deleteStack(stack);
	cout << endl << "launching test: " << test1() << endl;
	return 0;
}<|endoftext|>


<commit_before>//
//   Copyright 2015 Pixar
//
//   Licensed under the Apache License, Version 2.0 (the "Apache License")
//   with the following modification; you may not use this file except in
//   compliance with the Apache License and the following modification to it:
//   Section 6. Trademarks. is deleted and replaced with:
//
//   6. Trademarks. This License does not grant permission to use the trade
//      names, trademarks, service marks, or product names of the Licensor
//      and its affiliates, except as required to comply with Section 4(c) of
//      the License and to reproduce the content of the NOTICE file.
//
//   You may obtain a copy of the Apache License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the Apache License with the above modification is
//   distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
//   KIND, either express or implied. See the Apache License for the specific
//   language governing permissions and limitations under the Apache License.
//

#include <sstream>
#include <string>
#include "glUtils.h"

#include <GLFW/glfw3.h>

namespace GLUtils {

// Note that glewIsSupported is required here for Core profile, glewGetExtension
// and GLEW_extension_name will not work on all drivers.
#ifdef OSD_USES_GLEW
#define IS_SUPPORTED(x) \
   (glewIsSupported(x) == GL_TRUE)
#endif

void
SetMinimumGLVersion() {
    #define glfwOpenWindowHint glfwWindowHint
    #define GLFW_OPENGL_VERSION_MAJOR GLFW_CONTEXT_VERSION_MAJOR
    #define GLFW_OPENGL_VERSION_MINOR GLFW_CONTEXT_VERSION_MINOR

    #ifdef CORE_PROFILE
    glfwOpenWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    #endif

    #if defined(__APPLE__)
        // Here 3.2 is the minimum GL version supported, GLFW will allocate a
        // higher version if possible. This works on OS X, but instead limits
        // the version to 3.2 on Linux. On Linux & Windows, specifying no
        // version hint should use the highest version available.
        //
        // http://www.glfw.org/faq.html#how-do-i-create-an-opengl-30-context
        // http://www.glfw.org/faq.html#what-versions-of-opengl-are-supported-by-glfw
        //
        int major = 3,
            minor = 2;

        glfwOpenWindowHint(GLFW_OPENGL_VERSION_MAJOR, major);
        glfwOpenWindowHint(GLFW_OPENGL_VERSION_MINOR, minor);

        glfwOpenWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
    #endif
}

void
PrintGLVersion() {
    std::cout << glGetString(GL_VENDOR) << "\n";
    std::cout << glGetString(GL_RENDERER) << "\n";
    std::cout << glGetString(GL_VERSION) << "\n";

    int i;
    std::cout << "Init OpenGL ";
    glGetIntegerv(GL_MAJOR_VERSION, &i);
    std::cout << i << ".";
    glGetIntegerv(GL_MINOR_VERSION, &i);
    std::cout << i << "\n";
}

void
CheckGLErrors(std::string const & where) {
    GLuint err;
    while ((err = glGetError()) != GL_NO_ERROR) {
        std::cerr << "GL error: "
                  << (where.empty() ? "" : where + " ")
                  << err << "\n";
    }
}

GLuint
CompileShader(GLenum shaderType, const char *source) {
    GLuint shader = glCreateShader(shaderType);
    glShaderSource(shader, 1, &source, NULL);
    glCompileShader(shader);

    GLint status;
    glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
    if (status == GL_FALSE) {
        GLchar emsg[40960];
        glGetShaderInfoLog(shader, sizeof(emsg), 0, emsg);
        fprintf(stderr, "Error compiling GLSL shader: %s\n", emsg);
        return 0;
    }

    return shader;
}

bool
SupportsAdaptiveTessellation() {
#ifdef OSD_USES_GLEW
    return IS_SUPPORTED("GL_ARB_tessellation_shader");
#else
#if defined(GL_ARB_tessellation_shader) || defined(GL_VERSION_4_0)
    return true;
#else
    return false;
#endif
#endif
}

void GetMajorMinorVersion(int *major, int *minor){
    const GLubyte *ver = glGetString(GL_SHADING_LANGUAGE_VERSION);
    if (!ver){
        *major = -1;
        *minor = -1;
    }
    else{
        std::stringstream ss;
        ss << std::string(ver, ver + 1) << " " << std::string(ver + 2, ver + 3);
        ss >> *major;
        ss >> *minor;
    }
}

std::string
GetShaderVersion(){
    std::string shader_version;
    int major, minor;
    GetMajorMinorVersion(&major, &minor);
    int version_number = major * 10 + minor;
    switch (version_number){
    case 20:
        shader_version = "110";
        break;
    case 21:
        shader_version = "120";
        break;
    case 30:
        shader_version = "130";
        break;
    case 31:
        shader_version = "140";
        break;
    case 32:
        shader_version = "150";
        break;
    default:
        std::stringstream ss;
        ss << version_number;
        shader_version = ss.str() + "0";
        break;
    }
    return shader_version;
}

/* Generates the version defintion needed by the glsl shaders based on the
 * opengl string
*/
std::string GetShaderVersionInclude(){
    return "#version " + GetShaderVersion() + "\n";
}

bool GL_ARBSeparateShaderObjectsOrGL_VERSION_4_1(){
#if defined(OSD_USES_GLEW)
    return IS_SUPPORTED("GL_ARB_separate_shader_objects") ||
            (GLEW_VERSION_4_1 && IS_SUPPORTED("GL_ARB_tessellation_shader"));
#else
#if defined(GL_ARB_separate_shader_objects) || defined(GL_VERSION_4_1)
    return true;
#else
    return false;
#endif
#endif
}

bool GL_ARBComputeShaderOrGL_VERSION_4_3() {
#if defined(OSD_USES_GLEW)
    return IS_SUPPORTED("GL_ARB_compute_shader") ||
           (GLEW_VERSION_4_3);
#else
#if defined(GL_ARB_compute_shader) || defined(GL_VERSION_4_3)
    return true;
#else
    return false;
#endif
#endif
}

#undef IS_SUPPORTED

}   // namesapce GLUtils
<commit_msg>Fix GL context init for Linux and Windows.<commit_after>//
//   Copyright 2015 Pixar
//
//   Licensed under the Apache License, Version 2.0 (the "Apache License")
//   with the following modification; you may not use this file except in
//   compliance with the Apache License and the following modification to it:
//   Section 6. Trademarks. is deleted and replaced with:
//
//   6. Trademarks. This License does not grant permission to use the trade
//      names, trademarks, service marks, or product names of the Licensor
//      and its affiliates, except as required to comply with Section 4(c) of
//      the License and to reproduce the content of the NOTICE file.
//
//   You may obtain a copy of the Apache License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the Apache License with the above modification is
//   distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
//   KIND, either express or implied. See the Apache License for the specific
//   language governing permissions and limitations under the Apache License.
//

#include <sstream>
#include <string>
#include "glUtils.h"

#include <GLFW/glfw3.h>

namespace GLUtils {

// Note that glewIsSupported is required here for Core profile, glewGetExtension
// and GLEW_extension_name will not work on all drivers.
#ifdef OSD_USES_GLEW
#define IS_SUPPORTED(x) \
   (glewIsSupported(x) == GL_TRUE)
#endif

void
SetMinimumGLVersion() {
    #define glfwOpenWindowHint glfwWindowHint
    #define GLFW_OPENGL_VERSION_MAJOR GLFW_CONTEXT_VERSION_MAJOR
    #define GLFW_OPENGL_VERSION_MINOR GLFW_CONTEXT_VERSION_MINOR

    #if defined(__APPLE__)

        #ifdef CORE_PROFILE
        glfwOpenWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
        #endif

        // Here 3.2 is the minimum GL version supported, GLFW will allocate a
        // higher version if possible. This works on OS X, but instead limits
        // the version to 3.2 on Linux. On Linux & Windows, specifying no
        // version hint should use the highest version available.
        //
        // http://www.glfw.org/faq.html#how-do-i-create-an-opengl-30-context
        // http://www.glfw.org/faq.html#what-versions-of-opengl-are-supported-by-glfw
        //
        int major = 3,
            minor = 2;

        glfwOpenWindowHint(GLFW_OPENGL_VERSION_MAJOR, major);
        glfwOpenWindowHint(GLFW_OPENGL_VERSION_MINOR, minor);

        glfwOpenWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
    #endif
}

void
PrintGLVersion() {
    std::cout << glGetString(GL_VENDOR) << "\n";
    std::cout << glGetString(GL_RENDERER) << "\n";
    std::cout << glGetString(GL_VERSION) << "\n";

    int i;
    std::cout << "Init OpenGL ";
    glGetIntegerv(GL_MAJOR_VERSION, &i);
    std::cout << i << ".";
    glGetIntegerv(GL_MINOR_VERSION, &i);
    std::cout << i << "\n";
}

void
CheckGLErrors(std::string const & where) {
    GLuint err;
    while ((err = glGetError()) != GL_NO_ERROR) {
        std::cerr << "GL error: "
                  << (where.empty() ? "" : where + " ")
                  << err << "\n";
    }
}

GLuint
CompileShader(GLenum shaderType, const char *source) {
    GLuint shader = glCreateShader(shaderType);
    glShaderSource(shader, 1, &source, NULL);
    glCompileShader(shader);

    GLint status;
    glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
    if (status == GL_FALSE) {
        GLchar emsg[40960];
        glGetShaderInfoLog(shader, sizeof(emsg), 0, emsg);
        fprintf(stderr, "Error compiling GLSL shader: %s\n", emsg);
        return 0;
    }

    return shader;
}

bool
SupportsAdaptiveTessellation() {
#ifdef OSD_USES_GLEW
    return IS_SUPPORTED("GL_ARB_tessellation_shader");
#else
#if defined(GL_ARB_tessellation_shader) || defined(GL_VERSION_4_0)
    return true;
#else
    return false;
#endif
#endif
}

void GetMajorMinorVersion(int *major, int *minor){
    const GLubyte *ver = glGetString(GL_SHADING_LANGUAGE_VERSION);
    if (!ver){
        *major = -1;
        *minor = -1;
    }
    else{
        std::stringstream ss;
        ss << std::string(ver, ver + 1) << " " << std::string(ver + 2, ver + 3);
        ss >> *major;
        ss >> *minor;
    }
}

std::string
GetShaderVersion(){
    std::string shader_version;
    int major, minor;
    GetMajorMinorVersion(&major, &minor);
    int version_number = major * 10 + minor;
    switch (version_number){
    case 20:
        shader_version = "110";
        break;
    case 21:
        shader_version = "120";
        break;
    case 30:
        shader_version = "130";
        break;
    case 31:
        shader_version = "140";
        break;
    case 32:
        shader_version = "150";
        break;
    default:
        std::stringstream ss;
        ss << version_number;
        shader_version = ss.str() + "0";
        break;
    }
    return shader_version;
}

/* Generates the version defintion needed by the glsl shaders based on the
 * opengl string
*/
std::string GetShaderVersionInclude(){
    return "#version " + GetShaderVersion() + "\n";
}

bool GL_ARBSeparateShaderObjectsOrGL_VERSION_4_1(){
#if defined(OSD_USES_GLEW)
    return IS_SUPPORTED("GL_ARB_separate_shader_objects") ||
            (GLEW_VERSION_4_1 && IS_SUPPORTED("GL_ARB_tessellation_shader"));
#else
#if defined(GL_ARB_separate_shader_objects) || defined(GL_VERSION_4_1)
    return true;
#else
    return false;
#endif
#endif
}

bool GL_ARBComputeShaderOrGL_VERSION_4_3() {
#if defined(OSD_USES_GLEW)
    return IS_SUPPORTED("GL_ARB_compute_shader") ||
           (GLEW_VERSION_4_3);
#else
#if defined(GL_ARB_compute_shader) || defined(GL_VERSION_4_3)
    return true;
#else
    return false;
#endif
#endif
}

#undef IS_SUPPORTED

}   // namesapce GLUtils
<|endoftext|>


<commit_before>#include "GUI.hpp"

#include <SFML/Graphics/Color.hpp>
#include <SFML/Graphics/RenderTarget.hpp>

#include <sstream>

GUI::GUI(unsigned int w, unsigned int h, sf::Font& font, unsigned int lives)
  : sf::Drawable(), sf::Transformable(), frog::Component(),
    m_lives(lives), m_score(0)
{
  setPosition(0, 0);
  m_background.setFillColor(sf::Color::Black);
  m_background.setSize( sf::Vector2f(w, h) );
  m_text.setFont(font);
  m_text.setColor(sf::Color::White);
  if (h > 64)
    {
      m_text.setCharacterSize(54);
    }else
    {
      m_text.setCharacterSize(h-10);
    }
  updateString();
  auto fr = m_text.getLocalBounds();
  m_text.setOrigin(fr.left+(fr.width/2), fr.top+(fr.height/2) );
  m_text.setPosition(w/2.0f, h/2.0f);

}

GUI::~GUI()
{
}

void GUI::draw(sf::RenderTarget& rt, sf::RenderStates rs) const
{
  rs.transform *= getTransform();
  rt.draw(m_background, rs);
  rt.draw(m_text, rs);
}

void GUI::setScore(unsigned int newscore)
{
  m_score = newscore;
}

void GUI::setLives(unsigned int newlives)
{
  m_lives = newlives;
}

void GUI::updateString()
{
  std::ostringstream oss;
  oss << "Score : " << m_score;
  oss << "\tLives : ";
  for (auto it = 1; it <= m_lives; it++)
    {
      oss << "X";
    }
  m_text.setString( oss.str() );
  oss.flush();

}

void GUI::update(const frog::ComponentHolder&)
{
}
<commit_msg>doc++<commit_after>#include "GUI.hpp"

#include <SFML/Graphics/Color.hpp>
#include <SFML/Graphics/RenderTarget.hpp>

#include <sstream>

GUI::GUI(unsigned int w, unsigned int h, sf::Font& font, unsigned int lives)
  : sf::Drawable(), sf::Transformable(), frog::Component(),
    m_lives(lives), m_score(0)
{
  setPosition(0, 0);
  // setting up the black frame
  m_background.setFillColor(sf::Color::Black);
  m_background.setSize( sf::Vector2f(w, h) );
  // setting up the text
  m_text.setFont(font);
  m_text.setColor(sf::Color::White);
  // ensure to keep text inside the background rectangle
  if (h > 64)
    {
      m_text.setCharacterSize(54);
    }else
    {
      m_text.setCharacterSize(h-10);
    }
  // initializing the text
  updateString();
  // placing the text at the center of the frame
  auto fr = m_text.getLocalBounds();
  m_text.setOrigin(fr.left+(fr.width/2), fr.top+(fr.height/2) );
  m_text.setPosition(w/2.0f, h/2.0f);

}

GUI::~GUI()
{
}

void GUI::draw(sf::RenderTarget& rt, sf::RenderStates rs) const
{
  // placing elements where GUI is placed (acts like a container)
  rs.transform *= getTransform();
  // drawing elements
  rt.draw(m_background, rs);
  rt.draw(m_text, rs);
}

void GUI::setScore(unsigned int newscore)
{
  m_score = newscore;
}

void GUI::setLives(unsigned int newlives)
{
  m_lives = newlives;
}

void GUI::updateString()
{
  std::ostringstream oss;
  oss << "Score : " << m_score;
  oss << "\tLives : ";
  for (auto it = 1; it <= m_lives; it++)
    {
      oss << "X";
    }
  m_text.setString( oss.str() );
  oss.flush();

}

void GUI::update(const frog::ComponentHolder&)
{
}
<|endoftext|>


<commit_before>// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

#include "core/framework/op_kernel.h"
#include "contrib_ops/cpu/bert/attention_cpu_base.h"
#include "core/providers/common.h"
#include "core/util/math.h"
#include "core/util/qmath.h"
#include "core/util/math_cpuonly.h"
#include "core/common/safeint.h"
#include "core/platform/threadpool.h"
#include "core/mlas/inc/mlas.h"

using onnxruntime::concurrency::ThreadPool;

namespace onnxruntime {
namespace contrib {

template <typename T>
class QAttention : public OpKernel, public AttentionCPUBase {
 public:
  QAttention(const OpKernelInfo& info);

  Status Compute(OpKernelContext* context) const override;

  Status PrePack(const Tensor& tensor, int input_idx, AllocatorPtr alloc,
                 bool& /*out*/ is_packed,
                 /*out*/ PrePackedWeights* prepacked_weights) override;

  Status UseSharedPrePackedBuffers(std::vector<BufferUniquePtr>& prepacked_buffers,
                                   int input_idx,
                                   /*out*/ bool& used_shared_buffers) override;

 private:
  BufferUniquePtr packed_weights_;
  size_t packed_weights_size_;
  TensorShape weight_shape_;
  bool weights_is_signed_;
};

// These ops are internal-only, so register outside of onnx
ONNX_OPERATOR_TYPED_KERNEL_EX(
    QAttention,
    kMSDomain,
    1,
    float,
    kCpuExecutionProvider,
    KernelDefBuilder()
        .TypeConstraint("T1", DataTypeImpl::GetTensorType<uint8_t>())
        .TypeConstraint("T2", {DataTypeImpl::GetTensorType<uint8_t>(), DataTypeImpl::GetTensorType<int8_t>()})
        .TypeConstraint("T3", DataTypeImpl::GetTensorType<float>())
        .TypeConstraint("T4", DataTypeImpl::GetTensorType<int32_t>()),
    QAttention<float>);

template <typename T>
QAttention<T>::QAttention(const OpKernelInfo& info) : OpKernel(info), AttentionCPUBase(info) {}

template <typename T>
Status QAttention<T>::PrePack(const Tensor& weights, int input_idx, AllocatorPtr alloc,
                              /*out*/ bool& is_packed,
                              /*out*/ PrePackedWeights* prepacked_weights) {
  if (1 != input_idx) {
    return Status::OK();
  }

  weight_shape_ = weights.Shape();
  const auto& weights_dims = weight_shape_.GetDims();
  if (weights_dims.size() != 2) {
    return Status::OK();
  }

  const size_t input_hidden_size = static_cast<size_t>(weights_dims[0]);
  const size_t hidden_size_x3 = static_cast<size_t>(weights_dims[1]);
  const size_t hidden_size = hidden_size_x3 / 3;
  const size_t head_size = hidden_size / num_heads_;

  // Bail out if the weights shape has an expected shape.
  if ((hidden_size == 0) || ((hidden_size % num_heads_) != 0) || (hidden_size_x3 != 3 * hidden_size)) {
    return Status::OK();
  }

  const auto* weights_data = static_cast<const uint8_t*>(weights.DataRaw());
  weights_is_signed_ = weights.IsDataType<int8_t>();

  packed_weights_size_ = MlasGemmPackBSize(head_size, input_hidden_size, weights_is_signed_);
  if (packed_weights_size_ == 0) {
    return Status::OK();
  }

  const size_t loop_len = 3 * num_heads_;
  size_t packed_weights_data_size = packed_weights_size_ * loop_len;
  auto* packed_weights_data = static_cast<uint8_t*>(alloc->Alloc(packed_weights_data_size));

  // Initialize memory to 0 as there could be some padding associated with pre-packed
  // buffer memory and we don not want it uninitialized and generate different hashes
  // if and when we try to cache this pre-packed buffer for sharing between sessions.
  memset(packed_weights_data, 0, packed_weights_data_size);

  packed_weights_ = BufferUniquePtr(packed_weights_data, BufferDeleter(alloc));

  for (size_t i = 0; i < loop_len; i++) {
    MlasGemmPackB(head_size, input_hidden_size, weights_data, hidden_size_x3, weights_is_signed_, packed_weights_data);
    packed_weights_data += packed_weights_size_;
    weights_data += head_size;
  }

  bool share_prepacked_weights = (prepacked_weights != nullptr);
  if (share_prepacked_weights) {
    prepacked_weights->buffers_.push_back(std::move(packed_weights_));
    prepacked_weights->buffer_sizes_.push_back(packed_weights_data_size);
  }

  is_packed = true;
  return Status::OK();
}

template <typename T>
Status QAttention<T>::UseSharedPrePackedBuffers(std::vector<BufferUniquePtr>& prepacked_buffers,
                                                int input_idx,
                                                /*out*/ bool& used_shared_buffers) {
  if (1 != input_idx) {
    return Status::OK();
  }

  used_shared_buffers = true;
  packed_weights_ = std::move(prepacked_buffers[0]);

  return Status::OK();
}

template <typename T>
Status QAttention<T>::Compute(OpKernelContext* context) const {
  // Input and output shapes:
  //   Input  0 - input             : (batch_size, sequence_length, input_hidden_size)
  //   Input  1 - weights           : (input_hidden_size, 3 * hidden_size)
  //   Input  2 - bias              : (3 * hidden_size)
  //   Input  3 - input_scale       : scalar
  //   Input  4 - weight_scale      : scalar for per tensor quantization, (3 * hidden_size) for per column quantization
  //   Input  5 - mask_index        : nullptr, (batch_size), (2 * batch_size), (batch_size, 1), (1, 1) or (batch_size, past_sequence_length + sequence_length)
  //   Input  6 - input_zero_point  : scalar
  //   Input  7 - weight_zero_point : scalar for per tensor quantization, (3 * hidden_size) for per column quantization
  //   Input  8 - past              : (2, batch_size, num_heads, past_sequence_length, head_size)
  //   Output 0                     : (batch_size, sequence_length, hidden_size)
  //   Output 1 - present           : (2, batch_size, num_heads, past_sequence_length + sequence_length, head_size)
  //   ORT_RETURN_IF_ERROR(CheckInputs(context));
  const Tensor* input = context->Input<Tensor>(0);
  const Tensor* weights = packed_weights_ ? nullptr : context->Input<Tensor>(1);
  const Tensor* bias = context->Input<Tensor>(2);
  const Tensor* input_scale_tensor = context->Input<Tensor>(3);
  const Tensor* weight_scale_tensor = context->Input<Tensor>(4);
  const Tensor* mask_index = context->Input<Tensor>(5);
  const Tensor* i_zp_tensor = context->Input<Tensor>(6);
  const Tensor* w_zp_tensor = context->Input<Tensor>(7);
  const Tensor* past_tensor = context->Input<Tensor>(8);

  const TensorShape& weights_shape = (packed_weights_ ? weight_shape_ : weights->Shape());
  ORT_RETURN_IF_ERROR(AttentionBase::CheckInputs(input->Shape(),
                                                 weights_shape,
                                                 bias->Shape(),
                                                 mask_index,
                                                 past_tensor,
                                                 nullptr));

  ORT_RETURN_IF_NOT(IsScalarOr1ElementVector(input_scale_tensor),
                    "input scale must be a scalar or 1D tensor of size 1");
  T input_scale = *(input_scale_tensor->template Data<T>());

  bool is_weight_scale_per_column = !IsScalarOr1ElementVector(weight_scale_tensor);
  const T* weight_scale_data = weight_scale_tensor->template Data<T>();

  std::vector<T> dequant_scales(weight_scale_data, weight_scale_data + weight_scale_tensor->Shape().Size());
  std::for_each(dequant_scales.begin(), dequant_scales.end(), [&input_scale](float& dequant_scale) {
    return dequant_scale *= input_scale;
  });

  uint8_t input_zero_point = 0;
  if (i_zp_tensor != nullptr) {
    ORT_RETURN_IF_NOT(IsScalarOr1ElementVector(i_zp_tensor),
                      "input zero point must be a scalar or 1D tensor of size 1.");
    input_zero_point = *i_zp_tensor->template Data<uint8_t>();
  }

  bool is_weight_zp_per_column = false;
  uint8_t weight_zp_default = 0;
  const uint8_t* weight_zp_data = nullptr;
  if (w_zp_tensor != nullptr) {
    is_weight_zp_per_column = !IsScalarOr1ElementVector(w_zp_tensor);
    weight_zp_data = static_cast<const uint8_t*>(w_zp_tensor->DataRaw());
  }

  const auto& shape = input->Shape();
  const int batch_size = static_cast<int>(shape[0]);
  const int sequence_length = static_cast<int>(shape[1]);
  const int input_hidden_size = static_cast<int>(shape[2]);

  const auto hidden_size_x3 = weights_shape.GetDims()[1];
  const int hidden_size = static_cast<int>(hidden_size_x3) / 3;
  const int head_size = hidden_size / num_heads_;

  std::vector<int64_t> output_shape(3);
  output_shape[0] = shape[0];
  output_shape[1] = shape[1];
  output_shape[2] = static_cast<int64_t>(hidden_size);
  Tensor* output = context->Output(0, output_shape);

  AllocatorPtr allocator;
  ORT_RETURN_IF_ERROR(context->GetTempSpaceAllocator(&allocator));

  constexpr size_t element_size = sizeof(T);

  auto* tp = context->GetOperatorThreadPool();
  // STEP.1: gemm_data(BS, 3NH) = Scale(input(BS, D) x weights(D, 3NH)) + bias(3NH)
  // D is hidden dimension of input, where input_hidden_size (D) could be larger than hidden_size (NH) when model is pruned.
  auto gemm_data = allocator->Alloc(SafeInt<size_t>(batch_size) * sequence_length * 3 * hidden_size * element_size);
  BufferUniquePtr gemm_buffer(gemm_data, BufferDeleter(allocator));

  auto Q = reinterpret_cast<T*>(gemm_data);
  auto K = Q + batch_size * sequence_length * hidden_size;
  auto V = K + batch_size * sequence_length * hidden_size;
  T* QKV[3] = {Q, K, V};

  {
    const int loop_len = 3 * batch_size * num_heads_;
    const auto* input_data = input->template Data<uint8_t>();
    const auto* bias_data = bias->template Data<T>();

    const auto* weights_data = packed_weights_ ? nullptr : static_cast<const uint8_t*>(weights->DataRaw());
    const bool weights_is_signed = packed_weights_ ? weights_is_signed_ : weights->IsDataType<int8_t>();

    MLAS_GEMM_U8X8_SHAPE_PARAMS gemm_shape;
    gemm_shape.M = sequence_length;
    gemm_shape.N = head_size;
    gemm_shape.K = input_hidden_size;
    gemm_shape.BIsSigned = weights_is_signed;

    std::vector<MLAS_GEMM_U8X8_DATA_PARAMS> gemm_data_vec(loop_len);
    std::vector<MLAS_QGEMM_SCALE_BIAS_OUTPUT_PROCESSOR> scale_bias_procs;
    scale_bias_procs.reserve(loop_len);

    for (int i = 0; i < loop_len; i++) {
      const int batch_index = static_cast<int>((i / 3) / num_heads_);
      const int head_index = static_cast<int>((i / 3) % num_heads_);
      const int qkv_index = static_cast<int>(i % 3);

      int input_offset = batch_index * sequence_length * input_hidden_size;
      int weights_offset = qkv_index * hidden_size + head_index * head_size;
      int weights_scale_offset = is_weight_scale_per_column ? weights_offset : 0;
      int weights_zp_offset = is_weight_zp_per_column ? weights_offset : 0;
      float* qkv_dest = QKV[qkv_index];
      int qkv_offset = (batch_index * num_heads_ + head_index) * (sequence_length * head_size);

      //                   original           transposed            iteration
      // A: input          (BxSxD)            (B.)S x D             S x D
      // B: weights        (Dx3xNxH)          D  x (3.N.)H          D x H
      // C: QKV[qkv_index] (3xBxNxSxH)        (3.B.N.)S x H         S x H

      scale_bias_procs.emplace_back(qkv_dest + qkv_offset,
                                    head_size,
                                    dequant_scales.data() + weights_scale_offset,
                                    bias_data + weights_offset,
                                    MLAS_QGEMM_OUTPUT_MODE::ZeroMode,
                                    is_weight_scale_per_column ? MLAS_QUANTIZATION_GRANULARITY::PerColumn : MLAS_QUANTIZATION_GRANULARITY::PerMatrix);

      auto& gemm_params = gemm_data_vec[i];
      gemm_params.A = input_data + input_offset;
      gemm_params.lda = input_hidden_size;
      gemm_params.ZeroPointA = input_zero_point;
      if (packed_weights_) {
        const auto* packed_weight =
            static_cast<const uint8_t*>(packed_weights_.get()) + packed_weights_size_ * (weights_offset / head_size);
        gemm_params.B = packed_weight;
        gemm_params.BIsPacked = true;
      } else {
        gemm_params.B = weights_data + weights_offset;
        gemm_params.ldb = 3 * hidden_size;
      }
      gemm_params.ZeroPointB = nullptr != weight_zp_data ? weight_zp_data + weights_zp_offset : &weight_zp_default;
      gemm_params.PerColumnZeroPoints = is_weight_zp_per_column;
      gemm_params.C = reinterpret_cast<int32_t*>(qkv_dest + qkv_offset);
      gemm_params.ldc = head_size;
      gemm_params.OutputProcessor = &(scale_bias_procs[i]);
    }

    MlasGemmBatch(gemm_shape, gemm_data_vec.data(), loop_len, tp);
  }

  // Compute the attention score and apply the score to V
  return ApplyAttention(Q, K, V, mask_index, past_tensor, output,
                        batch_size, sequence_length,
                        head_size, head_size, hidden_size, nullptr, context);
}

}  // namespace contrib
}  // namespace onnxruntime
<commit_msg>Fix 4byte to 8byte static warnings in attention_quant.cc (#8715)<commit_after>// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

#include "core/framework/op_kernel.h"
#include "contrib_ops/cpu/bert/attention_cpu_base.h"
#include "core/providers/common.h"
#include "core/util/math.h"
#include "core/util/qmath.h"
#include "core/util/math_cpuonly.h"
#include "core/common/safeint.h"
#include "core/platform/threadpool.h"
#include "core/mlas/inc/mlas.h"

using onnxruntime::concurrency::ThreadPool;

namespace onnxruntime {
namespace contrib {

template <typename T>
class QAttention : public OpKernel, public AttentionCPUBase {
 public:
  QAttention(const OpKernelInfo& info);

  Status Compute(OpKernelContext* context) const override;

  Status PrePack(const Tensor& tensor, int input_idx, AllocatorPtr alloc,
                 bool& /*out*/ is_packed,
                 /*out*/ PrePackedWeights* prepacked_weights) override;

  Status UseSharedPrePackedBuffers(std::vector<BufferUniquePtr>& prepacked_buffers,
                                   int input_idx,
                                   /*out*/ bool& used_shared_buffers) override;

 private:
  BufferUniquePtr packed_weights_;
  size_t packed_weights_size_;
  TensorShape weight_shape_;
  bool weights_is_signed_;
};

// These ops are internal-only, so register outside of onnx
ONNX_OPERATOR_TYPED_KERNEL_EX(
    QAttention,
    kMSDomain,
    1,
    float,
    kCpuExecutionProvider,
    KernelDefBuilder()
        .TypeConstraint("T1", DataTypeImpl::GetTensorType<uint8_t>())
        .TypeConstraint("T2", {DataTypeImpl::GetTensorType<uint8_t>(), DataTypeImpl::GetTensorType<int8_t>()})
        .TypeConstraint("T3", DataTypeImpl::GetTensorType<float>())
        .TypeConstraint("T4", DataTypeImpl::GetTensorType<int32_t>()),
    QAttention<float>);

template <typename T>
QAttention<T>::QAttention(const OpKernelInfo& info) : OpKernel(info), AttentionCPUBase(info) {}

template <typename T>
Status QAttention<T>::PrePack(const Tensor& weights, int input_idx, AllocatorPtr alloc,
                              /*out*/ bool& is_packed,
                              /*out*/ PrePackedWeights* prepacked_weights) {
  if (1 != input_idx) {
    return Status::OK();
  }

  weight_shape_ = weights.Shape();
  const auto& weights_dims = weight_shape_.GetDims();
  if (weights_dims.size() != 2) {
    return Status::OK();
  }

  const size_t input_hidden_size = static_cast<size_t>(weights_dims[0]);
  const size_t hidden_size_x3 = static_cast<size_t>(weights_dims[1]);
  const size_t hidden_size = hidden_size_x3 / 3;
  const size_t head_size = hidden_size / num_heads_;

  // Bail out if the weights shape has an expected shape.
  if ((hidden_size == 0) || ((hidden_size % num_heads_) != 0) || (hidden_size_x3 != 3 * hidden_size)) {
    return Status::OK();
  }

  const auto* weights_data = static_cast<const uint8_t*>(weights.DataRaw());
  weights_is_signed_ = weights.IsDataType<int8_t>();

  packed_weights_size_ = MlasGemmPackBSize(head_size, input_hidden_size, weights_is_signed_);
  if (packed_weights_size_ == 0) {
    return Status::OK();
  }

  const size_t loop_len = 3 * num_heads_;
  size_t packed_weights_data_size = packed_weights_size_ * loop_len;
  auto* packed_weights_data = static_cast<uint8_t*>(alloc->Alloc(packed_weights_data_size));

  // Initialize memory to 0 as there could be some padding associated with pre-packed
  // buffer memory and we don not want it uninitialized and generate different hashes
  // if and when we try to cache this pre-packed buffer for sharing between sessions.
  memset(packed_weights_data, 0, packed_weights_data_size);

  packed_weights_ = BufferUniquePtr(packed_weights_data, BufferDeleter(alloc));

  for (size_t i = 0; i < loop_len; i++) {
    MlasGemmPackB(head_size, input_hidden_size, weights_data, hidden_size_x3, weights_is_signed_, packed_weights_data);
    packed_weights_data += packed_weights_size_;
    weights_data += head_size;
  }

  bool share_prepacked_weights = (prepacked_weights != nullptr);
  if (share_prepacked_weights) {
    prepacked_weights->buffers_.push_back(std::move(packed_weights_));
    prepacked_weights->buffer_sizes_.push_back(packed_weights_data_size);
  }

  is_packed = true;
  return Status::OK();
}

template <typename T>
Status QAttention<T>::UseSharedPrePackedBuffers(std::vector<BufferUniquePtr>& prepacked_buffers,
                                                int input_idx,
                                                /*out*/ bool& used_shared_buffers) {
  if (1 != input_idx) {
    return Status::OK();
  }

  used_shared_buffers = true;
  packed_weights_ = std::move(prepacked_buffers[0]);

  return Status::OK();
}

template <typename T>
Status QAttention<T>::Compute(OpKernelContext* context) const {
  // Input and output shapes:
  //   Input  0 - input             : (batch_size, sequence_length, input_hidden_size)
  //   Input  1 - weights           : (input_hidden_size, 3 * hidden_size)
  //   Input  2 - bias              : (3 * hidden_size)
  //   Input  3 - input_scale       : scalar
  //   Input  4 - weight_scale      : scalar for per tensor quantization, (3 * hidden_size) for per column quantization
  //   Input  5 - mask_index        : nullptr, (batch_size), (2 * batch_size), (batch_size, 1), (1, 1) or (batch_size, past_sequence_length + sequence_length)
  //   Input  6 - input_zero_point  : scalar
  //   Input  7 - weight_zero_point : scalar for per tensor quantization, (3 * hidden_size) for per column quantization
  //   Input  8 - past              : (2, batch_size, num_heads, past_sequence_length, head_size)
  //   Output 0                     : (batch_size, sequence_length, hidden_size)
  //   Output 1 - present           : (2, batch_size, num_heads, past_sequence_length + sequence_length, head_size)
  //   ORT_RETURN_IF_ERROR(CheckInputs(context));
  const Tensor* input = context->Input<Tensor>(0);
  const Tensor* weights = packed_weights_ ? nullptr : context->Input<Tensor>(1);
  const Tensor* bias = context->Input<Tensor>(2);
  const Tensor* input_scale_tensor = context->Input<Tensor>(3);
  const Tensor* weight_scale_tensor = context->Input<Tensor>(4);
  const Tensor* mask_index = context->Input<Tensor>(5);
  const Tensor* i_zp_tensor = context->Input<Tensor>(6);
  const Tensor* w_zp_tensor = context->Input<Tensor>(7);
  const Tensor* past_tensor = context->Input<Tensor>(8);

  const TensorShape& weights_shape = (packed_weights_ ? weight_shape_ : weights->Shape());
  ORT_RETURN_IF_ERROR(AttentionBase::CheckInputs(input->Shape(),
                                                 weights_shape,
                                                 bias->Shape(),
                                                 mask_index,
                                                 past_tensor,
                                                 nullptr));

  ORT_RETURN_IF_NOT(IsScalarOr1ElementVector(input_scale_tensor),
                    "input scale must be a scalar or 1D tensor of size 1");
  T input_scale = *(input_scale_tensor->template Data<T>());

  bool is_weight_scale_per_column = !IsScalarOr1ElementVector(weight_scale_tensor);
  const T* weight_scale_data = weight_scale_tensor->template Data<T>();

  std::vector<T> dequant_scales(weight_scale_data, weight_scale_data + weight_scale_tensor->Shape().Size());
  std::for_each(dequant_scales.begin(), dequant_scales.end(), [&input_scale](float& dequant_scale) {
    return dequant_scale *= input_scale;
  });

  uint8_t input_zero_point = 0;
  if (i_zp_tensor != nullptr) {
    ORT_RETURN_IF_NOT(IsScalarOr1ElementVector(i_zp_tensor),
                      "input zero point must be a scalar or 1D tensor of size 1.");
    input_zero_point = *i_zp_tensor->template Data<uint8_t>();
  }

  bool is_weight_zp_per_column = false;
  uint8_t weight_zp_default = 0;
  const uint8_t* weight_zp_data = nullptr;
  if (w_zp_tensor != nullptr) {
    is_weight_zp_per_column = !IsScalarOr1ElementVector(w_zp_tensor);
    weight_zp_data = static_cast<const uint8_t*>(w_zp_tensor->DataRaw());
  }

  const auto& shape = input->Shape();
  const int batch_size = static_cast<int>(shape[0]);
  const int sequence_length = static_cast<int>(shape[1]);
  const int input_hidden_size = static_cast<int>(shape[2]);

  const auto hidden_size_x3 = weights_shape.GetDims()[1];
  const int hidden_size = static_cast<int>(hidden_size_x3) / 3;
  const int head_size = hidden_size / num_heads_;

  std::vector<int64_t> output_shape(3);
  output_shape[0] = shape[0];
  output_shape[1] = shape[1];
  output_shape[2] = static_cast<int64_t>(hidden_size);
  Tensor* output = context->Output(0, output_shape);

  AllocatorPtr allocator;
  ORT_RETURN_IF_ERROR(context->GetTempSpaceAllocator(&allocator));

  constexpr size_t element_size = sizeof(T);

  auto* tp = context->GetOperatorThreadPool();
  // STEP.1: gemm_data(BS, 3NH) = Scale(input(BS, D) x weights(D, 3NH)) + bias(3NH)
  // D is hidden dimension of input, where input_hidden_size (D) could be larger than hidden_size (NH) when model is pruned.
  auto gemm_data = allocator->Alloc(SafeInt<size_t>(batch_size) * sequence_length * 3 * hidden_size * element_size);
  BufferUniquePtr gemm_buffer(gemm_data, BufferDeleter(allocator));

  auto Q = reinterpret_cast<T*>(gemm_data);
  auto K = Q + static_cast<int64_t>(batch_size) * sequence_length * hidden_size;
  auto V = K + static_cast<int64_t>(batch_size) * sequence_length * hidden_size;
  T* QKV[3] = {Q, K, V};

  {
    const int loop_len = 3 * batch_size * num_heads_;
    const auto* input_data = input->template Data<uint8_t>();
    const auto* bias_data = bias->template Data<T>();

    const auto* weights_data = packed_weights_ ? nullptr : static_cast<const uint8_t*>(weights->DataRaw());
    const bool weights_is_signed = packed_weights_ ? weights_is_signed_ : weights->IsDataType<int8_t>();

    MLAS_GEMM_U8X8_SHAPE_PARAMS gemm_shape;
    gemm_shape.M = sequence_length;
    gemm_shape.N = head_size;
    gemm_shape.K = input_hidden_size;
    gemm_shape.BIsSigned = weights_is_signed;

    std::vector<MLAS_GEMM_U8X8_DATA_PARAMS> gemm_data_vec(loop_len);
    std::vector<MLAS_QGEMM_SCALE_BIAS_OUTPUT_PROCESSOR> scale_bias_procs;
    scale_bias_procs.reserve(loop_len);

    for (int i = 0; i < loop_len; i++) {
      const int batch_index = static_cast<int>((i / 3) / num_heads_);
      const int head_index = static_cast<int>((i / 3) % num_heads_);
      const int qkv_index = static_cast<int>(i % 3);

      int input_offset = batch_index * sequence_length * input_hidden_size;
      int weights_offset = qkv_index * hidden_size + head_index * head_size;
      int weights_scale_offset = is_weight_scale_per_column ? weights_offset : 0;
      int weights_zp_offset = is_weight_zp_per_column ? weights_offset : 0;
      float* qkv_dest = QKV[qkv_index];
      int qkv_offset = (batch_index * num_heads_ + head_index) * (sequence_length * head_size);

      //                   original           transposed            iteration
      // A: input          (BxSxD)            (B.)S x D             S x D
      // B: weights        (Dx3xNxH)          D  x (3.N.)H          D x H
      // C: QKV[qkv_index] (3xBxNxSxH)        (3.B.N.)S x H         S x H

      scale_bias_procs.emplace_back(qkv_dest + qkv_offset,
                                    head_size,
                                    dequant_scales.data() + weights_scale_offset,
                                    bias_data + weights_offset,
                                    MLAS_QGEMM_OUTPUT_MODE::ZeroMode,
                                    is_weight_scale_per_column ? MLAS_QUANTIZATION_GRANULARITY::PerColumn : MLAS_QUANTIZATION_GRANULARITY::PerMatrix);

      auto& gemm_params = gemm_data_vec[i];
      gemm_params.A = input_data + input_offset;
      gemm_params.lda = input_hidden_size;
      gemm_params.ZeroPointA = input_zero_point;
      if (packed_weights_) {
        const auto* packed_weight =
            static_cast<const uint8_t*>(packed_weights_.get()) + packed_weights_size_ * (weights_offset / head_size);
        gemm_params.B = packed_weight;
        gemm_params.BIsPacked = true;
      } else {
        gemm_params.B = weights_data + weights_offset;
        gemm_params.ldb = static_cast<int64_t>(3) * hidden_size;
      }
      gemm_params.ZeroPointB = nullptr != weight_zp_data ? weight_zp_data + weights_zp_offset : &weight_zp_default;
      gemm_params.PerColumnZeroPoints = is_weight_zp_per_column;
      gemm_params.C = reinterpret_cast<int32_t*>(qkv_dest + qkv_offset);
      gemm_params.ldc = head_size;
      gemm_params.OutputProcessor = &(scale_bias_procs[i]);
    }

    MlasGemmBatch(gemm_shape, gemm_data_vec.data(), loop_len, tp);
  }

  // Compute the attention score and apply the score to V
  return ApplyAttention(Q, K, V, mask_index, past_tensor, output,
                        batch_size, sequence_length,
                        head_size, head_size, hidden_size, nullptr, context);
}

}  // namespace contrib
}  // namespace onnxruntime
<|endoftext|>