code: def torch_minimize(
      closure: Callable[[], Tuple[Tensor, Sequence[Optional[Tensor]]]],
      parameters: Dict[str, Tensor],
      bounds: Optional[
          Dict[str, Tuple[Optional[float], Optional[float]]]
      ] = None,
      callback: Optional[
          Callable[[Dict[str, Tensor], OptimizationResult], None]
      ] = None,
      optimizer: Union[
          Optimizer, Callable[[List[Tensor]], Optimizer]
      ] = Adam,
      scheduler: Optional[
          Union[LRScheduler, Callable[[Optimizer], LRScheduler]]
      ] = None,
      step_limit: Optional[int] = None,
      timeout_sec: Optional[float] = None,
      stopping_criterion: Optional[Callable[[Tensor], bool]] = None,
  ) -> OptimizationResult:
      r"""Generic torch.optim-based optimization routine.
  
      Args:
          closure: Callable that returns a tensor and an iterable of gradient tensors.
              Responsible for setting relevant parameters' `grad` attributes.
          parameters: A dictionary of tensors to be optimized.
          bounds: An optional dictionary of bounds for elements of `parameters`.
          callback: A callable taking `parameters` and an OptimizationResult as arguments.
          optimizer: A `torch.optim.Optimizer` instance or a factory that takes
              a list of parameters and returns an `Optimizer` instance.
          scheduler: A `torch.optim.lr_scheduler._LRScheduler` instance or a factory
              that takes a `Optimizer` instance and returns a `_LRSchedule` instance.
          step_limit: Integer specifying a maximum number of optimization steps.
              One of `step_limit`, `stopping_criterion`, or `timeout_sec` must be passed.
          timeout_sec: Timeout in seconds before terminating the optimization loop.
              One of `step_limit`, `stopping_criterion`, or `timeout_sec` must be passed.
          stopping_criterion: A StoppingCriterion for the optimization loop.
  
      Returns:
          An OptimizationResult summarizing the final state of the run.
      """
      result: OptimizationResult
      start_time = monotonic()
  
      if step_limit is None:
          if stopping_criterion is None and timeout_sec is None:
              raise RuntimeError(
                  "No termination conditions were given."
              )
          step_limit = maxsize
  
      if not isinstance(optimizer, Optimizer):
          optimizer = optimizer(list(parameters.values()))
  
      if not (scheduler is None or isinstance(scheduler, LRScheduler)):
          scheduler = scheduler(optimizer)
  
      _bounds = (
          {}
          if bounds is None
          else {
              name: limits
              for name, limits in bounds.items()
              if name in parameters
          }
      )
      for step in range(1, step_limit + 1):
          fval, _ = closure()
          runtime = monotonic() - start_time
          result = OptimizationResult(
              step=step,
              fval=fval.detach().cpu().item(),
              status=OptimizationStatus.RUNNING,
              runtime=runtime,
          )
  
          # TODO: Update stopping_criterion API to return a message.
          if stopping_criterion and stopping_criterion(fval):
              result.status = OptimizationStatus.STOPPED
              result.message = "`torch_minimize` stopped due to `stopping_criterion`."
  
          if timeout_sec is not None and runtime >= timeout_sec:
              result.status = OptimizationStatus.STOPPED
              result.message = f"`torch_minimize` stopped due to timeout after {runtime} seconds."
  
          if callback:
              callback(parameters, result)
  
          if result.status != OptimizationStatus.RUNNING:
              break
  
          optimizer.step()
          for name, (lower, upper) in _bounds.items():
              parameters[name].data = parameters[name].clamp(
                  min=lower, max=upper
              )
  
          if scheduler:
              scheduler.step()
  
      if result.status != OptimizationStatus.RUNNING:
          return replace(result, runtime=monotonic() - start_time)
  
      # Account for final parameter update when stopping due to step_limit
      return OptimizationResult(
          step=step,
          fval=closure()[0].detach().cpu().item(),
          status=OptimizationStatus.STOPPED,
          runtime=monotonic() - start_time,
          message=f"`torch_minimize` stopped after reaching step_limit={step_limit}.",
      )
  
imports: from __future__ import annotations
  import re
  from dataclasses import dataclass, replace
  from enum import auto, Enum
  from itertools import count
  from sys import maxsize
  from time import monotonic
  from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, Union
  from botorch.optim.closures import NdarrayOptimizationClosure
  from botorch.optim.utils.numpy_utils import get_bounds_as_ndarray
  from botorch.optim.utils.timeout import minimize_with_timeout
  from numpy import asarray, float64, ndarray
  from torch import Tensor
  from torch.optim.adam import Adam
  from torch.optim.optimizer import Optimizer
label_desc: Use image node_id 2 for calling a global function with example usage: torch_minimize(closure, parameters, bounds, callback, optimizer, scheduler, step_limit, timeout_sec, stopping_criterion) and returns: OptimizationResult, replace