{"action":"created","comment":{"url":"https://api.github.com/repos/pytorch/pytorch/pulls/comments/176905969","pull_request_review_id":106695481,"id":176905969,"diff_hunk":"@@ -5,33 +5,152 @@\n #endif\n \n #include \"ATen/ATen.h\"\n+#include \"ATen/Config.h\"\n #include \"ATen/NativeFunctions.h\"\n-#include \"ATen/WrapDimUtils.h\"\n-#include \"ATen/ExpandUtils.h\"\n+#include \"ATen/native/SpectralOpsUtils.h\"\n \n #include <algorithm>\n-#include <functional>\n-#include <numeric>\n #include <vector>\n-\n+#include <cmath>\n \n namespace at { namespace native {\n \n-// Since real-to-complex satisfy the Hermitian symmetry, i.e.,\n-// X[m, \\omega] = X[m, N - \\omega]*. We return only the first floor(N / 2) + 1\n-// values by default. This is also the assumption in libraries including cuFFT.\n-static inline int64_t infer_ft_complex_length(int64_t real_length) {\n-  return (real_length >> 1) + 1;\n+static inline Tensor _fft(const Tensor &self, const int64_t signal_ndim,\n+           const bool complex_input, const bool complex_output,\n+           const bool inverse, IntList signal_sizes, const bool normalized,\n+           const bool onesided) {\n+\n+  if (signal_ndim < 1 || signal_ndim > 3) {\n+    std::ostringstream ss;\n+    ss << \"Expected signal_ndim to be 1, 2, or 3, but got signal_ndim=\"\n+       << signal_ndim;\n+    throw std::runtime_error(ss.str());\n+  }\n+  if (!at::isFloatingType(self.type().scalarType())) {\n+    std::ostringstream ss;\n+    ss << \"Expected an input tensor of floating types, but got input \"\n+       << self.type() << self.sizes();\n+    throw std::runtime_error(ss.str());\n+  }\n+\n+  auto signal_tensor_ndim = signal_ndim + (int) complex_input;  // add complex dim\n+  if (self.dim() != signal_tensor_ndim && self.dim() != signal_tensor_ndim + 1) {\n+    std::ostringstream ss;\n+    ss << \"Given signal_ndim=\" << signal_ndim << \", expected an input tensor \"\n+       << \"of \" << signal_tensor_ndim << \"D or \" << signal_tensor_ndim + 1\n+       << \"D (batch mode)\";\n+    if (complex_input) {\n+      ss << \" (complex input adds an extra dimension)\";\n+    }\n+    ss << \", but got input \" << self.type() << self.sizes();\n+    throw std::runtime_error(ss.str());\n+  }\n+  bool is_batched = self.dim() == signal_tensor_ndim + 1;\n+\n+  Tensor input = self;\n+\n+  if (!is_batched) {\n+    input = input.unsqueeze(0);\n+  }\n+\n+  if (complex_input) {\n+    if (input.size(signal_ndim + 1) != 2) {\n+      std::ostringstream ss;\n+      ss << \"Expected an input tensor with a last dimension of size 2 \"\n+         << \"representing real + imaginary components, but got input \"\n+         << self.type() << self.sizes();\n+      throw std::runtime_error(ss.str());\n+    }\n+  }\n+\n+  // build signal_sizes and output_size\n+  if (signal_sizes.size() > 0 && (int64_t) signal_sizes.size() != signal_ndim) {\n+    std::ostringstream ss;\n+    ss << \"Expected signal_sizes to be empty (default) or of signal_ndim=\"\n+       << signal_ndim << \"D, but got signal_sizes=\" << signal_sizes;\n+    throw std::runtime_error(ss.str());\n+  }\n+  std::vector<int64_t> output_sizes(signal_ndim + 1 + (int) complex_output);\n+  output_sizes[0] = input.size(0);\n+  std::vector<int64_t> checked_signal_sizes(signal_ndim);\n+  int64_t input_size;\n+  for (int64_t i = 0; i < signal_ndim; i++) {\n+    input_size = input.size(i + 1);\n+    if (i == signal_ndim - 1 && onesided && complex_input && !complex_output) {\n+      // if last dim and complex-to-real onesided, input is only half of\n+      // signal, and we need to infer basing on signal_sizes, if given\n+      int64_t inferred_size;\n+      if (signal_sizes.size() > 0) {\n+        inferred_size = infer_ft_complex_to_real_onesided_size(input_size, signal_sizes[i]);\n+      } else {\n+        inferred_size = infer_ft_complex_to_real_onesided_size(input_size);\n+      }\n+      checked_signal_sizes[i] = inferred_size;\n+      output_sizes[i + 1] = inferred_size;\n+    } else {\n+      if (i == signal_ndim - 1 && onesided && !complex_input && complex_output) {\n+        // if last dim and real-to-complex onesided, output should be only\n+        // half of the signal, and we need to infer using input_size\n+        output_sizes[i + 1] = infer_ft_real_to_complex_onesided_size(input_size);\n+      } else {\n+        output_sizes[i + 1] = input_size;\n+      }\n+      checked_signal_sizes[i] = input_size;\n+      if (signal_sizes.size() > 0 && signal_sizes[i] != checked_signal_sizes[i]) {\n+        std::ostringstream ss;\n+        ss << \"Expected given signal_sizes=\" << signal_sizes << \" to have same \"\n+           << \"shape with input at signal dimension \" << i << \", but got \"\n+           << \"signal_sizes[\" << i << \"] = \" << signal_sizes[i] << \" and \"\n+           << \"input.size(\" << i + (int)is_batched << \") = \" << input_size;\n+        throw std::runtime_error(ss.str());\n+      }\n+    }\n+  }\n+  if (complex_output) {\n+    output_sizes[signal_ndim + 1] = 2;\n+  }\n+\n+  Tensor output = at::_fft_with_size(input, signal_ndim, complex_input,\n+                                     complex_output, inverse,\n+                                     checked_signal_sizes, normalized, onesided,\n+                                     output_sizes);\n+\n+  // un-batch if needed\n+  if (!is_batched) {\n+    return output.squeeze_(0);\n+  } else {\n+    return output;\n+  }\n }\n \n+Tensor fft(const Tensor& self, const int64_t signal_ndim, const bool normalized) {\n+  return _fft(self, signal_ndim, true, true, false, {}, normalized, false);\n+}\n+\n+Tensor ifft(const Tensor& self, const int64_t signal_ndim, const bool normalized) {\n+  return _fft(self, signal_ndim, true, true, true, {}, normalized, false);\n+}\n+\n+Tensor rfft(const Tensor& self, const int64_t signal_ndim, const bool normalized,\n+           const bool onesided) {\n+  return _fft(self, signal_ndim, false, true, false, {}, normalized, onesided);\n+}\n+\n+Tensor irfft(const Tensor& self, const int64_t signal_ndim,  IntList signal_sizes,\n+            const bool normalized, const bool onesided) {\n+  return _fft(self, signal_ndim, true, false, true, signal_sizes, normalized, onesided);\n+}","path":"aten/src/ATen/native/SpectralOps.cpp","position":148,"original_position":148,"commit_id":"1e3e1fdf253de18864657ebecdf9065ce2f465e1","original_commit_id":"1e3e1fdf253de18864657ebecdf9065ce2f465e1","user":{"login":"apaszke","id":4583066,"avatar_url":"https://avatars3.githubusercontent.com/u/4583066?v=4","gravatar_id":"","url":"https://api.github.com/users/apaszke","html_url":"https://github.com/apaszke","followers_url":"https://api.github.com/users/apaszke/followers","following_url":"https://api.github.com/users/apaszke/following{/other_user}","gists_url":"https://api.github.com/users/apaszke/gists{/gist_id}","starred_url":"https://api.github.com/users/apaszke/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/apaszke/subscriptions","organizations_url":"https://api.github.com/users/apaszke/orgs","repos_url":"https://api.github.com/users/apaszke/repos","events_url":"https://api.github.com/users/apaszke/events{/privacy}","received_events_url":"https://api.github.com/users/apaszke/received_events","type":"User","site_admin":false},"body":"Can we please use the `_fft(..., /*arg_name=*/true, /*arg_name2=*/false)` notation? That would make it much more readable. I'm concerned about having 3 booleans in a row","created_at":"2018-03-24T10:47:27Z","updated_at":"2018-03-24T10:48:01Z","html_url":"https://github.com/pytorch/pytorch/pull/5856#discussion_r176905969","pull_request_url":"https://api.github.com/repos/pytorch/pytorch/pulls/5856","author_association":"OWNER","_links":{"self":{"href":"https://api.github.com/repos/pytorch/pytorch/pulls/comments/176905969"},"html":{"href":"https://github.com/pytorch/pytorch/pull/5856#discussion_r176905969"},"pull_request":{"href":"https://api.github.com/repos/pytorch/pytorch/pulls/5856"}},"in_reply_to_id":176787371},"pull_request":{"url":"https://api.github.com/repos/pytorch/pytorch/pulls/5856","id":175715446,"html_url":"https://github.com/pytorch/pytorch/pull/5856","diff_url":"https://github.com/pytorch/pytorch/pull/5856.diff","patch_url":"https://github.com/pytorch/pytorch/pull/5856.patch","issue_url":"https://api.github.com/repos/pytorch/pytorch/issues/5856","number":5856,"state":"open","locked":false,"title":"[fft][2 of 3] Forward for fft methods","user":{"login":"SsnL","id":5674597,"avatar_url":"https://avatars2.githubusercontent.com/u/5674597?v=4","gravatar_id":"","url":"https://api.github.com/users/SsnL","html_url":"https://github.com/SsnL","followers_url":"https://api.github.com/users/SsnL/followers","following_url":"https://api.github.com/users/SsnL/following{/other_user}","gists_url":"https://api.github.com/users/SsnL/gists{/gist_id}","starred_url":"https://api.github.com/users/SsnL/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/SsnL/subscriptions","organizations_url":"https://api.github.com/users/SsnL/orgs","repos_url":"https://api.github.com/users/SsnL/repos","events_url":"https://api.github.com/users/SsnL/events{/privacy}","received_events_url":"https://api.github.com/users/SsnL/received_events","type":"User","site_admin":false},"body":"This builds upon #5855 , and is the second of three PRs that #5537 will be split into.","created_at":"2018-03-17T18:53:18Z","updated_at":"2018-03-24T10:48:01Z","closed_at":null,"merged_at":null,"merge_commit_sha":"20ddabb9738b53d1fdb1e142a0c7bf2c02492bf2","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/pytorch/pytorch/pulls/5856/commits","review_comments_url":"https://api.github.com/repos/pytorch/pytorch/pulls/5856/comments","review_comment_url":"https://api.github.com/repos/pytorch/pytorch/pulls/comments{/number}","comments_url":"https://api.github.com/repos/pytorch/pytorch/issues/5856/comments","statuses_url":"https://api.github.com/repos/pytorch/pytorch/statuses/1e3e1fdf253de18864657ebecdf9065ce2f465e1","head":{"label":"SsnL:fft_fwd","ref":"fft_fwd","sha":"1e3e1fdf253de18864657ebecdf9065ce2f465e1","user":{"login":"SsnL","id":5674597,"avatar_url":"https://avatars2.githubusercontent.com/u/5674597?v=4","gravatar_id":"","url":"https://api.github.com/users/SsnL","html_url":"https://github.com/SsnL","followers_url":"https://api.github.com/users/SsnL/followers","following_url":"https://api.github.com/users/SsnL/following{/other_user}","gists_url":"https://api.github.com/users/SsnL/gists{/gist_id}","starred_url":"https://api.github.com/users/SsnL/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/SsnL/subscriptions","organizations_url":"https://api.github.com/users/SsnL/orgs","repos_url":"https://api.github.com/users/SsnL/repos","events_url":"https://api.github.com/users/SsnL/events{/privacy}","received_events_url":"https://api.github.com/users/SsnL/received_events","type":"User","site_admin":false},"repo":{"id":103305558,"name":"pytorch","full_name":"SsnL/pytorch","owner":{"login":"SsnL","id":5674597,"avatar_url":"https://avatars2.githubusercontent.com/u/5674597?v=4","gravatar_id":"","url":"https://api.github.com/users/SsnL","html_url":"https://github.com/SsnL","followers_url":"https://api.github.com/users/SsnL/followers","following_url":"https://api.github.com/users/SsnL/following{/other_user}","gists_url":"https://api.github.com/users/SsnL/gists{/gist_id}","starred_url":"https://api.github.com/users/SsnL/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/SsnL/subscriptions","organizations_url":"https://api.github.com/users/SsnL/orgs","repos_url":"https://api.github.com/users/SsnL/repos","events_url":"https://api.github.com/users/SsnL/events{/privacy}","received_events_url":"https://api.github.com/users/SsnL/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/SsnL/pytorch","description":"Tensors and Dynamic neural networks in Python  with strong GPU acceleration","fork":true,"url":"https://api.github.com/repos/SsnL/pytorch","forks_url":"https://api.github.com/repos/SsnL/pytorch/forks","keys_url":"https://api.github.com/repos/SsnL/pytorch/keys{/key_id}","collaborators_url":"https://api.github.com/repos/SsnL/pytorch/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/SsnL/pytorch/teams","hooks_url":"https://api.github.com/repos/SsnL/pytorch/hooks","issue_events_url":"https://api.github.com/repos/SsnL/pytorch/issues/events{/number}","events_url":"https://api.github.com/repos/SsnL/pytorch/events","assignees_url":"https://api.github.com/repos/SsnL/pytorch/assignees{/user}","branches_url":"https://api.github.com/repos/SsnL/pytorch/branches{/branch}","tags_url":"https://api.github.com/repos/SsnL/pytorch/tags","blobs_url":"https://api.github.com/repos/SsnL/pytorch/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/SsnL/pytorch/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/SsnL/pytorch/git/refs{/sha}","trees_url":"https://api.github.com/repos/SsnL/pytorch/git/trees{/sha}","statuses_url":"https://api.github.com/repos/SsnL/pytorch/statuses/{sha}","languages_url":"https://api.github.com/repos/SsnL/pytorch/languages","stargazers_url":"https://api.github.com/repos/SsnL/pytorch/stargazers","contributors_url":"https://api.github.com/repos/SsnL/pytorch/contributors","subscribers_url":"https://api.github.com/repos/SsnL/pytorch/subscribers","subscription_url":"https://api.github.com/repos/SsnL/pytorch/subscription","commits_url":"https://api.github.com/repos/SsnL/pytorch/commits{/sha}","git_commits_url":"https://api.github.com/repos/SsnL/pytorch/git/commits{/sha}","comments_url":"https://api.github.com/repos/SsnL/pytorch/comments{/number}","issue_comment_url":"https://api.github.com/repos/SsnL/pytorch/issues/comments{/number}","contents_url":"https://api.github.com/repos/SsnL/pytorch/contents/{+path}","compare_url":"https://api.github.com/repos/SsnL/pytorch/compare/{base}...{head}","merges_url":"https://api.github.com/repos/SsnL/pytorch/merges","archive_url":"https://api.github.com/repos/SsnL/pytorch/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/SsnL/pytorch/downloads","issues_url":"https://api.github.com/repos/SsnL/pytorch/issues{/number}","pulls_url":"https://api.github.com/repos/SsnL/pytorch/pulls{/number}","milestones_url":"https://api.github.com/repos/SsnL/pytorch/milestones{/number}","notifications_url":"https://api.github.com/repos/SsnL/pytorch/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/SsnL/pytorch/labels{/name}","releases_url":"https://api.github.com/repos/SsnL/pytorch/releases{/id}","deployments_url":"https://api.github.com/repos/SsnL/pytorch/deployments","created_at":"2017-09-12T18:13:43Z","updated_at":"2018-03-23T16:52:54Z","pushed_at":"2018-03-24T03:19:19Z","git_url":"git://github.com/SsnL/pytorch.git","ssh_url":"git@github.com:SsnL/pytorch.git","clone_url":"https://github.com/SsnL/pytorch.git","svn_url":"https://github.com/SsnL/pytorch","homepage":"http://pytorch.org","size":25110,"stargazers_count":0,"watchers_count":0,"language":"Python","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":0,"license":null,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"pytorch:master","ref":"master","sha":"d707dae013778aa5fef01c787c903b0edce90add","user":{"login":"pytorch","id":21003710,"avatar_url":"https://avatars3.githubusercontent.com/u/21003710?v=4","gravatar_id":"","url":"https://api.github.com/users/pytorch","html_url":"https://github.com/pytorch","followers_url":"https://api.github.com/users/pytorch/followers","following_url":"https://api.github.com/users/pytorch/following{/other_user}","gists_url":"https://api.github.com/users/pytorch/gists{/gist_id}","starred_url":"https://api.github.com/users/pytorch/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pytorch/subscriptions","organizations_url":"https://api.github.com/users/pytorch/orgs","repos_url":"https://api.github.com/users/pytorch/repos","events_url":"https://api.github.com/users/pytorch/events{/privacy}","received_events_url":"https://api.github.com/users/pytorch/received_events","type":"Organization","site_admin":false},"repo":{"id":65600975,"name":"pytorch","full_name":"pytorch/pytorch","owner":{"login":"pytorch","id":21003710,"avatar_url":"https://avatars3.githubusercontent.com/u/21003710?v=4","gravatar_id":"","url":"https://api.github.com/users/pytorch","html_url":"https://github.com/pytorch","followers_url":"https://api.github.com/users/pytorch/followers","following_url":"https://api.github.com/users/pytorch/following{/other_user}","gists_url":"https://api.github.com/users/pytorch/gists{/gist_id}","starred_url":"https://api.github.com/users/pytorch/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pytorch/subscriptions","organizations_url":"https://api.github.com/users/pytorch/orgs","repos_url":"https://api.github.com/users/pytorch/repos","events_url":"https://api.github.com/users/pytorch/events{/privacy}","received_events_url":"https://api.github.com/users/pytorch/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/pytorch/pytorch","description":"Tensors and Dynamic neural networks in Python  with strong GPU acceleration","fork":false,"url":"https://api.github.com/repos/pytorch/pytorch","forks_url":"https://api.github.com/repos/pytorch/pytorch/forks","keys_url":"https://api.github.com/repos/pytorch/pytorch/keys{/key_id}","collaborators_url":"https://api.github.com/repos/pytorch/pytorch/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/pytorch/pytorch/teams","hooks_url":"https://api.github.com/repos/pytorch/pytorch/hooks","issue_events_url":"https://api.github.com/repos/pytorch/pytorch/issues/events{/number}","events_url":"https://api.github.com/repos/pytorch/pytorch/events","assignees_url":"https://api.github.com/repos/pytorch/pytorch/assignees{/user}","branches_url":"https://api.github.com/repos/pytorch/pytorch/branches{/branch}","tags_url":"https://api.github.com/repos/pytorch/pytorch/tags","blobs_url":"https://api.github.com/repos/pytorch/pytorch/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/pytorch/pytorch/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/pytorch/pytorch/git/refs{/sha}","trees_url":"https://api.github.com/repos/pytorch/pytorch/git/trees{/sha}","statuses_url":"https://api.github.com/repos/pytorch/pytorch/statuses/{sha}","languages_url":"https://api.github.com/repos/pytorch/pytorch/languages","stargazers_url":"https://api.github.com/repos/pytorch/pytorch/stargazers","contributors_url":"https://api.github.com/repos/pytorch/pytorch/contributors","subscribers_url":"https://api.github.com/repos/pytorch/pytorch/subscribers","subscription_url":"https://api.github.com/repos/pytorch/pytorch/subscription","commits_url":"https://api.github.com/repos/pytorch/pytorch/commits{/sha}","git_commits_url":"https://api.github.com/repos/pytorch/pytorch/git/commits{/sha}","comments_url":"https://api.github.com/repos/pytorch/pytorch/comments{/number}","issue_comment_url":"https://api.github.com/repos/pytorch/pytorch/issues/comments{/number}","contents_url":"https://api.github.com/repos/pytorch/pytorch/contents/{+path}","compare_url":"https://api.github.com/repos/pytorch/pytorch/compare/{base}...{head}","merges_url":"https://api.github.com/repos/pytorch/pytorch/merges","archive_url":"https://api.github.com/repos/pytorch/pytorch/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/pytorch/pytorch/downloads","issues_url":"https://api.github.com/repos/pytorch/pytorch/issues{/number}","pulls_url":"https://api.github.com/repos/pytorch/pytorch/pulls{/number}","milestones_url":"https://api.github.com/repos/pytorch/pytorch/milestones{/number}","notifications_url":"https://api.github.com/repos/pytorch/pytorch/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/pytorch/pytorch/labels{/name}","releases_url":"https://api.github.com/repos/pytorch/pytorch/releases{/id}","deployments_url":"https://api.github.com/repos/pytorch/pytorch/deployments","created_at":"2016-08-13T05:26:41Z","updated_at":"2018-03-24T10:43:37Z","pushed_at":"2018-03-24T09:12:24Z","git_url":"git://github.com/pytorch/pytorch.git","ssh_url":"git@github.com:pytorch/pytorch.git","clone_url":"https://github.com/pytorch/pytorch.git","svn_url":"https://github.com/pytorch/pytorch","homepage":"http://pytorch.org","size":25346,"stargazers_count":13184,"watchers_count":13184,"language":"Python","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":2816,"mirror_url":null,"archived":false,"open_issues_count":808,"license":{"key":"other","name":"Other","spdx_id":null,"url":null},"forks":2816,"open_issues":808,"watchers":13184,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/pytorch/pytorch/pulls/5856"},"html":{"href":"https://github.com/pytorch/pytorch/pull/5856"},"issue":{"href":"https://api.github.com/repos/pytorch/pytorch/issues/5856"},"comments":{"href":"https://api.github.com/repos/pytorch/pytorch/issues/5856/comments"},"review_comments":{"href":"https://api.github.com/repos/pytorch/pytorch/pulls/5856/comments"},"review_comment":{"href":"https://api.github.com/repos/pytorch/pytorch/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/pytorch/pytorch/pulls/5856/commits"},"statuses":{"href":"https://api.github.com/repos/pytorch/pytorch/statuses/1e3e1fdf253de18864657ebecdf9065ce2f465e1"}},"author_association":"CONTRIBUTOR"}}
{"actor":{"display_login":"apaszke"}}