Helper for einsum() that computes the result of a two-argument einsum(). Args: t0: a `Tensor` t0_axis_labels: a string of axis labels. This string's length must equal the rank of t0. t1: a `Tensor` t1_axis_labels: a string to axis labels. This string's length must equal
(t0, t0_axis_labels, t1, t1_axis_labels, axes_to_sum)
| 392 | |
| 393 | |
| 394 | def _einsum_reduction(t0, t0_axis_labels, t1, t1_axis_labels, axes_to_sum): |
| 395 | """Helper for einsum() that computes the result of a two-argument einsum(). |
| 396 | |
| 397 | Args: |
| 398 | t0: a `Tensor` |
| 399 | t0_axis_labels: a string of axis labels. This string's length must equal |
| 400 | the rank of t0. |
| 401 | t1: a `Tensor` |
| 402 | t1_axis_labels: a string to axis labels. This string's length must equal |
| 403 | the rank of t1. |
| 404 | axes_to_sum: set of labels of axes to be summed over |
| 405 | |
| 406 | Returns: |
| 407 | A `Tensor` whose elements are obtained by summing, over all axes in |
| 408 | `axes_to_sum`, the corresponding elements of `t0` and `t1`. |
| 409 | |
| 410 | For example, if t0_axis_labels == 'abijk', t1_axis_labels == 'acjkl', and |
| 411 | axes_to_sum == {j,k}, this will return a tensor x where |
| 412 | |
| 413 | out[a,b,c,i,l] = sum_j sum_k t0[a,b,i,j,k] * t1[a,c,j,k,l] |
| 414 | |
| 415 | Raises: |
| 416 | ValueError: if the rank of `t0` does not match the length of |
| 417 | `t0_axis_labels`, or that of `t1` does not match the length of |
| 418 | `t1_axis_labels`. |
| 419 | """ |
| 420 | if len(t0_axis_labels) != len(t0.get_shape()): |
| 421 | raise ValueError( |
| 422 | 'Tensor t0 of rank %d does not match einsum reduction of length %d' % |
| 423 | (len(t0.get_shape()), len(t0_axis_labels))) |
| 424 | if len(t1_axis_labels) != len(t1.get_shape()): |
| 425 | raise ValueError( |
| 426 | 'Tensor t1 of rank %d does not match einsum reduction of length %d' % |
| 427 | (len(t1.get_shape()), len(t1_axis_labels))) |
| 428 | |
| 429 | # This function computes the result of a two-argument einsum() using batch |
| 430 | # matrix multiplication. This involves |
| 431 | # 1. transposing t0 and t1 so that axes are in the correct order for |
| 432 | # batch matrix multiplication, and |
| 433 | # 2. reshaping t0 and t1 so that they are both of rank 3. |
| 434 | |
| 435 | # First, we divide axes into three groups: |
| 436 | # * "preserved" axes are present in both inputs and the output |
| 437 | # * "summed" axes are present in both inputs but not the output |
| 438 | # * "broadcast" axes are present in exactly one input and the output |
| 439 | # |
| 440 | # As an example, if the einsum is abijk,acjkl->abcil, then "a" is a |
| 441 | # preserved axis, "b" and "c" are broadcast axes, and "j" and "k" are |
| 442 | # summed axes. |
| 443 | assert all(a in t0_axis_labels and a in t1_axis_labels for a in axes_to_sum) |
| 444 | preserved_axes = (set(t0_axis_labels) & set(t1_axis_labels)) - axes_to_sum |
| 445 | broadcast_axes = {} |
| 446 | for i, sym_list in enumerate([t0_axis_labels, t1_axis_labels]): |
| 447 | broadcast_axes[i] = set(sym_list) - preserved_axes - axes_to_sum |
| 448 | |
| 449 | # Reorder the axes so that: |
| 450 | # 1. preserved axes come first in both inputs |
| 451 | # 2. in input 0, broadcast axes come next, followed by summed axes |
no test coverage detected