Skip to content

Conversation

choidongyeon
Copy link

@choidongyeon choidongyeon commented Apr 1, 2020

See issue #33494 Complex number printing inconsistent with float.

Changes introduces an optional argument in Formatter's format function to discern whether a tensor is a float tensor or not. This way, there is consistency between float tensors and complex tensors so that the complex tensors print in the same manner as float tensors:

  • Only a decimal point and no zeros for integer values.
  • Trailing zeros only if the value is truly a float.
  • White space introduced to fill the gap so that +/- symbols and commas align.

Here are some example outputs.

print(torch.zeros((2,2), dtype=torch.float64))

yields

tensor([[0., 0.],
        [0., 0.]], dtype=torch.float64)
print(torch.zeros((2,2), dtype=torch.complex64))

previously yielded

tensor([[(0.0000 + 0.0000j), (0.0000 + 0.0000j)],
        [(0.0000 + 0.0000j), (0.0000 + 0.0000j)]], dtype=torch.complex64)

and now yields

tensor([[(0 + 0.j), (0 + 0.j)],
        [(0 + 0.j), (0 + 0.j)]], dtype=torch.complex64)

This new print version is more consistent with float tensor's pretty print.

The following example mixes integer and decimals:

print(torch.tensor([[1 + 1.340j, 3 + 4j], [1.2 + 1.340j, 6.5 + 7j]], dtype=torch.complex64))

This yields:

tensor([[                     (1.0000 + 1.3400j),
                              (3.0000 + 4.0000j)],
        [                     (1.2000 + 1.3400j),
                              (6.5000 + 7.0000j)]], dtype=torch.complex64)

The following example

torch.tensor([1,2,3,4.5])

yields

tensor([1.0000, 2.0000, 3.0000, 4.5000]) .

@dr-ci
Copy link

dr-ci bot commented Apr 1, 2020

💊 CircleCI build failures summary and remediations

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


💚 💚 Looks good so far! There are no CircleCI 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 on the GitHub issue tracker.

See how this bot performed.

This comment has been revised 26 times.

@choidongyeon choidongyeon changed the title Complex print Address printing inconsistency between float and complex tensors Apr 1, 2020
@choidongyeon
Copy link
Author

@anjali411, does this address the printing inconsistency to your liking?

@zou3519 zou3519 requested a review from anjali411 April 6, 2020 14:24
@zou3519 zou3519 added the triaged This issue has been looked at a team member, and triaged and prioritized into an appropriate module label Apr 6, 2020
@anjali411 anjali411 added the module: complex Related to complex number support in PyTorch label Apr 6, 2020
p = PRINT_OPTS.precision
ret = '({{:.{}f}} {{}} {{:.{}f}}j)'.format(p, p).format(value.real, '+-'[value.imag < 0], abs(value.imag))
# format real and imaginary values according to type
real_val = '({{:.0f}}. '.format(p).format(value.real) if value.real.is_integer() \
Copy link
Contributor

Choose a reason for hiding this comment

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

real_val = '({{:.0f}}.'.format(p).format(value.real) if value.real.is_integer() \

Copy link
Contributor

Choose a reason for hiding this comment

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

I think we should remove the extra white space

Copy link
Author

Choose a reason for hiding this comment

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

Addressed in PR update.

# format real and imaginary values according to type
real_val = '({{:.0f}}. '.format(p).format(value.real) if value.real.is_integer() \
else '({{:.{}f}}'.format(p).format(value.real)
imag_val = '{{:.0f}}.j )'.format(p).format(value.imag) if value.imag.is_integer() \
Copy link
Contributor

Choose a reason for hiding this comment

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

imag_val = '{{:.0f}}.j)'.format(p).format(value.imag) if value.imag.is_integer() \

same as above

Copy link
Author

Choose a reason for hiding this comment

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

Addressed in PR update.

@anjali411
Copy link
Contributor

anjali411 commented Apr 6, 2020

