Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable product for bool tensor #48637

Closed
wants to merge 5 commits into from

Conversation

Kiyosora
Copy link
Contributor

@Kiyosora Kiyosora commented Dec 1, 2020

Fixes #48351

@dr-ci
Copy link

dr-ci bot commented Dec 1, 2020

💊 CI failures summary and remediations

As of commit 21bd03a (more details on the Dr. CI page):


💚 💚 Looks good so far! There are no failures yet. 💚 💚


This comment was automatically generated by Dr. CI (expand for details).Follow this link to opt-out of these comments for your Pull Requests.

Please report bugs/suggestions to the (internal) Dr. CI Users group.

This comment has been revised 46 times.

@Kiyosora Kiyosora force-pushed the product_for_bool_tensor branch 5 times, most recently from 7e3937f to 220e51d Compare December 2, 2020 08:11
@Kiyosora Kiyosora changed the title [WIP] Enable product for bool tensor Enable product for bool tensor Dec 2, 2020
@Kiyosora Kiyosora marked this pull request as ready for review December 2, 2020 08:37
@bdhirsh bdhirsh requested a review from mruberry December 2, 2020 14:17
@codecov
Copy link

codecov bot commented Dec 3, 2020

Codecov Report

Merging #48637 (21bd03a) into master (e9d7d37) will decrease coverage by 0.00%.
The diff coverage is n/a.

@@            Coverage Diff             @@
##           master   #48637      +/-   ##
==========================================
- Coverage   80.62%   80.62%   -0.01%     
==========================================
  Files        1875     1875              
  Lines      202769   202769              
==========================================
- Hits       163486   163479       -7     
- Misses      39283    39290       +7     

@heitorschueroff heitorschueroff added module: boolean tensor triaged This issue has been looked at a team member, and triaged and prioritized into an appropriate module labels Dec 3, 2020
@@ -814,6 +814,17 @@ def test_prod(self, device, dtype):
torch.prod(x, 1, out=res2)
self.assertEqual(res1, res2)

def test_prod_bool(self, device):
tests = [
([True, True], True),
Copy link
Collaborator

@mruberry mruberry Dec 8, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add an empty tensor input, too, and instead of comparing with expected create a NumPy array from the inputs and compare with the result of np.prod.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed! Thanks for suggestion!


for val, exp in tests:
tensor = torch.tensor(val, device=device)
self.assertEqual(torch.prod(tensor, dtype=torch.bool), torch.tensor(exp, device=device))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if dtype isn't set? That is, what is the default behavior when a bool tensor is passed to prod?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case (a bool tensor passed to prod without dtype variate), an int64 type Tensor would be returned, which is consistent with the performance in NumPy.

>>> np.prod(np.array([True,True]))
1
>>> torch.prod(torch.tensor([True, True]))
tensor(1)
>>> np.prod(np.array([True,True])).dtype
dtype('int64')
>>> torch.prod(torch.tensor([True, True])).dtype
torch.int64

The only difference is that for the input of an empty Tensor, the result type of PyTorch is float32, while for NumPy, it is float64. I think it's caused by the different default types between PyTorch and NumPy, so I guess it's just fine.

>>> np.prod(np.array([]))
1.0
>>> np.prod(np.array([])).dtype
dtype('float64')
>>> torch.prod(torch.tensor([]))
tensor(1.)
>>> torch.prod(torch.tensor([])).dtype
torch.float32

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the returned dtype is int64 when a boolean tensor is given as input, which code path is exercised in the function for boolean tensors? Because won't iter.dtype() return intt64?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the iter.dtype() here would return int64. The code would finaly goes to prod_out_impl, and it would use get_dtype method to casting inputs from kBool type to kLong before the Iterator be created.

ScalarType src_type = self.scalar_type();
if (promote_integers && at::isIntegralType(src_type, /*includeBool=*/true)) {
return kLong;
}
return src_type;

static Tensor& prod_out_impl(Tensor& result, const Tensor& self, IntArrayRef dim,
bool keepdim, c10::optional<ScalarType> opt_dtype) {
ScalarType dtype = get_dtype(result, self, opt_dtype, true);
auto iter = make_reduction("prod", result, self, dim, keepdim, dtype);

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's my thinking, too. Would you verify? Also, if that's the case, then is this code ever used?

if (iter.dtype() == ScalarType::Bool) {
  using scalar_t = bool;
  binary_kernel_reduce_vec(
    iter,
    [=](scalar_t a, scalar_t b) -> scalar_t { return a && b; },
    [=](Vec256<scalar_t> a, Vec256<scalar_t> b) { return a && b; },
    /*identity=*/1);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, As shown below, I verified it in my environment.

When setting dtype as torch.bool, it would using the code of a&&b , and in other cases (including the above-mentioned default int64 scene), the part of a*b would be used.

Such verification has been verified in both CPU and CUDA environments, and the performance of the two is consistent.

static void prod_kernel_impl(TensorIterator& iter) {
  // Workaround for the error: '*' in boolean context, suggest '&&' instead [-Werror=int-in-bool    -context]
  if (iter.dtype() == ScalarType::Bool) {
    std::cout << "Using a && b" << std::endl;
    using scalar_t = bool;
    binary_kernel_reduce_vec(
      iter,
      [=](scalar_t a, scalar_t b) -> scalar_t { return a && b; },
      [=](Vec256<scalar_t> a, Vec256<scalar_t> b) { return a && b; },
      /*identity=*/1);
  } else {
    std::cout << "Using a * b" << std::endl;
    AT_DISPATCH_ALL_TYPES_AND_COMPLEX(iter.dtype(), "prod_cpu", [&] {
      binary_kernel_reduce_vec(
        iter,
        [=](scalar_t a, scalar_t b) -> scalar_t { return a * b; },
        [=](Vec256 <scalar_t> a, Vec256 <scalar_t> b) { return a * b; },
        /*identity=*/1);
      });
  }
}
>>> import torch
>>> torch.prod(torch.tensor([True, True]), dtype=torch.bool)
Using a && b
tensor(True)
>>> torch.prod(torch.tensor([True, True]))
Using a * b
tensor(1)
>>> torch.prod(torch.tensor([True, True], device='cuda'), dtype=torch.bool)
Using a && b
tensor(True, device='cuda:0')
>>> torch.prod(torch.tensor([True, True]))
Using a * b
tensor(1)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the explanation, @Kiyosora, that's very helpful.

In the future we may want to become more clever with this dispatch so we can compute in bool when the input is bool, even if the output dtype is something else (like int64), but this seems OK for now.

I've made one suggestion about extending the test to validate both the default bool->int64 behavior is consistent with NumPy and the already tested bool->bool behavior is, too.

@mruberry
Copy link
Collaborator

mruberry commented Dec 8, 2020

Hey @Kiyosora! Thanks for submitting this PR. Just a couple questions for you.

@Kiyosora Kiyosora force-pushed the product_for_bool_tensor branch 2 times, most recently from 425e462 to dbadd20 Compare December 9, 2020 06:16
@Kiyosora
Copy link
Contributor Author

Kiyosora commented Dec 9, 2020

Hey @Kiyosora! Thanks for submitting this PR. Just a couple questions for you.

Thanks for reviewing this PR, @mruberry ! 😃
It's looks fine that the result is consistent with NumPy when a bool tensor pass to prod.
In addition, the test case also has a bit of improvement, please take a look in your convenience.

@@ -814,6 +814,13 @@ def test_prod(self, device, dtype):
torch.prod(x, 1, out=res2)
self.assertEqual(res1, res2)

def test_prod_bool(self, device):
vals = [[True, True], [True, False], [False, False], []]
for val in vals:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you extend this test to verify bool->int64 consistent with NumPy, too?

Copy link
Contributor Author

@Kiyosora Kiyosora Dec 16, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed! Thanks for your suggestion!

@Kiyosora Kiyosora force-pushed the product_for_bool_tensor branch 2 times, most recently from 233dbab to 65ad982 Compare December 15, 2020 08:13
Copy link
Collaborator

@mruberry mruberry left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool! Thanks @Kiyosora!

Copy link
Contributor

@facebook-github-bot facebook-github-bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mruberry has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator.

@facebook-github-bot
Copy link
Contributor

@mruberry merged this pull request in 983bfc7.

hwangdeyu pushed a commit to hwangdeyu/pytorch that referenced this pull request Jan 6, 2021
Summary:
Fixes pytorch#48351

Pull Request resolved: pytorch#48637

Reviewed By: mrshenli

Differential Revision: D25658596

Pulled By: mruberry

fbshipit-source-id: ff3ada74b6d281c8e4753ed38339a1c036f722ee
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cla signed Merged module: boolean tensor open source triaged This issue has been looked at a team member, and triaged and prioritized into an appropriate module
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Bool tensor should support product
5 participants