Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Logging Tutorial

This tutorial teaches you how to use the Minto library’s logging functionality and various logging control methods.

1. Basic Log Output

Let’s start by trying the basic logging functionality.

import time

import jijmodeling as jm
import ommx_openjij_adapter as oj_ad

import minto

# Create an Experiment with logging enabled
exp = minto.Experiment(
    name="tutorial_experiment",
    auto_saving=False,  # Disable saving for tutorial
)


def create_problem():
    n = jm.Placeholder("n")
    rows = jm.Placeholder("rows", ndim=1)
    cols = jm.Placeholder("cols", ndim=1)
    value = jm.Placeholder("value", ndim=1)
    nnz = value.len_at(0)
    problem = jm.Problem("qubo")
    i = jm.Element("i", nnz)
    x = jm.BinaryVar("x", shape=(n,))

    problem += jm.sum(i, value[i] * x[rows[i]] * x[cols[i]])

    return problem


def qubo_instance_data():
    n = 5
    rows = [0, 1, 2, 3, 4]
    cols = [1, 2, 3, 4, 0]
    value = [1.0, -1.0, 2.0, -2.0, 3.0]
    return {"n": n, "rows": rows, "cols": cols, "value": value}


interpreter = jm.Interpreter(qubo_instance_data())
instance = interpreter.eval_problem(create_problem())

# Create and execute a run
run = exp.run()
with run:
    num_sweeps = 1000
    run.log_parameter("solver_type", "SimulatedAnnealing")
    run.log_parameter("temperature", 1.0)
    run.log_parameter("num_sweeps", num_sweeps)

    solution = oj_ad.OMMXOpenJijSAAdapter.solve(instance, num_sweeps=num_sweeps)
    run.log_solution(solution)

# Finish the experiment
exp.finish_experiment()

2. Custom Log Configuration

Let’s customize how logs are displayed.

from minto.logging_config import LogConfig, LogFormat, LogLevel

# Simple log configuration
simple_config = LogConfig(
    format=LogFormat.SIMPLE, show_timestamps=False, show_icons=False, show_colors=False
)

print("=== Simple Log Configuration ===")
exp_simple = minto.Experiment(
    name="simple_experiment",
    verbose_logging=True,
    log_config=simple_config,
    auto_saving=False,
    collect_environment=False,
)

run = exp_simple.run()
with run:
    run.log_parameter("method", "QAOA")
    run.log_parameter("layers", 3)

exp_simple.finish_experiment()
# Detailed log configuration
detailed_config = LogConfig(
    level=LogLevel.DEBUG,
    format=LogFormat.DETAILED,
    show_timestamps=True,
    show_icons=True,
    show_colors=False,  # Colors are often disabled in Jupyter environments
    max_parameter_length=200,  # Increase maximum parameter display length
)

print("\n=== Detailed Log Configuration ===")
exp_detailed = minto.Experiment(
    name="detailed_experiment",
    verbose_logging=True,
    log_config=detailed_config,
    auto_saving=False,
    collect_environment=False,
)

run = exp_detailed.run()
with run:
    run.log_parameter("algorithm", "Quantum Annealing")
    run.log_parameter("annealing_time", 20.0)

    # Log objects
    problem_data = {"variables": 100, "constraints": 50, "objective": "minimize"}
    run.log_object("problem_instance", problem_data)

exp_detailed.finish_experiment()

3. Solver Execution Logging

Let’s learn how to automatically log solver execution time and parameters.

# Define QUBO problem using jijmodeling (same as in basic example)
def create_problem():
    n = jm.Placeholder("n")
    rows = jm.Placeholder("rows", ndim=1)
    cols = jm.Placeholder("cols", ndim=1)
    value = jm.Placeholder("value", ndim=1)
    nnz = value.len_at(0)
    problem = jm.Problem("qubo")
    i = jm.Element("i", nnz)
    x = jm.BinaryVar("x", shape=(n,))

    problem += jm.sum(i, value[i] * x[rows[i]] * x[cols[i]])

    return problem


def qubo_instance_data():
    n = 5
    rows = [0, 1, 2, 3, 4]
    cols = [1, 2, 3, 4, 0]
    value = [1.0, -1.0, 2.0, -2.0, 3.0]
    return {"n": n, "rows": rows, "cols": cols, "value": value}


# Prepare the problem instance
interpreter = jm.Interpreter(qubo_instance_data())
instance = interpreter.eval_problem(create_problem())

# Execute solver logging with real OpenJij solver
exp = minto.Experiment(
    name="solver_logging_experiment",
    verbose_logging=True,
    auto_saving=False,
    collect_environment=False,
)

run = exp.run()
with run:
    # Method 1: Manual parameter logging and solver execution
    run.log_parameter("solver_type", "SimulatedAnnealing")
    run.log_parameter("num_sweeps", 2000)
    run.log_parameter("temperature", 1.0)

    # Execute solver directly
    solution = oj_ad.OMMXOpenJijSAAdapter.solve(instance, num_sweeps=2000)
    run.log_solution(solution)

    print("\n--- Using log_solver wrapper ---")

    # Method 2: Using log_solver wrapper for automatic parameter logging
    def solve_with_openjij(instance, num_sweeps=1000, seed=None):
        """Wrapper function for OpenJij solver"""
        return oj_ad.OMMXOpenJijSAAdapter.solve(
            instance, num_sweeps=num_sweeps, seed=seed
        )

    # Wrap the solver function to automatically log parameters and execution time
    wrapped_solver = run.log_solver(
        "OpenJij_SA",
        solve_with_openjij,
        exclude_params=["seed"],  # Exclude seed from logging if desired
    )

    # Execute solver with automatic parameter and timing logging
    solution2 = wrapped_solver(instance=instance, num_sweeps=1500, seed=42)

    run.log_solution(solution2)

