Replace tqdm with a logger for different modules. - #3

Open
MPMPMPMPMPMPMP wants to merge 27 commits into
variPEPS:mainfrom
MPMPMPMPMPMPMP:logger
Open

Replace tqdm with a logger for different modules.#3
MPMPMPMPMPMPMP wants to merge 27 commits into
variPEPS:mainfrom
MPMPMPMPMPMPMP:logger

Conversation

@MPMPMPMPMPMPMP

Copy link
Copy Markdown
Contributor

TLDR;
This pull request introduces a major refactor to the logging and progress reporting system across the codebase, replacing the use of tqdm_loggable and custom print/debug statements with Python's standard logging module. It also adds configurable logging levels and destinations to the configuration, and updates many routines to use structured logging for progress, warnings, and informational output.
If you think something is missing please let me know!

Simple usage (just inlude this in your

varipeps.config.log_level_ctmrg = varipeps.config.loglevel.info
varipeps.config.log_level_optimizer = varipeps.config.loglevel.info
varipeps.config.log_level_line_search = varipeps.config.loglevel.info
varipeps.config.log_level_expectation = varipeps.config.loglevel.warning
from varipeps.utils.logging_config import init_logging
init_logging()

It makes the log look the following way (depending on the log levels you choose):

09:50:32 INFO CTMRG: ✅ converged, took 75.72 seconds. (Steps: 16, Smallest SVD Norm: 1.809e-06)
09:50:44 INFO Custom VJP: ✅ converged, took 11.27 seconds. (Steps: 11)
09:50:44 INFO 🔎 Line search step 2, E=-0.017773, alpha=0.1317
09:52:06 INFO CTMRG: ✅ converged, took 81.63 seconds. (Steps: 17, Smallest SVD Norm: 1.685e-06)
09:52:17 INFO Custom VJP: ✅ converged, took 11.27 seconds. (Steps: 12)
09:52:17 INFO 🔎 Line search step 3, E=-0.075328, alpha=0.6585
09:52:17 INFO 📉 Step 1 | Energy: -0.07532772 | Retries: 0 | Conv: 3.414e-01 | Line search step: 0.65852732 | Max. trunc. err.: 1.685e-06
09:52:22 INFO CTMRG: ✅ converged, took 5.40 seconds. (Steps: 1, Smallest SVD Norm: 1.693e-06)
09:52:34 INFO Custom VJP: ✅ converged, took 11.33 seconds. (Steps: 12)
09:55:08 INFO CTMRG: ✅ converged, took 153.38 seconds. (Steps: 29, Smallest SVD Norm: 1.842e-06)
09:58:03 INFO CTMRG: ✅ converged, took 168.39 seconds. (Steps: 31, Smallest SVD Norm: 1.769e-06)
09:58:21 WARNING Custom VJP: ❌ did not converge, took 17.50 seconds. (Steps: 125)
10:00:44 INFO CTMRG: ✅ converged, took 143.25 seconds. (Steps: 27, Smallest SVD Norm: 1.587e-06)

(please don't tell me to remove the emojis I like the eye candy ... )

Key changes include:

Logging System Overhaul:

  • Introduced a new LogLevel enum and added multiple logging-related configuration options (e.g., log_level_global, log_to_console, log_to_file) to the VariPEPS_Config dataclass, allowing fine-grained control over logging behavior. [1][2]
  • Removed all usage of tqdm_loggable and associated progress bar logic from the codebase, including in __init__.py, line_search.py, and optimizer.py. [1][2][3]
  • Replaced all progress and debug print statements (including debug_print and tqdm.write) with calls to the standard logging module at appropriate levels (info, debug, warning) throughout the optimization and CTMRG routines. [1][2][3][4]

CTMRG and Optimization Routine Updates:

  • Updated ctmrg/routine.py to use a module-level logger, providing structured logs for convergence, step progress, and parameter changes, including timing information for key operations. [1][2][3][4][5][6][7][8]
  • Updated optimization/line_search.py and optimization/optimizer.py to use their own loggers, replacing progress bar and print statements with logging, and providing detailed info and warning logs for line search steps, autosaving, and convergence issues. [1][2][3][4][5][6][7][8][9][10][11][12][13][14]

Configuration and Code Cleanup:

  • Cleaned up imports and removed now-unused modules and variables related to the old progress system. [1][2]
  • Registered new configuration options and enums in the config module wrapper for export.

These changes collectively modernize the codebase's output and monitoring capabilities, making it easier for users and developers to control and interpret runtime information.


Logging System Overhaul:

  • Added a LogLevel enum and new logging configuration options to VariPEPS_Config, allowing fine-grained control over logging levels and destinations. [1][2]
  • Removed all usage of tqdm_loggable and associated progress bar logic, standardizing on Python's logging module. [1][2][3]

CTMRG and Optimization Routine Updates:

  • Refactored ctmrg/routine.py and optimization/line_search.py to use structured logging for step progress, convergence, and parameter changes, including detailed info and warning messages. [1][2][3][4][5][6][7][8][9][10][11][12][13][14][15]
  • Updated optimizer.py to use logging for autosaving, convergence checks, and progress reporting, replacing all progress bar and print-based feedback. [1][2][3][4][5][6][7][8]

Configuration and Code Cleanup:

  • Cleaned up imports and removed unused variables and modules related to the old progress and debug print system. [1][2]
  • Registered new logging-related configuration options for export in the config module wrapper.

@JanLuca

Copy link
Copy Markdown
Member

Hey, first thank you very much for the idea and implementation! Three things I spotted:

  1. We have to be careful to use logger.isEnabledFor(logging.DEBUG) inside jitted code since this is not a functional operatrion.
  2. Why did you check everywhere separately for logger.isEnabledFor and did not use a logic just inside the debug_print function which is already used everywhere in the code?
  3. For interactive usage I really liked the progress bar. Maybe we can find a logic to select tqdm as backend for interactive usage. Likely, one could define a logger backend for that.

I will take some time tomorrow to walk through it in detail.

@MPMPMPMPMPMPMP

MPMPMPMPMPMPMP commented Nov 6, 2025

Copy link
Copy Markdown
ContributorAuthor
  1. We check logger.isEnabledFor(logging.DEBUG) before using jax.debug.callback because in JIT-compiled code this check is evaluated at compile time. If it’s False, the logging branch is removed and unnecessary values aren’t materialized. This ensures the callback is only used when logging is actually enabled.

  2. This allows for more fine-grained message reporting. For example, if you only want to know when CTMRG fails to converge, you can set the log level to WARNING. But if you want detailed information about each step and the convergence process, you can set it to DEBUG. Only in that case will it have a noticeable effect on performance because then the logger.isEnabledFor(...) would evaluate to true. And it would also be more difficult to handle everything in the debug_print function.

  3. This part is a matter of personal preference in my opinion I think it is possible, but it’s not particularly useful because you cannot see the previous steps and with the current implementation if you set everything to warning except the varipeps.optimizer you will get only a message when a full step was completed, and depending on the state that might only be a message every minute. And because the optimization could terminate early, before the max steps, a progress bar would also not help since there is no actual bar just a counter.

This is the with all the loggers set to info if you set everything to warning except the optimization, then you see only the steps.
image

@JanLuca

Copy link
Copy Markdown
Member

I really like to use the progress bar where I can see the current energy without scrolling through a wall of text.

@JanLucaJanLuca left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Besides the minor comments, I like the PR :) Thank you very much for the work!

Only the optimizer.py file I could not really review since the change of the indention makes the comparison hard

Comment threadvaripeps/ctmrg/routine.py Outdated
Comment threadvaripeps/ctmrg/routine.py Outdated
Comment threadvaripeps/ctmrg/routine.py Outdated
Comment threadvaripeps/config.py Outdated
Comment on lines +1 to +79
from __future__ import annotations

import logging
from typing import Any

from varipeps import config as _cfg_mod # uses the global config instance

_LOGGING_INITIALIZED = False

def _to_py_log_level(level: Any) -> int:
# Accept both enum values and raw ints; OFF disables effectively
try:
val = int(level)
except Exception:
val = logging.INFO
if val == 0: # OFF
return logging.CRITICAL + 10
return val

def init_logging(cfg: Any | None = None) -> None:
"""
Initialize logging based on the provided config (or global config).
Safe to call multiple times; replaces handlers to avoid duplicates.
"""
global _LOGGING_INITIALIZED
if cfg is None:
cfg = _cfg_mod.config

root = logging.getLogger("varipeps")
# Remove old handlers to prevent duplicate logs
for h in list(root.handlers):
root.removeHandler(h)

root.setLevel(_to_py_log_level(getattr(cfg, "log_level_global", logging.INFO)))
root.propagate = False

# fmt = logging.Formatter(
# fmt="%(asctime)s %(levelname)s %(name)s: %(message)s",
# datefmt="%H:%M:%S",
# )

fmt = logging.Formatter(
fmt="%(asctime)s %(levelname)s %(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
)

if getattr(cfg, "log_to_console", True):
sh = logging.StreamHandler()
sh.setFormatter(fmt)
root.addHandler(sh)

if getattr(cfg, "log_to_file", False):
fh = logging.FileHandler(getattr(cfg, "log_file", "varipeps.log"))
fh.setFormatter(fmt)
root.addHandler(fh)

# Per-module levels
logging.getLogger("varipeps.optimizer").setLevel(
_to_py_log_level(getattr(cfg, "log_level_optimizer", logging.INFO))
)
logging.getLogger("varipeps.ctmrg").setLevel(
_to_py_log_level(getattr(cfg, "log_level_ctmrg", logging.INFO))
)
logging.getLogger("varipeps.line_search").setLevel(
_to_py_log_level(getattr(cfg, "log_level_line_search", logging.INFO))
)
logging.getLogger("varipeps.expectation").setLevel(
_to_py_log_level(getattr(cfg, "log_level_expectation", logging.INFO))
)

_LOGGING_INITIALIZED = True

def ensure_logging_configured(cfg: Any | None = None) -> None:
"""
Initialize logging once on first call; subsequent calls are no-ops.
"""
global _LOGGING_INITIALIZED
if not _LOGGING_INITIALIZED:
init_logging(cfg)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I would move that to the config file since there one could catch changes of the configuration and update the logger instances for the new values. Will prepare a commit to showcase that.

The HDF5 file stores config values like log_level_global as a numeric (e.g., numpy.int64). When loading, that integer is passed into VariPEPS_Config. In setattr, the field type for log_level_global is the Enum LogLevel, so integers must be coerced to LogLevel. Your version’s coercion path doesn’t catch your value, so it falls through and raises:
Type mismatch for option 'log_level_global', got '<class numpy.int64>', expected '<enum 'LogLevel'>'.
Why it falls through
The loader passes a numpy integer (or a 0-d/1-d array) instead of a Python int.
The Enum branch in setattr is too strict about the numeric checks, so it doesn’t convert that value into LogLevel.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@MPMPMPMPMPMPMP@JanLuca
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all \u003cpre\u003e\u003ccode\u003e blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks"); } } catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); } })(); (function(){ try { var __m = "github.com"; var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Replace tqdm with a logger for different modules. - #3

Open
MPMPMPMPMPMPMP wants to merge 27 commits into
variPEPS:mainfrom
MPMPMPMPMPMPMP:logger
Open

Replace tqdm with a logger for different modules.#3
MPMPMPMPMPMPMP wants to merge 27 commits into
variPEPS:mainfrom
MPMPMPMPMPMPMP:logger

Conversation

@MPMPMPMPMPMPMP

Copy link
Copy Markdown
Contributor

TLDR;
This pull request introduces a major refactor to the logging and progress reporting system across the codebase, replacing the use of tqdm_loggable and custom print/debug statements with Python's standard logging module. It also adds configurable logging levels and destinations to the configuration, and updates many routines to use structured logging for progress, warnings, and informational output.
If you think something is missing please let me know!

Simple usage (just inlude this in your

varipeps.config.log_level_ctmrg = varipeps.config.loglevel.info
varipeps.config.log_level_optimizer = varipeps.config.loglevel.info
varipeps.config.log_level_line_search = varipeps.config.loglevel.info
varipeps.config.log_level_expectation = varipeps.config.loglevel.warning
from varipeps.utils.logging_config import init_logging
init_logging()

It makes the log look the following way (depending on the log levels you choose):

09:50:32 INFO CTMRG: ✅ converged, took 75.72 seconds. (Steps: 16, Smallest SVD Norm: 1.809e-06)
09:50:44 INFO Custom VJP: ✅ converged, took 11.27 seconds. (Steps: 11)
09:50:44 INFO 🔎 Line search step 2, E=-0.017773, alpha=0.1317
09:52:06 INFO CTMRG: ✅ converged, took 81.63 seconds. (Steps: 17, Smallest SVD Norm: 1.685e-06)
09:52:17 INFO Custom VJP: ✅ converged, took 11.27 seconds. (Steps: 12)
09:52:17 INFO 🔎 Line search step 3, E=-0.075328, alpha=0.6585
09:52:17 INFO 📉 Step 1 | Energy: -0.07532772 | Retries: 0 | Conv: 3.414e-01 | Line search step: 0.65852732 | Max. trunc. err.: 1.685e-06
09:52:22 INFO CTMRG: ✅ converged, took 5.40 seconds. (Steps: 1, Smallest SVD Norm: 1.693e-06)
09:52:34 INFO Custom VJP: ✅ converged, took 11.33 seconds. (Steps: 12)
09:55:08 INFO CTMRG: ✅ converged, took 153.38 seconds. (Steps: 29, Smallest SVD Norm: 1.842e-06)
09:58:03 INFO CTMRG: ✅ converged, took 168.39 seconds. (Steps: 31, Smallest SVD Norm: 1.769e-06)
09:58:21 WARNING Custom VJP: ❌ did not converge, took 17.50 seconds. (Steps: 125)
10:00:44 INFO CTMRG: ✅ converged, took 143.25 seconds. (Steps: 27, Smallest SVD Norm: 1.587e-06)

(please don't tell me to remove the emojis I like the eye candy ... )

Key changes include:

Logging System Overhaul:

  • Introduced a new LogLevel enum and added multiple logging-related configuration options (e.g., log_level_global, log_to_console, log_to_file) to the VariPEPS_Config dataclass, allowing fine-grained control over logging behavior. [1][2]
  • Removed all usage of tqdm_loggable and associated progress bar logic from the codebase, including in __init__.py, line_search.py, and optimizer.py. [1][2][3]
  • Replaced all progress and debug print statements (including debug_print and tqdm.write) with calls to the standard logging module at appropriate levels (info, debug, warning) throughout the optimization and CTMRG routines. [1][2][3][4]

CTMRG and Optimization Routine Updates:

  • Updated ctmrg/routine.py to use a module-level logger, providing structured logs for convergence, step progress, and parameter changes, including timing information for key operations. [1][2][3][4][5][6][7][8]
  • Updated optimization/line_search.py and optimization/optimizer.py to use their own loggers, replacing progress bar and print statements with logging, and providing detailed info and warning logs for line search steps, autosaving, and convergence issues. [1][2][3][4][5][6][7][8][9][10][11][12][13][14]

Configuration and Code Cleanup:

  • Cleaned up imports and removed now-unused modules and variables related to the old progress system. [1][2]
  • Registered new configuration options and enums in the config module wrapper for export.

These changes collectively modernize the codebase's output and monitoring capabilities, making it easier for users and developers to control and interpret runtime information.


Logging System Overhaul:

  • Added a LogLevel enum and new logging configuration options to VariPEPS_Config, allowing fine-grained control over logging levels and destinations. [1][2]
  • Removed all usage of tqdm_loggable and associated progress bar logic, standardizing on Python's logging module. [1][2][3]

CTMRG and Optimization Routine Updates:

  • Refactored ctmrg/routine.py and optimization/line_search.py to use structured logging for step progress, convergence, and parameter changes, including detailed info and warning messages. [1][2][3][4][5][6][7][8][9][10][11][12][13][14][15]
  • Updated optimizer.py to use logging for autosaving, convergence checks, and progress reporting, replacing all progress bar and print-based feedback. [1][2][3][4][5][6][7][8]

Configuration and Code Cleanup:

  • Cleaned up imports and removed unused variables and modules related to the old progress and debug print system. [1][2]
  • Registered new logging-related configuration options for export in the config module wrapper.

@JanLuca

Copy link
Copy Markdown
Member

Hey, first thank you very much for the idea and implementation! Three things I spotted:

  1. We have to be careful to use logger.isEnabledFor(logging.DEBUG) inside jitted code since this is not a functional operatrion.
  2. Why did you check everywhere separately for logger.isEnabledFor and did not use a logic just inside the debug_print function which is already used everywhere in the code?
  3. For interactive usage I really liked the progress bar. Maybe we can find a logic to select tqdm as backend for interactive usage. Likely, one could define a logger backend for that.

I will take some time tomorrow to walk through it in detail.

@MPMPMPMPMPMPMP

MPMPMPMPMPMPMP commented Nov 6, 2025

Copy link
Copy Markdown
ContributorAuthor
  1. We check logger.isEnabledFor(logging.DEBUG) before using jax.debug.callback because in JIT-compiled code this check is evaluated at compile time. If it’s False, the logging branch is removed and unnecessary values aren’t materialized. This ensures the callback is only used when logging is actually enabled.

  2. This allows for more fine-grained message reporting. For example, if you only want to know when CTMRG fails to converge, you can set the log level to WARNING. But if you want detailed information about each step and the convergence process, you can set it to DEBUG. Only in that case will it have a noticeable effect on performance because then the logger.isEnabledFor(...) would evaluate to true. And it would also be more difficult to handle everything in the debug_print function.

  3. This part is a matter of personal preference in my opinion I think it is possible, but it’s not particularly useful because you cannot see the previous steps and with the current implementation if you set everything to warning except the varipeps.optimizer you will get only a message when a full step was completed, and depending on the state that might only be a message every minute. And because the optimization could terminate early, before the max steps, a progress bar would also not help since there is no actual bar just a counter.

This is the with all the loggers set to info if you set everything to warning except the optimization, then you see only the steps.
image

@JanLuca

Copy link
Copy Markdown
Member

I really like to use the progress bar where I can see the current energy without scrolling through a wall of text.

@JanLucaJanLuca left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Besides the minor comments, I like the PR :) Thank you very much for the work!

Only the optimizer.py file I could not really review since the change of the indention makes the comparison hard

Comment threadvaripeps/ctmrg/routine.py Outdated
Comment threadvaripeps/ctmrg/routine.py Outdated
Comment threadvaripeps/ctmrg/routine.py Outdated
Comment threadvaripeps/config.py Outdated
Comment on lines +1 to +79
from __future__ import annotations

import logging
from typing import Any

from varipeps import config as _cfg_mod # uses the global config instance

_LOGGING_INITIALIZED = False

def _to_py_log_level(level: Any) -> int:
# Accept both enum values and raw ints; OFF disables effectively
try:
val = int(level)
except Exception:
val = logging.INFO
if val == 0: # OFF
return logging.CRITICAL + 10
return val

def init_logging(cfg: Any | None = None) -> None:
"""
Initialize logging based on the provided config (or global config).
Safe to call multiple times; replaces handlers to avoid duplicates.
"""
global _LOGGING_INITIALIZED
if cfg is None:
cfg = _cfg_mod.config

root = logging.getLogger("varipeps")
# Remove old handlers to prevent duplicate logs
for h in list(root.handlers):
root.removeHandler(h)

root.setLevel(_to_py_log_level(getattr(cfg, "log_level_global", logging.INFO)))
root.propagate = False

# fmt = logging.Formatter(
# fmt="%(asctime)s %(levelname)s %(name)s: %(message)s",
# datefmt="%H:%M:%S",
# )

fmt = logging.Formatter(
fmt="%(asctime)s %(levelname)s %(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
)

if getattr(cfg, "log_to_console", True):
sh = logging.StreamHandler()
sh.setFormatter(fmt)
root.addHandler(sh)

if getattr(cfg, "log_to_file", False):
fh = logging.FileHandler(getattr(cfg, "log_file", "varipeps.log"))
fh.setFormatter(fmt)
root.addHandler(fh)

# Per-module levels
logging.getLogger("varipeps.optimizer").setLevel(
_to_py_log_level(getattr(cfg, "log_level_optimizer", logging.INFO))
)
logging.getLogger("varipeps.ctmrg").setLevel(
_to_py_log_level(getattr(cfg, "log_level_ctmrg", logging.INFO))
)
logging.getLogger("varipeps.line_search").setLevel(
_to_py_log_level(getattr(cfg, "log_level_line_search", logging.INFO))
)
logging.getLogger("varipeps.expectation").setLevel(
_to_py_log_level(getattr(cfg, "log_level_expectation", logging.INFO))
)

_LOGGING_INITIALIZED = True

def ensure_logging_configured(cfg: Any | None = None) -> None:
"""
Initialize logging once on first call; subsequent calls are no-ops.
"""
global _LOGGING_INITIALIZED
if not _LOGGING_INITIALIZED:
init_logging(cfg)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I would move that to the config file since there one could catch changes of the configuration and update the logger instances for the new values. Will prepare a commit to showcase that.

The HDF5 file stores config values like log_level_global as a numeric (e.g., numpy.int64). When loading, that integer is passed into VariPEPS_Config. In setattr, the field type for log_level_global is the Enum LogLevel, so integers must be coerced to LogLevel. Your version’s coercion path doesn’t catch your value, so it falls through and raises:
Type mismatch for option 'log_level_global', got '<class numpy.int64>', expected '<enum 'LogLevel'>'.
Why it falls through
The loader passes a numpy integer (or a 0-d/1-d array) instead of a Python int.
The Enum branch in setattr is too strict about the numeric checks, so it doesn’t convert that value into LogLevel.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@MPMPMPMPMPMPMP@JanLuca
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Replace tqdm with a logger for different modules. - #3

Open
MPMPMPMPMPMPMP wants to merge 27 commits into
variPEPS:mainfrom
MPMPMPMPMPMPMP:logger
Open

Replace tqdm with a logger for different modules.#3
MPMPMPMPMPMPMP wants to merge 27 commits into
variPEPS:mainfrom
MPMPMPMPMPMPMP:logger

Conversation

@MPMPMPMPMPMPMP

Copy link
Copy Markdown
Contributor

TLDR;
This pull request introduces a major refactor to the logging and progress reporting system across the codebase, replacing the use of tqdm_loggable and custom print/debug statements with Python's standard logging module. It also adds configurable logging levels and destinations to the configuration, and updates many routines to use structured logging for progress, warnings, and informational output.
If you think something is missing please let me know!

Simple usage (just inlude this in your

varipeps.config.log_level_ctmrg = varipeps.config.loglevel.info
varipeps.config.log_level_optimizer = varipeps.config.loglevel.info
varipeps.config.log_level_line_search = varipeps.config.loglevel.info
varipeps.config.log_level_expectation = varipeps.config.loglevel.warning
from varipeps.utils.logging_config import init_logging
init_logging()

It makes the log look the following way (depending on the log levels you choose):

09:50:32 INFO CTMRG: ✅ converged, took 75.72 seconds. (Steps: 16, Smallest SVD Norm: 1.809e-06)
09:50:44 INFO Custom VJP: ✅ converged, took 11.27 seconds. (Steps: 11)
09:50:44 INFO 🔎 Line search step 2, E=-0.017773, alpha=0.1317
09:52:06 INFO CTMRG: ✅ converged, took 81.63 seconds. (Steps: 17, Smallest SVD Norm: 1.685e-06)
09:52:17 INFO Custom VJP: ✅ converged, took 11.27 seconds. (Steps: 12)
09:52:17 INFO 🔎 Line search step 3, E=-0.075328, alpha=0.6585
09:52:17 INFO 📉 Step 1 | Energy: -0.07532772 | Retries: 0 | Conv: 3.414e-01 | Line search step: 0.65852732 | Max. trunc. err.: 1.685e-06
09:52:22 INFO CTMRG: ✅ converged, took 5.40 seconds. (Steps: 1, Smallest SVD Norm: 1.693e-06)
09:52:34 INFO Custom VJP: ✅ converged, took 11.33 seconds. (Steps: 12)
09:55:08 INFO CTMRG: ✅ converged, took 153.38 seconds. (Steps: 29, Smallest SVD Norm: 1.842e-06)
09:58:03 INFO CTMRG: ✅ converged, took 168.39 seconds. (Steps: 31, Smallest SVD Norm: 1.769e-06)
09:58:21 WARNING Custom VJP: ❌ did not converge, took 17.50 seconds. (Steps: 125)
10:00:44 INFO CTMRG: ✅ converged, took 143.25 seconds. (Steps: 27, Smallest SVD Norm: 1.587e-06)

(please don't tell me to remove the emojis I like the eye candy ... )

Key changes include:

Logging System Overhaul:

  • Introduced a new LogLevel enum and added multiple logging-related configuration options (e.g., log_level_global, log_to_console, log_to_file) to the VariPEPS_Config dataclass, allowing fine-grained control over logging behavior. [1][2]
  • Removed all usage of tqdm_loggable and associated progress bar logic from the codebase, including in __init__.py, line_search.py, and optimizer.py. [1][2][3]
  • Replaced all progress and debug print statements (including debug_print and tqdm.write) with calls to the standard logging module at appropriate levels (info, debug, warning) throughout the optimization and CTMRG routines. [1][2][3][4]

CTMRG and Optimization Routine Updates:

  • Updated ctmrg/routine.py to use a module-level logger, providing structured logs for convergence, step progress, and parameter changes, including timing information for key operations. [1][2][3][4][5][6][7][8]
  • Updated optimization/line_search.py and optimization/optimizer.py to use their own loggers, replacing progress bar and print statements with logging, and providing detailed info and warning logs for line search steps, autosaving, and convergence issues. [1][2][3][4][5][6][7][8][9][10][11][12][13][14]

Configuration and Code Cleanup:

  • Cleaned up imports and removed now-unused modules and variables related to the old progress system. [1][2]
  • Registered new configuration options and enums in the config module wrapper for export.

These changes collectively modernize the codebase's output and monitoring capabilities, making it easier for users and developers to control and interpret runtime information.


Logging System Overhaul:

  • Added a LogLevel enum and new logging configuration options to VariPEPS_Config, allowing fine-grained control over logging levels and destinations. [1][2]
  • Removed all usage of tqdm_loggable and associated progress bar logic, standardizing on Python's logging module. [1][2][3]

CTMRG and Optimization Routine Updates:

  • Refactored ctmrg/routine.py and optimization/line_search.py to use structured logging for step progress, convergence, and parameter changes, including detailed info and warning messages. [1][2][3][4][5][6][7][8][9][10][11][12][13][14][15]
  • Updated optimizer.py to use logging for autosaving, convergence checks, and progress reporting, replacing all progress bar and print-based feedback. [1][2][3][4][5][6][7][8]

Configuration and Code Cleanup:

  • Cleaned up imports and removed unused variables and modules related to the old progress and debug print system. [1][2]
  • Registered new logging-related configuration options for export in the config module wrapper.

@JanLuca

Copy link
Copy Markdown
Member

Hey, first thank you very much for the idea and implementation! Three things I spotted:

  1. We have to be careful to use logger.isEnabledFor(logging.DEBUG) inside jitted code since this is not a functional operatrion.
  2. Why did you check everywhere separately for logger.isEnabledFor and did not use a logic just inside the debug_print function which is already used everywhere in the code?
  3. For interactive usage I really liked the progress bar. Maybe we can find a logic to select tqdm as backend for interactive usage. Likely, one could define a logger backend for that.

I will take some time tomorrow to walk through it in detail.

@MPMPMPMPMPMPMP

MPMPMPMPMPMPMP commented Nov 6, 2025

Copy link
Copy Markdown
ContributorAuthor
  1. We check logger.isEnabledFor(logging.DEBUG) before using jax.debug.callback because in JIT-compiled code this check is evaluated at compile time. If it’s False, the logging branch is removed and unnecessary values aren’t materialized. This ensures the callback is only used when logging is actually enabled.

  2. This allows for more fine-grained message reporting. For example, if you only want to know when CTMRG fails to converge, you can set the log level to WARNING. But if you want detailed information about each step and the convergence process, you can set it to DEBUG. Only in that case will it have a noticeable effect on performance because then the logger.isEnabledFor(...) would evaluate to true. And it would also be more difficult to handle everything in the debug_print function.

  3. This part is a matter of personal preference in my opinion I think it is possible, but it’s not particularly useful because you cannot see the previous steps and with the current implementation if you set everything to warning except the varipeps.optimizer you will get only a message when a full step was completed, and depending on the state that might only be a message every minute. And because the optimization could terminate early, before the max steps, a progress bar would also not help since there is no actual bar just a counter.

This is the with all the loggers set to info if you set everything to warning except the optimization, then you see only the steps.
image

@JanLuca

Copy link
Copy Markdown
Member

I really like to use the progress bar where I can see the current energy without scrolling through a wall of text.

@JanLucaJanLuca left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Besides the minor comments, I like the PR :) Thank you very much for the work!

Only the optimizer.py file I could not really review since the change of the indention makes the comparison hard

Comment threadvaripeps/ctmrg/routine.py Outdated
Comment threadvaripeps/ctmrg/routine.py Outdated
Comment threadvaripeps/ctmrg/routine.py Outdated
Comment threadvaripeps/config.py Outdated
Comment on lines +1 to +79
from __future__ import annotations

import logging
from typing import Any

from varipeps import config as _cfg_mod # uses the global config instance

_LOGGING_INITIALIZED = False

def _to_py_log_level(level: Any) -> int:
# Accept both enum values and raw ints; OFF disables effectively
try:
val = int(level)
except Exception:
val = logging.INFO
if val == 0: # OFF
return logging.CRITICAL + 10
return val

def init_logging(cfg: Any | None = None) -> None:
"""
Initialize logging based on the provided config (or global config).
Safe to call multiple times; replaces handlers to avoid duplicates.
"""
global _LOGGING_INITIALIZED
if cfg is None:
cfg = _cfg_mod.config

root = logging.getLogger("varipeps")
# Remove old handlers to prevent duplicate logs
for h in list(root.handlers):
root.removeHandler(h)

root.setLevel(_to_py_log_level(getattr(cfg, "log_level_global", logging.INFO)))
root.propagate = False

# fmt = logging.Formatter(
# fmt="%(asctime)s %(levelname)s %(name)s: %(message)s",
# datefmt="%H:%M:%S",
# )

fmt = logging.Formatter(
fmt="%(asctime)s %(levelname)s %(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
)

if getattr(cfg, "log_to_console", True):
sh = logging.StreamHandler()
sh.setFormatter(fmt)
root.addHandler(sh)

if getattr(cfg, "log_to_file", False):
fh = logging.FileHandler(getattr(cfg, "log_file", "varipeps.log"))
fh.setFormatter(fmt)
root.addHandler(fh)

# Per-module levels
logging.getLogger("varipeps.optimizer").setLevel(
_to_py_log_level(getattr(cfg, "log_level_optimizer", logging.INFO))
)
logging.getLogger("varipeps.ctmrg").setLevel(
_to_py_log_level(getattr(cfg, "log_level_ctmrg", logging.INFO))
)
logging.getLogger("varipeps.line_search").setLevel(
_to_py_log_level(getattr(cfg, "log_level_line_search", logging.INFO))
)
logging.getLogger("varipeps.expectation").setLevel(
_to_py_log_level(getattr(cfg, "log_level_expectation", logging.INFO))
)

_LOGGING_INITIALIZED = True

def ensure_logging_configured(cfg: Any | None = None) -> None:
"""
Initialize logging once on first call; subsequent calls are no-ops.
"""
global _LOGGING_INITIALIZED
if not _LOGGING_INITIALIZED:
init_logging(cfg)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I would move that to the config file since there one could catch changes of the configuration and update the logger instances for the new values. Will prepare a commit to showcase that.

The HDF5 file stores config values like log_level_global as a numeric (e.g., numpy.int64). When loading, that integer is passed into VariPEPS_Config. In setattr, the field type for log_level_global is the Enum LogLevel, so integers must be coerced to LogLevel. Your version’s coercion path doesn’t catch your value, so it falls through and raises:
Type mismatch for option 'log_level_global', got '<class numpy.int64>', expected '<enum 'LogLevel'>'.
Why it falls through
The loader passes a numpy integer (or a 0-d/1-d array) instead of a Python int.
The Enum branch in setattr is too strict about the numeric checks, so it doesn’t convert that value into LogLevel.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@MPMPMPMPMPMPMP@JanLuca
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length \u003e 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Replace tqdm with a logger for different modules. - #3

Open
MPMPMPMPMPMPMP wants to merge 27 commits into
variPEPS:mainfrom
MPMPMPMPMPMPMP:logger
Open

Replace tqdm with a logger for different modules.#3
MPMPMPMPMPMPMP wants to merge 27 commits into
variPEPS:mainfrom
MPMPMPMPMPMPMP:logger

Conversation

@MPMPMPMPMPMPMP

Copy link
Copy Markdown
Contributor

TLDR;
This pull request introduces a major refactor to the logging and progress reporting system across the codebase, replacing the use of tqdm_loggable and custom print/debug statements with Python's standard logging module. It also adds configurable logging levels and destinations to the configuration, and updates many routines to use structured logging for progress, warnings, and informational output.
If you think something is missing please let me know!

Simple usage (just inlude this in your

varipeps.config.log_level_ctmrg = varipeps.config.loglevel.info
varipeps.config.log_level_optimizer = varipeps.config.loglevel.info
varipeps.config.log_level_line_search = varipeps.config.loglevel.info
varipeps.config.log_level_expectation = varipeps.config.loglevel.warning
from varipeps.utils.logging_config import init_logging
init_logging()

It makes the log look the following way (depending on the log levels you choose):

09:50:32 INFO CTMRG: ✅ converged, took 75.72 seconds. (Steps: 16, Smallest SVD Norm: 1.809e-06)
09:50:44 INFO Custom VJP: ✅ converged, took 11.27 seconds. (Steps: 11)
09:50:44 INFO 🔎 Line search step 2, E=-0.017773, alpha=0.1317
09:52:06 INFO CTMRG: ✅ converged, took 81.63 seconds. (Steps: 17, Smallest SVD Norm: 1.685e-06)
09:52:17 INFO Custom VJP: ✅ converged, took 11.27 seconds. (Steps: 12)
09:52:17 INFO 🔎 Line search step 3, E=-0.075328, alpha=0.6585
09:52:17 INFO 📉 Step 1 | Energy: -0.07532772 | Retries: 0 | Conv: 3.414e-01 | Line search step: 0.65852732 | Max. trunc. err.: 1.685e-06
09:52:22 INFO CTMRG: ✅ converged, took 5.40 seconds. (Steps: 1, Smallest SVD Norm: 1.693e-06)
09:52:34 INFO Custom VJP: ✅ converged, took 11.33 seconds. (Steps: 12)
09:55:08 INFO CTMRG: ✅ converged, took 153.38 seconds. (Steps: 29, Smallest SVD Norm: 1.842e-06)
09:58:03 INFO CTMRG: ✅ converged, took 168.39 seconds. (Steps: 31, Smallest SVD Norm: 1.769e-06)
09:58:21 WARNING Custom VJP: ❌ did not converge, took 17.50 seconds. (Steps: 125)
10:00:44 INFO CTMRG: ✅ converged, took 143.25 seconds. (Steps: 27, Smallest SVD Norm: 1.587e-06)

(please don't tell me to remove the emojis I like the eye candy ... )

Key changes include:

Logging System Overhaul:

  • Introduced a new LogLevel enum and added multiple logging-related configuration options (e.g., log_level_global, log_to_console, log_to_file) to the VariPEPS_Config dataclass, allowing fine-grained control over logging behavior. [1][2]
  • Removed all usage of tqdm_loggable and associated progress bar logic from the codebase, including in __init__.py, line_search.py, and optimizer.py. [1][2][3]
  • Replaced all progress and debug print statements (including debug_print and tqdm.write) with calls to the standard logging module at appropriate levels (info, debug, warning) throughout the optimization and CTMRG routines. [1][2][3][4]

CTMRG and Optimization Routine Updates:

  • Updated ctmrg/routine.py to use a module-level logger, providing structured logs for convergence, step progress, and parameter changes, including timing information for key operations. [1][2][3][4][5][6][7][8]
  • Updated optimization/line_search.py and optimization/optimizer.py to use their own loggers, replacing progress bar and print statements with logging, and providing detailed info and warning logs for line search steps, autosaving, and convergence issues. [1][2][3][4][5][6][7][8][9][10][11][12][13][14]

Configuration and Code Cleanup:

  • Cleaned up imports and removed now-unused modules and variables related to the old progress system. [1][2]
  • Registered new configuration options and enums in the config module wrapper for export.

These changes collectively modernize the codebase's output and monitoring capabilities, making it easier for users and developers to control and interpret runtime information.


Logging System Overhaul:

  • Added a LogLevel enum and new logging configuration options to VariPEPS_Config, allowing fine-grained control over logging levels and destinations. [1][2]
  • Removed all usage of tqdm_loggable and associated progress bar logic, standardizing on Python's logging module. [1][2][3]

CTMRG and Optimization Routine Updates:

  • Refactored ctmrg/routine.py and optimization/line_search.py to use structured logging for step progress, convergence, and parameter changes, including detailed info and warning messages. [1][2][3][4][5][6][7][8][9][10][11][12][13][14][15]
  • Updated optimizer.py to use logging for autosaving, convergence checks, and progress reporting, replacing all progress bar and print-based feedback. [1][2][3][4][5][6][7][8]

Configuration and Code Cleanup:

  • Cleaned up imports and removed unused variables and modules related to the old progress and debug print system. [1][2]
  • Registered new logging-related configuration options for export in the config module wrapper.

@JanLuca

Copy link
Copy Markdown
Member

Hey, first thank you very much for the idea and implementation! Three things I spotted:

  1. We have to be careful to use logger.isEnabledFor(logging.DEBUG) inside jitted code since this is not a functional operatrion.
  2. Why did you check everywhere separately for logger.isEnabledFor and did not use a logic just inside the debug_print function which is already used everywhere in the code?
  3. For interactive usage I really liked the progress bar. Maybe we can find a logic to select tqdm as backend for interactive usage. Likely, one could define a logger backend for that.

I will take some time tomorrow to walk through it in detail.

@MPMPMPMPMPMPMP

MPMPMPMPMPMPMP commented Nov 6, 2025

Copy link
Copy Markdown
ContributorAuthor
  1. We check logger.isEnabledFor(logging.DEBUG) before using jax.debug.callback because in JIT-compiled code this check is evaluated at compile time. If it’s False, the logging branch is removed and unnecessary values aren’t materialized. This ensures the callback is only used when logging is actually enabled.

  2. This allows for more fine-grained message reporting. For example, if you only want to know when CTMRG fails to converge, you can set the log level to WARNING. But if you want detailed information about each step and the convergence process, you can set it to DEBUG. Only in that case will it have a noticeable effect on performance because then the logger.isEnabledFor(...) would evaluate to true. And it would also be more difficult to handle everything in the debug_print function.

  3. This part is a matter of personal preference in my opinion I think it is possible, but it’s not particularly useful because you cannot see the previous steps and with the current implementation if you set everything to warning except the varipeps.optimizer you will get only a message when a full step was completed, and depending on the state that might only be a message every minute. And because the optimization could terminate early, before the max steps, a progress bar would also not help since there is no actual bar just a counter.

This is the with all the loggers set to info if you set everything to warning except the optimization, then you see only the steps.
image

@JanLuca

Copy link
Copy Markdown
Member

I really like to use the progress bar where I can see the current energy without scrolling through a wall of text.

@JanLucaJanLuca left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Besides the minor comments, I like the PR :) Thank you very much for the work!

Only the optimizer.py file I could not really review since the change of the indention makes the comparison hard

Comment threadvaripeps/ctmrg/routine.py Outdated
Comment threadvaripeps/ctmrg/routine.py Outdated
Comment threadvaripeps/ctmrg/routine.py Outdated
Comment threadvaripeps/config.py Outdated
Comment on lines +1 to +79
from __future__ import annotations

import logging
from typing import Any

from varipeps import config as _cfg_mod # uses the global config instance

_LOGGING_INITIALIZED = False

def _to_py_log_level(level: Any) -> int:
# Accept both enum values and raw ints; OFF disables effectively
try:
val = int(level)
except Exception:
val = logging.INFO
if val == 0: # OFF
return logging.CRITICAL + 10
return val

def init_logging(cfg: Any | None = None) -> None:
"""
Initialize logging based on the provided config (or global config).
Safe to call multiple times; replaces handlers to avoid duplicates.
"""
global _LOGGING_INITIALIZED
if cfg is None:
cfg = _cfg_mod.config

root = logging.getLogger("varipeps")
# Remove old handlers to prevent duplicate logs
for h in list(root.handlers):
root.removeHandler(h)

root.setLevel(_to_py_log_level(getattr(cfg, "log_level_global", logging.INFO)))
root.propagate = False

# fmt = logging.Formatter(
# fmt="%(asctime)s %(levelname)s %(name)s: %(message)s",
# datefmt="%H:%M:%S",
# )

fmt = logging.Formatter(
fmt="%(asctime)s %(levelname)s %(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
)

if getattr(cfg, "log_to_console", True):
sh = logging.StreamHandler()
sh.setFormatter(fmt)
root.addHandler(sh)

if getattr(cfg, "log_to_file", False):
fh = logging.FileHandler(getattr(cfg, "log_file", "varipeps.log"))
fh.setFormatter(fmt)
root.addHandler(fh)

# Per-module levels
logging.getLogger("varipeps.optimizer").setLevel(
_to_py_log_level(getattr(cfg, "log_level_optimizer", logging.INFO))
)
logging.getLogger("varipeps.ctmrg").setLevel(
_to_py_log_level(getattr(cfg, "log_level_ctmrg", logging.INFO))
)
logging.getLogger("varipeps.line_search").setLevel(
_to_py_log_level(getattr(cfg, "log_level_line_search", logging.INFO))
)
logging.getLogger("varipeps.expectation").setLevel(
_to_py_log_level(getattr(cfg, "log_level_expectation", logging.INFO))
)

_LOGGING_INITIALIZED = True

def ensure_logging_configured(cfg: Any | None = None) -> None:
"""
Initialize logging once on first call; subsequent calls are no-ops.
"""
global _LOGGING_INITIALIZED
if not _LOGGING_INITIALIZED:
init_logging(cfg)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I would move that to the config file since there one could catch changes of the configuration and update the logger instances for the new values. Will prepare a commit to showcase that.

The HDF5 file stores config values like log_level_global as a numeric (e.g., numpy.int64). When loading, that integer is passed into VariPEPS_Config. In setattr, the field type for log_level_global is the Enum LogLevel, so integers must be coerced to LogLevel. Your version’s coercion path doesn’t catch your value, so it falls through and raises:
Type mismatch for option 'log_level_global', got '<class numpy.int64>', expected '<enum 'LogLevel'>'.
Why it falls through
The loader passes a numpy integer (or a 0-d/1-d array) instead of a Python int.
The Enum branch in setattr is too strict about the numeric checks, so it doesn’t convert that value into LogLevel.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@MPMPMPMPMPMPMP@JanLuca
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Replace tqdm with a logger for different modules. - #3

Open
MPMPMPMPMPMPMP wants to merge 27 commits into
variPEPS:mainfrom
MPMPMPMPMPMPMP:logger
Open

Replace tqdm with a logger for different modules.#3
MPMPMPMPMPMPMP wants to merge 27 commits into
variPEPS:mainfrom
MPMPMPMPMPMPMP:logger

Conversation

@MPMPMPMPMPMPMP

Copy link
Copy Markdown
Contributor

TLDR;
This pull request introduces a major refactor to the logging and progress reporting system across the codebase, replacing the use of tqdm_loggable and custom print/debug statements with Python's standard logging module. It also adds configurable logging levels and destinations to the configuration, and updates many routines to use structured logging for progress, warnings, and informational output.
If you think something is missing please let me know!

Simple usage (just inlude this in your

varipeps.config.log_level_ctmrg = varipeps.config.loglevel.info
varipeps.config.log_level_optimizer = varipeps.config.loglevel.info
varipeps.config.log_level_line_search = varipeps.config.loglevel.info
varipeps.config.log_level_expectation = varipeps.config.loglevel.warning
from varipeps.utils.logging_config import init_logging
init_logging()

It makes the log look the following way (depending on the log levels you choose):

09:50:32 INFO CTMRG: ✅ converged, took 75.72 seconds. (Steps: 16, Smallest SVD Norm: 1.809e-06)
09:50:44 INFO Custom VJP: ✅ converged, took 11.27 seconds. (Steps: 11)
09:50:44 INFO 🔎 Line search step 2, E=-0.017773, alpha=0.1317
09:52:06 INFO CTMRG: ✅ converged, took 81.63 seconds. (Steps: 17, Smallest SVD Norm: 1.685e-06)
09:52:17 INFO Custom VJP: ✅ converged, took 11.27 seconds. (Steps: 12)
09:52:17 INFO 🔎 Line search step 3, E=-0.075328, alpha=0.6585
09:52:17 INFO 📉 Step 1 | Energy: -0.07532772 | Retries: 0 | Conv: 3.414e-01 | Line search step: 0.65852732 | Max. trunc. err.: 1.685e-06
09:52:22 INFO CTMRG: ✅ converged, took 5.40 seconds. (Steps: 1, Smallest SVD Norm: 1.693e-06)
09:52:34 INFO Custom VJP: ✅ converged, took 11.33 seconds. (Steps: 12)
09:55:08 INFO CTMRG: ✅ converged, took 153.38 seconds. (Steps: 29, Smallest SVD Norm: 1.842e-06)
09:58:03 INFO CTMRG: ✅ converged, took 168.39 seconds. (Steps: 31, Smallest SVD Norm: 1.769e-06)
09:58:21 WARNING Custom VJP: ❌ did not converge, took 17.50 seconds. (Steps: 125)
10:00:44 INFO CTMRG: ✅ converged, took 143.25 seconds. (Steps: 27, Smallest SVD Norm: 1.587e-06)

(please don't tell me to remove the emojis I like the eye candy ... )

Key changes include:

Logging System Overhaul:

  • Introduced a new LogLevel enum and added multiple logging-related configuration options (e.g., log_level_global, log_to_console, log_to_file) to the VariPEPS_Config dataclass, allowing fine-grained control over logging behavior. [1][2]
  • Removed all usage of tqdm_loggable and associated progress bar logic from the codebase, including in __init__.py, line_search.py, and optimizer.py. [1][2][3]
  • Replaced all progress and debug print statements (including debug_print and tqdm.write) with calls to the standard logging module at appropriate levels (info, debug, warning) throughout the optimization and CTMRG routines. [1][2][3][4]

CTMRG and Optimization Routine Updates:

  • Updated ctmrg/routine.py to use a module-level logger, providing structured logs for convergence, step progress, and parameter changes, including timing information for key operations. [1][2][3][4][5][6][7][8]
  • Updated optimization/line_search.py and optimization/optimizer.py to use their own loggers, replacing progress bar and print statements with logging, and providing detailed info and warning logs for line search steps, autosaving, and convergence issues. [1][2][3][4][5][6][7][8][9][10][11][12][13][14]

Configuration and Code Cleanup:

  • Cleaned up imports and removed now-unused modules and variables related to the old progress system. [1][2]
  • Registered new configuration options and enums in the config module wrapper for export.

These changes collectively modernize the codebase's output and monitoring capabilities, making it easier for users and developers to control and interpret runtime information.


Logging System Overhaul:

  • Added a LogLevel enum and new logging configuration options to VariPEPS_Config, allowing fine-grained control over logging levels and destinations. [1][2]
  • Removed all usage of tqdm_loggable and associated progress bar logic, standardizing on Python's logging module. [1][2][3]

CTMRG and Optimization Routine Updates:

  • Refactored ctmrg/routine.py and optimization/line_search.py to use structured logging for step progress, convergence, and parameter changes, including detailed info and warning messages. [1][2][3][4][5][6][7][8][9][10][11][12][13][14][15]
  • Updated optimizer.py to use logging for autosaving, convergence checks, and progress reporting, replacing all progress bar and print-based feedback. [1][2][3][4][5][6][7][8]

Configuration and Code Cleanup:

  • Cleaned up imports and removed unused variables and modules related to the old progress and debug print system. [1][2]
  • Registered new logging-related configuration options for export in the config module wrapper.

@JanLuca

Copy link
Copy Markdown
Member

Hey, first thank you very much for the idea and implementation! Three things I spotted:

  1. We have to be careful to use logger.isEnabledFor(logging.DEBUG) inside jitted code since this is not a functional operatrion.
  2. Why did you check everywhere separately for logger.isEnabledFor and did not use a logic just inside the debug_print function which is already used everywhere in the code?
  3. For interactive usage I really liked the progress bar. Maybe we can find a logic to select tqdm as backend for interactive usage. Likely, one could define a logger backend for that.

I will take some time tomorrow to walk through it in detail.

@MPMPMPMPMPMPMP

MPMPMPMPMPMPMP commented Nov 6, 2025

Copy link
Copy Markdown
ContributorAuthor
  1. We check logger.isEnabledFor(logging.DEBUG) before using jax.debug.callback because in JIT-compiled code this check is evaluated at compile time. If it’s False, the logging branch is removed and unnecessary values aren’t materialized. This ensures the callback is only used when logging is actually enabled.

  2. This allows for more fine-grained message reporting. For example, if you only want to know when CTMRG fails to converge, you can set the log level to WARNING. But if you want detailed information about each step and the convergence process, you can set it to DEBUG. Only in that case will it have a noticeable effect on performance because then the logger.isEnabledFor(...) would evaluate to true. And it would also be more difficult to handle everything in the debug_print function.

  3. This part is a matter of personal preference in my opinion I think it is possible, but it’s not particularly useful because you cannot see the previous steps and with the current implementation if you set everything to warning except the varipeps.optimizer you will get only a message when a full step was completed, and depending on the state that might only be a message every minute. And because the optimization could terminate early, before the max steps, a progress bar would also not help since there is no actual bar just a counter.

This is the with all the loggers set to info if you set everything to warning except the optimization, then you see only the steps.
image

@JanLuca

Copy link
Copy Markdown
Member

I really like to use the progress bar where I can see the current energy without scrolling through a wall of text.

@JanLucaJanLuca left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Besides the minor comments, I like the PR :) Thank you very much for the work!

Only the optimizer.py file I could not really review since the change of the indention makes the comparison hard

Comment threadvaripeps/ctmrg/routine.py Outdated
Comment threadvaripeps/ctmrg/routine.py Outdated
Comment threadvaripeps/ctmrg/routine.py Outdated
Comment threadvaripeps/config.py Outdated
Comment on lines +1 to +79
from __future__ import annotations

import logging
from typing import Any

from varipeps import config as _cfg_mod # uses the global config instance

_LOGGING_INITIALIZED = False

def _to_py_log_level(level: Any) -> int:
# Accept both enum values and raw ints; OFF disables effectively
try:
val = int(level)
except Exception:
val = logging.INFO
if val == 0: # OFF
return logging.CRITICAL + 10
return val

def init_logging(cfg: Any | None = None) -> None:
"""
Initialize logging based on the provided config (or global config).
Safe to call multiple times; replaces handlers to avoid duplicates.
"""
global _LOGGING_INITIALIZED
if cfg is None:
cfg = _cfg_mod.config

root = logging.getLogger("varipeps")
# Remove old handlers to prevent duplicate logs
for h in list(root.handlers):
root.removeHandler(h)

root.setLevel(_to_py_log_level(getattr(cfg, "log_level_global", logging.INFO)))
root.propagate = False

# fmt = logging.Formatter(
# fmt="%(asctime)s %(levelname)s %(name)s: %(message)s",
# datefmt="%H:%M:%S",
# )

fmt = logging.Formatter(
fmt="%(asctime)s %(levelname)s %(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
)

if getattr(cfg, "log_to_console", True):
sh = logging.StreamHandler()
sh.setFormatter(fmt)
root.addHandler(sh)

if getattr(cfg, "log_to_file", False):
fh = logging.FileHandler(getattr(cfg, "log_file", "varipeps.log"))
fh.setFormatter(fmt)
root.addHandler(fh)

# Per-module levels
logging.getLogger("varipeps.optimizer").setLevel(
_to_py_log_level(getattr(cfg, "log_level_optimizer", logging.INFO))
)
logging.getLogger("varipeps.ctmrg").setLevel(
_to_py_log_level(getattr(cfg, "log_level_ctmrg", logging.INFO))
)
logging.getLogger("varipeps.line_search").setLevel(
_to_py_log_level(getattr(cfg, "log_level_line_search", logging.INFO))
)
logging.getLogger("varipeps.expectation").setLevel(
_to_py_log_level(getattr(cfg, "log_level_expectation", logging.INFO))
)

_LOGGING_INITIALIZED = True

def ensure_logging_configured(cfg: Any | None = None) -> None:
"""
Initialize logging once on first call; subsequent calls are no-ops.
"""
global _LOGGING_INITIALIZED
if not _LOGGING_INITIALIZED:
init_logging(cfg)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I would move that to the config file since there one could catch changes of the configuration and update the logger instances for the new values. Will prepare a commit to showcase that.

The HDF5 file stores config values like log_level_global as a numeric (e.g., numpy.int64). When loading, that integer is passed into VariPEPS_Config. In setattr, the field type for log_level_global is the Enum LogLevel, so integers must be coerced to LogLevel. Your version’s coercion path doesn’t catch your value, so it falls through and raises:
Type mismatch for option 'log_level_global', got '<class numpy.int64>', expected '<enum 'LogLevel'>'.
Why it falls through
The loader passes a numpy integer (or a 0-d/1-d array) instead of a Python int.
The Enum branch in setattr is too strict about the numeric checks, so it doesn’t convert that value into LogLevel.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@MPMPMPMPMPMPMP@JanLuca
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Replace tqdm with a logger for different modules. - #3

Open
MPMPMPMPMPMPMP wants to merge 27 commits into
variPEPS:mainfrom
MPMPMPMPMPMPMP:logger
Open

Replace tqdm with a logger for different modules.#3
MPMPMPMPMPMPMP wants to merge 27 commits into
variPEPS:mainfrom
MPMPMPMPMPMPMP:logger

Conversation

@MPMPMPMPMPMPMP

Copy link
Copy Markdown
Contributor

TLDR;
This pull request introduces a major refactor to the logging and progress reporting system across the codebase, replacing the use of tqdm_loggable and custom print/debug statements with Python's standard logging module. It also adds configurable logging levels and destinations to the configuration, and updates many routines to use structured logging for progress, warnings, and informational output.
If you think something is missing please let me know!

Simple usage (just inlude this in your

varipeps.config.log_level_ctmrg = varipeps.config.loglevel.info
varipeps.config.log_level_optimizer = varipeps.config.loglevel.info
varipeps.config.log_level_line_search = varipeps.config.loglevel.info
varipeps.config.log_level_expectation = varipeps.config.loglevel.warning
from varipeps.utils.logging_config import init_logging
init_logging()

It makes the log look the following way (depending on the log levels you choose):

09:50:32 INFO CTMRG: ✅ converged, took 75.72 seconds. (Steps: 16, Smallest SVD Norm: 1.809e-06)
09:50:44 INFO Custom VJP: ✅ converged, took 11.27 seconds. (Steps: 11)
09:50:44 INFO 🔎 Line search step 2, E=-0.017773, alpha=0.1317
09:52:06 INFO CTMRG: ✅ converged, took 81.63 seconds. (Steps: 17, Smallest SVD Norm: 1.685e-06)
09:52:17 INFO Custom VJP: ✅ converged, took 11.27 seconds. (Steps: 12)
09:52:17 INFO 🔎 Line search step 3, E=-0.075328, alpha=0.6585
09:52:17 INFO 📉 Step 1 | Energy: -0.07532772 | Retries: 0 | Conv: 3.414e-01 | Line search step: 0.65852732 | Max. trunc. err.: 1.685e-06
09:52:22 INFO CTMRG: ✅ converged, took 5.40 seconds. (Steps: 1, Smallest SVD Norm: 1.693e-06)
09:52:34 INFO Custom VJP: ✅ converged, took 11.33 seconds. (Steps: 12)
09:55:08 INFO CTMRG: ✅ converged, took 153.38 seconds. (Steps: 29, Smallest SVD Norm: 1.842e-06)
09:58:03 INFO CTMRG: ✅ converged, took 168.39 seconds. (Steps: 31, Smallest SVD Norm: 1.769e-06)
09:58:21 WARNING Custom VJP: ❌ did not converge, took 17.50 seconds. (Steps: 125)
10:00:44 INFO CTMRG: ✅ converged, took 143.25 seconds. (Steps: 27, Smallest SVD Norm: 1.587e-06)

(please don't tell me to remove the emojis I like the eye candy ... )

Key changes include:

Logging System Overhaul:

  • Introduced a new LogLevel enum and added multiple logging-related configuration options (e.g., log_level_global, log_to_console, log_to_file) to the VariPEPS_Config dataclass, allowing fine-grained control over logging behavior. [1][2]
  • Removed all usage of tqdm_loggable and associated progress bar logic from the codebase, including in __init__.py, line_search.py, and optimizer.py. [1][2][3]
  • Replaced all progress and debug print statements (including debug_print and tqdm.write) with calls to the standard logging module at appropriate levels (info, debug, warning) throughout the optimization and CTMRG routines. [1][2][3][4]

CTMRG and Optimization Routine Updates:

  • Updated ctmrg/routine.py to use a module-level logger, providing structured logs for convergence, step progress, and parameter changes, including timing information for key operations. [1][2][3][4][5][6][7][8]
  • Updated optimization/line_search.py and optimization/optimizer.py to use their own loggers, replacing progress bar and print statements with logging, and providing detailed info and warning logs for line search steps, autosaving, and convergence issues. [1][2][3][4][5][6][7][8][9][10][11][12][13][14]

Configuration and Code Cleanup:

  • Cleaned up imports and removed now-unused modules and variables related to the old progress system. [1][2]
  • Registered new configuration options and enums in the config module wrapper for export.

These changes collectively modernize the codebase's output and monitoring capabilities, making it easier for users and developers to control and interpret runtime information.


Logging System Overhaul:

  • Added a LogLevel enum and new logging configuration options to VariPEPS_Config, allowing fine-grained control over logging levels and destinations. [1][2]
  • Removed all usage of tqdm_loggable and associated progress bar logic, standardizing on Python's logging module. [1][2][3]

CTMRG and Optimization Routine Updates:

  • Refactored ctmrg/routine.py and optimization/line_search.py to use structured logging for step progress, convergence, and parameter changes, including detailed info and warning messages. [1][2][3][4][5][6][7][8][9][10][11][12][13][14][15]
  • Updated optimizer.py to use logging for autosaving, convergence checks, and progress reporting, replacing all progress bar and print-based feedback. [1][2][3][4][5][6][7][8]

Configuration and Code Cleanup:

  • Cleaned up imports and removed unused variables and modules related to the old progress and debug print system. [1][2]
  • Registered new logging-related configuration options for export in the config module wrapper.

@JanLuca

Copy link
Copy Markdown
Member

Hey, first thank you very much for the idea and implementation! Three things I spotted:

  1. We have to be careful to use logger.isEnabledFor(logging.DEBUG) inside jitted code since this is not a functional operatrion.
  2. Why did you check everywhere separately for logger.isEnabledFor and did not use a logic just inside the debug_print function which is already used everywhere in the code?
  3. For interactive usage I really liked the progress bar. Maybe we can find a logic to select tqdm as backend for interactive usage. Likely, one could define a logger backend for that.

I will take some time tomorrow to walk through it in detail.

@MPMPMPMPMPMPMP

MPMPMPMPMPMPMP commented Nov 6, 2025

Copy link
Copy Markdown
ContributorAuthor
  1. We check logger.isEnabledFor(logging.DEBUG) before using jax.debug.callback because in JIT-compiled code this check is evaluated at compile time. If it’s False, the logging branch is removed and unnecessary values aren’t materialized. This ensures the callback is only used when logging is actually enabled.

  2. This allows for more fine-grained message reporting. For example, if you only want to know when CTMRG fails to converge, you can set the log level to WARNING. But if you want detailed information about each step and the convergence process, you can set it to DEBUG. Only in that case will it have a noticeable effect on performance because then the logger.isEnabledFor(...) would evaluate to true. And it would also be more difficult to handle everything in the debug_print function.

  3. This part is a matter of personal preference in my opinion I think it is possible, but it’s not particularly useful because you cannot see the previous steps and with the current implementation if you set everything to warning except the varipeps.optimizer you will get only a message when a full step was completed, and depending on the state that might only be a message every minute. And because the optimization could terminate early, before the max steps, a progress bar would also not help since there is no actual bar just a counter.

This is the with all the loggers set to info if you set everything to warning except the optimization, then you see only the steps.
image

@JanLuca

Copy link
Copy Markdown
Member

I really like to use the progress bar where I can see the current energy without scrolling through a wall of text.

@JanLucaJanLuca left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Besides the minor comments, I like the PR :) Thank you very much for the work!

Only the optimizer.py file I could not really review since the change of the indention makes the comparison hard

Comment threadvaripeps/ctmrg/routine.py Outdated
Comment threadvaripeps/ctmrg/routine.py Outdated
Comment threadvaripeps/ctmrg/routine.py Outdated
Comment threadvaripeps/config.py Outdated
Comment on lines +1 to +79
from __future__ import annotations

import logging
from typing import Any

from varipeps import config as _cfg_mod # uses the global config instance

_LOGGING_INITIALIZED = False

def _to_py_log_level(level: Any) -> int:
# Accept both enum values and raw ints; OFF disables effectively
try:
val = int(level)
except Exception:
val = logging.INFO
if val == 0: # OFF
return logging.CRITICAL + 10
return val

def init_logging(cfg: Any | None = None) -> None:
"""
Initialize logging based on the provided config (or global config).
Safe to call multiple times; replaces handlers to avoid duplicates.
"""
global _LOGGING_INITIALIZED
if cfg is None:
cfg = _cfg_mod.config

root = logging.getLogger("varipeps")
# Remove old handlers to prevent duplicate logs
for h in list(root.handlers):
root.removeHandler(h)

root.setLevel(_to_py_log_level(getattr(cfg, "log_level_global", logging.INFO)))
root.propagate = False

# fmt = logging.Formatter(
# fmt="%(asctime)s %(levelname)s %(name)s: %(message)s",
# datefmt="%H:%M:%S",
# )

fmt = logging.Formatter(
fmt="%(asctime)s %(levelname)s %(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
)

if getattr(cfg, "log_to_console", True):
sh = logging.StreamHandler()
sh.setFormatter(fmt)
root.addHandler(sh)

if getattr(cfg, "log_to_file", False):
fh = logging.FileHandler(getattr(cfg, "log_file", "varipeps.log"))
fh.setFormatter(fmt)
root.addHandler(fh)

# Per-module levels
logging.getLogger("varipeps.optimizer").setLevel(
_to_py_log_level(getattr(cfg, "log_level_optimizer", logging.INFO))
)
logging.getLogger("varipeps.ctmrg").setLevel(
_to_py_log_level(getattr(cfg, "log_level_ctmrg", logging.INFO))
)
logging.getLogger("varipeps.line_search").setLevel(
_to_py_log_level(getattr(cfg, "log_level_line_search", logging.INFO))
)
logging.getLogger("varipeps.expectation").setLevel(
_to_py_log_level(getattr(cfg, "log_level_expectation", logging.INFO))
)

_LOGGING_INITIALIZED = True

def ensure_logging_configured(cfg: Any | None = None) -> None:
"""
Initialize logging once on first call; subsequent calls are no-ops.
"""
global _LOGGING_INITIALIZED
if not _LOGGING_INITIALIZED:
init_logging(cfg)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I would move that to the config file since there one could catch changes of the configuration and update the logger instances for the new values. Will prepare a commit to showcase that.

The HDF5 file stores config values like log_level_global as a numeric (e.g., numpy.int64). When loading, that integer is passed into VariPEPS_Config. In setattr, the field type for log_level_global is the Enum LogLevel, so integers must be coerced to LogLevel. Your version’s coercion path doesn’t catch your value, so it falls through and raises:
Type mismatch for option 'log_level_global', got '<class numpy.int64>', expected '<enum 'LogLevel'>'.
Why it falls through
The loader passes a numpy integer (or a 0-d/1-d array) instead of a Python int.
The Enum branch in setattr is too strict about the numeric checks, so it doesn’t convert that value into LogLevel.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@MPMPMPMPMPMPMP@JanLuca
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Replace tqdm with a logger for different modules. - #3

Open
MPMPMPMPMPMPMP wants to merge 27 commits into
variPEPS:mainfrom
MPMPMPMPMPMPMP:logger
Open

Replace tqdm with a logger for different modules.#3
MPMPMPMPMPMPMP wants to merge 27 commits into
variPEPS:mainfrom
MPMPMPMPMPMPMP:logger

Conversation

@MPMPMPMPMPMPMP

Copy link
Copy Markdown
Contributor

TLDR;
This pull request introduces a major refactor to the logging and progress reporting system across the codebase, replacing the use of tqdm_loggable and custom print/debug statements with Python's standard logging module. It also adds configurable logging levels and destinations to the configuration, and updates many routines to use structured logging for progress, warnings, and informational output.
If you think something is missing please let me know!

Simple usage (just inlude this in your

varipeps.config.log_level_ctmrg = varipeps.config.loglevel.info
varipeps.config.log_level_optimizer = varipeps.config.loglevel.info
varipeps.config.log_level_line_search = varipeps.config.loglevel.info
varipeps.config.log_level_expectation = varipeps.config.loglevel.warning
from varipeps.utils.logging_config import init_logging
init_logging()

It makes the log look the following way (depending on the log levels you choose):

09:50:32 INFO CTMRG: ✅ converged, took 75.72 seconds. (Steps: 16, Smallest SVD Norm: 1.809e-06)
09:50:44 INFO Custom VJP: ✅ converged, took 11.27 seconds. (Steps: 11)
09:50:44 INFO 🔎 Line search step 2, E=-0.017773, alpha=0.1317
09:52:06 INFO CTMRG: ✅ converged, took 81.63 seconds. (Steps: 17, Smallest SVD Norm: 1.685e-06)
09:52:17 INFO Custom VJP: ✅ converged, took 11.27 seconds. (Steps: 12)
09:52:17 INFO 🔎 Line search step 3, E=-0.075328, alpha=0.6585
09:52:17 INFO 📉 Step 1 | Energy: -0.07532772 | Retries: 0 | Conv: 3.414e-01 | Line search step: 0.65852732 | Max. trunc. err.: 1.685e-06
09:52:22 INFO CTMRG: ✅ converged, took 5.40 seconds. (Steps: 1, Smallest SVD Norm: 1.693e-06)
09:52:34 INFO Custom VJP: ✅ converged, took 11.33 seconds. (Steps: 12)
09:55:08 INFO CTMRG: ✅ converged, took 153.38 seconds. (Steps: 29, Smallest SVD Norm: 1.842e-06)
09:58:03 INFO CTMRG: ✅ converged, took 168.39 seconds. (Steps: 31, Smallest SVD Norm: 1.769e-06)
09:58:21 WARNING Custom VJP: ❌ did not converge, took 17.50 seconds. (Steps: 125)
10:00:44 INFO CTMRG: ✅ converged, took 143.25 seconds. (Steps: 27, Smallest SVD Norm: 1.587e-06)

(please don't tell me to remove the emojis I like the eye candy ... )

Key changes include:

Logging System Overhaul:

  • Introduced a new LogLevel enum and added multiple logging-related configuration options (e.g., log_level_global, log_to_console, log_to_file) to the VariPEPS_Config dataclass, allowing fine-grained control over logging behavior. [1][2]
  • Removed all usage of tqdm_loggable and associated progress bar logic from the codebase, including in __init__.py, line_search.py, and optimizer.py. [1][2][3]
  • Replaced all progress and debug print statements (including debug_print and tqdm.write) with calls to the standard logging module at appropriate levels (info, debug, warning) throughout the optimization and CTMRG routines. [1][2][3][4]

CTMRG and Optimization Routine Updates:

  • Updated ctmrg/routine.py to use a module-level logger, providing structured logs for convergence, step progress, and parameter changes, including timing information for key operations. [1][2][3][4][5][6][7][8]
  • Updated optimization/line_search.py and optimization/optimizer.py to use their own loggers, replacing progress bar and print statements with logging, and providing detailed info and warning logs for line search steps, autosaving, and convergence issues. [1][2][3][4][5][6][7][8][9][10][11][12][13][14]

Configuration and Code Cleanup:

  • Cleaned up imports and removed now-unused modules and variables related to the old progress system. [1][2]
  • Registered new configuration options and enums in the config module wrapper for export.

These changes collectively modernize the codebase's output and monitoring capabilities, making it easier for users and developers to control and interpret runtime information.


Logging System Overhaul:

  • Added a LogLevel enum and new logging configuration options to VariPEPS_Config, allowing fine-grained control over logging levels and destinations. [1][2]
  • Removed all usage of tqdm_loggable and associated progress bar logic, standardizing on Python's logging module. [1][2][3]

CTMRG and Optimization Routine Updates:

  • Refactored ctmrg/routine.py and optimization/line_search.py to use structured logging for step progress, convergence, and parameter changes, including detailed info and warning messages. [1][2][3][4][5][6][7][8][9][10][11][12][13][14][15]
  • Updated optimizer.py to use logging for autosaving, convergence checks, and progress reporting, replacing all progress bar and print-based feedback. [1][2][3][4][5][6][7][8]

Configuration and Code Cleanup:

  • Cleaned up imports and removed unused variables and modules related to the old progress and debug print system. [1][2]
  • Registered new logging-related configuration options for export in the config module wrapper.

@JanLuca

Copy link
Copy Markdown
Member

Hey, first thank you very much for the idea and implementation! Three things I spotted:

  1. We have to be careful to use logger.isEnabledFor(logging.DEBUG) inside jitted code since this is not a functional operatrion.
  2. Why did you check everywhere separately for logger.isEnabledFor and did not use a logic just inside the debug_print function which is already used everywhere in the code?
  3. For interactive usage I really liked the progress bar. Maybe we can find a logic to select tqdm as backend for interactive usage. Likely, one could define a logger backend for that.

I will take some time tomorrow to walk through it in detail.

@MPMPMPMPMPMPMP

MPMPMPMPMPMPMP commented Nov 6, 2025

Copy link
Copy Markdown
ContributorAuthor
  1. We check logger.isEnabledFor(logging.DEBUG) before using jax.debug.callback because in JIT-compiled code this check is evaluated at compile time. If it’s False, the logging branch is removed and unnecessary values aren’t materialized. This ensures the callback is only used when logging is actually enabled.

  2. This allows for more fine-grained message reporting. For example, if you only want to know when CTMRG fails to converge, you can set the log level to WARNING. But if you want detailed information about each step and the convergence process, you can set it to DEBUG. Only in that case will it have a noticeable effect on performance because then the logger.isEnabledFor(...) would evaluate to true. And it would also be more difficult to handle everything in the debug_print function.

  3. This part is a matter of personal preference in my opinion I think it is possible, but it’s not particularly useful because you cannot see the previous steps and with the current implementation if you set everything to warning except the varipeps.optimizer you will get only a message when a full step was completed, and depending on the state that might only be a message every minute. And because the optimization could terminate early, before the max steps, a progress bar would also not help since there is no actual bar just a counter.

This is the with all the loggers set to info if you set everything to warning except the optimization, then you see only the steps.
image

@JanLuca

Copy link
Copy Markdown
Member

I really like to use the progress bar where I can see the current energy without scrolling through a wall of text.

@JanLucaJanLuca left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Besides the minor comments, I like the PR :) Thank you very much for the work!

Only the optimizer.py file I could not really review since the change of the indention makes the comparison hard

Comment threadvaripeps/ctmrg/routine.py Outdated
Comment threadvaripeps/ctmrg/routine.py Outdated
Comment threadvaripeps/ctmrg/routine.py Outdated
Comment threadvaripeps/config.py Outdated
Comment on lines +1 to +79
from __future__ import annotations

import logging
from typing import Any

from varipeps import config as _cfg_mod # uses the global config instance

_LOGGING_INITIALIZED = False

def _to_py_log_level(level: Any) -> int:
# Accept both enum values and raw ints; OFF disables effectively
try:
val = int(level)
except Exception:
val = logging.INFO
if val == 0: # OFF
return logging.CRITICAL + 10
return val

def init_logging(cfg: Any | None = None) -> None:
"""
Initialize logging based on the provided config (or global config).
Safe to call multiple times; replaces handlers to avoid duplicates.
"""
global _LOGGING_INITIALIZED
if cfg is None:
cfg = _cfg_mod.config

root = logging.getLogger("varipeps")
# Remove old handlers to prevent duplicate logs
for h in list(root.handlers):
root.removeHandler(h)

root.setLevel(_to_py_log_level(getattr(cfg, "log_level_global", logging.INFO)))
root.propagate = False

# fmt = logging.Formatter(
# fmt="%(asctime)s %(levelname)s %(name)s: %(message)s",
# datefmt="%H:%M:%S",
# )

fmt = logging.Formatter(
fmt="%(asctime)s %(levelname)s %(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
)

if getattr(cfg, "log_to_console", True):
sh = logging.StreamHandler()
sh.setFormatter(fmt)
root.addHandler(sh)

if getattr(cfg, "log_to_file", False):
fh = logging.FileHandler(getattr(cfg, "log_file", "varipeps.log"))
fh.setFormatter(fmt)
root.addHandler(fh)

# Per-module levels
logging.getLogger("varipeps.optimizer").setLevel(
_to_py_log_level(getattr(cfg, "log_level_optimizer", logging.INFO))
)
logging.getLogger("varipeps.ctmrg").setLevel(
_to_py_log_level(getattr(cfg, "log_level_ctmrg", logging.INFO))
)
logging.getLogger("varipeps.line_search").setLevel(
_to_py_log_level(getattr(cfg, "log_level_line_search", logging.INFO))
)
logging.getLogger("varipeps.expectation").setLevel(
_to_py_log_level(getattr(cfg, "log_level_expectation", logging.INFO))
)

_LOGGING_INITIALIZED = True

def ensure_logging_configured(cfg: Any | None = None) -> None:
"""
Initialize logging once on first call; subsequent calls are no-ops.
"""
global _LOGGING_INITIALIZED
if not _LOGGING_INITIALIZED:
init_logging(cfg)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I would move that to the config file since there one could catch changes of the configuration and update the logger instances for the new values. Will prepare a commit to showcase that.

The HDF5 file stores config values like log_level_global as a numeric (e.g., numpy.int64). When loading, that integer is passed into VariPEPS_Config. In setattr, the field type for log_level_global is the Enum LogLevel, so integers must be coerced to LogLevel. Your version’s coercion path doesn’t catch your value, so it falls through and raises:
Type mismatch for option 'log_level_global', got '<class numpy.int64>', expected '<enum 'LogLevel'>'.
Why it falls through
The loader passes a numpy integer (or a 0-d/1-d array) instead of a Python int.
The Enum branch in setattr is too strict about the numeric checks, so it doesn’t convert that value into LogLevel.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@MPMPMPMPMPMPMP@JanLuca
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Replace tqdm with a logger for different modules. - #3

Open
MPMPMPMPMPMPMP wants to merge 27 commits into
variPEPS:mainfrom
MPMPMPMPMPMPMP:logger
Open

Replace tqdm with a logger for different modules.#3
MPMPMPMPMPMPMP wants to merge 27 commits into
variPEPS:mainfrom
MPMPMPMPMPMPMP:logger

Conversation

@MPMPMPMPMPMPMP

Copy link
Copy Markdown
Contributor

TLDR;
This pull request introduces a major refactor to the logging and progress reporting system across the codebase, replacing the use of tqdm_loggable and custom print/debug statements with Python's standard logging module. It also adds configurable logging levels and destinations to the configuration, and updates many routines to use structured logging for progress, warnings, and informational output.
If you think something is missing please let me know!

Simple usage (just inlude this in your

varipeps.config.log_level_ctmrg = varipeps.config.loglevel.info
varipeps.config.log_level_optimizer = varipeps.config.loglevel.info
varipeps.config.log_level_line_search = varipeps.config.loglevel.info
varipeps.config.log_level_expectation = varipeps.config.loglevel.warning
from varipeps.utils.logging_config import init_logging
init_logging()

It makes the log look the following way (depending on the log levels you choose):

09:50:32 INFO CTMRG: ✅ converged, took 75.72 seconds. (Steps: 16, Smallest SVD Norm: 1.809e-06)
09:50:44 INFO Custom VJP: ✅ converged, took 11.27 seconds. (Steps: 11)
09:50:44 INFO 🔎 Line search step 2, E=-0.017773, alpha=0.1317
09:52:06 INFO CTMRG: ✅ converged, took 81.63 seconds. (Steps: 17, Smallest SVD Norm: 1.685e-06)
09:52:17 INFO Custom VJP: ✅ converged, took 11.27 seconds. (Steps: 12)
09:52:17 INFO 🔎 Line search step 3, E=-0.075328, alpha=0.6585
09:52:17 INFO 📉 Step 1 | Energy: -0.07532772 | Retries: 0 | Conv: 3.414e-01 | Line search step: 0.65852732 | Max. trunc. err.: 1.685e-06
09:52:22 INFO CTMRG: ✅ converged, took 5.40 seconds. (Steps: 1, Smallest SVD Norm: 1.693e-06)
09:52:34 INFO Custom VJP: ✅ converged, took 11.33 seconds. (Steps: 12)
09:55:08 INFO CTMRG: ✅ converged, took 153.38 seconds. (Steps: 29, Smallest SVD Norm: 1.842e-06)
09:58:03 INFO CTMRG: ✅ converged, took 168.39 seconds. (Steps: 31, Smallest SVD Norm: 1.769e-06)
09:58:21 WARNING Custom VJP: ❌ did not converge, took 17.50 seconds. (Steps: 125)
10:00:44 INFO CTMRG: ✅ converged, took 143.25 seconds. (Steps: 27, Smallest SVD Norm: 1.587e-06)

(please don't tell me to remove the emojis I like the eye candy ... )

Key changes include:

Logging System Overhaul:

  • Introduced a new LogLevel enum and added multiple logging-related configuration options (e.g., log_level_global, log_to_console, log_to_file) to the VariPEPS_Config dataclass, allowing fine-grained control over logging behavior. [1][2]
  • Removed all usage of tqdm_loggable and associated progress bar logic from the codebase, including in __init__.py, line_search.py, and optimizer.py. [1][2][3]
  • Replaced all progress and debug print statements (including debug_print and tqdm.write) with calls to the standard logging module at appropriate levels (info, debug, warning) throughout the optimization and CTMRG routines. [1][2][3][4]

CTMRG and Optimization Routine Updates:

  • Updated ctmrg/routine.py to use a module-level logger, providing structured logs for convergence, step progress, and parameter changes, including timing information for key operations. [1][2][3][4][5][6][7][8]
  • Updated optimization/line_search.py and optimization/optimizer.py to use their own loggers, replacing progress bar and print statements with logging, and providing detailed info and warning logs for line search steps, autosaving, and convergence issues. [1][2][3][4][5][6][7][8][9][10][11][12][13][14]

Configuration and Code Cleanup:

  • Cleaned up imports and removed now-unused modules and variables related to the old progress system. [1][2]
  • Registered new configuration options and enums in the config module wrapper for export.

These changes collectively modernize the codebase's output and monitoring capabilities, making it easier for users and developers to control and interpret runtime information.


Logging System Overhaul:

  • Added a LogLevel enum and new logging configuration options to VariPEPS_Config, allowing fine-grained control over logging levels and destinations. [1][2]
  • Removed all usage of tqdm_loggable and associated progress bar logic, standardizing on Python's logging module. [1][2][3]

CTMRG and Optimization Routine Updates:

  • Refactored ctmrg/routine.py and optimization/line_search.py to use structured logging for step progress, convergence, and parameter changes, including detailed info and warning messages. [1][2][3][4][5][6][7][8][9][10][11][12][13][14][15]
  • Updated optimizer.py to use logging for autosaving, convergence checks, and progress reporting, replacing all progress bar and print-based feedback. [1][2][3][4][5][6][7][8]

Configuration and Code Cleanup:

  • Cleaned up imports and removed unused variables and modules related to the old progress and debug print system. [1][2]
  • Registered new logging-related configuration options for export in the config module wrapper.

@JanLuca

Copy link
Copy Markdown
Member

Hey, first thank you very much for the idea and implementation! Three things I spotted:

  1. We have to be careful to use logger.isEnabledFor(logging.DEBUG) inside jitted code since this is not a functional operatrion.
  2. Why did you check everywhere separately for logger.isEnabledFor and did not use a logic just inside the debug_print function which is already used everywhere in the code?
  3. For interactive usage I really liked the progress bar. Maybe we can find a logic to select tqdm as backend for interactive usage. Likely, one could define a logger backend for that.

I will take some time tomorrow to walk through it in detail.

@MPMPMPMPMPMPMP

MPMPMPMPMPMPMP commented Nov 6, 2025

Copy link
Copy Markdown
ContributorAuthor
  1. We check logger.isEnabledFor(logging.DEBUG) before using jax.debug.callback because in JIT-compiled code this check is evaluated at compile time. If it’s False, the logging branch is removed and unnecessary values aren’t materialized. This ensures the callback is only used when logging is actually enabled.

  2. This allows for more fine-grained message reporting. For example, if you only want to know when CTMRG fails to converge, you can set the log level to WARNING. But if you want detailed information about each step and the convergence process, you can set it to DEBUG. Only in that case will it have a noticeable effect on performance because then the logger.isEnabledFor(...) would evaluate to true. And it would also be more difficult to handle everything in the debug_print function.

  3. This part is a matter of personal preference in my opinion I think it is possible, but it’s not particularly useful because you cannot see the previous steps and with the current implementation if you set everything to warning except the varipeps.optimizer you will get only a message when a full step was completed, and depending on the state that might only be a message every minute. And because the optimization could terminate early, before the max steps, a progress bar would also not help since there is no actual bar just a counter.

This is the with all the loggers set to info if you set everything to warning except the optimization, then you see only the steps.
image

@JanLuca

Copy link
Copy Markdown
Member

I really like to use the progress bar where I can see the current energy without scrolling through a wall of text.

@JanLucaJanLuca left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Besides the minor comments, I like the PR :) Thank you very much for the work!

Only the optimizer.py file I could not really review since the change of the indention makes the comparison hard

Comment threadvaripeps/ctmrg/routine.py Outdated
Comment threadvaripeps/ctmrg/routine.py Outdated
Comment threadvaripeps/ctmrg/routine.py Outdated
Comment threadvaripeps/config.py Outdated
Comment on lines +1 to +79
from __future__ import annotations

import logging
from typing import Any

from varipeps import config as _cfg_mod # uses the global config instance

_LOGGING_INITIALIZED = False

def _to_py_log_level(level: Any) -> int:
# Accept both enum values and raw ints; OFF disables effectively
try:
val = int(level)
except Exception:
val = logging.INFO
if val == 0: # OFF
return logging.CRITICAL + 10
return val

def init_logging(cfg: Any | None = None) -> None:
"""
Initialize logging based on the provided config (or global config).
Safe to call multiple times; replaces handlers to avoid duplicates.
"""
global _LOGGING_INITIALIZED
if cfg is None:
cfg = _cfg_mod.config

root = logging.getLogger("varipeps")
# Remove old handlers to prevent duplicate logs
for h in list(root.handlers):
root.removeHandler(h)

root.setLevel(_to_py_log_level(getattr(cfg, "log_level_global", logging.INFO)))
root.propagate = False

# fmt = logging.Formatter(
# fmt="%(asctime)s %(levelname)s %(name)s: %(message)s",
# datefmt="%H:%M:%S",
# )

fmt = logging.Formatter(
fmt="%(asctime)s %(levelname)s %(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
)

if getattr(cfg, "log_to_console", True):
sh = logging.StreamHandler()
sh.setFormatter(fmt)
root.addHandler(sh)

if getattr(cfg, "log_to_file", False):
fh = logging.FileHandler(getattr(cfg, "log_file", "varipeps.log"))
fh.setFormatter(fmt)
root.addHandler(fh)

# Per-module levels
logging.getLogger("varipeps.optimizer").setLevel(
_to_py_log_level(getattr(cfg, "log_level_optimizer", logging.INFO))
)
logging.getLogger("varipeps.ctmrg").setLevel(
_to_py_log_level(getattr(cfg, "log_level_ctmrg", logging.INFO))
)
logging.getLogger("varipeps.line_search").setLevel(
_to_py_log_level(getattr(cfg, "log_level_line_search", logging.INFO))
)
logging.getLogger("varipeps.expectation").setLevel(
_to_py_log_level(getattr(cfg, "log_level_expectation", logging.INFO))
)

_LOGGING_INITIALIZED = True

def ensure_logging_configured(cfg: Any | None = None) -> None:
"""
Initialize logging once on first call; subsequent calls are no-ops.
"""
global _LOGGING_INITIALIZED
if not _LOGGING_INITIALIZED:
init_logging(cfg)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I would move that to the config file since there one could catch changes of the configuration and update the logger instances for the new values. Will prepare a commit to showcase that.

The HDF5 file stores config values like log_level_global as a numeric (e.g., numpy.int64). When loading, that integer is passed into VariPEPS_Config. In setattr, the field type for log_level_global is the Enum LogLevel, so integers must be coerced to LogLevel. Your version’s coercion path doesn’t catch your value, so it falls through and raises:
Type mismatch for option 'log_level_global', got '<class numpy.int64>', expected '<enum 'LogLevel'>'.
Why it falls through
The loader passes a numpy integer (or a 0-d/1-d array) instead of a Python int.
The Enum branch in setattr is too strict about the numeric checks, so it doesn’t convert that value into LogLevel.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@MPMPMPMPMPMPMP@JanLuca