Skip to content

src/debug/debug.cc

Namespaces

Name
tenzr
tenzr::debug

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/debug/debug.h"

#include <cmath>
#include <iomanip>
#include <sstream>

#include "tenzr/common/error.h"
#include "tenzr/txpr/emitter.h"

namespace tenzr::debug {

  void Print(std::string_view label, const Array& x, bool print_data, std::ostream& os) {
    if (x.IsSymbolic()) {
      os << "[tenzr::debug::print] " << label << ": VarId=" << x.SymbolicVarId().id << ", shape=" << x.shape().ToString() << ", dtype=" << ToString(x.Dtype())
         << " (Symbolic Tracer)\n";
    } else {
      os << "[tenzr::debug::print] " << label << ": shape=" << x.shape().ToString() << ", dtype=" << ToString(x.Dtype()) << "\n";
      if (print_data) {
        os << x << "\n";
      }
    }
  }

  void CheckFinite(const Array& x, std::string_view op_context) {
    if (x.IsSymbolic() || !x.IsDefined()) {
      return;
    }

    if (x.Dtype() != DType::kFloat32 && x.Dtype() != DType::kFloat64) {
      return;
    }

    size_t nan_count = 0;
    size_t inf_count = 0;
    const size_t total = static_cast<size_t>(x.shape().NumElements());

    if (x.Dtype() == DType::kFloat32) {
      auto vec = x.ToVector<float>();
      for (float v : vec) {
        if (std::isnan(v)) {
          nan_count++;
        } else if (std::isinf(v)) {
          inf_count++;
        }
      }
    } else {
      auto vec = x.ToVector<double>();
      for (double v : vec) {
        if (std::isnan(v)) {
          nan_count++;
        } else if (std::isinf(v)) {
          inf_count++;
        }
      }
    }

    if (nan_count > 0 || inf_count > 0) {
      std::ostringstream oss;
      oss << "[tenzr::debug::check_finite] Detected non-finite values in " << (op_context.empty() ? "Array" : std::string(op_context)) << ": " << nan_count
          << " NaNs, " << inf_count << " Infs out of " << total << " elements (shape: " << x.shape().ToString() << ", dtype: " << ToString(x.Dtype()) << ")";
      TENZR_THROW(oss.str());
    }
  }

  std::string Inspect(const Array& x) {
    std::ostringstream oss;
    oss << "=== Tenzr Array Forensic Inspection ===\n";
    oss << "  Defined:      " << (x.IsDefined() ? "true" : "false") << "\n";
    if (!x.IsDefined()) {
      return oss.str();
    }

    oss << "  Symbolic:     " << (x.IsSymbolic() ? "true (Tracer active)" : "false (Materialized)") << "\n";
    oss << "  Shape:        " << x.shape().ToString() << " (Rank: " << x.shape().Rank() << ")\n";
    oss << "  DType:        " << ToString(x.Dtype()) << "\n";
    oss << "  Num Elements: " << x.shape().NumElements() << "\n";
    oss << "  Total Bytes:  " << (x.shape().NumElements() * static_cast<int64_t>(SizeOf(x.Dtype()))) << " bytes\n";
    oss << "  Residency:    Host=" << (x.HasData() ? "Yes" : "No") << ", Device=" << (x.HasDeviceData() ? "Yes" : "No") << "\n";

    if (!x.IsSymbolic() && (x.Dtype() == DType::kFloat32 || x.Dtype() == DType::kFloat64)) {
      size_t nans = 0;
      size_t infs = 0;
      double sum = 0.0;
      double min_val = std::numeric_limits<double>::infinity();
      double max_val = -std::numeric_limits<double>::infinity();

      if (x.Dtype() == DType::kFloat32) {
        auto vec = x.ToVector<float>();
        for (float v : vec) {
          if (std::isnan(v)) {
            nans++;
          } else if (std::isinf(v)) {
            infs++;
          } else {
            double d = static_cast<double>(v);
            sum += d;
            min_val = std::min(min_val, d);
            max_val = std::max(max_val, d);
          }
        }
      } else {
        auto vec = x.ToVector<double>();
        for (double v : vec) {
          if (std::isnan(v)) {
            nans++;
          } else if (std::isinf(v)) {
            infs++;
          } else {
            sum += v;
            min_val = std::min(min_val, v);
            max_val = std::max(max_val, v);
          }
        }
      }

      size_t finite_count = static_cast<size_t>(x.shape().NumElements()) - nans - infs;
      oss << "  Finite State: " << ((nans == 0 && infs == 0) ? "Valid" : "DEGRADED") << " (" << nans << " NaNs, " << infs << " Infs)\n";
      if (finite_count > 0) {
        oss << "  Statistics:   Min=" << min_val << ", Max=" << max_val << ", Mean=" << (sum / static_cast<double>(finite_count)) << "\n";
      }
    }

    return oss.str();
  }

  std::string FormatTxpr(const txpr::Txpr& txpr) {
    std::ostringstream oss;
    const auto& vars = txpr.Variables();
    const auto& in_binders = txpr.InBinders();
    const auto& out_binders = txpr.OutBinders();
    const auto& eqns = txpr.Equations();

    oss << "txpr {\n";
    oss << "  inputs: (";
    for (size_t i = 0; i < in_binders.size(); ++i) {
      uint32_t id = in_binders[i].id;
      const auto& v = vars[id];
      oss << "v" << id << ": " << ToString(v.dtype) << v.shape.ToString();
      if (i + 1 < in_binders.size()) {
        oss << ", ";
      }
    }
    oss << ")\n";

    for (const auto& eqn : eqns) {
      oss << "    ";
      for (size_t i = 0; i < eqn.out_vars.size(); ++i) {
        uint32_t oid = eqn.out_vars[i].id;
        const auto& ov = vars[oid];
        oss << "v" << oid << ": " << ToString(ov.dtype) << ov.shape.ToString();
        if (i + 1 < eqn.out_vars.size()) {
          oss << ", ";
        }
      }
      oss << " = " << txpr::OpCodeToString(eqn.op);
      if (eqn.op == txpr::OpCode::kCustomOp && !eqn.attrs.custom_op_name.empty()) {
        oss << "(" << eqn.attrs.custom_op_name << ")";
      }
      oss << " [ ";
      for (size_t i = 0; i < eqn.in_vars.size(); ++i) {
        oss << "v" << eqn.in_vars[i].id;
        if (i + 1 < eqn.in_vars.size()) {
          oss << ", ";
        }
      }
      oss << " ]\n";
    }

    oss << "  outputs: (";
    for (size_t i = 0; i < out_binders.size(); ++i) {
      oss << "v" << out_binders[i].id;
      if (i + 1 < out_binders.size()) {
        oss << ", ";
      }
    }
    oss << ")\n";
    oss << "}\n";

    return oss.str();
  }

  void DumpTxpr(const txpr::Txpr& txpr, std::ostream& os) { os << FormatTxpr(txpr); }

  std::string FormatStablehlo(const txpr::Txpr& txpr) {
    txpr::TxprEmitter emitter;
    return emitter.Emit(txpr).mlir_text;
  }

  void DumpStablehlo(const txpr::Txpr& txpr, std::ostream& os) { os << FormatStablehlo(txpr) << "\n"; }

}  // namespace tenzr::debug