Skip to content

Commit

Permalink
Remove deprecated torch.lstsq (pytorch#70980)
Browse files Browse the repository at this point in the history
The time has come to remove deprecated linear algebra related functions. This PR removes `torch.lstsq`.

There's a note in `tools/codegen/gen.py` about `lstsq` schema in `native_function.yaml` that I will not remove:
https://github.com/pytorch/pytorch/blob/87139d8532c99ff5dbeef1b97948d71793aa7851/tools/codegen/gen.py#L734-L770

cc @jianyuh @nikitaved @pearu @mruberry @walterddr @IvanYashchuk @xwang233 @lezcano
Pull Request resolved: pytorch#70980
Approved by: https://github.com/lezcano, https://github.com/kit1980
  • Loading branch information
IvanYashchuk authored and pytorchmergebot committed Sep 23, 2022
1 parent 6380016 commit 539076e
Show file tree
Hide file tree
Showing 18 changed files with 26 additions and 438 deletions.
6 changes: 0 additions & 6 deletions aten/src/ATen/autocast_mode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -600,12 +600,6 @@ TORCH_LIBRARY_IMPL(aten, AutocastCPU, m) {
std::tuple<Tensor, Tensor> (const Tensor &),
&ADD_NS(geqrf)>::type::call)));

m.impl(TORCH_SELECTIVE_NAME("aten::lstsq"),
TORCH_FN((&WrapFunction<CastPolicy::fp32, DeviceType::CPU,
std::tuple<Tensor, Tensor> (const Tensor &, const Tensor &),
std::tuple<Tensor, Tensor> (const Tensor &, const Tensor &),
&ADD_NS(lstsq)>::type::call)));

