prompt: 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()
  
  ```
  
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"}], "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"}]}