We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Make the number of axes for batch dimensions configurable in Linear
L.Linear accepts multiple channel dimensions. It is useful in computer vision applications where the output of the final convolution layer can be directly fed to a linear link. This feature can be seen as a flexible number of channel axes.

On the other hand, the number of batch axes is currently fixed to one, which would be cumbersome in some use cases, especially when we have to deal with a sequence of input arrays. In this case, the first two axes may be used to represent the mini-batch and the sequence. We currently need to reshape it before and after feeding it to the linear link, which has overheads both for coding and runtime.

It can be resolved by introducing an option to Linear (and also to the F.linear function) that indicates the number of batch axes.

The same discussions would also be applied to convolution links/functions.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `chainer/functions/connection/linear.py`
Content:
```
1 import numpy
2 
3 from chainer.backends import intel64
4 from chainer import function_node
5 import chainer.functions
6 from chainer.utils import type_check
7 
8 
9 class LinearFunction(function_node.FunctionNode):
10 
11     _config_use_ideep = None
12 
13     def check_type_forward(self, in_types):
14         n_in = in_types.size()
15         type_check.expect(2 <= n_in, n_in <= 3)
16         x_type, w_type = in_types[:2]
17 
18         type_check.expect(
19             x_type.dtype.kind == 'f',
20             w_type.dtype.kind == 'f',
21             x_type.ndim == 2,
22             w_type.ndim == 2,
23             x_type.shape[1] == w_type.shape[1],
24         )
25         if type_check.eval(n_in) == 3:
26             b_type = in_types[2]
27             type_check.expect(
28                 b_type.dtype == x_type.dtype,
29                 b_type.ndim == 1,
30                 b_type.shape[0] == w_type.shape[0],
31             )
32 
33     def forward(self, inputs):
34         self._config_use_ideep = chainer.config.use_ideep
35         if (intel64.should_use_ideep('>=auto')
36                 and intel64.inputs_all_ready(inputs)):
37             # iDeep implementation
38             return self._forward_ideep(inputs)
39 
40         # Generic implementation
41         if len(inputs) == 3:
42             x, W, b = inputs
43         else:
44             (x, W), b = inputs, None
45 
46         # NumPy raises an error when the array is not contiguous.
47         # See: https://github.com/chainer/chainer/issues/2744
48         # TODO(niboshi): Remove this code when NumPy is fixed.
49         if (isinstance(x, numpy.ndarray) and
50                 not (x.flags.c_contiguous or x.flags.f_contiguous) and
51                 1 in x.shape):
52             x = numpy.ascontiguousarray(x)
53 
54         y = x.dot(W.T).astype(x.dtype, copy=False)
55         if b is not None:
56             y += b
57         self.retain_inputs((0, 1))  # b is not retained
58         return y,
59 
60     def _forward_ideep(self, inputs):
61         if len(inputs) == 3:
62             x, W, b = inputs
63         else:
64             (x, W), b = inputs, None
65 
66         y = intel64.ideep.linear.Forward(
67             intel64.ideep.array(x),
68             intel64.ideep.array(W),
69             intel64.ideep.array(b) if b is not None else None)
70 
71         self.retain_inputs((0, 1))
72         return y,
73 
74     def backward(self, indexes, grad_outputs):
75         x, W = self.get_retained_inputs()
76         gy, = grad_outputs
77         ret = []
78         with chainer.using_config('use_ideep', self._config_use_ideep):
79             if 0 in indexes:
80                 gx, = LinearGradData().apply((W, gy))
81                 ret.append(chainer.functions.cast(gx, x.dtype))
82             if 1 in indexes:
83                 gW, = LinearGradWeight(W.dtype).apply((x, gy))
84                 ret.append(chainer.functions.cast(gW, W.dtype))
85             if 2 in indexes:
86                 gb = chainer.functions.sum(gy, axis=0)
87                 ret.append(gb)
88 
89         return ret
90 
91 
92 class LinearGradData(function_node.FunctionNode):
93 
94     _config_use_ideep = None
95 
96     def forward(self, inputs):
97         self._config_use_ideep = chainer.config.use_ideep
98         if (intel64.should_use_ideep('>=auto')
99                 and intel64.inputs_all_ready(inputs)):
100             # iDeep implementation
101             return self._forward_ideep(inputs)
102 
103         # Generic implementation
104         self.retain_inputs((0, 1))
105         W, gy = inputs
106 
107         if (isinstance(gy, numpy.ndarray) and
108                 not (gy.flags.c_contiguous or gy.flags.f_contiguous) and
109                 1 in gy.shape):
110             gy = numpy.ascontiguousarray(gy)
111 
112         gx = gy.dot(W).astype(gy.dtype, copy=False)
113         return gx,
114 
115     def _forward_ideep(self, inputs):
116         self.retain_inputs((0, 1))
117         W, gy = inputs
118         gx = intel64.ideep.linear.BackwardData(
119             intel64.ideep.array(W),
120             intel64.ideep.array(gy))
121         return gx,
122 
123     def backward(self, indexes, grad_outputs):
124         W, gy = self.get_retained_inputs()
125         ggx, = grad_outputs
126 
127         ret = []
128         with chainer.using_config('use_ideep', self._config_use_ideep):
129             if 0 in indexes:
130                 gw, = LinearGradWeight(W.dtype).apply((ggx, gy))
131                 ret.append(chainer.functions.cast(gw, W.dtype))
132             if 1 in indexes:
133                 ggy = linear(ggx, W)
134                 ret.append(chainer.functions.cast(ggy, gy.dtype))
135         return ret
136 
137 
138 class LinearGradWeight(function_node.FunctionNode):
139 
140     _config_use_ideep = None
141 
142     def __init__(self, w_dtype):
143         self._w_dtype = w_dtype
144 
145     def forward(self, inputs):
146         self._config_use_ideep = chainer.config.use_ideep
147         if (intel64.should_use_ideep('>=auto')
148                 and self._w_dtype == numpy.float32
149                 and intel64.inputs_all_ready(inputs)):
150             # iDeep implementation
151             return self._forward_ideep(inputs)
152 
153         # Generic implementation
154         self.retain_inputs((0, 1))
155         x, gy = inputs
156 
157         if (isinstance(gy, numpy.ndarray) and
158                 not (gy.flags.c_contiguous or gy.flags.f_contiguous) and
159                 1 in gy.shape):
160             gy = numpy.ascontiguousarray(gy)
161 
162         gW = gy.T.dot(x).astype(self._w_dtype, copy=False)
163         return gW,
164 
165     def _forward_ideep(self, inputs):
166         self.retain_inputs((0, 1))
167         x, gy = inputs
168         gW = intel64.ideep.linear.BackwardWeights(
169             intel64.ideep.array(x),
170             intel64.ideep.array(gy))
171         return gW,
172 
173     def backward(self, indexes, grad_outputs):
174         x, gy = self.get_retained_inputs()
175         ggW, = grad_outputs
176 
177         ret = []
178         with chainer.using_config('use_ideep', self._config_use_ideep):
179             if 0 in indexes:
180                 gx, = LinearGradData().apply((ggW, gy))
181                 ret.append(chainer.functions.cast(gx, x.dtype))
182             if 1 in indexes:
183                 ggy = linear(x, ggW)
184                 ret.append(chainer.functions.cast(ggy, gy.dtype))
185         return ret
186 
187 
188 def linear(x, W, b=None):
189     """Linear function, or affine transformation.
190 
191     It accepts two or three arguments: an input minibatch ``x``, a weight
192     matrix ``W``, and optionally a bias vector ``b``. It computes
193 
194     .. math:: Y = xW^\\top + b.
195 
196     Args:
197         x (:class:`~chainer.Variable` or :class:`numpy.ndarray` or \
198         :class:`cupy.ndarray`): Input variable, which is a :math:`(s_B, s_1, \
199             s_2, ..., s_n)`-shaped float array. Its first dimension
200             :math:`(s_B)` is assumed to be the *minibatch dimension*. The
201             other dimensions are treated as concatenated one dimension whose
202             size must be :math:`(s_1 * ... * s_n = N)`.
203         W (:class:`~chainer.Variable` or :class:`numpy.ndarray` or \
204         :class:`cupy.ndarray`): Weight variable of shape :math:`(M, N)`,
205             where :math:`(N = s_1 * ... * s_n)`.
206         b (:class:`~chainer.Variable` or :class:`numpy.ndarray` or \
207         :class:`cupy.ndarray`): Bias variable (optional) of shape
208             :math:`(M,)`.
209 
210     Returns:
211         ~chainer.Variable: Output variable. A float array with shape
212         of :math:`(s_B, M)`.
213 
214     .. seealso:: :class:`~chainer.links.Linear`
215 
216     .. admonition:: Example
217 
218         >>> x = np.random.uniform(0, 1, (3, 4)).astype(np.float32)
219         >>> W = np.random.uniform(0, 1, (5, 4)).astype(np.float32)
220         >>> b = np.random.uniform(0, 1, (5,)).astype(np.float32)
221         >>> y = F.linear(x, W, b)
222         >>> y.shape
223         (3, 5)
224 
225     """
226     if x.ndim > 2:
227         x = x.reshape(len(x), -1)
228 
229     if b is None:
230         args = x, W
231     else:
232         args = x, W, b
233 
234     y, = LinearFunction().apply(args)
235     return y
236
```
--- 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()