m.impl(TORCH_SELECTIVE_NAME("aten::_lu_with_info"),
TORCH_FN((&WrapFunction<CastPolicy::fp32, DeviceType::CPU,
std::tuple<Tensor, Tensor, Tensor> (const Tensor &, bool, bool),
Expand Down
100 changes: 0 additions & 100 deletions aten/src/ATen/native/BatchLinearAlgebra.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3739,106 +3739,6 @@ std::tuple<Tensor, Tensor, Tensor, Tensor> linalg_lstsq(
return std::make_tuple(solution, residuals, rank, singular_values);
}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ legacy_lstsq ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

// This wraps Lapack's gels routine, which uses a QR or LQ factorization to
// solve any linear system, minimizing ||A.X - B||
// A & B must be fortran-contiguous matrixes.
// On exit, A is overwritten with the QR/LQ factorization of input A
// B is overwritten with the solution vectors
template <typename scalar_t>
static void apply_lstsq(const Tensor& B, const Tensor& A) {
#if !AT_BUILD_WITH_LAPACK()
TORCH_INTERNAL_ASSERT(false, "lstsq: LAPACK library not found in compilation");
#else

int m, n, nrhs, lda, ldb, info, lwork;
scalar_t wkopt = 0.0;
lwork = -1; // work length
m = A.size(0);
n = A.size(1);
nrhs = B.size(1);
info = 0;
lda = m;
ldb = (m > n) ? m : n;

auto B_data = B.data_ptr<scalar_t>();
auto A_data = A.data_ptr<scalar_t>();

// get info how much space is needed
lapackGels<scalar_t>('N', m, n, nrhs, A_data, lda, B_data, ldb, &wkopt, lwork, &info);

lwork = static_cast<int>(wkopt);
Tensor work_tensor = at::empty({lwork}, A.scalar_type());
auto work = work_tensor.data_ptr<scalar_t>();

lapackGels<scalar_t>('N', m, n, nrhs, A_data, lda, B_data, ldb, work, lwork, &info);

TORCH_CHECK(
info >= 0,
"Lapack Error in gels : Illegal argument ", -info);
TORCH_CHECK(
info == 0,
"Lapack Error in gels: The ", info, "-th diagonal element of the ",
"triangular factor of A is zero");
#endif
}

std::tuple<Tensor, Tensor> legacy_lstsq(const Tensor& B, const Tensor& A) {
TORCH_WARN_ONCE(
"torch.lstsq is deprecated in favor of torch.linalg.lstsq and will be removed in a future PyTorch release.\n",
"torch.linalg.lstsq has reversed arguments and does not return the QR decomposition in "
"the returned tuple (although it returns other information about the problem).\n",
"To get the qr decomposition consider using torch.linalg.qr.\n",
"The returned solution in torch.lstsq stored the residuals of the solution in the ",
"last m - n columns of the returned value whenever m > n. In torch.linalg.lstsq, the ",
"residuals in the field 'residuals' of the returned named tuple.\n",
"The unpacking of the solution, as in\n",
"X, _ = torch.lstsq(B, A).solution[:A.size(1)]\n",
"should be replaced with\n",
"X = torch.linalg.lstsq(A, B).solution");

TORCH_CHECK(A.scalar_type() == B.scalar_type(), "Exepected A and B dtypes to match but found ",
A.scalar_type(), " and ", B.scalar_type());
TORCH_CHECK(A.dim() == 2, "Expected A to have 2 dimensions, but got ", A.dim());
TORCH_CHECK(A.numel() != 0, "A should not be empty");
TORCH_CHECK(B.dim() == 1 || B.dim() == 2, "Expected B to have 1 or 2 "
"dimensions, but got ", B.dim());
TORCH_CHECK(B.numel() != 0, "B should not be empty");
TORCH_CHECK(A.size(0) == B.size(0), "Expected A and B to have same size "
"at dim 0, but A has ", A.size(0), " rows and B has ", B.size(0), " rows");

const auto a_sizes = A.sizes();
const auto ldb = std::max(a_sizes[0], a_sizes[1]);

auto A_working = cloneBatchedColumnMajor(A);
auto B_working = copyBatchedColumnMajor(B.dim() == 1 ? B.unsqueeze(1) : B, ldb);

AT_DISPATCH_FLOATING_TYPES(B.scalar_type(), "lstsq_cpu", [&] {
apply_lstsq<scalar_t>(B_working, A_working);
});

return std::tuple<Tensor, Tensor>(B_working, A_working);
}

std::tuple<Tensor&,Tensor&> legacy_lstsq_out(
const Tensor& B, const Tensor& A, Tensor& B_out, Tensor& A_out) {
const auto dtype = A.scalar_type();
TORCH_CHECK(B.scalar_type() == dtype, "exepected A and B dtypes to match but found ",
A.scalar_type(), " and ", B.scalar_type());
TORCH_CHECK(A_out.scalar_type() == dtype, "A_out to have scalar type ", dtype,
" but found", A_out.scalar_type());
TORCH_CHECK(B_out.scalar_type() == dtype, "A_out to have scalar type ", dtype,
" but found", B_out.scalar_type());
Tensor A_tmp, B_tmp;
std::tie(B_tmp, A_tmp) = native::legacy_lstsq(B, A);
resize_output(A_out, A_tmp.sizes());
A_out.copy_(A_tmp);
resize_output(B_out, B_tmp.sizes());
B_out.copy_(B_tmp);
return std::tuple<Tensor&, Tensor&>(B_out, A_out);
}

DEFINE_DISPATCH(ldl_factor_stub);

TORCH_IMPL_FUNC(linalg_ldl_factor_ex_out)
Expand Down
27 changes: 1 addition & 26 deletions aten/src/ATen/native/cuda/LinearAlgebraStubs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,7 @@ namespace native {
#if defined(BUILD_LAZY_CUDA_LINALG)
namespace {
cuda::detail::LinalgDispatch disp = {_symeig_helper_cuda,
_cholesky_solve_helper_cuda,
legacy_lstsq_cuda};
_cholesky_solve_helper_cuda};

at::DynamicLibrary& getTorchLinalgLibrary() {
static at::DynamicLibrary lib("libtorch_cuda_linalg.so", nullptr, true);
Expand Down Expand Up @@ -170,12 +169,6 @@ void registerLinalgDispatch(const LinalgDispatch& disp_) {
}
}} //namespace cuda::detail

std::tuple<Tensor, Tensor> legacy_lstsq_cuda(const Tensor &B, const Tensor &A) {
getTorchLinalgLibrary();
TORCH_CHECK(disp.legacy_lstsq != legacy_lstsq_cuda, "Can't find legacy_lstsq_cuda");
return disp.legacy_lstsq(B, A);
}

Tensor _cholesky_solve_helper_cuda(const Tensor& self, const Tensor& A, bool upper) {
getTorchLinalgLibrary();
TORCH_CHECK(disp.cholesky_solve_helper != _cholesky_solve_helper_cuda, "Can't find _cholesky_solve_helper_cuda");
Expand All @@ -190,22 +183,4 @@ std::tuple<Tensor, Tensor> _symeig_helper_cuda(const Tensor& self, bool eigenvec

#endif /*defined(BUILD_LAZY_CUDA_LINALG)*/

std::tuple<Tensor&, Tensor&> legacy_lstsq_out_cuda(
const Tensor& B, const Tensor& A, Tensor& B_out, Tensor& A_out) {
const auto dtype = A.scalar_type();
TORCH_CHECK(B.scalar_type() == dtype, "exepected A and B dtypes to match but found ",
A.scalar_type(), " and ", B.scalar_type());
TORCH_CHECK(A_out.scalar_type() == dtype, "A_out to have scalar type ", dtype,
" but found", A_out.scalar_type());
TORCH_CHECK(B_out.scalar_type() == dtype, "A_out to have scalar type ", dtype,
" but found", B_out.scalar_type());
Tensor A_tmp, B_tmp;
std::tie(B_tmp, A_tmp) = native::legacy_lstsq_cuda(B, A);
resize_output(A_out, A_tmp.sizes());
A_out.copy_(A_tmp);
resize_output(B_out, B_tmp.sizes());
B_out.copy_(B_tmp);
return std::tuple<Tensor&, Tensor&>(B_out, A_out);
}

}} // namespace at::native
63 changes: 1 addition & 62 deletions aten/src/ATen/native/cuda/linalg/BatchLinearAlgebra.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@
#include <ATen/ops/linalg_eigh.h>
#include <ATen/ops/linalg_eigvalsh.h>
#include <ATen/ops/linalg_solve_triangular.h>
#include <ATen/ops/lstsq_native.h>
#include <ATen/ops/zeros.h>
#include <ATen/ops/_linalg_check_errors.h>
#endif
Expand Down Expand Up @@ -2791,72 +2790,12 @@ void lstsq_kernel(const Tensor& a, Tensor& b, Tensor& /*rank*/, Tensor& /*singul

REGISTER_CUDA_DISPATCH(lstsq_stub, &lstsq_kernel);

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ legacy_lstsq ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

std::tuple<Tensor, Tensor> legacy_lstsq_cuda(const Tensor &B, const Tensor &A) {
TORCH_WARN_ONCE(
"torch.lstsq is deprecated in favor of torch.linalg.lstsq and will be removed in a future PyTorch release.\n",
"torch.linalg.lstsq has reversed arguments and does not return the QR decomposition in "
"the returned tuple (although it returns other information about the problem).\n",
"To get the qr decomposition consider using torch.linalg.qr.\n",
"The returned solution in torch.lstsq stored the residuals of the solution in the ",
"last m - n columns of the returned value whenever m > n. In torch.linalg.lstsq, the ",
"residuals in the field 'residuals' of the returned named tuple.\n",
"The unpacking of the solution, as in\n",
"X, _ = torch.lstsq(B, A).solution[:A.size(1)]\n",
"should be replaced with\n",
"X = torch.linalg.lstsq(A, B).solution"
);

#if !AT_MAGMA_ENABLED()
TORCH_CHECK(false, "solve: MAGMA library not found in "
"compilation. Please rebuild with MAGMA.");
#else
const auto dtype = A.scalar_type();
TORCH_CHECK(B.scalar_type() == dtype, "exepected A and B dtypes to match but found ",
dtype, " and ", B.scalar_type());
TORCH_CHECK(A.numel() > 0 && A.dim() == 2, "A should be (non-empty) 2 dimensional");
TORCH_CHECK(B.numel() > 0 && B.dim() == 2, "B should be (non-empty) 2 dimensional");
auto a_sizes = A.sizes();
auto b_sizes = B.sizes();
TORCH_CHECK(a_sizes[0] == b_sizes[0], "Expected A and b to have same size "
"at dim 0, but A has ", a_sizes[0], " rows and B has ", b_sizes[0], " rows");
TORCH_CHECK(a_sizes[0] >= a_sizes[1], "Expected A with shape (m x n) to have "
"m >= n. The case for m < n is not implemented yet.");

Tensor A_working = cloneBatchedColumnMajor(A);
Tensor B_working = cloneBatchedColumnMajor(B);

int64_t m = a_sizes[0];
int64_t n = a_sizes[1];
int64_t nrhs = b_sizes[1];

int info;
AT_DISPATCH_FLOATING_TYPES(A.scalar_type(), "legacy_lstsq_cuda", [&] {
scalar_t *a_data = A_working.data_ptr<scalar_t>();
scalar_t *b_data = B_working.data_ptr<scalar_t>();
scalar_t wkopt;
magmaGels(MagmaNoTrans, m, n, nrhs, a_data, m, b_data, m, &wkopt, -1, &info);

const auto hwork_size = static_cast<magma_int_t>(wkopt);
scalar_t *hwork = nullptr;
ALLOCATE_ARRAY(hwork, scalar_t, hwork_size);

magmaGels(MagmaNoTrans, m, n, nrhs, a_data, m, b_data, m, hwork, hwork_size, &info);
});

TORCH_CHECK(info == 0, "MAGMA gels : Argument %d : illegal value", -info);
return std::tuple<Tensor, Tensor>(B_working, A_working);
#endif // AT_MAGMA_ENABLED()
}


#if defined(BUILD_LAZY_CUDA_LINALG)
struct DispatchInitializer {
DispatchInitializer() {
cuda::detail::LinalgDispatch disp{ _symeig_helper_cuda,
_cholesky_solve_helper_cuda,
legacy_lstsq_cuda};
_cholesky_solve_helper_cuda};
cuda::detail::registerLinalgDispatch(disp);
};
} initializer;
Expand Down
1 change: 0 additions & 1 deletion aten/src/ATen/native/cuda/linalg/BatchLinearAlgebraLib.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,6 @@ namespace cuda { namespace detail {
struct LinalgDispatch {
std::tuple<Tensor, Tensor> (*symeig_helper)(const Tensor& self, bool eigenvectors, bool upper);
Tensor (*cholesky_solve_helper)(const Tensor& self, const Tensor& A, bool upper);
std::tuple<Tensor, Tensor> (*legacy_lstsq)(const Tensor &B, const Tensor &A);
};
C10_EXPORT void registerLinalgDispatch(const LinalgDispatch&);
}} // namespace cuda::detail
Expand Down
11 changes: 0 additions & 11 deletions aten/src/ATen/native/native_functions.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8119,17 +8119,6 @@
- func: cross_entropy_loss(Tensor self, Tensor target, Tensor? weight=None, int reduction=Mean, int ignore_index=-100, float label_smoothing=0.0) -> Tensor
python_module: nn

- func: lstsq.X(Tensor self, Tensor A, *, Tensor(a!) X, Tensor(b!) qr) -> (Tensor(a!) solution, Tensor(b!) QR)
dispatch:
CPU: legacy_lstsq_out
CUDA: legacy_lstsq_out_cuda

- func: lstsq(Tensor self, Tensor A) -> (Tensor solution, Tensor QR)
variants: method, function
dispatch:
CPU: legacy_lstsq
CUDA: legacy_lstsq_cuda

- func: triangular_solve.X(Tensor self, Tensor A, bool upper=True, bool transpose=False, bool unitriangular=False, *, Tensor(a!) X, Tensor(b!) M) -> (Tensor(a!) solution, Tensor(b!) cloned_coefficient)
structured: True
dispatch:
Expand Down
1 change: 0 additions & 1 deletion docs/source/tensors.rst
Original file line number Diff line number Diff line change
Expand Up @@ -483,7 +483,6 @@ Tensor class reference
Tensor.logit
Tensor.logit_
Tensor.long
Tensor.lstsq
Tensor.lt
Tensor.lt_
Tensor.less
Expand Down
1 change: 0 additions & 1 deletion docs/source/torch.rst
Original file line number Diff line number Diff line change
Expand Up @@ -566,7 +566,6 @@ BLAS and LAPACK Operations
det
logdet
slogdet
lstsq
lu
lu_solve
lu_unpack
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@
("aten::_syevd_helper", datetime.date(9999, 1, 1)),
("aten::_linalg_solve_out_helper_", datetime.date(9999, 1, 1)),
("aten::select_backward", datetime.date(9999, 1, 1)),
("aten::lstsq", datetime.date(9999, 1, 1)),
("aten::lstsq.X", datetime.date(9999, 1, 1)),
("aten::slice_backward", datetime.date(9999, 1, 1)),
("aten::diagonal_backward", datetime.date(9999, 1, 1)),
("aten::rowwise_prune", datetime.date(9999, 1, 1)),
Expand Down
Loading

0 comments on commit 539076e

Please sign in to comment.