prompt: You will be provided with a partial code base and an issue statement explaining a problem to resolve.
  <issue>
  Loss metric to use required_output_keys
  ## 🚀 Feature
  
  Currently, if we have custom metrics that require data other then `y_pred` and `y`, [we suggest](https://discuss.pytorch.org/t/how-access-inputs-in-custom-ignite-metric/91221/6) to do the following: 
  ```python
  metrics = {
      "Accuracy": Accuracy(),
      "Loss": Loss(criterion, output_transform=lambda out_dict: (out_dict["y_pred"], out_dict["y"])),
      "CustomMetric": CustomMetric()
  }
  
  evaluator = create_supervised_evaluator(
      model, 
      metrics=metrics, 
      output_transform=lambda x, y, y_pred: {"x": x, "y": y, "y_pred": y_pred}
  )
  ```
  
  where `CustomMetric` is defined as 
  ```python
  class CustomMetric(Metric):
  
      required_output_keys = ("y_pred", "y", "x")
  ```
  
  The idea is to extend this for `Loss` metric to support `required_output_keys`. The main issue with `Loss` now is with `(prediction, target, kwargs)` optional input, where `kwargs` is a dict for extra args for criterion function.
  
  </issue>
  <code>
  [start of ignite/metrics/loss.py]
  1 from typing import Callable, Dict, Sequence, Tuple, Union, cast
  2 
  3 import torch
  4 
  5 from ignite.exceptions import NotComputableError
  6 from ignite.metrics.metric import Metric, reinit__is_reduced, sync_all_reduce
  7 
  8 __all__ = ["Loss"]
  9 
  10 
  11 class Loss(Metric):
  12     """
  13     Calculates the average loss according to the passed loss_fn.
  14 
  15     Args:
  16         loss_fn: a callable taking a prediction tensor, a target
  17             tensor, optionally other arguments, and returns the average loss
  18             over all observations in the batch.
  19         output_transform: a callable that is used to transform the
  20             :class:`~ignite.engine.engine.Engine`'s ``process_function``'s output into the
  21             form expected by the metric.
  22             This can be useful if, for example, you have a multi-output model and
  23             you want to compute the metric with respect to one of the outputs.
  24             The output is expected to be a tuple `(prediction, target)` or
  25             (prediction, target, kwargs) where kwargs is a dictionary of extra
  26             keywords arguments. If extra keywords arguments are provided they are passed to `loss_fn`.
  27         batch_size: a callable taking a target tensor that returns the
  28             first dimension size (usually the batch size).
  29         device: specifies which device updates are accumulated on. Setting the
  30             metric's device to be the same as your ``update`` arguments ensures the ``update`` method is
  31             non-blocking. By default, CPU.
  32 
  33     """
  34 
  35     required_output_keys = None
  36 
  37     def __init__(
  38         self,
  39         loss_fn: Callable,
  40         output_transform: Callable = lambda x: x,
  41         batch_size: Callable = len,
  42         device: Union[str, torch.device] = torch.device("cpu"),
  43     ):
  44         super(Loss, self).__init__(output_transform, device=device)
  45         self._loss_fn = loss_fn
  46         self._batch_size = batch_size
  47 
  48     @reinit__is_reduced
  49     def reset(self) -> None:
  50         self._sum = torch.tensor(0.0, device=self._device)
  51         self._num_examples = 0
  52 
  53     @reinit__is_reduced
  54     def update(self, output: Sequence[Union[torch.Tensor, Dict]]) -> None:
  55         if len(output) == 2:
  56             y_pred, y = cast(Tuple[torch.Tensor, torch.Tensor], output)
  57             kwargs = {}  # type: Dict
  58         else:
  59             y_pred, y, kwargs = cast(Tuple[torch.Tensor, torch.Tensor, Dict], output)
  60         average_loss = self._loss_fn(y_pred, y, **kwargs).detach()
  61 
  62         if len(average_loss.shape) != 0:
  63             raise ValueError("loss_fn did not return the average loss.")
  64 
  65         n = self._batch_size(y)
  66         self._sum += average_loss.to(self._device) * n
  67         self._num_examples += n
  68 
  69     @sync_all_reduce("_sum", "_num_examples")
  70     def compute(self) -> float:
  71         if self._num_examples == 0:
  72             raise NotComputableError("Loss must have at least one example before it can be computed.")
  73         return self._sum.item() / self._num_examples
  74
  [end of ignite/metrics/loss.py]
  </code>
  
  I need you to solve this issue by generating a single patch file that I can apply directly to this repository using git apply. Please respond with a single patch file in the following format.
  <patch>
  diff --git a/file.py b/file.py
  --- a/file.py
  +++ b/file.py
  @@ -1,27 +1,35 @@
   def euclidean(a, b):
  -    while b:
  -        a, b = b, a % b
  -    return a
  +    if b == 0:
  +        return a
  +    return euclidean(b, a % b)
   
   
   def bresenham(x0, y0, x1, y1):
       points = []
       dx = abs(x1 - x0)
       dy = abs(y1 - y0)
  -    sx = 1 if x0 < x1 else -1
  -    sy = 1 if y0 < y1 else -1
  -    err = dx - dy
  +    x, y = x0, y0
  +    sx = -1 if x0 > x1 else 1
  +    sy = -1 if y0 > y1 else 1
   
  -    while True:
  -        points.append((x0, y0))
  -        if x0 == x1 and y0 == y1:
  -            break
  -        e2 = 2 * err
  -        if e2 > -dy:
  -            err -= dy
  -            x0 += sx
  -        if e2 < dx:
  -            err += dx
  -            y0 += sy
  +    if dx > dy:
  +        err = dx / 2.0
  +        while x != x1:
  +            points.append((x, y))
  +            err -= dy
  +            if err < 0:
  +                y += sy
  +                err += dx
  +            x += sx
  +    else:
  +        err = dy / 2.0
  +        while y != y1:
  +            points.append((x, y))
  +            err -= dx
  +            if err < 0:
  +                x += sx
  +                err += dy
  +            y += sy
  +
  +    points.append((x, y))
       return points
  
  </patch>
golden_diff: diff --git a/ignite/metrics/loss.py b/ignite/metrics/loss.py
  --- a/ignite/metrics/loss.py
  +++ b/ignite/metrics/loss.py
  @@ -30,9 +30,52 @@
               metric's device to be the same as your ``update`` arguments ensures the ``update`` method is
               non-blocking. By default, CPU.
   
  +    Attributes:
  +        required_output_keys: dictionary defines required keys to be found in ``engine.state.output`` if the
  +            latter is a dictionary. Default, ``("y_pred", "y", "criterion_kwargs")``. This is useful when the
  +            criterion function requires additional arguments, which can be passed using ``criterion_kwargs``.
  +            See notes below for an example.
  +
  +    Note:
  +
  +        Let's implement a Loss metric that requires ``x``, ``y_pred``, ``y`` and ``criterion_kwargs`` as input
  +        for ``criterion`` function. In the example below we show how to setup standard metric like Accuracy
  +        and the Loss metric using an ``evaluator`` created with
  +        :meth:`~ignite.engine.create_supervised_evaluator` method.
  +
  +        .. code-block:: python
  +
  +            import torch
  +            import torch.nn as nn
  +            from torch.nn.functional import nll_loss
  +
  +            from ignite.metrics import Accuracy, Loss
  +            from ignite.engine import create_supervised_evaluator
  +
  +            model = ...
  +
  +            criterion = nll_loss
  +
  +            metrics = {
  +                "Accuracy": Accuracy(),
  +                "Loss": Loss(criterion)
  +            }
  +
  +            # global criterion kwargs
  +            criterion_kwargs = {...}
  +
  +            evaluator = create_supervised_evaluator(
  +                model,
  +                metrics=metrics,
  +                output_transform=lambda x, y, y_pred: {
  +                    "x": x, "y": y, "y_pred": y_pred, "criterion_kwargs": criterion_kwargs}
  +            )
  +
  +            res = evaluator.run(data)
  +
       """
   
  -    required_output_keys = None
  +    required_output_keys = ("y_pred", "y", "criterion_kwargs")
   
       def __init__(
           self,
  
verification_info: {"golden_diff": "diff --git a/ignite/metrics/loss.py b/ignite/metrics/loss.py\n--- a/ignite/metrics/loss.py\n+++ b/ignite/metrics/loss.py\n@@ -30,9 +30,52 @@\n             metric's device to be the same as your ``update`` arguments ensures the ``update`` method is\n             non-blocking. By default, CPU.\n \n+    Attributes:\n+        required_output_keys: dictionary defines required keys to be found in ``engine.state.output`` if the\n+            latter is a dictionary. Default, ``(\"y_pred\", \"y\", \"criterion_kwargs\")``. This is useful when the\n+            criterion function requires additional arguments, which can be passed using ``criterion_kwargs``.\n+            See notes below for an example.\n+\n+    Note:\n+\n+        Let's implement a Loss metric that requires ``x``, ``y_pred``, ``y`` and ``criterion_kwargs`` as input\n+        for ``criterion`` function. In the example below we show how to setup standard metric like Accuracy\n+        and the Loss metric using an ``evaluator`` created with\n+        :meth:`~ignite.engine.create_supervised_evaluator` method.\n+\n+        .. code-block:: python\n+\n+            import torch\n+            import torch.nn as nn\n+            from torch.nn.functional import nll_loss\n+\n+            from ignite.metrics import Accuracy, Loss\n+            from ignite.engine import create_supervised_evaluator\n+\n+            model = ...\n+\n+            criterion = nll_loss\n+\n+            metrics = {\n+                \"Accuracy\": Accuracy(),\n+                \"Loss\": Loss(criterion)\n+            }\n+\n+            # global criterion kwargs\n+            criterion_kwargs = {...}\n+\n+            evaluator = create_supervised_evaluator(\n+                model,\n+                metrics=metrics,\n+                output_transform=lambda x, y, y_pred: {\n+                    \"x\": x, \"y\": y, \"y_pred\": y_pred, \"criterion_kwargs\": criterion_kwargs}\n+            )\n+\n+            res = evaluator.run(data)\n+\n     \"\"\"\n \n-    required_output_keys = None\n+    required_output_keys = (\"y_pred\", \"y\", \"criterion_kwargs\")\n \n     def __init__(\n         self,\n", "issue": "Loss metric to use required_output_keys\n## \ud83d\ude80 Feature\r\n\r\nCurrently, if we have custom metrics that require data other then `y_pred` and `y`, [we suggest](https://discuss.pytorch.org/t/how-access-inputs-in-custom-ignite-metric/91221/6) to do the following: \r\n```python\r\nmetrics = {\r\n    \"Accuracy\": Accuracy(),\r\n    \"Loss\": Loss(criterion, output_transform=lambda out_dict: (out_dict[\"y_pred\"], out_dict[\"y\"])),\r\n    \"CustomMetric\": CustomMetric()\r\n}\r\n\r\nevaluator = create_supervised_evaluator(\r\n    model, \r\n    metrics=metrics, \r\n    output_transform=lambda x, y, y_pred: {\"x\": x, \"y\": y, \"y_pred\": y_pred}\r\n)\r\n```\r\n\r\nwhere `CustomMetric` is defined as \r\n```python\r\nclass CustomMetric(Metric):\r\n\r\n    required_output_keys = (\"y_pred\", \"y\", \"x\")\r\n```\r\n\r\nThe idea is to extend this for `Loss` metric to support `required_output_keys`. The main issue with `Loss` now is with `(prediction, target, kwargs)` optional input, where `kwargs` is a dict for extra args for criterion function.\n", "before_files": [{"content": "from typing import Callable, Dict, Sequence, Tuple, Union, cast\n\nimport torch\n\nfrom ignite.exceptions import NotComputableError\nfrom ignite.metrics.metric import Metric, reinit__is_reduced, sync_all_reduce\n\n__all__ = [\"Loss\"]\n\n\nclass Loss(Metric):\n    \"\"\"\n    Calculates the average loss according to the passed loss_fn.\n\n    Args:\n        loss_fn: a callable taking a prediction tensor, a target\n            tensor, optionally other arguments, and returns the average loss\n            over all observations in the batch.\n        output_transform: a callable that is used to transform the\n            :class:`~ignite.engine.engine.Engine`'s ``process_function``'s output into the\n            form expected by the metric.\n            This can be useful if, for example, you have a multi-output model and\n            you want to compute the metric with respect to one of the outputs.\n            The output is expected to be a tuple `(prediction, target)` or\n            (prediction, target, kwargs) where kwargs is a dictionary of extra\n            keywords arguments. If extra keywords arguments are provided they are passed to `loss_fn`.\n        batch_size: a callable taking a target tensor that returns the\n            first dimension size (usually the batch size).\n        device: specifies which device updates are accumulated on. Setting the\n            metric's device to be the same as your ``update`` arguments ensures the ``update`` method is\n            non-blocking. By default, CPU.\n\n    \"\"\"\n\n    required_output_keys = None\n\n    def __init__(\n        self,\n        loss_fn: Callable,\n        output_transform: Callable = lambda x: x,\n        batch_size: Callable = len,\n        device: Union[str, torch.device] = torch.device(\"cpu\"),\n    ):\n        super(Loss, self).__init__(output_transform, device=device)\n        self._loss_fn = loss_fn\n        self._batch_size = batch_size\n\n    @reinit__is_reduced\n    def reset(self) -> None:\n        self._sum = torch.tensor(0.0, device=self._device)\n        self._num_examples = 0\n\n    @reinit__is_reduced\n    def update(self, output: Sequence[Union[torch.Tensor, Dict]]) -> None:\n        if len(output) == 2:\n            y_pred, y = cast(Tuple[torch.Tensor, torch.Tensor], output)\n            kwargs = {}  # type: Dict\n        else:\n            y_pred, y, kwargs = cast(Tuple[torch.Tensor, torch.Tensor, Dict], output)\n        average_loss = self._loss_fn(y_pred, y, **kwargs).detach()\n\n        if len(average_loss.shape) != 0:\n            raise ValueError(\"loss_fn did not return the average loss.\")\n\n        n = self._batch_size(y)\n        self._sum += average_loss.to(self._device) * n\n        self._num_examples += n\n\n    @sync_all_reduce(\"_sum\", \"_num_examples\")\n    def compute(self) -> float:\n        if self._num_examples == 0:\n            raise NotComputableError(\"Loss must have at least one example before it can be computed.\")\n        return self._sum.item() / self._num_examples\n", "path": "ignite/metrics/loss.py"}]}