Repository: scikit-hep/pyhf

Issue:
tensorflow tile inconsistent with other tensorlibs
Demonstrated rather simply below:

```python
>>> import pyhf
>>> pyhf.tensorlib.tile(pyhf.tensorlib.astensor([1.]), (2, 1))
array([[1.],
       [1.]])

>>> pyhf.set_backend('jax')
>>> pyhf.tensorlib.tile(pyhf.tensorlib.astensor([1.]), (2, 1))

>>> pyhf.set_backend('pytorch')
>>> pyhf.tensorlib.tile(pyhf.tensorlib.astensor([1.]), (2, 1))
tensor([[1.],
        [1.]])

>>> pyhf.set_backend('tensorflow')
>>> pyhf.tensorlib.tile(pyhf.tensorlib.astensor([1.]), (2, 1))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/kratsg/.virtualenvs/pyhf/lib/python3.7/site-packages/pyhf/tensor/tensorflow_backend.py", line 78, in tile
    return tf.tile(tensor_in, repeats)
  File "/Users/kratsg/.virtualenvs/pyhf/lib/python3.7/site-packages/tensorflow/python/ops/gen_array_ops.py", line 11393, in tile
    input, multiples, name=name, ctx=_ctx)
  File "/Users/kratsg/.virtualenvs/pyhf/lib/python3.7/site-packages/tensorflow/python/ops/gen_array_ops.py", line 11433, in tile_eager_fallback
    ctx=ctx, name=name)
  File "/Users/kratsg/.virtualenvs/pyhf/lib/python3.7/site-packages/tensorflow/python/eager/execute.py", line 60, in quick_execute
    inputs, attrs, num_outputs)
tensorflow.python.framework.errors_impl.InvalidArgumentError: Expected multiples argument to be a vector of length 1 but got length 2 [Op:Tile]
```


Discussion:
Should we get this addressed _before_ PR #731 goes in? I'm a bit hesitant to specifically add test cases that treat backends different (one of the reasons PR #817 ~is~ was taking _forever_ to be ready).

Resolving patch:
diff --git a/src/pyhf/tensor/tensorflow_backend.py b/src/pyhf/tensor/tensorflow_backend.py
--- a/src/pyhf/tensor/tensorflow_backend.py
+++ b/src/pyhf/tensor/tensorflow_backend.py
@@ -75,7 +75,14 @@ def tile(self, tensor_in, repeats):
             TensorFlow Tensor: The tensor with repeated axes
 
         """
-        return tf.tile(tensor_in, repeats)
+        try:
+            return tf.tile(tensor_in, repeats)
+        except tf.python.framework.errors_impl.InvalidArgumentError:
+            shape = tf.shape(tensor_in).numpy().tolist()
+            diff = len(repeats) - len(shape)
+            if diff < 0:
+                raise
+            return tf.tile(tf.reshape(tensor_in, [1] * diff + shape), repeats)
 
     def conditional(self, predicate, true_callable, false_callable):
         """


Test patch:
diff --git a/tests/test_tensor.py b/tests/test_tensor.py
--- a/tests/test_tensor.py
+++ b/tests/test_tensor.py
@@ -234,9 +234,19 @@ def test_tensor_tile(backend):
     assert tb.tolist(tb.tile(tb.astensor(a), (1, 2))) == [[1, 1], [2, 2], [3, 3]]
 
     a = [1, 2, 3]
-    tb = pyhf.tensorlib
     assert tb.tolist(tb.tile(tb.astensor(a), (2,))) == [1, 2, 3, 1, 2, 3]
 
+    a = [10, 20]
+    assert tb.tolist(tb.tile(tb.astensor(a), (2, 1))) == [[10, 20], [10, 20]]
+    assert tb.tolist(tb.tile(tb.astensor(a), (2, 1, 3))) == [
+        [[10.0, 20.0, 10.0, 20.0, 10.0, 20.0]],
+        [[10.0, 20.0, 10.0, 20.0, 10.0, 20.0]],
+    ]
+
+    if tb.name == 'tensorflow':
+        with pytest.raises(tf.python.framework.errors_impl.InvalidArgumentError):
+            tb.tile(tb.astensor([[[10, 20, 30]]]), (2, 1))
+
 
 def test_1D_gather(backend):
     tb = pyhf.tensorlib