Repository: kymatio/kymatio

Issue:
JTFS `frequency_averaging` produces IndexError
I have encountered an issue with frequency averaging in JTFS. I am attempting to average over one octave.

```
import torch
from kymatio.torch import TimeFrequencyScattering
jtfs_kwargs = {'shape': 2**15, 'T': 2**15, 'Q': (12, 1), 'J': 8, 'J_fr': 6, 'F': 12}
jtfs = TimeFrequencyScattering(**jtfs_kwargs, format="time")
jtfs(torch.randn(2**15))
```

Stack trace:
```
  File "/homes/cv300/venvs/dptm-wyn/lib/python3.8/site-packages/kymatio/scattering1d/core/timefrequency_scattering.py", line 445, in jtfs_average_and_format
    path = frequency_averaging(path, backend,
  File "/homes/cv300/venvs/dptm-wyn/lib/python3.8/site-packages/kymatio/scattering1d/core/timefrequency_scattering.py", line 378, in frequency_averaging
    S_c = backend.cdgmm(U_hat, phi_fr_f['levels'][k_in])
IndexError: list index out of range
```

Frequency averaging fails as the `phi_fr_f` is selected at a resolution that is out of bounds.

the input resolution is determined by `k_in = U_2['j_fr'][-1]`. somehow this `j_fr` is larger to the number of resolutions. In this case, `J_fr = 6`, `len(phi_fr_f['levels']) == 4` and the violating `j_fr = 4`

I do not know whether we have created an insufficient number of `phi_fr_f` resolutions. 

@lostanlen 
JTFS `frequency_averaging` produces IndexError
I have encountered an issue with frequency averaging in JTFS. I am attempting to average over one octave.

```
import torch
from kymatio.torch import TimeFrequencyScattering
jtfs_kwargs = {'shape': 2**15, 'T': 2**15, 'Q': (12, 1), 'J': 8, 'J_fr': 6, 'F': 12}
jtfs = TimeFrequencyScattering(**jtfs_kwargs, format="time")
jtfs(torch.randn(2**15))
```

Stack trace:
```
  File "/homes/cv300/venvs/dptm-wyn/lib/python3.8/site-packages/kymatio/scattering1d/core/timefrequency_scattering.py", line 445, in jtfs_average_and_format
    path = frequency_averaging(path, backend,
  File "/homes/cv300/venvs/dptm-wyn/lib/python3.8/site-packages/kymatio/scattering1d/core/timefrequency_scattering.py", line 378, in frequency_averaging
    S_c = backend.cdgmm(U_hat, phi_fr_f['levels'][k_in])
IndexError: list index out of range
```

Frequency averaging fails as the `phi_fr_f` is selected at a resolution that is out of bounds.

the input resolution is determined by `k_in = U_2['j_fr'][-1]`. somehow this `j_fr` is larger to the number of resolutions. In this case, `J_fr = 6`, `len(phi_fr_f['levels']) == 4` and the violating `j_fr = 4`

I do not know whether we have created an insufficient number of `phi_fr_f` resolutions. 

@lostanlen 


Discussion:
after an offline conversation with @cyrusvahidi , we have traced down the problem to line 376

https://github.com/kymatio/kymatio/blob/a175292e5cc8d8194f5fea86ed36116b66f72c31/kymatio/scattering1d/core/timefrequency_scattering.py#L370-L384

it should read as `k_in = min(U_2['j_fr'][-1], log2_F)`
after an offline conversation with @cyrusvahidi , we have traced down the problem to line 376

https://github.com/kymatio/kymatio/blob/a175292e5cc8d8194f5fea86ed36116b66f72c31/kymatio/scattering1d/core/timefrequency_scattering.py#L370-L384

it should read as `k_in = min(U_2['j_fr'][-1], log2_F)`

Resolving patch:
diff --git a/kymatio/scattering1d/core/timefrequency_scattering.py b/kymatio/scattering1d/core/timefrequency_scattering.py
--- a/kymatio/scattering1d/core/timefrequency_scattering.py
+++ b/kymatio/scattering1d/core/timefrequency_scattering.py
@@ -373,7 +373,7 @@ def frequency_averaging(U_2, backend, phi_fr_f, oversampling_fr, average_fr):
             S_2_T = backend.average_global(U_2_T)
             n1_stride = U_2['n1_max']
         elif average_fr == 'local':
-            k_in = U_2['j_fr'][-1]
+            k_in = min(U_2['j_fr'][-1], log2_F)
             k_J = max(log2_F - k_in - oversampling_fr, 0)
             U_hat = backend.rfft(U_2_T)
             S_c = backend.cdgmm(U_hat, phi_fr_f['levels'][k_in])


Test patch:
diff --git a/tests/scattering1d/test_timefrequency_scattering.py b/tests/scattering1d/test_timefrequency_scattering.py
--- a/tests/scattering1d/test_timefrequency_scattering.py
+++ b/tests/scattering1d/test_timefrequency_scattering.py
@@ -586,4 +586,33 @@ def test_jtfs_torch_tf_frontends(frontend):
     assert S.F == (2**S.J_fr)
     Sx = S(x)
     assert isinstance(Sx, torch.Tensor) if frontend == "torch" else isinstance(Sx, tf.Tensor)
-    assert Sx.ndim == 3
\ No newline at end of file
+    assert Sx.ndim == 3
+
+
+frontends = ["torch", "tensorflow"]
+@pytest.mark.parametrize("frontend", frontends)
+def test_F(frontend):
+    """
+    Applies scattering on a stored signal with non-default F
+    """
+    kwargs = {"J": 8, "J_fr": 3, "shape": (8192,), "Q": 3}
+    x = torch.zeros(kwargs["shape"])
+    x[kwargs["shape"][0] // 2] = 1
+
+    # default F
+    jtfs0 = TimeFrequencyScattering(frontend="torch", format="joint", **kwargs)
+
+    # explicit F
+    jtfs1 = TimeFrequencyScattering(frontend="torch", format="joint", F=2**kwargs['J_fr'], **kwargs)
+
+    Sx0 = jtfs0(x)
+    Sx1 = jtfs1(x)
+    assert torch.equal(Sx0, Sx1)
+    assert Sx0.shape == Sx1.shape
+
+    """ non-default F smaller than 2**J_fr. This checks that the subsampling
+        index does not exceed log2_F and cause an IndexError at runtime (#976).
+    """
+    jtfs2 = TimeFrequencyScattering(frontend="torch", format="joint", F=2**kwargs['J_fr'] // 4, **kwargs)
+    Sx2 = jtfs2(x)
+    assert Sx0.shape[0] == Sx2.shape[0] and Sx0.shape[2] == Sx2.shape[2] and Sx0.shape[1] < Sx2.shape[1]
\ No newline at end of file