I think this behavior is in line with what we do for floating point tensors:

print(torch.tensor([[1 + 1.340j, 3 + 4j], [1.2 + 1.340j, 6.5 + 7j]], dtype=torch.complex64))

tensor([[                     (1.0000 + 1.3400j),
                              (3.0000 + 4.0000j)],
        [                     (1.2000 + 1.3400j),
                              (6.5000 + 7.0000j)]], dtype=torch.complex64)

for example, this: >>> torch.tensor([1,2,3,4.5])

gives

tensor([1.0000, 2.0000, 3.0000, 4.5000])

However the first case that you mentioned is correct and to generalize it I think we wouldn't want to print the zeros after decimal if none of the entries have any non zero value after decimal

@choidongyeon
Copy link
Author

Updated the PR description with current outputs.

@choidongyeon
Copy link
Author

Based on the CircleCI build failures summary and remediations, it seems that of the six failing tests, five of them are due to upstream breakages. My understanding based on other PRs I've read is that the sixth failure (rocmdeb) is flaky. Is there anything else I should address?

@choidongyeon choidongyeon requested a review from anjali411 April 6, 2020 20:25
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.

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

p = PRINT_OPTS.precision
ret = '({{:.{}f}} {{}} {{:.{}f}}j)'.format(p, p).format(value.real, '+-'[value.imag < 0], abs(value.imag))
# format real and imaginary values according to type
if not is_float_tensor:
Copy link
Contributor

Choose a reason for hiding this comment

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

what are you trying to do here?

Strictly speaking, is_floating_point() returns false for complex, so it's misleading to have is_float_tensor check and do something for it under complex branch

Copy link
Author

Choose a reason for hiding this comment

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

You said we only want to truncate the zeros only in cases where all elements of the complex are integers. This checks to see if the complex tensor has any float components. I can change the variable name, but that's what it does.

Copy link
Contributor

Choose a reason for hiding this comment

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

hmm maybe change it to has_non_zero_decimal_val ?

Copy link
Author

Choose a reason for hiding this comment

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

Oh, I changed it to complex_contains_float. Can change it tohas_non_zero_decimal_val if you prefer that though, let me know!

Copy link
Contributor

Choose a reason for hiding this comment

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

yeah let's change it to has_non_zero_decimal_val :)

Copy link
Author

Choose a reason for hiding this comment

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

Okay! Doing it right now.

Copy link
Author

Choose a reason for hiding this comment

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

Updated PR, with values consistent as shown in the PR description.

@choidongyeon choidongyeon requested a review from anjali411 April 7, 2020 19:03
@anjali411
Copy link
Contributor

anjali411 commented Apr 8, 2020

Hey @choidongyeon thanks for the PR :D it looks good! wondering if you looked into why we have the extra space in printing, for eg.

tensor([[                     (1.0000 + 1.3400j),
                              (3.0000 + 4.0000j)],
        [                     (1.2000 + 1.3400j),
                              (6.5000 + 7.0000j)]], dtype=torch.complex64)

is it because we are not using masked_select?

@choidongyeon
Copy link
Author

choidongyeon commented Apr 8, 2020

@anjali411 Thanks for merging the changes from master in for me. Regarding your question about why we have the extra spaces - I haven't investigated too deeply but I know that it has to do with self.max_width. At t he moment, format returns: return (self.max_width - len(ret)) * ' ' + ret where self.max_width seems to be set in the Formatter's constructor. One way to address the issue would be to reverse the order of the formatting (assume integer, then filter out cases with actual decimals):

        elif self.complex_dtype:
            p = PRINT_OPTS.precision
            ret = "({{:.0f}} {{}} {{:.0f}}.j)".format(p, p).format(value.real, '+-'[value.imag < 0], abs(value.imag))
            if has_non_zero_decimal_val:
                # complex tensor contains decimal values elements only
                return '({{:.{}f}} {{}} {{:.{}f}}j)'.format(p, p).format(value.real, '+-'[value.imag < 0], abs(value.imag))
        else:
            ret = '{}'.format(value)
        return (self.max_width - len(ret)) * ' ' + ret