exp.finish_experiment()

4. Multiple Run Execution

Let’s execute multiple runs and verify the hierarchical log display.

# Parameter sweep experiment
exp = minto.Experiment(
    name="parameter_sweep",
    verbose_logging=True,
    auto_saving=False,
    collect_environment=False,
)

# Execute with different temperature parameters
temperatures = [0.1, 0.5, 1.0, 2.0]

for i, temp in enumerate(temperatures):
    run = exp.run()
    with run:
        run.log_parameter("temperature", temp)
        run.log_parameter("algorithm", "Simulated Annealing")
        run.log_parameter("max_iterations", 1000)

        # Execute dummy optimization
        time.sleep(0.05)  # Simulate computation time

        # Simulate results based on temperature
        import random

        energy = random.uniform(-100, -50) * (1 + temp)

        run.log_parameter("final_energy", energy)

exp.finish_experiment()

5. Using Global Configuration

Let’s learn how to set global log configuration and share it across multiple experiments.

from minto.logger import configure_logging, get_logger

# Global log configuration
configure_logging(
    enabled=True,
    level=LogLevel.INFO,
    show_timestamps=True,
    show_icons=True,
    show_colors=False,  # For Jupyter environments
)

print("=== Experiment 1 using Global Configuration ===")
exp1 = minto.Experiment(
    name="global_config_exp1",
    verbose_logging=True,  # Global configuration is automatically applied
    auto_saving=False,
    collect_environment=False,
)

run = exp1.run()
with run:
    run.log_parameter("method", "Genetic Algorithm")
    run.log_parameter("population_size", 100)

exp1.finish_experiment()

print("\n=== Experiment 2 using Global Configuration ===")
exp2 = minto.Experiment(
    name="global_config_exp2",
    verbose_logging=True,  # Same global configuration is applied
    auto_saving=False,
    collect_environment=False,
)

run = exp2.run()
with run:
    run.log_parameter("method", "Particle Swarm Optimization")
    run.log_parameter("swarm_size", 50)

exp2.finish_experiment()

6. Direct Logger Usage

Let’s also check how to use the logger directly without the Experiment/Run classes.

# Get the global logger
logger = get_logger()

print("=== Direct Logger Usage ===")

# Manual experiment logging
logger.log_experiment_start("manual_experiment")

# Manual run logging
logger.log_run_start(0)
logger.log_parameter("manual_param", "direct_logging")
logger.log_object("custom_object", {"type": "manual", "value": 42})

# Log solver execution
logger.log_solver("manual_solver", execution_time=1.5)

# End run and experiment
logger.log_run_end(0, 2.0)
logger.log_experiment_end("manual_experiment", 2.0, 1)

7. Environment-Specific Configuration Examples

Let’s check recommended configurations for different environments (development, production, CI).

# Development environment configuration
dev_config = LogConfig(
    level=LogLevel.DEBUG,
    format=LogFormat.DETAILED,
    show_timestamps=True,
    show_icons=True,
    show_colors=False,  # For Jupyter
    max_parameter_length=300,  # Allow longer parameter values in development
)

print("=== Development Environment Configuration ===")
exp_dev = minto.Experiment(
    name="development_test",
    verbose_logging=True,
    log_config=dev_config,
    auto_saving=False,
    collect_environment=False,
)

run = exp_dev.run()
with run:
    run.log_parameter("debug_mode", True)
    run.log_parameter("verbose_output", True)

exp_dev.finish_experiment()

# Production environment configuration
prod_config = LogConfig(
    level=LogLevel.WARNING,
    format=LogFormat.MINIMAL,  # Use MINIMAL instead of COMPACT
    show_timestamps=True,
    show_icons=False,
    show_colors=False,
    max_parameter_length=50,  # Limit parameter length in production
)

print("\n=== Production Environment Configuration ===")
exp_prod = minto.Experiment(
    name="production_test",
    verbose_logging=True,
    log_config=prod_config,
    auto_saving=False,
    collect_environment=False,
)

run = exp_prod.run()
with run:
    run.log_parameter("production_mode", True)
    # Only WARNING level and above are displayed, so normal parameter logs won't be shown

exp_prod.finish_experiment()

8. Disabling Logging

Set just the verbose_logging parameter to False to disable logging for an individual experiment.

# Method 1: Disable logging for individual experiments
print("=== Method 1: Disable logging for individual experiments ===")
exp_no_log = minto.Experiment(
    name="no_logging_experiment",
    verbose_logging=False,  # Disable logging for this experiment
    auto_saving=False,
    collect_environment=False,
)

run = exp_no_log.run()
with run:
    # These log calls will not produce any output
    run.log_parameter("param1", "value1")
    run.log_parameter("param2", 42)
    print("Parameters logged, but no log output shown")

exp_no_log.finish_experiment()

Summary

In this tutorial, we learned about the Minto library’s logging functionality:

  1. Basic Log Output: Easy logging activation with verbose_logging=True

  2. Custom Configuration: Detailed control of log display using LogConfig

  3. Solver Logging: Automatic logging of solver execution with log_solver

  4. Hierarchical Display: Clear hierarchical structure for multiple runs

  5. Global Configuration: Batch configuration with configure_logging

  6. Direct Logger: Flexible control with low-level APIs

  7. Environment Adaptation: Optimization for development and production environments

By utilizing these features, you can efficiently monitor experiment progress and significantly improve debugging and optimization work.