Skip to content

print functions can output to any stream#869

Draft
manuschneider wants to merge 1 commit into
masterfrom
print_objects
Draft

print functions can output to any stream#869
manuschneider wants to merge 1 commit into
masterfrom
print_objects

Conversation

@manuschneider

Copy link
Copy Markdown
Collaborator

Separates the print part in #790 and implements #868.

operator<<(std::ostream file = std::cout) handles full output
pybind uses print(obj, file)
print_diagram, print_info etc have file as an optional argument, both on C++ and Python sides

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request refactors printing and representation methods across various classes (such as Gncon, Network, Symmetry, UniTensor, Scalar, and Storage) to accept a customizable std::ostream destination, defaulting to std::cout. It also updates the corresponding Python bindings to support writing to file-like objects and improves repr implementations using std::ostringstream. The review feedback highlights critical compilation issues in the Python bindings, specifically the use of a non-existent <pybind11/warnings.h> header and an invalid py::warnings::warn API in pybind/scalar_py.cpp. Additionally, a definition mismatch was identified in src/BlockUniTensor.cpp, where beauty_print_block was incorrectly defined as a free function with a default argument instead of a const member function.

Comment thread pybind/scalar_py.cpp
Comment on lines 12 to +13
#include <pybind11/functional.h>
#include <pybind11/warnings.h>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The header <pybind11/warnings.h> does not exist in standard pybind11. Including it will cause a compilation error. Please remove this include.

#include <pybind11/functional.h>

Comment thread pybind/scalar_py.cpp
Comment on lines +124 to +126
py::warnings::warn(
"Scalar.print() is deprecated; use `print(scalar)` (or `print(scalar, file=...)`).",
PyExc_FutureWarning, 2);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

py::warnings::warn is not a valid pybind11 API. To raise a deprecation warning in Python from C++, use the standard Python C API function PyErr_WarnEx with PyExc_FutureWarning.

        PyErr_WarnEx(
          PyExc_FutureWarning,
          "Scalar.print() is deprecated; use 'print(scalar)' (or 'print(scalar, file=...)').",
          2);

