prompt: You will be provided with a partial code base and an issue statement explaining a problem to resolve.
  <issue>
  HyperTile node is nondeterministic across executions and messes with global randomness
  The HyperTile node uses the random module and seeds the global random with its own counter variable.
  
  Unfortunately, this counter variable is retained across executions if the HyperTile parameters don't change, and so every execution will have different results.
  
  The effect on global random can be avoided just by using a `random.Random()` instance instead of a counter, but since ComfyUI doesn't provide any kind of after-exec function for nodes, there doesn't seem to be a way to reset it to its initial state after one prompt is executed.
  
  I suppose you could work around this by setting having IS_CHANGED return something so that the node gets always executed, thus reinitializing randomness, but that might cause any nodes that come after the HyperTile node to needlessly re-execute.
  
  
  
  </issue>
  <code>
  [start of comfy_extras/nodes_hypertile.py]
  1 #Taken from: https://github.com/tfernd/HyperTile/
  2 
  3 import math
  4 from einops import rearrange
  5 import random
  6 
  7 def random_divisor(value: int, min_value: int, /, max_options: int = 1, counter = 0) -> int:
  8     min_value = min(min_value, value)
  9 
  10     # All big divisors of value (inclusive)
  11     divisors = [i for i in range(min_value, value + 1) if value % i == 0]
  12 
  13     ns = [value // i for i in divisors[:max_options]]  # has at least 1 element
  14 
  15     random.seed(counter)
  16     idx = random.randint(0, len(ns) - 1)
  17 
  18     return ns[idx]
  19 
  20 class HyperTile:
  21     @classmethod
  22     def INPUT_TYPES(s):
  23         return {"required": { "model": ("MODEL",),
  24                              "tile_size": ("INT", {"default": 256, "min": 1, "max": 2048}),
  25                              "swap_size": ("INT", {"default": 2, "min": 1, "max": 128}),
  26                              "max_depth": ("INT", {"default": 0, "min": 0, "max": 10}),
  27                              "scale_depth": ("BOOLEAN", {"default": False}),
  28                               }}
  29     RETURN_TYPES = ("MODEL",)
  30     FUNCTION = "patch"
  31 
  32     CATEGORY = "_for_testing"
  33 
  34     def patch(self, model, tile_size, swap_size, max_depth, scale_depth):
  35         model_channels = model.model.model_config.unet_config["model_channels"]
  36 
  37         apply_to = set()
  38         temp = model_channels
  39         for x in range(max_depth + 1):
  40             apply_to.add(temp)
  41             temp *= 2
  42 
  43         latent_tile_size = max(32, tile_size) // 8
  44         self.temp = None
  45         self.counter = 1
  46 
  47         def hypertile_in(q, k, v, extra_options):
  48             if q.shape[-1] in apply_to:
  49                 shape = extra_options["original_shape"]
  50                 aspect_ratio = shape[-1] / shape[-2]
  51 
  52                 hw = q.size(1)
  53                 h, w = round(math.sqrt(hw * aspect_ratio)), round(math.sqrt(hw / aspect_ratio))
  54 
  55                 factor = 2**((q.shape[-1] // model_channels) - 1) if scale_depth else 1
  56                 nh = random_divisor(h, latent_tile_size * factor, swap_size, self.counter)
  57                 self.counter += 1
  58                 nw = random_divisor(w, latent_tile_size * factor, swap_size, self.counter)
  59                 self.counter += 1
  60 
  61                 if nh * nw > 1:
  62                     q = rearrange(q, "b (nh h nw w) c -> (b nh nw) (h w) c", h=h // nh, w=w // nw, nh=nh, nw=nw)
  63                     self.temp = (nh, nw, h, w)
  64                 return q, k, v
  65 
  66             return q, k, v
  67         def hypertile_out(out, extra_options):
  68             if self.temp is not None:
  69                 nh, nw, h, w = self.temp
  70                 self.temp = None
  71                 out = rearrange(out, "(b nh nw) hw c -> b nh nw hw c", nh=nh, nw=nw)
  72                 out = rearrange(out, "b nh nw (h w) c -> b (nh h nw w) c", h=h // nh, w=w // nw)
  73             return out
  74 
  75 
  76         m = model.clone()
  77         m.set_model_attn1_patch(hypertile_in)
  78         m.set_model_attn1_output_patch(hypertile_out)
  79         return (m, )
  80 
  81 NODE_CLASS_MAPPINGS = {
  82     "HyperTile": HyperTile,
  83 }
  84
  [end of comfy_extras/nodes_hypertile.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/comfy_extras/nodes_hypertile.py b/comfy_extras/nodes_hypertile.py
  --- a/comfy_extras/nodes_hypertile.py
  +++ b/comfy_extras/nodes_hypertile.py
  @@ -2,9 +2,10 @@
   
   import math
   from einops import rearrange
  -import random
  +# Use torch rng for consistency across generations
  +from torch import randint
   
  -def random_divisor(value: int, min_value: int, /, max_options: int = 1, counter = 0) -> int:
  +def random_divisor(value: int, min_value: int, /, max_options: int = 1) -> int:
       min_value = min(min_value, value)
   
       # All big divisors of value (inclusive)
  @@ -12,8 +13,7 @@
   
       ns = [value // i for i in divisors[:max_options]]  # has at least 1 element
   
  -    random.seed(counter)
  -    idx = random.randint(0, len(ns) - 1)
  +    idx = randint(low=0, high=len(ns) - 1, size=(1,)).item()
   
       return ns[idx]
   
  @@ -42,7 +42,6 @@
   
           latent_tile_size = max(32, tile_size) // 8
           self.temp = None
  -        self.counter = 1
   
           def hypertile_in(q, k, v, extra_options):
               if q.shape[-1] in apply_to:
  @@ -53,10 +52,8 @@
                   h, w = round(math.sqrt(hw * aspect_ratio)), round(math.sqrt(hw / aspect_ratio))
   
                   factor = 2**((q.shape[-1] // model_channels) - 1) if scale_depth else 1
  -                nh = random_divisor(h, latent_tile_size * factor, swap_size, self.counter)
  -                self.counter += 1
  -                nw = random_divisor(w, latent_tile_size * factor, swap_size, self.counter)
  -                self.counter += 1
  +                nh = random_divisor(h, latent_tile_size * factor, swap_size)
  +                nw = random_divisor(w, latent_tile_size * factor, swap_size)
   
                   if nh * nw > 1:
                       q = rearrange(q, "b (nh h nw w) c -> (b nh nw) (h w) c", h=h // nh, w=w // nw, nh=nh, nw=nw)
  
verification_info: {"golden_diff": "diff --git a/comfy_extras/nodes_hypertile.py b/comfy_extras/nodes_hypertile.py\n--- a/comfy_extras/nodes_hypertile.py\n+++ b/comfy_extras/nodes_hypertile.py\n@@ -2,9 +2,10 @@\n \n import math\n from einops import rearrange\n-import random\n+# Use torch rng for consistency across generations\n+from torch import randint\n \n-def random_divisor(value: int, min_value: int, /, max_options: int = 1, counter = 0) -> int:\n+def random_divisor(value: int, min_value: int, /, max_options: int = 1) -> int:\n     min_value = min(min_value, value)\n \n     # All big divisors of value (inclusive)\n@@ -12,8 +13,7 @@\n \n     ns = [value // i for i in divisors[:max_options]]  # has at least 1 element\n \n-    random.seed(counter)\n-    idx = random.randint(0, len(ns) - 1)\n+    idx = randint(low=0, high=len(ns) - 1, size=(1,)).item()\n \n     return ns[idx]\n \n@@ -42,7 +42,6 @@\n \n         latent_tile_size = max(32, tile_size) // 8\n         self.temp = None\n-        self.counter = 1\n \n         def hypertile_in(q, k, v, extra_options):\n             if q.shape[-1] in apply_to:\n@@ -53,10 +52,8 @@\n                 h, w = round(math.sqrt(hw * aspect_ratio)), round(math.sqrt(hw / aspect_ratio))\n \n                 factor = 2**((q.shape[-1] // model_channels) - 1) if scale_depth else 1\n-                nh = random_divisor(h, latent_tile_size * factor, swap_size, self.counter)\n-                self.counter += 1\n-                nw = random_divisor(w, latent_tile_size * factor, swap_size, self.counter)\n-                self.counter += 1\n+                nh = random_divisor(h, latent_tile_size * factor, swap_size)\n+                nw = random_divisor(w, latent_tile_size * factor, swap_size)\n \n                 if nh * nw > 1:\n                     q = rearrange(q, \"b (nh h nw w) c -> (b nh nw) (h w) c\", h=h // nh, w=w // nw, nh=nh, nw=nw)\n", "issue": "HyperTile node is nondeterministic across executions and messes with global randomness\nThe HyperTile node uses the random module and seeds the global random with its own counter variable.\r\n\r\nUnfortunately, this counter variable is retained across executions if the HyperTile parameters don't change, and so every execution will have different results.\r\n\r\nThe effect on global random can be avoided just by using a `random.Random()` instance instead of a counter, but since ComfyUI doesn't provide any kind of after-exec function for nodes, there doesn't seem to be a way to reset it to its initial state after one prompt is executed.\r\n\r\nI suppose you could work around this by setting having IS_CHANGED return something so that the node gets always executed, thus reinitializing randomness, but that might cause any nodes that come after the HyperTile node to needlessly re-execute.\r\n\r\n\n", "before_files": [{"content": "#Taken from: https://github.com/tfernd/HyperTile/\n\nimport math\nfrom einops import rearrange\nimport random\n\ndef random_divisor(value: int, min_value: int, /, max_options: int = 1, counter = 0) -> int:\n    min_value = min(min_value, value)\n\n    # All big divisors of value (inclusive)\n    divisors = [i for i in range(min_value, value + 1) if value % i == 0]\n\n    ns = [value // i for i in divisors[:max_options]]  # has at least 1 element\n\n    random.seed(counter)\n    idx = random.randint(0, len(ns) - 1)\n\n    return ns[idx]\n\nclass HyperTile:\n    @classmethod\n    def INPUT_TYPES(s):\n        return {\"required\": { \"model\": (\"MODEL\",),\n                             \"tile_size\": (\"INT\", {\"default\": 256, \"min\": 1, \"max\": 2048}),\n                             \"swap_size\": (\"INT\", {\"default\": 2, \"min\": 1, \"max\": 128}),\n                             \"max_depth\": (\"INT\", {\"default\": 0, \"min\": 0, \"max\": 10}),\n                             \"scale_depth\": (\"BOOLEAN\", {\"default\": False}),\n                              }}\n    RETURN_TYPES = (\"MODEL\",)\n    FUNCTION = \"patch\"\n\n    CATEGORY = \"_for_testing\"\n\n    def patch(self, model, tile_size, swap_size, max_depth, scale_depth):\n        model_channels = model.model.model_config.unet_config[\"model_channels\"]\n\n        apply_to = set()\n        temp = model_channels\n        for x in range(max_depth + 1):\n            apply_to.add(temp)\n            temp *= 2\n\n        latent_tile_size = max(32, tile_size) // 8\n        self.temp = None\n        self.counter = 1\n\n        def hypertile_in(q, k, v, extra_options):\n            if q.shape[-1] in apply_to:\n                shape = extra_options[\"original_shape\"]\n                aspect_ratio = shape[-1] / shape[-2]\n\n                hw = q.size(1)\n                h, w = round(math.sqrt(hw * aspect_ratio)), round(math.sqrt(hw / aspect_ratio))\n\n                factor = 2**((q.shape[-1] // model_channels) - 1) if scale_depth else 1\n                nh = random_divisor(h, latent_tile_size * factor, swap_size, self.counter)\n                self.counter += 1\n                nw = random_divisor(w, latent_tile_size * factor, swap_size, self.counter)\n                self.counter += 1\n\n                if nh * nw > 1:\n                    q = rearrange(q, \"b (nh h nw w) c -> (b nh nw) (h w) c\", h=h // nh, w=w // nw, nh=nh, nw=nw)\n                    self.temp = (nh, nw, h, w)\n                return q, k, v\n\n            return q, k, v\n        def hypertile_out(out, extra_options):\n            if self.temp is not None:\n                nh, nw, h, w = self.temp\n                self.temp = None\n                out = rearrange(out, \"(b nh nw) hw c -> b nh nw hw c\", nh=nh, nw=nw)\n                out = rearrange(out, \"b nh nw (h w) c -> b (nh h nw w) c\", h=h // nh, w=w // nw)\n            return out\n\n\n        m = model.clone()\n        m.set_model_attn1_patch(hypertile_in)\n        m.set_model_attn1_output_patch(hypertile_out)\n        return (m, )\n\nNODE_CLASS_MAPPINGS = {\n    \"HyperTile\": HyperTile,\n}\n", "path": "comfy_extras/nodes_hypertile.py"}]}