diff --git a/doc/array.rst b/doc/array.rst
index e572c900fbbc01ff95fcef8bbc4aa59a2399ba1b..9078f6f166e9cf2f92067fa9a653132f0d6c982a 100644
--- a/doc/array.rst
+++ b/doc/array.rst
@@ -144,6 +144,12 @@ Constructing :class:`Array` Instances
 .. autofunction:: take
 .. autofunction:: concatenate
 
+Manipulating :class:`Array` instances
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. autofunction:: transpose
+.. autofunction:: reshape
+
 Conditionals
 ^^^^^^^^^^^^
 
diff --git a/pyopencl/array.py b/pyopencl/array.py
index 3db01624cba69cadda6544fcc1c57f70c2fb5e43..9fa3c84f1d2d5f200aca1007a8409f08d0365c5f 100644
--- a/pyopencl/array.py
+++ b/pyopencl/array.py
@@ -338,6 +338,10 @@ class Array(object):
 
         The tuple of lengths of each dimension in the array.
 
+    .. attribute :: ndim
+
+        The number of dimensions in :attr:`shape`.
+
     .. attribute :: dtype
 
         The :class:`numpy.dtype` of the items in the GPU array.
@@ -370,6 +374,8 @@ class Array(object):
     .. automethod :: reshape
     .. automethod :: ravel
     .. automethod :: view
+    .. automethod :: transpose
+    .. attribute :: T
     .. automethod :: set
     .. automethod :: get
     .. automethod :: copy
@@ -559,6 +565,10 @@ class Array(object):
 
         self.offset = offset
 
+    @property
+    def ndim(self):
+        return len(self.shape)
+
     @property
     def context(self):
         return self.base_data.context
@@ -1280,6 +1290,9 @@ class Array(object):
             raise ValueError("order must be either 'C' or 'F'")
 
         # TODO: add more error-checking, perhaps
+        if not self.flags.forc:
+            raise RuntimeError("only contiguous arrays may "
+                    "be used as arguments to this operation")
 
         if isinstance(shape[0], tuple) or isinstance(shape[0], list):
             shape = tuple(shape[0])
@@ -1426,6 +1439,39 @@ class Array(object):
                 shape=new_shape, dtype=dtype,
                 strides=new_strides)
 
+    def transpose(self, axes=None):
+        """Permute the dimensions of an array.
+
+        :arg axes: list of ints, optional.
+            By default, reverse the dimensions, otherwise permute the axes
+            according to the values given.
+
+        :returns: :class:`Array` A view of the array with its axes permuted.
+
+        .. versionadded:: 2015.2
+        """
+
+        if axes is None:
+            axes = range(self.ndim-1, -1, -1)
+
+        if len(axes) != len(self.shape):
+            raise ValueError("axes don't match array")
+
+        new_shape = [self.shape[axes[i]] for i in xrange(len(axes))]
+        new_strides = [self.strides[axes[i]] for i in xrange(len(axes))]
+
+        return self._new_with_changes(
+                self.base_data, self.offset,
+                shape=tuple(new_shape),
+                strides=tuple(new_strides))
+
+    @property
+    def T(self):  # noqa
+        """
+        .. versionadded:: 2015.2
+        """
+        return self.transpose()
+
     # }}}
 
     def map_to_host(self, queue=None, flags=None, is_blocking=True, wait_for=None):
@@ -1538,6 +1584,11 @@ class Array(object):
                             "more than one ellipsis not allowed in index")
                 seen_ellipsis = True
 
+            elif index_entry is np.newaxis:
+                new_shape.append(1)
+                new_strides.append(0)
+                index_axis += 1
+
             else:
                 raise IndexError("invalid subindex in axis %d" % index_axis)
 
@@ -2136,6 +2187,32 @@ def hstack(arrays, queue=None):
 # }}}
 
 
+# {{{ shape manipulation
+
+def transpose(a, axes=None):
+    """Permute the dimensions of an array.
+
+    :arg a: :class:`Array`
+    :arg axes: list of ints, optional.
+        By default, reverse the dimensions, otherwise permute the axes
+        according to the values given.
+
+    :returns: :class:`Array` A view of the array with its axes permuted.
+    """
+    return a.transpose(axes)
+
+
+def reshape(a, shape):
+    """Gives a new shape to an array without changing its data.
+
+    .. versionadded:: 2015.2
+    """
+
+    return a.reshape(shape)
+
+# }}}
+
+
 # {{{ conditionals
 
 @elwise_kernel_runner
diff --git a/test/test_array.py b/test/test_array.py
index 485a46a62d03976711d6c4170efbc4f4022b63ef..4c545bfeaa3d572c443798f4eb21cac699a43e6b 100644
--- a/test/test_array.py
+++ b/test/test_array.py
@@ -830,6 +830,36 @@ def test_skip_slicing(ctx_factory):
     assert np.array_equal(b[1].get(), b_host[1])
 
 
+def test_transpose(ctx_factory):
+    context = ctx_factory()
+    queue = cl.CommandQueue(context)
+
+    from pyopencl.clrandom import rand as clrand
+
+    a_gpu = clrand(queue, (10, 20, 30), dtype=np.float32)
+    a = a_gpu.get()
+
+    # FIXME: not contiguous
+    #assert np.allclose(a_gpu.transpose((1,2,0)).get(), a.transpose((1,2,0)))
+    assert np.array_equal(a_gpu.T.get(), a.T)
+
+
+def test_newaxis(ctx_factory):
+    context = ctx_factory()
+    queue = cl.CommandQueue(context)
+
+    from pyopencl.clrandom import rand as clrand
+
+    a_gpu = clrand(queue, (10, 20, 30), dtype=np.float32)
+    a = a_gpu.get()
+
+    b_gpu = a_gpu[:, np.newaxis]
+    b = a[:, np.newaxis]
+
+    assert b_gpu.shape == b.shape
+    assert b_gpu.strides == b.strides
+
+
 if __name__ == "__main__":
     # make sure that import failures get reported, instead of skipping the
     # tests.