Then, our return values will look like this:

tensor([[0., 0.],
        [0., 0.]], dtype=torch.float64) 

tensor([[(0 + 0.j), (0 + 0.j)],
        [(0 + 0.j), (0 + 0.j)]], dtype=torch.complex64) 

tensor([1.2000, 1.3400], dtype=torch.float64) 

tensor([(1.2000 + 1.3400j)], dtype=torch.complex64) 

tensor([[(1.0000 + 1.3400j),
         (3.0000 + 4.0000j)],
        [(1.2000 + 1.3400j),
         (6.5000 + 7.0000j)]], dtype=torch.complex64) 

tensor([1.0000, 2.0000, 3.0000, 4.5000]) 

The only thing is, I feel like there should be a more elegant solution to preserve the current form of the code (one return statement). I can create another issue and look into it separately. Or just push the proposed change here in. Personally, I think we should close this issue out by merging it, but let me know!

@anjali411
Copy link
Contributor

@anjali411 Thanks for merging the changes from master in for me. Regarding your question about why we have the extra spaces - I haven't investigated too deeply but I know that it has to do with self.max_width. At t he moment, format returns: return (self.max_width - len(ret)) * ' ' + ret where self.max_width seems to be set in the Formatter's constructor. One way to address the issue would be to reverse the order of the formatting (assume integer, then filter out cases with actual decimals):

        elif self.complex_dtype:
            p = PRINT_OPTS.precision
            ret = "({{:.0f}} {{}} {{:.0f}}.j)".format(p, p).format(value.real, '+-'[value.imag < 0], abs(value.imag))
            if has_non_zero_decimal_val:
                # complex tensor contains decimal values elements only
                return '({{:.{}f}} {{}} {{:.{}f}}j)'.format(p, p).format(value.real, '+-'[value.imag < 0], abs(value.imag))
        else:
            ret = '{}'.format(value)
        return (self.max_width - len(ret)) * ' ' + ret

Then, our return values will look like this:

tensor([[0., 0.],
        [0., 0.]], dtype=torch.float64) 

tensor([[(0 + 0.j), (0 + 0.j)],
        [(0 + 0.j), (0 + 0.j)]], dtype=torch.complex64) 

tensor([1.2000, 1.3400], dtype=torch.float64) 

tensor([(1.2000 + 1.3400j)], dtype=torch.complex64) 

tensor([[(1.0000 + 1.3400j),
         (3.0000 + 4.0000j)],
        [(1.2000 + 1.3400j),
         (6.5000 + 7.0000j)]], dtype=torch.complex64) 

tensor([1.0000, 2.0000, 3.0000, 4.5000]) 

The only thing is, I feel like there should be a more elegant solution to preserve the current form of the code (one return statement). I can create another issue and look into it separately. Or just push the proposed change here in. Personally, I think we should close this issue out by merging it, but let me know!

Hi @choidongyeon I see. I agree we should figure out a more elegant solution for this. I'll merge this PR. would you like to create a follow up issue and work on it?

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.

@anjali411 is landing this pull request. If you are a Facebook employee, you can view this diff on Phabricator.

@choidongyeon
Copy link
Author

choidongyeon commented Apr 8, 2020

@anjali411 Thanks for merging the changes from master in for me. Regarding your question about why we have the extra spaces - I haven't investigated too deeply but I know that it has to do with self.max_width. At t he moment, format returns: return (self.max_width - len(ret)) * ' ' + ret where self.max_width seems to be set in the Formatter's constructor. One way to address the issue would be to reverse the order of the formatting (assume integer, then filter out cases with actual decimals):

        elif self.complex_dtype:
            p = PRINT_OPTS.precision
            ret = "({{:.0f}} {{}} {{:.0f}}.j)".format(p, p).format(value.real, '+-'[value.imag < 0], abs(value.imag))
            if has_non_zero_decimal_val:
                # complex tensor contains decimal values elements only
                return '({{:.{}f}} {{}} {{:.{}f}}j)'.format(p, p).format(value.real, '+-'[value.imag < 0], abs(value.imag))
        else:
            ret = '{}'.format(value)
        return (self.max_width - len(ret)) * ' ' + ret

