File: Data\IProgressChannel.cs
Web Access
Project: src\src\Microsoft.ML.Core\Microsoft.ML.Core.csproj (Microsoft.ML.Core)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System;
using System.Collections.Generic;
 
namespace Microsoft.ML.Runtime;
 
/// <summary>
/// This is a factory interface for <see cref="IProgressChannel"/>.
/// Both <see cref="IHostEnvironment"/> and <see cref="IProgressChannel"/> implement this interface,
/// to allow for nested progress reporters.
///
/// REVIEW: make <see cref="IChannelProvider"/> implement this, instead of the environment?
/// </summary>
public interface IProgressChannelProvider
{
    /// <summary>
    /// Create a progress channel for a computation named <paramref name="name"/>.
    /// </summary>
    IProgressChannel StartProgressChannel(string name);
}
 
/// <summary>
/// A common interface for progress reporting.
/// It is expected that the progress channel interface is used from only one thread.
///
/// Supported workflow:
/// 1) Create the channel via <see cref="IProgressChannelProvider.StartProgressChannel"/>.
/// 2) Call <see cref="SetHeader"/> as many times as desired (including 0).
///       Each call to <see cref="SetHeader"/> supersedes the previous one.
/// 3) Report checkpoints (0 or more) by calling <see cref="Checkpoint"/>.
/// 4) Repeat steps 2-3 as often as necessary.
/// 5) Dispose the channel.
/// </summary>
public interface IProgressChannel : IProgressChannelProvider, IDisposable
{
    /// <summary>
    /// Set up the reporting structure:
    /// - Set the 'header' of the progress reports, defining which progress units and metrics are going to be reported.
    /// - Provide a thread-safe delegate to be invoked whenever anyone needs to know the progress.
    ///
    /// It is acceptable to call <see cref="SetHeader"/> multiple times (or none), regardless of whether the calculation is running
    /// or not. Because of synchronization, the computation should not deny calls to the 'old' <paramref name="fillAction"/>
    /// delegates even after a new one is provided.
    /// </summary>
    /// <param name="header">The header object.</param>
    /// <param name="fillAction">The delegate to provide actual progress. The <see cref="IProgressEntry"/> parameter of
    /// the delegate will correspond to the provided <paramref name="header"/>.</param>
    void SetHeader(ProgressHeader header, Action<IProgressEntry> fillAction);
 
    /// <summary>
    /// Submit a 'checkpoint' entry. These entries are guaranteed to be delivered to the progress listener,
    /// if it is interested. Typically, this would contain some intermediate metrics, that are only calculated
    /// at certain moments ('checkpoints') of the computation.
    ///
    /// For example, SDCA may report a checkpoint every time it computes the loss, or LBFGS may report a checkpoint
    /// every iteration.
    ///
    /// The only parameter, <paramref name="values"/>, is interpreted in the following fashion:
    /// * First MetricNames.Length items, if present, are metrics.
    /// * Subsequent ProgressNames.Length items, if present, are progress units.
    /// * Subsequent ProgressNames.Length items, if present, are progress limits.
    /// * If any more values remain, an exception is thrown.
    /// </summary>
    /// <param name="values">The metrics, progress units and progress limits.</param>
    void Checkpoint(params Double?[] values);
}
 
/// <summary>
/// This is the 'header' of the progress report.
/// </summary>
public sealed class ProgressHeader
{
    /// <summary>
    /// These are the names of the progress 'units', from the least granular to the most granular.
    /// For example, neural network might have {'epoch', 'example'} and FastTree might have {'tree', 'split', 'feature'}.
    /// Will never be null, but can be empty.
    /// </summary>
    public readonly IReadOnlyList<string> UnitNames;
 
    /// <summary>
    /// These are the names of the reported metrics. For example, this could be the 'loss', 'weight updates/sec' etc.
    /// Will never be null, but can be empty.
    /// </summary>
    public readonly IReadOnlyList<string> MetricNames;
 
    /// <summary>
    /// Initialize the header. This will take ownership of the arrays.
    /// Both arrays can be null, even simultaneously. This 'empty' header indicated that the calculation doesn't report
    /// any units of progress, but the tracker can still track start, stop and elapsed time. Of course, if there's any
    /// progress or metrics to report, it is always better to report them.
    /// </summary>
    /// <param name="metricNames">The metrics that the calculation reports. These are completely independent, and there
    /// is no contract on whether the metric values should increase or not. As naming convention, <paramref name="metricNames"/>
    /// can have multiple words with spaces, and should be title-cased.</param>
    /// <param name="unitNames">The names of the progress units, listed from least granular to most granular.
    /// The idea is that the progress should be lexicographically increasing (like [0,0], [0,10], [1,0], [1,15], [2,5] etc.).
    /// As naming convention, <paramref name="unitNames"/> should be lower-cased and typically plural
    /// (for example, iterations, clusters, examples). </param>
    public ProgressHeader(string[] metricNames, string[] unitNames)
    {
        Contracts.CheckValueOrNull(unitNames);
        Contracts.CheckValueOrNull(metricNames);
 
        UnitNames = unitNames ?? new string[0];
        MetricNames = metricNames ?? new string[0];
    }
 
    /// <summary>
    /// A constructor for no metrics, just progress units. As naming convention, <paramref name="unitNames"/> should be lower-cased
    /// and typically plural (for example, iterations, clusters, examples).
    /// </summary>
    public ProgressHeader(params string[] unitNames)
        : this(null, unitNames)
    {
    }
}
 
/// <summary>
/// A metric/progress holder item.
/// </summary>
public interface IProgressEntry
{
    /// <summary>
    /// Set the progress value for the index <paramref name="index"/> to <paramref name="value"/>,
    /// and the limit value for the progress becomes 'unknown'.
    /// </summary>
    void SetProgress(int index, Double value);
 
    /// <summary>
    /// Set the progress value for the index <paramref name="index"/> to <paramref name="value"/>,
    /// and the limit value to <paramref name="lim"/>. If <paramref name="lim"/> is a NAN, it is set to null instead.
    /// </summary>
    void SetProgress(int index, Double value, Double lim);
 
    /// <summary>
    /// Sets the metric with index <paramref name="index"/> to <paramref name="value"/>.
    /// </summary>
    void SetMetric(int index, Double value);
 
}