class_docstr: Auxiliary class that keeps track of local & global training progress, measured in epochs.
  An epoch can be incremented after collaboration accumulates a said number of gradients (target_batch_size).
  Similarly to pytorch LR scheduler, epoch can be incremented on a single optimizer update or many local updates.
  
  :param min_refresh_period: wait for at least this many seconds before fetching new collaboration state
  :param max_refresh_period: wait for at most this many seconds before fetching new collaboration state
  :param default_refresh_period: if no peers are detected, attempt to fetch collaboration state this often (seconds)
  :param expected_drift_peers: assume that this many new peers can join between epochs
  :param expected_drift_rate: assumes that this fraction of current collaboration can join/leave between epochs
  :note: The expected collaboration drift parameters are used to adjust the frequency with which this optimizer will
    refresh the collaboration-wide statistics (to avoid missing the moment when peers transition to the next epoch)
  :param performance_ema_alpha: smoothing value used to estimate this peer's performance (samples per second)
  :param metadata_expiration: peer's metadata (e.g. samples processed) is stored onto DHT for this many seconds
  
  Example:
  
  >>> tracker = ProgressTracker(hivemind.DHT(...), prefix="my_experiment_with_several_peers", target_batch_size=100)
  >>> local_epoch, local_samples = 0, 0
  >>> while True:
  >>>     accumulate_gradients(batch_size=32)
  >>>     local_samples += 32
  >>>     tracker.report_local_progress(local_epoch, local_samples)
  >>>     if local_epoch < tracker.global_progress.epoch:
  >>>         download_state_from_peers()  # if peer is out of sync, synchronize it with the swarm
  >>>     if tracker.accumulated_enough_samples:
  >>>         with tracker.pause_updates():
  >>>             aggregate_gradients_with_peers()
  >>>             update_model_parameters()
  >>>             local_epoch = tracker.update_epoch(local_epoch + 1)
  >>>             local_samples = 0
docstr: Estimate (absolute) time when this peer should increment epoch
docstr_tok: ["Estimate","(","absolute",")","time","when","this","peer","should","increment","epoch"]
code: def estimated_next_update_time(self) -> DHTExpiration:
      """Estimate (absolute) time when this peer should increment epoch"""
      if self.ready_to_update_epoch:
          return get_dht_time()
      return self.global_progress.eta_next_epoch
  
imports: import asyncio
  import contextlib
  import logging
  import threading
  from dataclasses import dataclass
  from typing import Dict, Optional
  import numpy
  from pydantic import BaseModel, StrictBool, StrictFloat, confloat, conint
  from hivemind.dht import DHT
  from hivemind.dht.schema import BytesWithPublicKey, RSASignatureValidator, SchemaValidator
  from hivemind.utils import DHTExpiration, ValueWithExpiration, enter_asynchronously, get_dht_time, get_logger
  from hivemind.utils.crypto import RSAPrivateKey
  from hivemind.utils.performance_ema import PerformanceEMA
label_desc: Use image node_id 4 for calling the ProgressTracker obj's underlying member method code with example usage: obj.estimated_next_update_time() and returns: self, get_dht_time