Then, our return values will look like this:

tensor([[0., 0.],
        [0., 0.]], dtype=torch.float64) 

tensor([[(0 + 0.j), (0 + 0.j)],
        [(0 + 0.j), (0 + 0.j)]], dtype=torch.complex64) 

tensor([1.2000, 1.3400], dtype=torch.float64) 

tensor([(1.2000 + 1.3400j)], dtype=torch.complex64) 

tensor([[(1.0000 + 1.3400j),
         (3.0000 + 4.0000j)],
        [(1.2000 + 1.3400j),
         (6.5000 + 7.0000j)]], dtype=torch.complex64) 

tensor([1.0000, 2.0000, 3.0000, 4.5000]) 

The only thing is, I feel like there should be a more elegant solution to preserve the current form of the code (one return statement). I can create another issue and look into it separately. Or just push the proposed change here in. Personally, I think we should close this issue out by merging it, but let me know!

Hi @choidongyeon I see. I agree we should figure out a more elegant solution for this. I'll merge this PR. would you like to create a follow up issue and work on it?

Sounds like a plan! Will do that right now.

Edit: no idea if I did this correctly but here it is: #36279

@facebook-github-bot
Copy link
Contributor

@anjali411 merged this pull request in fdf7a83.

ashishfarmer pushed a commit to ashishfarmer/pytorch that referenced this pull request Apr 13, 2020
…orch#35841)

Summary:
See issue [pytorch#33494 Complex number printing inconsistent with float](pytorch#33494).

Changes introduces an optional argument in Formatter's ```format``` function to discern whether a tensor is a float tensor or not. This way, there is consistency between float tensors and complex tensors so that the complex tensors print in the same manner as float tensors:

- Only a decimal point and no zeros for integer values.
- Trailing zeros only if the value is truly a float.
- White space introduced to fill the gap so that +/- symbols and commas align.

Here are some example outputs.

```
print(torch.zeros((2,2), dtype=torch.float64))
```
yields
```
tensor([[0., 0.],
        [0., 0.]], dtype=torch.float64)
```

```
print(torch.zeros((2,2), dtype=torch.complex64))
```
previously yielded
```
tensor([[(0.0000 + 0.0000j), (0.0000 + 0.0000j)],
        [(0.0000 + 0.0000j), (0.0000 + 0.0000j)]], dtype=torch.complex64)
```
and now yields
```
tensor([[(0 + 0.j), (0 + 0.j)],
        [(0 + 0.j), (0 + 0.j)]], dtype=torch.complex64)
```
This new print version is more consistent with float tensor's pretty print.

The following example mixes integer and decimals:
```
print(torch.tensor([[1 + 1.340j, 3 + 4j], [1.2 + 1.340j, 6.5 + 7j]], dtype=torch.complex64))
```
This yields:
```
tensor([[                     (1.0000 + 1.3400j),
                              (3.0000 + 4.0000j)],
        [                     (1.2000 + 1.3400j),
                              (6.5000 + 7.0000j)]], dtype=torch.complex64)
```

The following example
```
torch.tensor([1,2,3,4.5])
```
yields
```
tensor([1.0000, 2.0000, 3.0000, 4.5000]) .
```
Pull Request resolved: pytorch#35841

Differential Revision: D20893848

Pulled By: anjali411

fbshipit-source-id: f84c533b8957a1563602439c07e60efbc79691bc
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Merged module: complex Related to complex number support in PyTorch 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.

6 participants