prompt: You will be provided with a partial code base and an issue statement explaining a problem to resolve.
  <issue>
  EarlyStoppingCallback considers first epoch as bad
  ## 🐛 Bug Report
  EarlyStoppingCallback considers first epoch as bad. This can lead for example to always stopping after first epoch if patience=1.
  
  
  ### How To Reproduce
  You can train a model with early stopping and patience=1 and see that it always stops after first epoch. Or you can use the unit test below that I added to pull request.
  
  #### Code sample
  ```python
  from unittest.mock import MagicMock, PropertyMock
  
  from catalyst.core import EarlyStoppingCallback
  
  
  def test_patience1():
      """@TODO: Docs. Contribution is welcome."""
      early_stop = EarlyStoppingCallback(1)
      runner = MagicMock()
      type(runner).stage_name = PropertyMock(return_value="training")
      type(runner).valid_metrics = PropertyMock(return_value={"loss": 0.001})
      stop_mock = PropertyMock(return_value=False)
      type(runner).need_early_stop = stop_mock
  
      early_stop.on_epoch_end(runner)
  
      assert stop_mock.mock_calls == []
  ```
  
  ### Expected behavior
  Training doesn't stop after first epoch. And the unit test passes.
  
  
  ### Environment
  ```bash
  Catalyst version: 20.06
  PyTorch version: 1.5.1
  Is debug build: No
  CUDA used to build PyTorch: None
  TensorFlow version: N/A
  TensorBoard version: 2.2.2
  
  OS: Mac OSX 10.15.5
  GCC version: Could not collect
  CMake version: version 3.8.0
  
  Python version: 3.7
  Is CUDA available: No
  CUDA runtime version: No CUDA
  GPU models and configuration: No CUDA
  Nvidia driver version: No CUDA
  cuDNN version: No CUDA
  
  Versions of relevant libraries:
  [pip3] catalyst-codestyle==20.4
  [pip3] catalyst-sphinx-theme==1.1.1
  [pip3] efficientnet-pytorch==0.6.3
  [pip3] numpy==1.18.5
  [pip3] segmentation-models-pytorch==0.1.0
  [pip3] tensorboard==2.2.2
  [pip3] tensorboard-plugin-wit==1.6.0.post3
  [pip3] tensorboardX==2.0
  [pip3] torch==1.5.1
  [pip3] torchvision==0.6.1
  [conda] catalyst-codestyle        20.4                      <pip>
  [conda] catalyst-sphinx-theme     1.1.1                     <pip>
  [conda] efficientnet-pytorch      0.6.3                     <pip>
  [conda] numpy                     1.18.5                    <pip>
  [conda] segmentation-models-pytorch 0.1.0                     <pip>
  [conda] tensorboard               2.2.2                     <pip>
  [conda] tensorboard-plugin-wit    1.6.0.post3               <pip>
  [conda] tensorboardX              2.0                       <pip>
  [conda] torch                     1.5.1                     <pip>
  [conda] torchvision               0.6.1                     <pip>
  ```
  
  
  </issue>
  <code>
  [start of catalyst/core/callbacks/early_stop.py]
  1 from catalyst.core.callback import Callback, CallbackNode, CallbackOrder
  2 from catalyst.core.runner import IRunner
  3 
  4 
  5 class CheckRunCallback(Callback):
  6     """@TODO: Docs. Contribution is welcome."""
  7 
  8     def __init__(self, num_batch_steps: int = 3, num_epoch_steps: int = 2):
  9         """@TODO: Docs. Contribution is welcome."""
  10         super().__init__(order=CallbackOrder.external, node=CallbackNode.all)
  11         self.num_batch_steps = num_batch_steps
  12         self.num_epoch_steps = num_epoch_steps
  13 
  14     def on_epoch_end(self, runner: IRunner):
  15         """@TODO: Docs. Contribution is welcome."""
  16         if runner.epoch >= self.num_epoch_steps:
  17             runner.need_early_stop = True
  18 
  19     def on_batch_end(self, runner: IRunner):
  20         """@TODO: Docs. Contribution is welcome."""
  21         if runner.loader_batch_step >= self.num_batch_steps:
  22             runner.need_early_stop = True
  23 
  24 
  25 class EarlyStoppingCallback(Callback):
  26     """@TODO: Docs. Contribution is welcome."""
  27 
  28     def __init__(
  29         self,
  30         patience: int,
  31         metric: str = "loss",
  32         minimize: bool = True,
  33         min_delta: float = 1e-6,
  34     ):
  35         """@TODO: Docs. Contribution is welcome."""
  36         super().__init__(order=CallbackOrder.external, node=CallbackNode.all)
  37         self.best_score = None
  38         self.metric = metric
  39         self.patience = patience
  40         self.num_bad_epochs = 0
  41         self.is_better = None
  42 
  43         if minimize:
  44             self.is_better = lambda score, best: score <= (best - min_delta)
  45         else:
  46             self.is_better = lambda score, best: score >= (best + min_delta)
  47 
  48     def on_epoch_end(self, runner: IRunner) -> None:
  49         """@TODO: Docs. Contribution is welcome."""
  50         if runner.stage_name.startswith("infer"):
  51             return
  52 
  53         score = runner.valid_metrics[self.metric]
  54         if self.best_score is None:
  55             self.best_score = score
  56         if self.is_better(score, self.best_score):
  57             self.num_bad_epochs = 0
  58             self.best_score = score
  59         else:
  60             self.num_bad_epochs += 1
  61 
  62         if self.num_bad_epochs >= self.patience:
  63             print(f"Early stop at {runner.epoch} epoch")
  64             runner.need_early_stop = True
  65
  [end of catalyst/core/callbacks/early_stop.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/catalyst/core/callbacks/early_stop.py b/catalyst/core/callbacks/early_stop.py
  --- a/catalyst/core/callbacks/early_stop.py
  +++ b/catalyst/core/callbacks/early_stop.py
  @@ -51,9 +51,7 @@
               return
   
           score = runner.valid_metrics[self.metric]
  -        if self.best_score is None:
  -            self.best_score = score
  -        if self.is_better(score, self.best_score):
  +        if self.best_score is None or self.is_better(score, self.best_score):
               self.num_bad_epochs = 0
               self.best_score = score
           else:
  
verification_info: {"golden_diff": "diff --git a/catalyst/core/callbacks/early_stop.py b/catalyst/core/callbacks/early_stop.py\n--- a/catalyst/core/callbacks/early_stop.py\n+++ b/catalyst/core/callbacks/early_stop.py\n@@ -51,9 +51,7 @@\n             return\n \n         score = runner.valid_metrics[self.metric]\n-        if self.best_score is None:\n-            self.best_score = score\n-        if self.is_better(score, self.best_score):\n+        if self.best_score is None or self.is_better(score, self.best_score):\n             self.num_bad_epochs = 0\n             self.best_score = score\n         else:\n", "issue": "EarlyStoppingCallback considers first epoch as bad\n## \ud83d\udc1b Bug Report\r\nEarlyStoppingCallback considers first epoch as bad. This can lead for example to always stopping after first epoch if patience=1.\r\n\r\n\r\n### How To Reproduce\r\nYou can train a model with early stopping and patience=1 and see that it always stops after first epoch. Or you can use the unit test below that I added to pull request.\r\n\r\n#### Code sample\r\n```python\r\nfrom unittest.mock import MagicMock, PropertyMock\r\n\r\nfrom catalyst.core import EarlyStoppingCallback\r\n\r\n\r\ndef test_patience1():\r\n    \"\"\"@TODO: Docs. Contribution is welcome.\"\"\"\r\n    early_stop = EarlyStoppingCallback(1)\r\n    runner = MagicMock()\r\n    type(runner).stage_name = PropertyMock(return_value=\"training\")\r\n    type(runner).valid_metrics = PropertyMock(return_value={\"loss\": 0.001})\r\n    stop_mock = PropertyMock(return_value=False)\r\n    type(runner).need_early_stop = stop_mock\r\n\r\n    early_stop.on_epoch_end(runner)\r\n\r\n    assert stop_mock.mock_calls == []\r\n```\r\n\r\n### Expected behavior\r\nTraining doesn't stop after first epoch. And the unit test passes.\r\n\r\n\r\n### Environment\r\n```bash\r\nCatalyst version: 20.06\r\nPyTorch version: 1.5.1\r\nIs debug build: No\r\nCUDA used to build PyTorch: None\r\nTensorFlow version: N/A\r\nTensorBoard version: 2.2.2\r\n\r\nOS: Mac OSX 10.15.5\r\nGCC version: Could not collect\r\nCMake version: version 3.8.0\r\n\r\nPython version: 3.7\r\nIs CUDA available: No\r\nCUDA runtime version: No CUDA\r\nGPU models and configuration: No CUDA\r\nNvidia driver version: No CUDA\r\ncuDNN version: No CUDA\r\n\r\nVersions of relevant libraries:\r\n[pip3] catalyst-codestyle==20.4\r\n[pip3] catalyst-sphinx-theme==1.1.1\r\n[pip3] efficientnet-pytorch==0.6.3\r\n[pip3] numpy==1.18.5\r\n[pip3] segmentation-models-pytorch==0.1.0\r\n[pip3] tensorboard==2.2.2\r\n[pip3] tensorboard-plugin-wit==1.6.0.post3\r\n[pip3] tensorboardX==2.0\r\n[pip3] torch==1.5.1\r\n[pip3] torchvision==0.6.1\r\n[conda] catalyst-codestyle        20.4                      <pip>\r\n[conda] catalyst-sphinx-theme     1.1.1                     <pip>\r\n[conda] efficientnet-pytorch      0.6.3                     <pip>\r\n[conda] numpy                     1.18.5                    <pip>\r\n[conda] segmentation-models-pytorch 0.1.0                     <pip>\r\n[conda] tensorboard               2.2.2                     <pip>\r\n[conda] tensorboard-plugin-wit    1.6.0.post3               <pip>\r\n[conda] tensorboardX              2.0                       <pip>\r\n[conda] torch                     1.5.1                     <pip>\r\n[conda] torchvision               0.6.1                     <pip>\r\n```\r\n\n", "before_files": [{"content": "from catalyst.core.callback import Callback, CallbackNode, CallbackOrder\nfrom catalyst.core.runner import IRunner\n\n\nclass CheckRunCallback(Callback):\n    \"\"\"@TODO: Docs. Contribution is welcome.\"\"\"\n\n    def __init__(self, num_batch_steps: int = 3, num_epoch_steps: int = 2):\n        \"\"\"@TODO: Docs. Contribution is welcome.\"\"\"\n        super().__init__(order=CallbackOrder.external, node=CallbackNode.all)\n        self.num_batch_steps = num_batch_steps\n        self.num_epoch_steps = num_epoch_steps\n\n    def on_epoch_end(self, runner: IRunner):\n        \"\"\"@TODO: Docs. Contribution is welcome.\"\"\"\n        if runner.epoch >= self.num_epoch_steps:\n            runner.need_early_stop = True\n\n    def on_batch_end(self, runner: IRunner):\n        \"\"\"@TODO: Docs. Contribution is welcome.\"\"\"\n        if runner.loader_batch_step >= self.num_batch_steps:\n            runner.need_early_stop = True\n\n\nclass EarlyStoppingCallback(Callback):\n    \"\"\"@TODO: Docs. Contribution is welcome.\"\"\"\n\n    def __init__(\n        self,\n        patience: int,\n        metric: str = \"loss\",\n        minimize: bool = True,\n        min_delta: float = 1e-6,\n    ):\n        \"\"\"@TODO: Docs. Contribution is welcome.\"\"\"\n        super().__init__(order=CallbackOrder.external, node=CallbackNode.all)\n        self.best_score = None\n        self.metric = metric\n        self.patience = patience\n        self.num_bad_epochs = 0\n        self.is_better = None\n\n        if minimize:\n            self.is_better = lambda score, best: score <= (best - min_delta)\n        else:\n            self.is_better = lambda score, best: score >= (best + min_delta)\n\n    def on_epoch_end(self, runner: IRunner) -> None:\n        \"\"\"@TODO: Docs. Contribution is welcome.\"\"\"\n        if runner.stage_name.startswith(\"infer\"):\n            return\n\n        score = runner.valid_metrics[self.metric]\n        if self.best_score is None:\n            self.best_score = score\n        if self.is_better(score, self.best_score):\n            self.num_bad_epochs = 0\n            self.best_score = score\n        else:\n            self.num_bad_epochs += 1\n\n        if self.num_bad_epochs >= self.patience:\n            print(f\"Early stop at {runner.epoch} epoch\")\n            runner.need_early_stop = True\n", "path": "catalyst/core/callbacks/early_stop.py"}]}