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.

ロギング

このチュートリアルでは、MINTOライブラリのロギング機能と様々なロギング制御メソッドの使い方を説明します。

1. 基本的なログ出力

基本的なログ出力機能を試してみましょう。

import time

import jijmodeling as jm
import ommx_openjij_adapter as oj_ad

from minto import Experiment

# Create an experiment with logging enabled
exp = 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. ログ設定のカスタマイズ

ログの表示方法をカスタマイズしてみましょう。

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 = 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 = 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. ソルバー実行ログ

ソルバーの実行時間とパラメータを、自動的に記録してみましょう。

# 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 = 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. 複数実行

複数回実行し、階層的なログ表示を確認してみましょう。

# Parameter sweep experiment
exp = 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_solution("best_solution", f"energy: {energy:.2f}")
        run.log_parameter("final_energy", energy)

exp.finish_experiment()

5. グローバル設定の使用

グローバルログを設定し、複数の実験間でそれを共有する方法を示します。

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 = 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 = 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. 直接的なロガーの利用

Experiment/Runクラスを用いずに、ロガーを直接利用する方法も示します。

# 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. 環境固有の設定例

開発環境・本番環境・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 = 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 = 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()

まとめ

このチュートリアルでは、Mintoライブラリのロギング機能について学びました。

  1. 基本的なログ出力: verbose_logging=Trueにより、簡単にロギングできます

  2. カスタム設定: LogConfigを用いて、ログ表示の詳細を設定することができます

  3. ソルバーロギング: log_solverにより、自動でソルバー実行のロギングが可能です

  4. 階層的なログ表示: 複数回実行を行った場合にも、明確な階層構造を表示してくれます

  5. グローバル設定: configure_loggingを用いることで、一括での設定ができます

  6. ダイレクトロガー: 低レベルAPIにより、柔軟な設定が可能です

  7. 環境適応: 開発環境と本番環境への最適化

これらの機能を活用することで、実験の進行状況を効率的にモニタリングし、デバッグと最適化作業を大幅に改善することができます。