```

diff --git a/chainer/functions/connection/linear.py b/chainer/functions/connection/linear.py
--- a/chainer/functions/connection/linear.py
+++ b/chainer/functions/connection/linear.py
@@ -185,7 +185,7 @@
         return ret
 
 
-def linear(x, W, b=None):
+def linear(x, W, b=None, n_batch_axes=1):
     """Linear function, or affine transformation.
 
     It accepts two or three arguments: an input minibatch ``x``, a weight
@@ -195,21 +195,25 @@
 
     Args:
         x (:class:`~chainer.Variable` or :class:`numpy.ndarray` or \
-        :class:`cupy.ndarray`): Input variable, which is a :math:`(s_B, s_1, \
-            s_2, ..., s_n)`-shaped float array. Its first dimension
-            :math:`(s_B)` is assumed to be the *minibatch dimension*. The
-            other dimensions are treated as concatenated one dimension whose
-            size must be :math:`(s_1 * ... * s_n = N)`.
+        :class:`cupy.ndarray`): Input variable, which is a :math:`(s_1, s_2, \
+            ..., s_n)`-shaped float array. Its first ``n_batch_axes``
+            dimensions are handled as *minibatch dimensions*. The
+            other dimensions are handled as concatenated one dimension whose
+            size must be :math:`(s_{\\rm n_batch_axes} * ... * s_n = N)`.
         W (:class:`~chainer.Variable` or :class:`numpy.ndarray` or \
         :class:`cupy.ndarray`): Weight variable of shape :math:`(M, N)`,
-            where :math:`(N = s_1 * ... * s_n)`.
+            where :math:`(N = s_{\\rm n_batch_axes} * ... * s_n)`.
         b (:class:`~chainer.Variable` or :class:`numpy.ndarray` or \
         :class:`cupy.ndarray`): Bias variable (optional) of shape
             :math:`(M,)`.
+        n_batch_axes (int): The number of batch axes. The default is 1. The
+            input variable is reshaped into
+            :math:`{\\rm n_batch_axes} + 1`-dimensional tensor.
+            This should be greater than 0.
 
     Returns:
         ~chainer.Variable: Output variable. A float array with shape
-        of :math:`(s_B, M)`.
+        of :math:`(s_1, ..., s_{\\rm n_batch_axes}, M)`.
 
     .. seealso:: :class:`~chainer.links.Linear`
 
@@ -223,13 +227,20 @@
         (3, 5)
 
     """
-    if x.ndim > 2:
-        x = x.reshape(len(x), -1)
-
+    if n_batch_axes <= 0:
+        raise ValueError('n_batch_axes should be greater than 0.')
+    if n_batch_axes > 1:
+        batch_shape = x.shape[:n_batch_axes]
+        batch_size = numpy.prod(batch_shape)
+        x = x.reshape(batch_size, -1)
+    elif x.ndim > 2:
+        x = x.reshape(x.shape[0], -1)
     if b is None:
         args = x, W
     else:
         args = x, W, b
 
     y, = LinearFunction().apply(args)
+    if n_batch_axes > 1:
+        y = y.reshape(batch_shape + (-1,))
     return y

{"golden_diff": "diff --git a/chainer/functions/connection/linear.py b/chainer/functions/connection/linear.py\n--- a/chainer/functions/connection/linear.py\n+++ b/chainer/functions/connection/linear.py\n@@ -185,7 +185,7 @@\n         return ret\n \n \n-def linear(x, W, b=None):\n+def linear(x, W, b=None, n_batch_axes=1):\n     \"\"\"Linear function, or affine transformation.\n \n     It accepts two or three arguments: an input minibatch ``x``, a weight\n@@ -195,21 +195,25 @@\n \n     Args:\n         x (:class:`~chainer.Variable` or :class:`numpy.ndarray` or \\\n-        :class:`cupy.ndarray`): Input variable, which is a :math:`(s_B, s_1, \\\n-            s_2, ..., s_n)`-shaped float array. Its first dimension\n-            :math:`(s_B)` is assumed to be the *minibatch dimension*. The\n-            other dimensions are treated as concatenated one dimension whose\n-            size must be :math:`(s_1 * ... * s_n = N)`.\n+        :class:`cupy.ndarray`): Input variable, which is a :math:`(s_1, s_2, \\\n+            ..., s_n)`-shaped float array. Its first ``n_batch_axes``\n+            dimensions are handled as *minibatch dimensions*. The\n+            other dimensions are handled as concatenated one dimension whose\n+            size must be :math:`(s_{\\\\rm n_batch_axes} * ... * s_n = N)`.\n         W (:class:`~chainer.Variable` or :class:`numpy.ndarray` or \\\n         :class:`cupy.ndarray`): Weight variable of shape :math:`(M, N)`,\n-            where :math:`(N = s_1 * ... * s_n)`.\n+            where :math:`(N = s_{\\\\rm n_batch_axes} * ... * s_n)`.\n         b (:class:`~chainer.Variable` or :class:`numpy.ndarray` or \\\n         :class:`cupy.ndarray`): Bias variable (optional) of shape\n             :math:`(M,)`.\n+        n_batch_axes (int): The number of batch axes. The default is 1. The\n+            input variable is reshaped into\n+            :math:`{\\\\rm n_batch_axes} + 1`-dimensional tensor.\n+            This should be greater than 0.\n \n     Returns:\n         ~chainer.Variable: Output variable. A float array with shape\n-        of :math:`(s_B, M)`.\n+        of :math:`(s_1, ..., s_{\\\\rm n_batch_axes}, M)`.\n \n     .. seealso:: :class:`~chainer.links.Linear`\n \n@@ -223,13 +227,20 @@\n         (3, 5)\n \n     \"\"\"\n-    if x.ndim > 2:\n-        x = x.reshape(len(x), -1)\n-\n+    if n_batch_axes <= 0:\n+        raise ValueError('n_batch_axes should be greater than 0.')\n+    if n_batch_axes > 1:\n+        batch_shape = x.shape[:n_batch_axes]\n+        batch_size = numpy.prod(batch_shape)\n+        x = x.reshape(batch_size, -1)\n+    elif x.ndim > 2:\n+        x = x.reshape(x.shape[0], -1)\n     if b is None:\n         args = x, W\n     else:\n         args = x, W, b\n \n     y, = LinearFunction().apply(args)\n+    if n_batch_axes > 1:\n+        y = y.reshape(batch_shape + (-1,))\n     return y\n", "issue": "Make the number of axes for batch dimensions configurable in Linear\nL.Linear accepts multiple channel dimensions. It is useful in computer vision applications where the output of the final convolution layer can be directly fed to a linear link. This feature can be seen as a flexible number of channel axes.\r\n\r\nOn the other hand, the number of batch axes is currently fixed to one, which would be cumbersome in some use cases, especially when we have to deal with a sequence of input arrays. In this case, the first two axes may be used to represent the mini-batch and the sequence. We currently need to reshape it before and after feeding it to the linear link, which has overheads both for coding and runtime.\r\n\r\nIt can be resolved by introducing an option to Linear (and also to the F.linear function) that indicates the number of batch axes.\r\n\r\nThe same discussions would also be applied to convolution links/functions.\n", "before_files": [{"content": "import numpy\n\nfrom chainer.backends import intel64\nfrom chainer import function_node\nimport chainer.functions\nfrom chainer.utils import type_check\n\n\nclass LinearFunction(function_node.FunctionNode):\n\n    _config_use_ideep = None\n\n    def check_type_forward(self, in_types):\n        n_in = in_types.size()\n        type_check.expect(2 <= n_in, n_in <= 3)\n        x_type, w_type = in_types[:2]\n\n        type_check.expect(\n            x_type.dtype.kind == 'f',\n            w_type.dtype.kind == 'f',\n            x_type.ndim == 2,\n            w_type.ndim == 2,\n            x_type.shape[1] == w_type.shape[1],\n        )\n        if type_check.eval(n_in) == 3:\n            b_type = in_types[2]\n            type_check.expect(\n                b_type.dtype == x_type.dtype,\n                b_type.ndim == 1,\n                b_type.shape[0] == w_type.shape[0],\n            )\n\n    def forward(self, inputs):\n        self._config_use_ideep = chainer.config.use_ideep\n        if (intel64.should_use_ideep('>=auto')\n                and intel64.inputs_all_ready(inputs)):\n            # iDeep implementation\n            return self._forward_ideep(inputs)\n\n        # Generic implementation\n        if len(inputs) == 3:\n            x, W, b = inputs\n        else:\n            (x, W), b = inputs, None\n\n        # NumPy raises an error when the array is not contiguous.\n        # See: https://github.com/chainer/chainer/issues/2744\n        # TODO(niboshi): Remove this code when NumPy is fixed.\n        if (isinstance(x, numpy.ndarray) and\n                not (x.flags.c_contiguous or x.flags.f_contiguous) and\n                1 in x.shape):\n            x = numpy.ascontiguousarray(x)\n\n        y = x.dot(W.T).astype(x.dtype, copy=False)\n        if b is not None:\n            y += b\n        self.retain_inputs((0, 1))  # b is not retained\n        return y,\n\n    def _forward_ideep(self, inputs):\n        if len(inputs) == 3:\n            x, W, b = inputs\n        else:\n            (x, W), b = inputs, None\n\n        y = intel64.ideep.linear.Forward(\n            intel64.ideep.array(x),\n            intel64.ideep.array(W),\n            intel64.ideep.array(b) if b is not None else None)\n\n        self.retain_inputs((0, 1))\n        return y,\n\n    def backward(self, indexes, grad_outputs):\n        x, W = self.get_retained_inputs()\n        gy, = grad_outputs\n        ret = []\n        with chainer.using_config('use_ideep', self._config_use_ideep):\n            if 0 in indexes:\n                gx, = LinearGradData().apply((W, gy))\n                ret.append(chainer.functions.cast(gx, x.dtype))\n            if 1 in indexes:\n                gW, = LinearGradWeight(W.dtype).apply((x, gy))\n                ret.append(chainer.functions.cast(gW, W.dtype))\n            if 2 in indexes:\n                gb = chainer.functions.sum(gy, axis=0)\n                ret.append(gb)\n\n        return ret\n\n\nclass LinearGradData(function_node.FunctionNode):\n\n    _config_use_ideep = None\n\n    def forward(self, inputs):\n        self._config_use_ideep = chainer.config.use_ideep\n        if (intel64.should_use_ideep('>=auto')\n                and intel64.inputs_all_ready(inputs)):\n            # iDeep implementation\n            return self._forward_ideep(inputs)\n\n        # Generic implementation\n        self.retain_inputs((0, 1))\n        W, gy = inputs\n\n        if (isinstance(gy, numpy.ndarray) and\n                not (gy.flags.c_contiguous or gy.flags.f_contiguous) and\n                1 in gy.shape):\n            gy = numpy.ascontiguousarray(gy)\n\n        gx = gy.dot(W).astype(gy.dtype, copy=False)\n        return gx,\n\n    def _forward_ideep(self, inputs):\n        self.retain_inputs((0, 1))\n        W, gy = inputs\n        gx = intel64.ideep.linear.BackwardData(\n            intel64.ideep.array(W),\n            intel64.ideep.array(gy))\n        return gx,\n\n    def backward(self, indexes, grad_outputs):\n        W, gy = self.get_retained_inputs()\n        ggx, = grad_outputs\n\n        ret = []\n        with chainer.using_config('use_ideep', self._config_use_ideep):\n            if 0 in indexes:\n                gw, = LinearGradWeight(W.dtype).apply((ggx, gy))\n                ret.append(chainer.functions.cast(gw, W.dtype))\n            if 1 in indexes:\n                ggy = linear(ggx, W)\n                ret.append(chainer.functions.cast(ggy, gy.dtype))\n        return ret\n\n\nclass LinearGradWeight(function_node.FunctionNode):\n\n    _config_use_ideep = None\n\n    def __init__(self, w_dtype):\n        self._w_dtype = w_dtype\n\n    def forward(self, inputs):\n        self._config_use_ideep = chainer.config.use_ideep\n        if (intel64.should_use_ideep('>=auto')\n                and self._w_dtype == numpy.float32\n                and intel64.inputs_all_ready(inputs)):\n            # iDeep implementation\n            return self._forward_ideep(inputs)\n\n        # Generic implementation\n        self.retain_inputs((0, 1))\n        x, gy = inputs\n\n        if (isinstance(gy, numpy.ndarray) and\n                not (gy.flags.c_contiguous or gy.flags.f_contiguous) and\n                1 in gy.shape):\n            gy = numpy.ascontiguousarray(gy)\n\n        gW = gy.T.dot(x).astype(self._w_dtype, copy=False)\n        return gW,\n\n    def _forward_ideep(self, inputs):\n        self.retain_inputs((0, 1))\n        x, gy = inputs\n        gW = intel64.ideep.linear.BackwardWeights(\n            intel64.ideep.array(x),\n            intel64.ideep.array(gy))\n        return gW,\n\n    def backward(self, indexes, grad_outputs):\n        x, gy = self.get_retained_inputs()\n        ggW, = grad_outputs\n\n        ret = []\n        with chainer.using_config('use_ideep', self._config_use_ideep):\n            if 0 in indexes:\n                gx, = LinearGradData().apply((ggW, gy))\n                ret.append(chainer.functions.cast(gx, x.dtype))\n            if 1 in indexes:\n                ggy = linear(x, ggW)\n                ret.append(chainer.functions.cast(ggy, gy.dtype))\n        return ret\n\n\ndef linear(x, W, b=None):\n    \"\"\"Linear function, or affine transformation.\n\n    It accepts two or three arguments: an input minibatch ``x``, a weight\n    matrix ``W``, and optionally a bias vector ``b``. It computes\n\n    .. math:: Y = xW^\\\\top + b.\n\n    Args:\n        x (:class:`~chainer.Variable` or :class:`numpy.ndarray` or \\\n        :class:`cupy.ndarray`): Input variable, which is a :math:`(s_B, s_1, \\\n            s_2, ..., s_n)`-shaped float array. Its first dimension\n            :math:`(s_B)` is assumed to be the *minibatch dimension*. The\n            other dimensions are treated as concatenated one dimension whose\n            size must be :math:`(s_1 * ... * s_n = N)`.\n        W (:class:`~chainer.Variable` or :class:`numpy.ndarray` or \\\n        :class:`cupy.ndarray`): Weight variable of shape :math:`(M, N)`,\n            where :math:`(N = s_1 * ... * s_n)`.\n        b (:class:`~chainer.Variable` or :class:`numpy.ndarray` or \\\n        :class:`cupy.ndarray`): Bias variable (optional) of shape\n            :math:`(M,)`.\n\n    Returns:\n        ~chainer.Variable: Output variable. A float array with shape\n        of :math:`(s_B, M)`.\n\n    .. seealso:: :class:`~chainer.links.Linear`\n\n    .. admonition:: Example\n\n        >>> x = np.random.uniform(0, 1, (3, 4)).astype(np.float32)\n        >>> W = np.random.uniform(0, 1, (5, 4)).astype(np.float32)\n        >>> b = np.random.uniform(0, 1, (5,)).astype(np.float32)\n        >>> y = F.linear(x, W, b)\n        >>> y.shape\n        (3, 5)\n\n    \"\"\"\n    if x.ndim > 2:\n        x = x.reshape(len(x), -1)\n\n    if b is None:\n        args = x, W\n    else:\n        args = x, W, b\n\n    y, = LinearFunction().apply(args)\n    return y\n", "path": "chainer/functions/connection/linear.py"}], "after_files": [{"content": "import numpy\n\nfrom chainer.backends import intel64\nfrom chainer import function_node\nimport chainer.functions\nfrom chainer.utils import type_check\n\n\nclass LinearFunction(function_node.FunctionNode):\n\n    _config_use_ideep = None\n\n    def check_type_forward(self, in_types):\n        n_in = in_types.size()\n        type_check.expect(2 <= n_in, n_in <= 3)\n        x_type, w_type = in_types[:2]\n\n        type_check.expect(\n            x_type.dtype.kind == 'f',\n            w_type.dtype.kind == 'f',\n            x_type.ndim == 2,\n            w_type.ndim == 2,\n            x_type.shape[1] == w_type.shape[1],\n        )\n        if type_check.eval(n_in) == 3:\n            b_type = in_types[2]\n            type_check.expect(\n                b_type.dtype == x_type.dtype,\n                b_type.ndim == 1,\n                b_type.shape[0] == w_type.shape[0],\n            )\n\n    def forward(self, inputs):\n        self._config_use_ideep = chainer.config.use_ideep\n        if (intel64.should_use_ideep('>=auto')\n                and intel64.inputs_all_ready(inputs)):\n            # iDeep implementation\n            return self._forward_ideep(inputs)\n\n        # Generic implementation\n        if len(inputs) == 3:\n            x, W, b = inputs\n        else:\n            (x, W), b = inputs, None\n\n        # NumPy raises an error when the array is not contiguous.\n        # See: https://github.com/chainer/chainer/issues/2744\n        # TODO(niboshi): Remove this code when NumPy is fixed.\n        if (isinstance(x, numpy.ndarray) and\n                not (x.flags.c_contiguous or x.flags.f_contiguous) and\n                1 in x.shape):\n            x = numpy.ascontiguousarray(x)\n\n        y = x.dot(W.T).astype(x.dtype, copy=False)\n        if b is not None:\n            y += b\n        self.retain_inputs((0, 1))  # b is not retained\n        return y,\n\n    def _forward_ideep(self, inputs):\n        if len(inputs) == 3:\n            x, W, b = inputs\n        else:\n            (x, W), b = inputs, None\n\n        y = intel64.ideep.linear.Forward(\n            intel64.ideep.array(x),\n            intel64.ideep.array(W),\n            intel64.ideep.array(b) if b is not None else None)\n\n        self.retain_inputs((0, 1))\n        return y,\n\n    def backward(self, indexes, grad_outputs):\n        x, W = self.get_retained_inputs()\n        gy, = grad_outputs\n        ret = []\n        with chainer.using_config('use_ideep', self._config_use_ideep):\n            if 0 in indexes:\n                gx, = LinearGradData().apply((W, gy))\n                ret.append(chainer.functions.cast(gx, x.dtype))\n            if 1 in indexes:\n                gW, = LinearGradWeight(W.dtype).apply((x, gy))\n                ret.append(chainer.functions.cast(gW, W.dtype))\n            if 2 in indexes:\n                gb = chainer.functions.sum(gy, axis=0)\n                ret.append(gb)\n\n        return ret\n\n\nclass LinearGradData(function_node.FunctionNode):\n\n    _config_use_ideep = None\n\n    def forward(self, inputs):\n        self._config_use_ideep = chainer.config.use_ideep\n        if (intel64.should_use_ideep('>=auto')\n                and intel64.inputs_all_ready(inputs)):\n            # iDeep implementation\n            return self._forward_ideep(inputs)\n\n        # Generic implementation\n        self.retain_inputs((0, 1))\n        W, gy = inputs\n\n        if (isinstance(gy, numpy.ndarray) and\n                not (gy.flags.c_contiguous or gy.flags.f_contiguous) and\n                1 in gy.shape):\n            gy = numpy.ascontiguousarray(gy)\n\n        gx = gy.dot(W).astype(gy.dtype, copy=False)\n        return gx,\n\n    def _forward_ideep(self, inputs):\n        self.retain_inputs((0, 1))\n        W, gy = inputs\n        gx = intel64.ideep.linear.BackwardData(\n            intel64.ideep.array(W),\n            intel64.ideep.array(gy))\n        return gx,\n\n    def backward(self, indexes, grad_outputs):\n        W, gy = self.get_retained_inputs()\n        ggx, = grad_outputs\n\n        ret = []\n        with chainer.using_config('use_ideep', self._config_use_ideep):\n            if 0 in indexes:\n                gw, = LinearGradWeight(W.dtype).apply((ggx, gy))\n                ret.append(chainer.functions.cast(gw, W.dtype))\n            if 1 in indexes:\n                ggy = linear(ggx, W)\n                ret.append(chainer.functions.cast(ggy, gy.dtype))\n        return ret\n\n\nclass LinearGradWeight(function_node.FunctionNode):\n\n    _config_use_ideep = None\n\n    def __init__(self, w_dtype):\n        self._w_dtype = w_dtype\n\n    def forward(self, inputs):\n        self._config_use_ideep = chainer.config.use_ideep\n        if (intel64.should_use_ideep('>=auto')\n                and self._w_dtype == numpy.float32\n                and intel64.inputs_all_ready(inputs)):\n            # iDeep implementation\n            return self._forward_ideep(inputs)\n\n        # Generic implementation\n        self.retain_inputs((0, 1))\n        x, gy = inputs\n\n        if (isinstance(gy, numpy.ndarray) and\n                not (gy.flags.c_contiguous or gy.flags.f_contiguous) and\n                1 in gy.shape):\n            gy = numpy.ascontiguousarray(gy)\n\n        gW = gy.T.dot(x).astype(self._w_dtype, copy=False)\n        return gW,\n\n    def _forward_ideep(self, inputs):\n        self.retain_inputs((0, 1))\n        x, gy = inputs\n        gW = intel64.ideep.linear.BackwardWeights(\n            intel64.ideep.array(x),\n            intel64.ideep.array(gy))\n        return gW,\n\n    def backward(self, indexes, grad_outputs):\n        x, gy = self.get_retained_inputs()\n        ggW, = grad_outputs\n\n        ret = []\n        with chainer.using_config('use_ideep', self._config_use_ideep):\n            if 0 in indexes:\n                gx, = LinearGradData().apply((ggW, gy))\n                ret.append(chainer.functions.cast(gx, x.dtype))\n            if 1 in indexes:\n                ggy = linear(x, ggW)\n                ret.append(chainer.functions.cast(ggy, gy.dtype))\n        return ret\n\n\ndef linear(x, W, b=None, n_batch_axes=1):\n    \"\"\"Linear function, or affine transformation.\n\n    It accepts two or three arguments: an input minibatch ``x``, a weight\n    matrix ``W``, and optionally a bias vector ``b``. It computes\n\n    .. math:: Y = xW^\\\\top + b.\n\n    Args:\n        x (:class:`~chainer.Variable` or :class:`numpy.ndarray` or \\\n        :class:`cupy.ndarray`): Input variable, which is a :math:`(s_1, s_2, \\\n            ..., s_n)`-shaped float array. Its first ``n_batch_axes``\n            dimensions are handled as *minibatch dimensions*. The\n            other dimensions are handled as concatenated one dimension whose\n            size must be :math:`(s_{\\\\rm n_batch_axes} * ... * s_n = N)`.\n        W (:class:`~chainer.Variable` or :class:`numpy.ndarray` or \\\n        :class:`cupy.ndarray`): Weight variable of shape :math:`(M, N)`,\n            where :math:`(N = s_{\\\\rm n_batch_axes} * ... * s_n)`.\n        b (:class:`~chainer.Variable` or :class:`numpy.ndarray` or \\\n        :class:`cupy.ndarray`): Bias variable (optional) of shape\n            :math:`(M,)`.\n        n_batch_axes (int): The number of batch axes. The default is 1. The\n            input variable is reshaped into\n            :math:`{\\\\rm n_batch_axes} + 1`-dimensional tensor.\n            This should be greater than 0.\n\n    Returns:\n        ~chainer.Variable: Output variable. A float array with shape\n        of :math:`(s_1, ..., s_{\\\\rm n_batch_axes}, M)`.\n\n    .. seealso:: :class:`~chainer.links.Linear`\n\n    .. admonition:: Example\n\n        >>> x = np.random.uniform(0, 1, (3, 4)).astype(np.float32)\n        >>> W = np.random.uniform(0, 1, (5, 4)).astype(np.float32)\n        >>> b = np.random.uniform(0, 1, (5,)).astype(np.float32)\n        >>> y = F.linear(x, W, b)\n        >>> y.shape\n        (3, 5)\n\n    \"\"\"\n    if n_batch_axes <= 0:\n        raise ValueError('n_batch_axes should be greater than 0.')\n    if n_batch_axes > 1:\n        batch_shape = x.shape[:n_batch_axes]\n        batch_size = numpy.prod(batch_shape)\n        x = x.reshape(batch_size, -1)\n    elif x.ndim > 2:\n        x = x.reshape(x.shape[0], -1)\n    if b is None:\n        args = x, W\n    else:\n        args = x, W, b\n\n    y, = LinearFunction().apply(args)\n    if n_batch_axes > 1:\n        y = y.reshape(batch_shape + (-1,))\n    return y\n", "path": "chainer/functions/connection/linear.py"}]}