Comment thread src/BlockUniTensor.cpp
Comment on lines +204 to +207
void beauty_print_block(const cytnx_uint64 &Nin, const cytnx_uint64 &Nout,
const std::vector<cytnx_uint64> &qn_indices,
const std::vector<Bond> &bonds, const Tensor &block) {
const std::vector<Bond> &bonds, const Tensor &block,
std::ostream &file = std::cout) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The function beauty_print_block is declared as a const member function of BlockUniTensor in include/UniTensor.hpp. However, here it is defined as a free function without the BlockUniTensor:: prefix and without the const qualifier. Additionally, default arguments should only be specified in the function declaration, not in the definition. Please define it as a member function and remove the default argument.

  void BlockUniTensor::beauty_print_block(const cytnx_uint64 &Nin, const cytnx_uint64 &Nout,
                                          const std::vector<cytnx_uint64> &qn_indices,
                                          const std::vector<Bond> &bonds, const Tensor &block,
                                          std::ostream &file) const {

@codecov

codecov Bot commented May 31, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 15.54878% with 277 lines in your changes missing coverage. Please review.
✅ Project coverage is 29.19%. Comparing base (113e5e5) to head (e183278).
⚠️ Report is 480 commits behind head on master.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
src/BlockFermionicUniTensor.cpp 0.00% 53 Missing ⚠️
src/BlockUniTensor.cpp 16.66% 33 Missing and 7 partials ⚠️
src/Symmetry.cpp 0.00% 32 Missing ⚠️
pybind/unitensor_py.cpp 25.00% 13 Missing and 11 partials ⚠️
src/DenseUniTensor.cpp 44.44% 7 Missing and 13 partials ⚠️
src/RegularNetwork.cpp 0.00% 17 Missing ⚠️
pybind/storage_py.cpp 20.00% 10 Missing and 2 partials ⚠️
pybind/network_py.cpp 15.38% 9 Missing and 2 partials ⚠️
src/stat/histogram.cpp 0.00% 11 Missing ⚠️
pybind/scalar_py.cpp 20.00% 7 Missing and 1 partial ⚠️
... and 14 more
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #869      +/-   ##
==========================================
- Coverage   29.23%   29.19%   -0.04%     
==========================================
  Files         241      241              
  Lines       35559    35607      +48     
  Branches    14822    14862      +40     
==========================================
  Hits        10394    10394              
- Misses      17941    17978      +37     
- Partials     7224     7235      +11     
Flag Coverage Δ
cpp 28.78% <15.54%> (-0.04%) ⬇️
python 52.71% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

Components Coverage Δ
C++ backend 30.43% <12.91%> (+<0.01%) ⬆️
Python bindings 16.88% <22.72%> (-0.21%) ⬇️
Python package 52.71% <ø> (ø)
Files with missing lines Coverage Δ
include/Symmetry.hpp 54.73% <0.00%> (ø)
include/backend/Storage.hpp 81.54% <0.00%> (+0.48%) ⬆️
include/Network.hpp 23.80% <0.00%> (ø)
include/UniTensor.hpp 47.66% <66.66%> (+0.03%) ⬆️
pybind/tensor_py.cpp 21.02% <50.00%> (-0.09%) ⬇️
src/UniTensor_base.cpp 27.86% <50.00%> (ø)
src/backend/Storage.cpp 29.89% <0.00%> (ø)
include/backend/Scalar.hpp 20.56% <0.00%> (ø)
src/Network.cpp 0.00% <0.00%> (ø)
src/Network_base.cpp 0.00% <0.00%> (ø)
... and 14 more

Continue to review full report in Codecov by Harness.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 113e5e5...e183278. Read the comment docs.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@pcchen pcchen added this to the v1.2.0 milestone Jun 1, 2026
@pcchen

pcchen commented Jul 10, 2026

Copy link
Copy Markdown
Collaborator

Code Review

Verdict: the design is right and we'd like it in — but the diff itself is no longer rebasable; requesting a staged redo on current master.

The design is good (and worth adopting as the repo pattern)

  • C++ side: print_info / print_diagram / print_blocks / PrintNet(..., std::ostream &file = std::cout) — defaulted, so zero behavior change for existing callers, while making print output testable and redirectable (exactly Allow to print output to any stream #868's ask). ✅
  • The pybind side is the best part: render into std::ostringstream, then route to Python — py::print(...) or file.attr("write")(...) honoring a file= kwarg. This is strictly better than the scoped_ostream_redirect idiom used today: it works with any Python file object, keeps Python-level print() semantics, and sidesteps the redirect/GIL interplay entirely (the exact composition trap documented in the feat(pybind): release the GIL around expensive linalg/contract calls #987 guard-discipline note). Worth making this the documented idiom going forward. ✅
  • Deprecating Scalar.print() toward print(scalar, file=...) with a FutureWarning is tasteful. ✅

The practical problem: this May-31 diff patches code that no longer exists

Since it was written, the 2026-07 refactor campaign rewrote most of its 29 files wholesale:

So the CONFLICTING state isn't a merge-button problem — a rebase is effectively a reimplementation. The good news: it's a smaller reimplementation now, because the refactors centralized printing (Symmetry's print_info is a single function on the variant; Scalar's print is one visit) — threading std::ostream& through is easier post-campaign than pre-.

Suggested path

  1. Redo on current master, in stages — start with the classes not under active rewrite (UniTensor / Network / Bond / Storage diagram+info printing), which is most of the user value; do Scalar last, after refactor(Scalar)!: replace PIMPL virtual dispatch with std::variant (#847, fixes #935) #1011 lands (trivial there: one visit function).
  2. Keep the pybind ostringstream → py::print / file.write pattern exactly as designed here.
  3. Add tests — the PR currently adds none, yet stream-parameterization is precisely what makes print output assertable (pin e.g. print_diagram output for a known tensor; refactor(Symmetry): make Symmetry a plain value type over std::variant #1010's pinned-format discipline is the model).
  4. Coordinate with code cleanup #790 (the parent this was split from) so the two don't drift.

To be explicit so the effort isn't wasted: the direction is accepted — #868 is worth implementing, and this PR's API/pybind shape is the one we want. It's only the mechanical base that has moved out from under it.

Posted by Claude Code on behalf of @pcchen

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants