aten/src/ATen/native/LinearAlgebra.cpp
/*
Matrix product of two Tensors.
The behavior depends on the dimensionality of the Tensors as follows:
- If both Tensors are 1-dimensional, the dot product (scalar) is returned.
- If both arguments are 2-dimensional, the matrix-matrix product is returned.
- If the first argument is 1-dimensional and the second argument is 2-dimensional,
  a 1 is prepended to its dimension for the purpose of the matrix multiply.
  After the matrix multiply, the prepended dimension is removed.
- If the first argument is 2-dimensional and the second argument is 1-dimensional,
  the matrix-vector product is returned.
- If both arguments are at least 1-dimensional and at least one argument is
  N-dimensional (where N > 2), then a batched matrix multiply is returned.  If the first
  argument is 1-dimensional, a 1 is prepended to its dimension for the purpose of the
  batched matrix multiply and removed after.  If the second argument is 1-dimensional, a
  1 is appended to its dimension for the purpose of the batched matrix multiple and removed after.
  The non-matrix (i.e. batch) dimensions are broadcasted (and thus
  must be broadcastable).  For example, if tensor1 is a (j x 1 x n x m) Tensor
  and tensor2 is a (k x m x p) Tensor, the returned tensor will be an (j x k x n x p) Tensor.
*/
Matrix product of two Tensors.
The behavior depends on the dimensionality of the Tensors as follows:
If both Tensors are 1-dimensional, the dot product (scalar) is returned.
If both arguments are 2-dimensional, the matrix-matrix product is returned.
If the first argument is 1-dimensional and the second argument is 2-dimensional,
a 1 is prepended to its dimension for the purpose of the matrix multiply.
After the matrix multiply, the prepended dimension is removed.
If the first argument is 2-dimensional and the second argument is 1-dimensional,
the matrix-vector product is returned.
If both arguments are at least 1-dimensional and at least one argument is
N-dimensional (where N > 2), then a batched matrix multiply is returned.  If the first
argument is 1-dimensional, a 1 is prepended to its dimension for the purpose of the
batched matrix multiply and removed after.  If the second argument is 1-dimensional, a
1 is appended to its dimension for the purpose of the batched matrix multiple and removed after.
The non-matrix  dimensions are broadcasted (and thus
must be broadcastable).  For example, if tensor1 is a (j x 1 x n x m) Tensor
and tensor2 is a (k x m x p) Tensor, the returned tensor will be an (j x k x n x p) Tensor.
Matrix product of two Tensors . The behavior depends on the dimensionality of the Tensors as follows : If both Tensors are 1 - dimensional the dot product ( scalar ) is returned . If both arguments are 2 - dimensional the matrix - matrix product is returned . If the first argument is 1 - dimensional and the second argument is 2 - dimensional a 1 is prepended to its dimension for the purpose of the matrix multiply . After the matrix multiply the prepended dimension is removed . If the first argument is 2 - dimensional and the second argument is 1 - dimensional the matrix - vector product is returned . If both arguments are at least 1 - dimensional and at least one argument is N - dimensional ( where N > 2 ) then a batched matrix multiply is returned . If the first argument is 1 - dimensional a 1 is prepended to its dimension for the purpose of the batched matrix multiply and removed after . If the second argument is 1 - dimensional a 1 is appended to its dimension for the purpose of the batched matrix multiple and removed after . The non - matrix dimensions are broadcasted ( and thus must be broadcastable ) . For example if tensor1 is a ( j x 1 x n x m ) Tensor and tensor2 is a ( k x m x p ) Tensor the returned tensor will be an ( j x k x n x p ) Tensor .
Tensor matmul(at::optional<Tensor> out_opt, const Tensor& tensor1, const Tensor& tensor2) {
  auto dim_tensor1 = tensor1.dim();
  auto dim_tensor2 = tensor2.dim();
  auto has_out = out_opt.has_value();
  Tensor out = out_opt.value_or(Tensor());
  if (dim_tensor1 == 1 && dim_tensor2 == 1) {
    return has_out ? at::native::dot_out(out, tensor1, tensor2) : tensor1.dot(tensor2);
  } else if (dim_tensor1 == 2 && dim_tensor2 == 1) {
    return has_out ? at::native::mv_out(out, tensor1, tensor2) : tensor1.mv(tensor2);
  } else if (dim_tensor1 == 1 && dim_tensor2 == 2) {
    return has_out ? at::native::mm_out(out, tensor1.unsqueeze(0), tensor2).squeeze_(0)
                   : tensor1.unsqueeze(0).mm(tensor2).squeeze_(0);
  } else if (dim_tensor1 == 2 && dim_tensor2 == 2) {
    return has_out ? at::native::mm_out(out, tensor1, tensor2) : tensor1.mm(tensor2);
  } else if (dim_tensor1 >= 3 && (dim_tensor2 == 1 || dim_tensor2 == 2)) {
    Tensor t2 = dim_tensor2 == 1 ? tensor2.unsqueeze(-1) : tensor2;
    auto size1 = tensor1.sizes();
    auto size2 = t2.sizes();
    std::vector<int64_t> output_size;
    output_size.insert(output_size.end(), size1.begin(), size1.end() - 1);
    if (dim_tensor2 > 1) {
      output_size.push_back(size2[dim_tensor2 - 1]);
    }
    Tensor t1 = tensor1.contiguous().view({-1, size1[size1.size() - 1]});
    Tensor output = has_out ? at::_unsafe_view(at::mm_out(out, t1, t2), output_size)
                            : at::_unsafe_view(t1.mm(t2), output_size);
    return has_out ? out.set_(output) : output;
  } else if ((dim_tensor1 >= 1 && dim_tensor2 >= 1) && (dim_tensor1 >= 3 || dim_tensor2 >= 3)) {
    int64_t n = dim_tensor1 > 1 ? tensor1.size(-2) : 1;
    int64_t m1 = tensor1.size(-1);
    IntList batch_tensor1(tensor1.sizes().data(), std::max<int64_t>(dim_tensor1 - 2, 0));
    int64_t m2 = dim_tensor2 > 1 ? tensor2.size(-2) : 1;
    int64_t p = tensor2.size(-1);
    IntList batch_tensor2(tensor2.sizes().data(), std::max<int64_t>(dim_tensor2 - 2, 0));
    std::vector<int64_t> expand_batch_portion = infer_size(batch_tensor1, batch_tensor2);
    std::vector<int64_t> tensor1_expand_size(expand_batch_portion);
    tensor1_expand_size.insert(tensor1_expand_size.end(), {n, m1});
    std::vector<int64_t> tensor2_expand_size(expand_batch_portion);
    tensor2_expand_size.insert(tensor2_expand_size.end(), {m2, p});
    int expand_batch_product = std::accumulate(expand_batch_portion.begin(), expand_batch_portion.end(),
                                               1, std::multiplies<int64_t>());
    std::vector<int64_t> tensor1_bmm_view({expand_batch_product});
    tensor1_bmm_view.insert(tensor1_bmm_view.end(), {n, m1});
    std::vector<int64_t> tensor2_bmm_view({expand_batch_product});
    tensor2_bmm_view.insert(tensor2_bmm_view.end(), {m2, p});
    Tensor tensor1_expanded = tensor1.expand(tensor1_expand_size).contiguous().view(tensor1_bmm_view);
    Tensor tensor2_expanded = tensor2.expand(tensor2_expand_size).contiguous().view(tensor2_bmm_view);
    std::vector<int64_t> output_shape(expand_batch_portion);
    if (dim_tensor1 > 1) {
      output_shape.push_back(n);
    }
    if (dim_tensor2 > 1) {
      output_shape.push_back(p);
    }
    Tensor output = has_out ? at::_unsafe_view(at::bmm_out(out, tensor1_expanded, tensor2_expanded), output_shape)
                            : at::_unsafe_view(tensor1_expanded.bmm(tensor2_expanded), output_shape);
    return has_out ? out.set_(output) : output;
  }
 AT_ERROR("both arguments to matmul need to be at least 1D, but they are ",
          dim_tensor1, "D and ", dim_tensor2, "D");
}
Tensor matmul ( at :: optional < Tensor > out_opt , const Tensor & tensor1 , const Tensor & tensor2 ) { auto dim_tensor1 = tensor1 . dim ( ) ; auto dim_tensor2 = tensor2 . dim ( ) ; auto has_out = out_opt . has_value ( ) ; Tensor out = out_opt . value_or ( Tensor ( ) ) ; if ( dim_tensor1 == 1 && dim_tensor2 == 1 ) { return has_out ? at :: native :: dot_out ( out , tensor1 , tensor2 ) : tensor1 . dot ( tensor2 ) ; } else if ( dim_tensor1 == 2 && dim_tensor2 == 1 ) { return has_out ? at :: native :: mv_out ( out , tensor1 , tensor2 ) : tensor1 . mv ( tensor2 ) ; } else if ( dim_tensor1 == 1 && dim_tensor2 == 2 ) { return has_out ? at :: native :: mm_out ( out , tensor1 . unsqueeze ( 0 ) , tensor2 ) . squeeze_ ( 0 ) : tensor1 . unsqueeze ( 0 ) . mm ( tensor2 ) . squeeze_ ( 0 ) ; } else if ( dim_tensor1 == 2 && dim_tensor2 == 2 ) { return has_out ? at :: native :: mm_out ( out , tensor1 , tensor2 ) : tensor1 . mm ( tensor2 ) ; } else if ( dim_tensor1 >= 3 && ( dim_tensor2 == 1 || dim_tensor2 == 2 ) ) { Tensor t2 = dim_tensor2 == 1 ? tensor2 . unsqueeze ( -1 ) : tensor2 ; auto size1 = tensor1 . sizes ( ) ; auto size2 = t2 . sizes ( ) ; std :: vector < int64_t > output_size ; output_size . insert ( output_size . end ( ) , size1 . begin ( ) , size1 . end ( ) - 1 ) ; if ( dim_tensor2 > 1 ) { output_size . push_back ( size2 [ dim_tensor2 - 1 ] ) ; } Tensor t1 = tensor1 . contiguous ( ) . view ( { -1 , size1 [ size1 . size ( ) - 1 ] } ) ; Tensor output = has_out ? at :: _unsafe_view ( at :: mm_out ( out , t1 , t2 ) , output_size ) : at :: _unsafe_view ( t1 . mm ( t2 ) , output_size ) ; return has_out ? out . set_ ( output ) : output ; } else if ( ( dim_tensor1 >= 1 && dim_tensor2 >= 1 ) && ( dim_tensor1 >= 3 || dim_tensor2 >= 3 ) ) { int64_t n = dim_tensor1 > 1 ? tensor1 . size ( -2 ) : 1 ; int64_t m1 = tensor1 . size ( -1 ) ; IntList batch_tensor1 ( tensor1 . sizes ( ) . data ( ) , std :: max < int64_t > ( dim_tensor1 - 2 , 0 ) ) ; int64_t m2 = dim_tensor2 > 1 ? tensor2 . size ( -2 ) : 1 ; int64_t p = tensor2 . size ( -1 ) ; IntList batch_tensor2 ( tensor2 . sizes ( ) . data ( ) , std :: max < int64_t > ( dim_tensor2 - 2 , 0 ) ) ; std :: vector < int64_t > expand_batch_portion = infer_size ( batch_tensor1 , batch_tensor2 ) ; std :: vector < int64_t > tensor1_expand_size ( expand_batch_portion ) ; tensor1_expand_size . insert ( tensor1_expand_size . end ( ) , { n , m1 } ) ; std :: vector < int64_t > tensor2_expand_size ( expand_batch_portion ) ; tensor2_expand_size . insert ( tensor2_expand_size . end ( ) , { m2 , p } ) ; int expand_batch_product = std :: accumulate ( expand_batch_portion . begin ( ) , expand_batch_portion . end ( ) , 1 , std :: multiplies < int64_t > ( ) ) ; std :: vector < int64_t > tensor1_bmm_view ( { expand_batch_product } ) ; tensor1_bmm_view . insert ( tensor1_bmm_view . end ( ) , { n , m1 } ) ; std :: vector < int64_t > tensor2_bmm_view ( { expand_batch_product } ) ; tensor2_bmm_view . insert ( tensor2_bmm_view . end ( ) , { m2 , p } ) ; Tensor tensor1_expanded = tensor1 . expand ( tensor1_expand_size ) . contiguous ( ) . view ( tensor1_bmm_view ) ; Tensor tensor2_expanded = tensor2 . expand ( tensor2_expand_size ) . contiguous ( ) . view ( tensor2_bmm_view ) ; std :: vector < int64_t > output_shape ( expand_batch_portion ) ; if ( dim_tensor1 > 1 ) { output_shape . push_back ( n ) ; } if ( dim_tensor2 > 1 ) { output_shape . push_back ( p ) ; } Tensor output = has_out ? at :: _unsafe_view ( at :: bmm_out ( out , tensor1_expanded , tensor2_expanded ) , output_shape ) : at :: _unsafe_view ( tensor1_expanded . bmm ( tensor2_expanded ) , output_shape ) ; return has_out ? out . set_ ( output ) : output ; } AT_ERROR ( " " , dim_tensor1 , " " , dim_tensor2 , " " ) ; }
Matrix product of two Tensors.
Matrix product of two Tensors .
// optimization: use mm instead of bmm by folding tensor1's batch into // its leading matrix dimension. // fold the batch into the first dimension // We are multiplying b1 x n x m1 by x2 x m2 x p (where b1 can be a list); // we track m1 vs m2 separately even though they must match for nicer error messages // expand the batch portion (i.e. cut off matrix dimensions and expand rest) // flatten expanded batches // reshape batches back into result