internlm/SWE-Fixer-Train-110K huggingface dataset:
Repository: facebookresearch/xformers

Issue:
fourier_mix attention does not support mixed precision training
Fourier mix attention does not support half precision.

```Traceback (most recent call last):
 ...
  File "/usr/local/lib/python3.7/dist-packages/torch/nn/modules/module.py", line 1102, in _call_impl
    return forward_call(*input, **kwargs)
  File "/content/muzik/src/modules/xformer/custom_block_factory.py", line 196, in forward
    x = self.mha(q, k, v, att_mask=att_mask)
  File "/usr/local/lib/python3.7/dist-packages/torch/nn/modules/module.py", line 1102, in _call_impl
    return forward_call(*input, **kwargs)
  File "/content/muzik/src/modules/xformer/custom_multi_head_dispatch.py", line 254, in forward
    y = self.attention(q=q, k=k, v=v, att_mask=att_mask)
  File "/usr/local/lib/python3.7/dist-packages/torch/nn/modules/module.py", line 1102, in _call_impl
    return forward_call(*input, **kwargs)
  File "/usr/local/lib/python3.7/dist-packages/xformers/components/attention/fourier_mix.py", line 25, in forward
    att = torch.fft.fft2(q).real
RuntimeError: Unsupported dtype Half```


Discussion:
thanks for the report ! Looks like it's pretty low level, in the fft op, I'll have a look

Resolving patch:
diff --git a/xformers/components/attention/fourier_mix.py b/xformers/components/attention/fourier_mix.py
--- a/xformers/components/attention/fourier_mix.py
+++ b/xformers/components/attention/fourier_mix.py
@@ -4,6 +4,7 @@
 # LICENSE file in the root directory of this source tree.
 
 import torch
+from torch.cuda.amp import autocast
 
 from xformers.components.attention import Attention, AttentionConfig, register_attention
 
@@ -22,7 +23,9 @@ def __init__(self, dropout: float, *_, **__):
         self.requires_input_projection = False
 
     def forward(self, q: torch.Tensor, *_, **__):
-        att = torch.fft.fft2(q).real
+        # Guard against autocast / fp16, not supported by torch.fft.fft2
+        with autocast(enabled=False):
+            att = torch.fft.fft2(q).real
 
         att = self.attn_drop(att)
 


