Skip to content

src/frontend/shape.cc

Namespaces

Name
tenzr

Source code

// Copyright 2026 The Tenzr Project Authors
//
// SPDX-License-Identifier: Apache-2.0
//
// 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
//
//      https://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 "tenzr/frontend/shape.h"

#include <numeric>
#include <sstream>

#include "tenzr/common/error.h"

namespace tenzr {

  Shape::Shape(std::initializer_list<int64_t> dims) : dims_(dims) {}

  Shape::Shape(std::vector<int64_t> dims) : dims_(std::move(dims)) {}

  int64_t Shape::Dim(size_t index) const {
    TENZR_CHECK(index < dims_.size(), "Shape index out of range: " + std::to_string(index) + " >= " + std::to_string(dims_.size()));
    return dims_[index];
  }

  int64_t Shape::NumElements() const {
    if (dims_.empty()) {
      return 1;  // Scalar
    }
    int64_t total = 1;
    for (int64_t d : dims_) {
      TENZR_CHECK(d >= 0, "Negative dimension not allowed: " + std::to_string(d));
      total *= d;
    }
    return total;
  }

  std::vector<int64_t> Shape::ComputeContiguousStrides() const {
    if (dims_.empty()) {
      return {};
    }
    std::vector<int64_t> strides(dims_.size(), 1);
    for (size_t i = dims_.size() - 1; i > 0; --i) {
      strides[i - 1] = strides[i] * dims_[i];
    }
    return strides;
  }

  std::string Shape::ToString() const {
    std::ostringstream oss;
    oss << "[";
    for (size_t i = 0; i < dims_.size(); ++i) {
      if (i > 0) {
        oss << ", ";
      }
      oss << dims_[i];
    }
    oss << "]";
    return oss.str();
  }

  std::string Shape::ToMlirTensorType(DType dtype) const {
    std::ostringstream oss;
    oss << "tensor<";
    for (size_t i = 0; i < dims_.size(); ++i) {
      oss << dims_[i] << "x";
    }
    oss << ToMlirType(dtype) << ">";
    return oss.str();
  }

  Shape Shape::Broadcast(const Shape& a, const Shape& b) {
    size_t rank_a = a.Rank();
    size_t rank_b = b.Rank();
    size_t out_rank = std::max(rank_a, rank_b);
    std::vector<int64_t> out_dims(out_rank);

    for (size_t i = 0; i < out_rank; ++i) {
      int64_t dim_a = (i < out_rank - rank_a) ? 1 : a.Dim(i - (out_rank - rank_a));
      int64_t dim_b = (i < out_rank - rank_b) ? 1 : b.Dim(i - (out_rank - rank_b));
      if (dim_a == dim_b) {
        out_dims[i] = dim_a;
      } else if (dim_a == 1) {
        out_dims[i] = dim_b;
      } else if (dim_b == 1) {
        out_dims[i] = dim_a;
      } else {
        TENZR_THROW("Incompatible shapes for broadcasting: " + a.ToString() + " and " + b.ToString());
      }
    }
    return Shape(out_dims);
  }

  bool Shape::CanBroadcast(const Shape& a, const Shape& b) noexcept {
    size_t rank_a = a.Rank();
    size_t rank_b = b.Rank();
    size_t max_rank = std::max(rank_a, rank_b);

    for (size_t i = 0; i < max_rank; ++i) {
      int64_t dim_a = (i < max_rank - rank_a) ? 1 : a.Dim(i - (max_rank - rank_a));
      int64_t dim_b = (i < max_rank - rank_b) ? 1 : b.Dim(i - (max_rank - rank_b));
      if (dim_a != dim_b && dim_a != 1 && dim_b != 1) {
        return false;
      }
    }
    return true;
  }

}  // namespace tenzr