We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN 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.



--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `comfy_extras/nodes_hypertile.py`
Content:
```
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 FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

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)

{"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"}], "after_files": [{"content": "#Taken from: https://github.com/tfernd/HyperTile/\n\nimport math\nfrom einops import rearrange\n# Use torch rng for consistency across generations\nfrom torch import randint\n\ndef 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    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    idx = randint(low=0, high=len(ns) - 1, size=(1,)).item()\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\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)\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                    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"}]}