Test patch:
diff --git a/tests/test_attentions.py b/tests/test_attentions.py
--- a/tests/test_attentions.py
+++ b/tests/test_attentions.py
@@ -124,6 +124,11 @@ def test_order_invariance(
             att_2 = multi_head(inputs, inputs_shuffled, inputs)
             assert (att != att_2).any()
 
+        # Test AMP, if available
+        if device.type == "cuda":
+            with torch.cuda.amp.autocast(enabled=True):
+                _ = multi_head(inputs, inputs_shuffled, inputs)
+
 
 @pytest.mark.parametrize("heads", [1, 4])
 @pytest.mark.parametrize("attention_name", ["scaled_dot_product"])
Repository: facebookresearch/xformers

Issue:
Update the fusedlinear perf curves
# 🐛 Bug
See https://github.com/facebookresearch/xformers/pull/283 and a discussion with @ptillet, I could not repro these curves even on my machine, must be some combo of a specific dev triton package and cuda version.



Discussion:

Resolving patch:
diff --git a/xformers/triton/k_fused_matmul_bw.py b/xformers/triton/k_fused_matmul_bw.py
--- a/xformers/triton/k_fused_matmul_bw.py
+++ b/xformers/triton/k_fused_matmul_bw.py
@@ -135,8 +135,8 @@ def fused_matmul_backward(
         grad_out_ = grad_act
 
     # The following ops can also be handled by triton
-    grad_in = grad_out_ @ weight
-    grad_weight = grad_out_.transpose(1, 0) @ inputs_ if trainable_weight else None
+    grad_in = triton.ops.matmul(grad_out_, weight)
+    grad_weight = triton.ops.matmul(grad_out_.transpose(1, 0), inputs_) if trainable_weight else None
     grad_bias = sum_2d_dim_0(grad_out_) if trainable_bias else None
 
     return grad_in.reshape_as(inputs), grad_weight, grad_bias
diff --git a/xformers/triton/k_fused_matmul_fw.py b/xformers/triton/k_fused_matmul_fw.py
--- a/xformers/triton/k_fused_matmul_fw.py
+++ b/xformers/triton/k_fused_matmul_fw.py
@@ -164,6 +164,7 @@ def fused_matmul(
 
     # 1D launch kernel where each block gets its own program.
     grid = lambda META: (triton.cdiv(M, META["BLOCK_M"]) * triton.cdiv(N, META["BLOCK_N"]),) # noqa
+    BLOCK_K = 32 if K < 1024 else 64
 
     # fmt: off
     kernel_fma[grid](
@@ -175,7 +176,7 @@ def fused_matmul(
         ACTIVATION=activation,                      # optional fused activation
         BIAS=bias is not None,                      # optional fused bias
         GROUP_M=8,                                  # speed optimization: group the programs
-        BLOCK_K=32,
+        BLOCK_K=BLOCK_K,
         SAVE_ACT_INPUTS=save_act_inputs
     )
     # fmt: on


Test patch:
Repository: facebookresearch/xformers

Issue:
Cannot pickle nystrom based model
When I `torch.save` my model with `nystrom` attention I get the following pickle error:

```py
AttributeError: Can't pickle local object 'get_avg_pool.<locals>.avg_pool'
```

I believe coming from this function:

https://github.com/facebookresearch/xformers/blob/9232b2d27a775a43173e2fd86c03251ab64f7ede/xformers/components/attention/nystrom.py#L60


Discussion:

Resolving patch:
diff --git a/xformers/components/attention/nystrom.py b/xformers/components/attention/nystrom.py
--- a/xformers/components/attention/nystrom.py
+++ b/xformers/components/attention/nystrom.py
@@ -57,24 +57,29 @@ class NystromSelfAttentionConfig(AttentionConfig):
     use_razavi_pinverse: Optional[bool]
 
 
-def get_avg_pool(n: int):
-    def avg_pool(x: torch.Tensor):
+class AvgPool(nn.Module):
+    def __init__(self, n: int):
+        super().__init__()
+        self.n = n
+
+    def forward(self, x: torch.Tensor):
         # Average independently for every segment in the sequence dimension
         seq_len = x.shape[1]
         head_dim = x.shape[2]
-        segments = seq_len // n
+        segments = seq_len // self.n
+        assert segments > 0, "num_landmarks should be smaller than the sequence length"
 
         # Dimensions are a match
-        if seq_len % n == 0:
+        if seq_len % self.n == 0:
             return x.reshape(
                 -1,
-                n,
+                self.n,
                 segments,
                 head_dim,
             ).mean(dim=-2)
 
         # Handle the last segment boundary being off
-        n_round = n - seq_len % n
+        n_round = self.n - seq_len % self.n
 
         x_avg_round = (
             x[:, : n_round * segments, :]
@@ -83,13 +88,11 @@ def avg_pool(x: torch.Tensor):
         )
         x_avg_off = (
             x[:, n_round * segments :, :]
-            .reshape(-1, n - n_round, segments + 1, head_dim)
+            .reshape(-1, self.n - n_round, segments + 1, head_dim)
             .mean(dim=-2)
         )
         return torch.cat((x_avg_round, x_avg_off), dim=-2)
 
-    return avg_pool
-
 
 @register_attention("nystrom", NystromSelfAttentionConfig)
 class NystromAttention(Attention):
@@ -147,7 +150,7 @@ def __init__(
         if landmark_pooling is not None:
             self.landmark_pooling = landmark_pooling
         else:
-            self.landmark_pooling = get_avg_pool(self.num_landmarks)
+            self.landmark_pooling = AvgPool(n=self.num_landmarks)
 
         # Optional lower triangular masks for causal attention
         self.causal_mask_1: Optional[torch.Tensor] = None


Test patch:
Repository: facebookresearch/xformers

Issue:
Rotary embeddings convert queries and keys to float32 when using mixed precision training
Not sure, if this is expected behaviour. The problem is that the cos_sin table in the RotaryEmbedding class is stored in float32 format, thus the returned queries and keys get converted to float32 aswell.


Discussion:
ohh, this is a great catch ! we can fix that, no biggie. Interesting because I'm using these all the time and never remarked that
it's also something which would fail in bf16 now that I think about it, worth a fix

Resolving patch:
diff --git a/xformers/components/positional_embedding/rotary.py b/xformers/components/positional_embedding/rotary.py
--- a/xformers/components/positional_embedding/rotary.py
+++ b/xformers/components/positional_embedding/rotary.py
@@ -61,16 +61,20 @@ def _update_cos_sin_tables(self, x, seq_dimension=1):
 
         # Reset the tables if the sequence length has changed,
         # or if we're on a new device (possibly due to tracing for instance)
-        if seq_len != self._seq_len_cached or self._cos_cached.device != x.device:
+        if (
+            seq_len != self._seq_len_cached
+            or self._cos_cached.device != x.device
+            or self._cos_cached.dtype != x.dtype
+        ):
             self._seq_len_cached = seq_len
-            t = torch.arange(x.shape[seq_dimension], device=x.device).type_as(
-                self.inv_freq
+            t = torch.arange(
+                x.shape[seq_dimension], device=x.device, dtype=torch.float32
             )
-            freqs = torch.einsum("i,j->ij", t, self.inv_freq)
+            freqs = torch.einsum("i,j->ij", t, self.inv_freq.to(x.dtype))
             emb = torch.cat((freqs, freqs), dim=-1).to(x.device)
 
-            self._cos_cached = emb.cos()[None, None, :, :]
-            self._sin_cached = emb.sin()[None, None, :, :]
+            self._cos_cached = emb.cos()[None, None, :, :].to(x.dtype)
+            self._sin_cached = emb.sin()[None, None, :, :].to(x.dtype)
 
         return self._cos_cached, self._sin_cached
 


Test patch:
diff --git a/tests/test_rotary_embeddings.py b/tests/test_rotary_embeddings.py
--- a/tests/test_rotary_embeddings.py
+++ b/tests/test_rotary_embeddings.py
@@ -52,16 +52,25 @@ def test_helper_methods():
 
 
 @pytest.mark.parametrize("device", DEVICES)
-def test_rotary_embeddings(device):
+@pytest.mark.parametrize("dtype", [torch.float16, torch.float32])
+def test_rotary_embeddings(device, dtype):
     rotary = RotaryEmbedding(EMB).to(device)
 
     # Generate dummy inputs
-    q = torch.ones((BATCH, HEADS, SEQ, EMB), device=device)  # uniform on purpose
+    q = torch.ones(
+        (BATCH, HEADS, SEQ, EMB), device=device, dtype=dtype
+    )  # uniform on purpose
     k = q.clone()
 
     q_rot, k_rot = rotary(q, k)
 
+    assert q_rot.dtype == q.dtype
+    assert k_rot.dtype == k.dtype
+
     # Check that the sequences now encode relative position information
+    q, k = q.float(), k.float()
+    q_rot, k_rot = q_rot.float(), k_rot.float()
+
     att = torch.einsum("bhne,bhme->bhnm", q, k)
     att_rot = torch.einsum("bhne,bhme->bhnm", q_rot, k_rot)
Repository: facebookresearch/xformers

Issue:
[factory] First and last layers are not properly tagged -> superfluous layernorms
# 🐛 Bug
Related to tests with https://github.com/facebookresearch/xformers/issues/330 when generating more complex models from configs, in some cases the layers get mislabelled (several "last layer") meaning that we get a lot of extra layernorms



Discussion:

Resolving patch:
diff --git a/xformers/factory/block_factory.py b/xformers/factory/block_factory.py
--- a/xformers/factory/block_factory.py
+++ b/xformers/factory/block_factory.py
@@ -175,10 +175,7 @@ def __init__(self, config: xFormerEncoderConfig, **kwargs):
         # Optional patch embedding
         self.patch_emb: Optional[nn.Module] = None
 
-        if (
-            config.patch_embedding_config is not None
-            and config.layer_position.is_first()
-        ):
+        if config.patch_embedding_config is not None:
             self.patch_emb = build_patch_embedding(
                 PatchEmbeddingConfig(**config.patch_embedding_config)
             )
diff --git a/xformers/factory/model_factory.py b/xformers/factory/model_factory.py
--- a/xformers/factory/model_factory.py
+++ b/xformers/factory/model_factory.py
@@ -149,10 +149,12 @@ def __init__(
             for i in range(config.num_layers):
                 # Label where this layer is in the stack
                 # (for instance useful for the positional encoding, or late layer norm)
-                if i > 0:
+                if len(recipient) > 0:
                     config.layer_position.mark_not_first()
-                if i < config.num_layers - 1:
+
+                if config != stack_configs[-1] or i < config.num_layers - 1:
                     config.layer_position.mark_not_last()
+
                 block = builder(config)  # type: ignore
 
                 # If reversible: extract the reversible sub-parts, else append the block as-is


Test patch:
Repository: facebookresearch/xformers

Issue:
Make optional Triton requirement more robust
# 🐛 Bug

Triton is currently an optional dependency, see
https://github.com/facebookresearch/xformers/blob/51dd1192620271598ba71bb1351c1c5508720b31/xformers/__init__.py#L75-L82

But if the installed version doesn't respect certain constraints, users can have issues at import time, such as https://github.com/huggingface/diffusers/pull/532#issuecomment-1249527248

We should probably make the triton requirement a bit more robust


Discussion:

Resolving patch:
diff --git a/xformers/__init__.py b/xformers/__init__.py
--- a/xformers/__init__.py
+++ b/xformers/__init__.py
@@ -75,9 +75,9 @@ def _register_extensions():
 if _is_triton_available:
     try:
         from xformers.triton.softmax import softmax as triton_softmax  # noqa
-    except ImportError as e:
+    except (ImportError, AttributeError) as e:
         logging.warning(
-            f"Triton is not available, some optimizations will not be enabled.\nError {e}"
+            f"A matching Triton is not available, some optimizations will not be enabled.\nError caught was: {e}"
         )
         _is_triton_available = False
 
@@ -87,6 +87,6 @@ def _register_extensions():
         from xformers.components.nvfuser import NVFusedBiasActivationDropout  # noqa
     except ImportError as e:
         logging.warning(
-            f"Functorch is not available, some optimizations will not be enabled.\nError {e}"
+            f"Functorch is not available, some optimizations will not be enabled.\nError caught was: {e}"
         )
         _is_functorch_available = False


Test patch:
Repository: facebookresearch/xformers

Issue:
Is xformers still not support cuda 12.0?
# ❓ Questions and Help
I got the following error while installing...


Looking in indexes: https://pypi.org/simple, https://pypi.ngc.nvidia.com/
Obtaining file:///F:/Stable_Diffusion/stable-diffusion-webui-master/repositories/xformers
Preparing metadata (setup.py) ... error
error: subprocess-exited-with-error

× python setup.py egg_info did not run successfully.
│ exit code: 1
╰─> [9 lines of output]
No CUDA runtime is found, using CUDA_HOME='C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.0'
Traceback (most recent call last):
File "", line 2, in
File "", line 34, in
File "F:\Stable_Diffusion\stable-diffusion-webui-master\repositories\xformers\setup.py", line 293, in
symlink_package(
File "F:\Stable_Diffusion\stable-diffusion-webui-master\repositories\xformers\setup.py", line 83, in symlink_package
os.symlink(src=path_from, dst=path_to)
OSError: [WinError 1314] A required privilege is not held by the client: 'F:\Stable_Diffusion\stable-diffusion-webui-master\repositories\xformers\third_party\flash-attention\flash_attn' -> 'F:\Stable_Diffusion\stable-diffusion-webui-master\repositories\xformers\xformers\_flash_attn'
[end of output]

note: This error originates from a subprocess, and is likely not a problem with pip.
error: metadata-generation-failed

× Encountered error while generating package metadata.
╰─> See above for output.

Is it for cuda 12. Should I degrade the cuda version?

or what is the problem, can anyone help?



Discussion:

Resolving patch:
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -76,11 +76,21 @@ def symlink_package(name: str, path: Path) -> None:
     cwd = Path(__file__).parent
     path_from = cwd / path
     path_to = os.path.join(cwd, *name.split("."))
+
+    # OSError: [WinError 1314] A required privilege is not held by the client
+    # Windows requires special permission to symlink. Fallback to copy
+    use_symlink = os.name != "nt"
     try:
-        os.remove(path_to)
+        if use_symlink:
+            os.remove(path_to)
+        else:
+            shutil.rmtree(path_to)
     except FileNotFoundError:
         pass
-    os.symlink(src=path_from, dst=path_to)
+    if use_symlink:
+        os.symlink(src=path_from, dst=path_to)
+    else:
+        shutil.copytree(src=path_from, dst=path_to)
 
 
 def get_cuda_version(cuda_dir) -> int:


Test patch:
Repository: facebookresearch/xformers

Issue:
Fused Matmul does not support ndims > 3
Added this issue for PR creation.


Discussion:

Resolving patch:
diff --git a/xformers/triton/k_fused_matmul_bw.py b/xformers/triton/k_fused_matmul_bw.py
--- a/xformers/triton/k_fused_matmul_bw.py
+++ b/xformers/triton/k_fused_matmul_bw.py
@@ -121,8 +121,8 @@ def fused_matmul_backward(
     if not grad_out.is_contiguous():
         grad_out = grad_out.contiguous()
 
-    grad_out_ = grad_out if grad_out.ndim == 2 else grad_out.flatten(0, 1)
-    inputs_ = inputs if inputs.ndim == 2 else inputs.flatten(0, 1)
+    grad_out_ = grad_out if grad_out.ndim == 2 else grad_out.flatten(0, -2)
+    inputs_ = inputs if inputs.ndim == 2 else inputs.flatten(0, -2)
 
     assert grad_out_.shape[1] == weight.shape[0], "Incompatible dimensions in between grad_out and weight"
 
diff --git a/xformers/triton/k_fused_matmul_fw.py b/xformers/triton/k_fused_matmul_fw.py
--- a/xformers/triton/k_fused_matmul_fw.py
+++ b/xformers/triton/k_fused_matmul_fw.py
@@ -213,7 +213,7 @@ def fused_matmul(
     if not x.is_contiguous():
         x = x.contiguous()
 
-    x_ = x if x.ndim == 2 else x.flatten(0, 1)
+    x_ = x if x.ndim == 2 else x.flatten(0, -2)
 
     assert (
         x_.shape[1] == weight.shape[1]
@@ -248,6 +248,6 @@ def fused_matmul(
     )
     # fmt: on
 
-    outputs = outputs if x.ndim == 2 else outputs.reshape(x.shape[0], -1, N)
+    outputs = outputs if x.ndim == 2 else outputs.reshape(*x.shape[:-1], N)
 
     return outputs, act_inputs if save_act_inputs else None


Test patch:
Repository: Theano/Theano

Issue:
Get rid of warning when using Scipy 0.10.x
WARNING: scipy version = 0.10.0b2. We request version >=0.7.0 for the sparse code as it has bugs fixed in the sparse matrix code.



Discussion:

Resolving patch:
diff --git a/theano/sparse/__init__.py b/theano/sparse/__init__.py
--- a/theano/sparse/__init__.py
+++ b/theano/sparse/__init__.py
@@ -1,7 +1,10 @@
+from pkg_resources import parse_version as V
 import sys
+
 try:
     import scipy
-    enable_sparse = scipy.__version__ >= '0.7'
+    enable_sparse = V(scipy.__version__) >= V('0.7')
+
     if not enable_sparse:
         sys.stderr.write("WARNING: scipy version = %s."
                 " We request version >=0.7.0 for the sparse code as it has"


Test patch:
diff --git a/theano/sandbox/linalg/tests/test_linalg.py b/theano/sandbox/linalg/tests/test_linalg.py
--- a/theano/sandbox/linalg/tests/test_linalg.py
+++ b/theano/sandbox/linalg/tests/test_linalg.py
@@ -1,3 +1,5 @@
+from pkg_resources import parse_version as V
+
 import numpy
 
 import theano
@@ -6,7 +8,7 @@
 
 try:
     import scipy
-    if scipy.__version__ < '0.7':
+    if V(scipy.__version__) < V('0.7'):
         raise ImportError()
     use_scipy = True
 except ImportError:
Repository: Theano/Theano

Issue:
C Code for Shape Op Broken
The c_code method for theano.tensor.basic.Shape (from #1409) does not quite work: https://gist.github.com/HapeMask/5807260 (this was generated using py3.3 but the same output comes from `nosetests2 ...`

It looks like this was copied from the Shape_i op, but not changed? If I fix the NameError by writing `return super(theano.tensor.opt.Shape_i(...)...` and replace `name` with `nodename` then the generated C code is incorrect.



Discussion:

Resolving patch:
diff --git a/theano/sandbox/cuda/rng_curand.py b/theano/sandbox/cuda/rng_curand.py
--- a/theano/sandbox/cuda/rng_curand.py
+++ b/theano/sandbox/cuda/rng_curand.py
@@ -107,16 +107,23 @@ def c_libraries(self):
 
     def c_support_code(self):
         return """
+        #if PY_MAJOR_VERSION >= 3
+        void free_generator(PyObject *_gen)
+        {
+            curandGenerator_t * gen = (curandGenerator_t*)NpyCapsule_AsVoidPtr(_gen);
+        #else
         void free_generator(void *_gen)
         {
             curandGenerator_t * gen = (curandGenerator_t*)_gen;
+        #endif
+
             curandStatus_t err = curandDestroyGenerator(*gen);
             if (err != CURAND_STATUS_SUCCESS)
             {
-                fprintf(stderr, "Failure (%%i) in destroying CURAND generator",
+                fprintf(stderr, "Failure (%i) in destroying CURAND generator.\\n",
                     (int)err);
             }
-            free(_gen);
+            free(gen);
         }
         """
 
@@ -136,10 +143,6 @@ def c_code(self, node, nodename, inp, out, sub):
 
         code = """
         //////// <code generated by CURAND_Base>
-        #if PY_MAJOR_VERSION >= 3
-        #include "numpy/npy_3kcompat.h"
-        #endif
-
         int odims[%(ndim)s];
         int n_elements = 1;
         int must_alloc_sample = ((NULL == %(o_sample)s)
diff --git a/theano/tensor/basic.py b/theano/tensor/basic.py
--- a/theano/tensor/basic.py
+++ b/theano/tensor/basic.py
@@ -2479,7 +2479,7 @@ def c_code(self, node, nodename, inp, out, sub):
             #TODO: if your type is not listed here, make a damn registry of
             #      shape_i ops for various types of variables.
             #      Do not continue this madness.
-            return super(Shape_i, self).c_code(node, name, (x,), (out,), sub)
+            return super(Shape, self).c_code(node, nodename, (x,), (out,), sub)
 
     def c_code_cache_version(self):
         return (1,)


Test patch:
Repository: Theano/Theano

Issue:
theano.test() fails in gof.cc.hash_from_code() with TypeError
With python 2.7.2, numpy 1.5.1, and theano commit 3810c977, I get a long list of errors when I run theano.test(). The errors all stem from a single line in gof.cc.hash_from_code():

```
    def hash_from_code(msg):
        return hashlib.md5(msg).hexdigest()
```

The exceptions are all TypeError with the message: "must be convertible to a buffer, not numpy.ndarray".

If I change the code like this:

```
    def hash_from_code(msg):
        try:
            return hashlib.md5(msg).hexdigest()
        except TypeError:
            return hashlib.md5(numpy.getbuffer(msg)).hexdigest()
```

then the errors go away, but I don't know theano well enough to say whether this is a valid solution.



Discussion:
numpy 1.5.1 is very old. I suppose the problem is related to that. Your fix
seam good, but I would add an assert isinstance(msg, numpy.ndarray). Can
you make a PR out of this?

On Tue, May 27, 2014 at 6:51 AM, micklat notifications@github.com wrote:

> With python 2.7.2, numpy 1.5.1, and theano commit 3810c97https://github.com/Theano/Theano/commit/3810c977,
> I get a long list of errors when I run theano.test(). The errors all stem
> from a single line in gof.cc.hash_from_code():
> 
> ```
> def hash_from_code(msg):
>     return hashlib.md5(msg).hexdigest()
> ```
> 
> The exceptions are all TypeError with the message: "must be convertible to
> a buffer, not numpy.ndarray".
> 
> If I change the code like this:
> 
> ```
> def hash_from_code(msg):
>     try:
>         return hashlib.md5(msg).hexdigest()
>     except TypeError:
>         return hashlib.md5(numpy.getbuffer(msg)).hexdigest()
> ```
> 
> then the errors go away, but I don't know theano well enough to say
> whether this is a valid solution.
> 
> —
> Reply to this email directly or view it on GitHubhttps://github.com/Theano/Theano/issues/1879
> .


Resolving patch:
diff --git a/theano/gof/cc.py b/theano/gof/cc.py
--- a/theano/gof/cc.py
+++ b/theano/gof/cc.py
@@ -29,7 +29,11 @@ def hash_from_code(msg):
     import hashlib
 
     def hash_from_code(msg):
-        return hashlib.md5(msg).hexdigest()
+        try:
+            return hashlib.md5(msg).hexdigest()
+        except TypeError:
+            assert isinstance(msg, numpy.ndarray)
+            return hashlib.md5(numpy.getbuffer(msg)).hexdigest()
 else:
     import md5
 


Test patch: