prompt: You will be provided with a partial code base and an issue statement explaining a problem to resolve.
  <issue>
  Use stable hashing in ps client
  
  
  </issue>
  <code>
  [start of python/tflib/ps/ps_client.py]
  1 import future
  2 
  3 
  4 # No partition, all in the first ps
  5 def no_partition(v, ps_size):
  6     return [v]
  7 
  8 
  9 # Partition v according to the name hashing.
  10 def hash_partition(v, ps_size):
  11     if v is None:
  12         return [None for i in range(ps_size)]
  13     if len(v) == 0:
  14         raise ValueError('Empty input used in HashPartition.')
  15     if ps_size == 1:
  16         return [v]
  17     if isinstance(v, dict):
  18         # (name, data) dict from push
  19         results = [{} for i in range(ps_size)]
  20         for name, data in v.items():
  21             index = hash(name) % ps_size
  22             results[index][name] = data
  23     elif isinstance(v, list):
  24         # name list from pull
  25         results = [[] for i in range(ps_size)]
  26         for name in v:
  27             index = hash(name) % ps_size
  28             results[index].append(name)
  29     else:
  30         raise TypeError('Illegal v type %s, only dict or '
  31                         'str list is supported.' % str(type(v)))
  32     return results
  33 
  34 
  35 # Multi-thread implementation of PSClientComm
  36 class MultiThreadPSClientComm(object):
  37     def __init__(self, ps):
  38         self._ps = ps
  39 
  40     def push(self, base_step, sub_step, grads):
  41         if len(grads) > 0:
  42             self._ps.push(base_step, sub_step, grads)
  43 
  44     def pull(self, names=None, min_step=0):
  45         return self._ps.pull(names=names, min_step=min_step)
  46 
  47 
  48 # ParameterSererClient uses PSClientComm for ps data transfer.
  49 class ParameterServerClient(object):
  50     def __init__(self,
  51                  ps_configs=None,
  52                  comm_class=MultiThreadPSClientComm,
  53                  partition_func=no_partition):
  54         self._ps_size = 1 if partition_func == no_partition else len(ps_configs)
  55         self._partition_func = partition_func
  56         self._clients = [comm_class(ps_configs[i])
  57                          for i in range(self._ps_size)]
  58         self._base_step = [0 for _ in range(self._ps_size)]
  59 
  60     def push(self, sub_step=0, grads=None):
  61         partition_result = self._partition_func(grads, self._ps_size)
  62         # TODO: multithread optimization, one thread per ps communication.
  63         for index, g in enumerate(partition_result):
  64             self._clients[index].push(self._base_step[index],
  65                                       sub_step, g)
  66 
  67     def pull(self, min_step=0, names=None):
  68         pull_result = {}
  69         partition_result = self._partition_func(names, self._ps_size)
  70         # TODO: multithread optimization, one thread per ps communication.
  71         for index, n in enumerate(partition_result):
  72             ps_step, ps_vars = self._clients[index].pull(
  73                 names=n, min_step=min_step)
  74             self._base_step[index] = ps_step
  75             pull_result.update(ps_vars)
  76         return self.get_min_base_step(), pull_result
  77 
  78     def get_min_base_step(self):
  79         return min(self._base_step)
  80
  [end of python/tflib/ps/ps_client.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/python/tflib/ps/ps_client.py b/python/tflib/ps/ps_client.py
  --- a/python/tflib/ps/ps_client.py
  +++ b/python/tflib/ps/ps_client.py
  @@ -1,5 +1,4 @@
  -import future
  -
  +from binascii import crc32
   
   # No partition, all in the first ps
   def no_partition(v, ps_size):
  @@ -8,6 +7,10 @@
   
   # Partition v according to the name hashing.
   def hash_partition(v, ps_size):
  +    # a simple stable string hash
  +    def _hash(s):
  +        return crc32(s.encode())
  +
       if v is None:
           return [None for i in range(ps_size)]
       if len(v) == 0:
  @@ -18,13 +21,13 @@
           # (name, data) dict from push
           results = [{} for i in range(ps_size)]
           for name, data in v.items():
  -            index = hash(name) % ps_size
  +            index = _hash(name) % ps_size
               results[index][name] = data
       elif isinstance(v, list):
           # name list from pull
           results = [[] for i in range(ps_size)]
           for name in v:
  -            index = hash(name) % ps_size
  +            index = _hash(name) % ps_size
               results[index].append(name)
       else:
           raise TypeError('Illegal v type %s, only dict or '
  
verification_info: {"golden_diff": "diff --git a/python/tflib/ps/ps_client.py b/python/tflib/ps/ps_client.py\n--- a/python/tflib/ps/ps_client.py\n+++ b/python/tflib/ps/ps_client.py\n@@ -1,5 +1,4 @@\n-import future\n-\n+from binascii import crc32\n \n # No partition, all in the first ps\n def no_partition(v, ps_size):\n@@ -8,6 +7,10 @@\n \n # Partition v according to the name hashing.\n def hash_partition(v, ps_size):\n+    # a simple stable string hash\n+    def _hash(s):\n+        return crc32(s.encode())\n+\n     if v is None:\n         return [None for i in range(ps_size)]\n     if len(v) == 0:\n@@ -18,13 +21,13 @@\n         # (name, data) dict from push\n         results = [{} for i in range(ps_size)]\n         for name, data in v.items():\n-            index = hash(name) % ps_size\n+            index = _hash(name) % ps_size\n             results[index][name] = data\n     elif isinstance(v, list):\n         # name list from pull\n         results = [[] for i in range(ps_size)]\n         for name in v:\n-            index = hash(name) % ps_size\n+            index = _hash(name) % ps_size\n             results[index].append(name)\n     else:\n         raise TypeError('Illegal v type %s, only dict or '\n", "issue": "Use stable hashing in ps client\n\n", "before_files": [{"content": "import future\n\n\n# No partition, all in the first ps\ndef no_partition(v, ps_size):\n    return [v]\n\n\n# Partition v according to the name hashing.\ndef hash_partition(v, ps_size):\n    if v is None:\n        return [None for i in range(ps_size)]\n    if len(v) == 0:\n        raise ValueError('Empty input used in HashPartition.')\n    if ps_size == 1:\n        return [v]\n    if isinstance(v, dict):\n        # (name, data) dict from push\n        results = [{} for i in range(ps_size)]\n        for name, data in v.items():\n            index = hash(name) % ps_size\n            results[index][name] = data\n    elif isinstance(v, list):\n        # name list from pull\n        results = [[] for i in range(ps_size)]\n        for name in v:\n            index = hash(name) % ps_size\n            results[index].append(name)\n    else:\n        raise TypeError('Illegal v type %s, only dict or '\n                        'str list is supported.' % str(type(v)))\n    return results\n\n\n# Multi-thread implementation of PSClientComm\nclass MultiThreadPSClientComm(object):\n    def __init__(self, ps):\n        self._ps = ps\n\n    def push(self, base_step, sub_step, grads):\n        if len(grads) > 0:\n            self._ps.push(base_step, sub_step, grads)\n\n    def pull(self, names=None, min_step=0):\n        return self._ps.pull(names=names, min_step=min_step)\n\n\n# ParameterSererClient uses PSClientComm for ps data transfer.\nclass ParameterServerClient(object):\n    def __init__(self,\n                 ps_configs=None,\n                 comm_class=MultiThreadPSClientComm,\n                 partition_func=no_partition):\n        self._ps_size = 1 if partition_func == no_partition else len(ps_configs)\n        self._partition_func = partition_func\n        self._clients = [comm_class(ps_configs[i])\n                         for i in range(self._ps_size)]\n        self._base_step = [0 for _ in range(self._ps_size)]\n\n    def push(self, sub_step=0, grads=None):\n        partition_result = self._partition_func(grads, self._ps_size)\n        # TODO: multithread optimization, one thread per ps communication.\n        for index, g in enumerate(partition_result):\n            self._clients[index].push(self._base_step[index],\n                                      sub_step, g)\n\n    def pull(self, min_step=0, names=None):\n        pull_result = {}\n        partition_result = self._partition_func(names, self._ps_size)\n        # TODO: multithread optimization, one thread per ps communication.\n        for index, n in enumerate(partition_result):\n            ps_step, ps_vars = self._clients[index].pull(\n                names=n, min_step=min_step)\n            self._base_step[index] = ps_step\n            pull_result.update(ps_vars)\n        return self.get_min_base_step(), pull_result\n\n    def get_min_base_step(self):\n        return min(self._base_step)\n", "path": "python/tflib/ps/ps_client.py"}]}