<h3 dir="auto">Describe the issue:</h3>
<p dir="auto">In numpy 1.19.3 or later, when using np.linalg.svd on a certain architecture, the 2nd and 3rd columns of the <code class="notranslate">u</code> result array and the 2nd and 3rd rows of the <code class="notranslate">vh</code> result arrays are switched.</p>
<h3 dir="auto">Reproduce the code example:</h3>
<div class="highlight highlight-source-python notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="import numpy as np
array = np.array([[-4.0, -2.0, -2.0], [-2.0, -4.0, -2.0], [-2.0, -2.0, -4.0]])
expected_u = np.array(
    [
        [-5.77350269e-01, 8.16496581e-01, -5.04179082e-17],
        [-5.77350269e-01, -4.08248290e-01, -7.07106781e-01],
        [-5.77350269e-01, -4.08248290e-01, 7.07106781e-01],
    ]
)
expected_vh = np.array(
    [
        [0.57735027, 0.57735027, 0.57735027],
        [-0.81649658, 0.40824829, 0.40824829],
        [-0.0, 0.70710678, -0.70710678],
    ]
)
(u, _, vh) = np.linalg.svd(array, full_matrices=False)
np.testing.assert_almost_equal(expected_u, u)
np.testing.assert_almost_equal(expected_vh, vh)"><pre class="notranslate"><span class="pl-k">import</span> <span class="pl-s1">numpy</span> <span class="pl-k">as</span> <span class="pl-s1">np</span>
<span class="pl-s1">array</span> <span class="pl-c1">=</span> <span class="pl-s1">np</span>.<span class="pl-en">array</span>([[<span class="pl-c1">-</span><span class="pl-c1">4.0</span>, <span class="pl-c1">-</span><span class="pl-c1">2.0</span>, <span class="pl-c1">-</span><span class="pl-c1">2.0</span>], [<span class="pl-c1">-</span><span class="pl-c1">2.0</span>, <span class="pl-c1">-</span><span class="pl-c1">4.0</span>, <span class="pl-c1">-</span><span class="pl-c1">2.0</span>], [<span class="pl-c1">-</span><span class="pl-c1">2.0</span>, <span class="pl-c1">-</span><span class="pl-c1">2.0</span>, <span class="pl-c1">-</span><span class="pl-c1">4.0</span>]])
<span class="pl-s1">expected_u</span> <span class="pl-c1">=</span> <span class="pl-s1">np</span>.<span class="pl-en">array</span>(
    [
        [<span class="pl-c1">-</span><span class="pl-c1">5.77350269e-01</span>, <span class="pl-c1">8.16496581e-01</span>, <span class="pl-c1">-</span><span class="pl-c1">5.04179082e-17</span>],
        [<span class="pl-c1">-</span><span class="pl-c1">5.77350269e-01</span>, <span class="pl-c1">-</span><span class="pl-c1">4.08248290e-01</span>, <span class="pl-c1">-</span><span class="pl-c1">7.07106781e-01</span>],
        [<span class="pl-c1">-</span><span class="pl-c1">5.77350269e-01</span>, <span class="pl-c1">-</span><span class="pl-c1">4.08248290e-01</span>, <span class="pl-c1">7.07106781e-01</span>],
    ]
)
<span class="pl-s1">expected_vh</span> <span class="pl-c1">=</span> <span class="pl-s1">np</span>.<span class="pl-en">array</span>(
    [
        [<span class="pl-c1">0.57735027</span>, <span class="pl-c1">0.57735027</span>, <span class="pl-c1">0.57735027</span>],
        [<span class="pl-c1">-</span><span class="pl-c1">0.81649658</span>, <span class="pl-c1">0.40824829</span>, <span class="pl-c1">0.40824829</span>],
        [<span class="pl-c1">-</span><span class="pl-c1">0.0</span>, <span class="pl-c1">0.70710678</span>, <span class="pl-c1">-</span><span class="pl-c1">0.70710678</span>],
    ]
)
(<span class="pl-s1">u</span>, <span class="pl-s1">_</span>, <span class="pl-s1">vh</span>) <span class="pl-c1">=</span> <span class="pl-s1">np</span>.<span class="pl-s1">linalg</span>.<span class="pl-en">svd</span>(<span class="pl-s1">array</span>, <span class="pl-s1">full_matrices</span><span class="pl-c1">=</span><span class="pl-c1">False</span>)
<span class="pl-s1">np</span>.<span class="pl-s1">testing</span>.<span class="pl-en">assert_almost_equal</span>(<span class="pl-s1">expected_u</span>, <span class="pl-s1">u</span>)
<span class="pl-s1">np</span>.<span class="pl-s1">testing</span>.<span class="pl-en">assert_almost_equal</span>(<span class="pl-s1">expected_vh</span>, <span class="pl-s1">vh</span>)</pre></div>
<h3 dir="auto">Error message:</h3>
<div class="highlight highlight-source-shell notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="Mismatched elements: 6 / 9 (66.7%)
Max absolute difference: 1.11535507
Max relative difference: 1.60871119e+16
 x: array([[-5.7735027e-01,  8.1649658e-01, -5.0417908e-17],
       [-5.7735027e-01, -4.0824829e-01, -7.0710678e-01],
       [-5.7735027e-01, -4.0824829e-01,  7.0710678e-01]])
 y: array([[-5.7735027e-01, -5.0754703e-17,  8.1649658e-01],
       [-5.7735027e-01, -7.0710678e-01, -4.0824829e-01],
       [-5.7735027e-01,  7.0710678e-01, -4.0824829e-01]])

Exited with code exit status 1"><pre class="notranslate">Mismatched elements: 6 / 9 (66.7%)
Max absolute difference: 1.11535507
Max relative difference: 1.60871119e+16
 x: array([[-5.7735027e-01,  8.1649658e-01, -5.0417908e-17],
       [-5.7735027e-01, -4.0824829e-01, -7.0710678e-01],
       [-5.7735027e-01, -4.0824829e-01,  7.0710678e-01]])
 y: array([[-5.7735027e-01, -5.0754703e-17,  8.1649658e-01],
       [-5.7735027e-01, -7.0710678e-01, -4.0824829e-01],
       [-5.7735027e-01,  7.0710678e-01, -4.0824829e-01]])

Exited with code <span class="pl-c1">exit</span> status 1</pre></div>
<h3 dir="auto">Runtime information:</h3>
<p dir="auto">1.19.3<br>
3.9.13 (main, May 27 2022, 22:45:39)<br>
[GCC 9.4.0]</p>
<p dir="auto">Note: This behavior is also occurring with numpy 1.24.1</p>
<h3 dir="auto">Context for the issue:</h3>
<p dir="auto">The behavior is observed in CircleCI with the following info:<br>
Docker with Python using cimg/python:3.9.13<br>
Model name: Intel(R) Xeon(R) Platinum 8124M CPU @ 3.00GHz</p>
<p dir="auto">Branch featuring bug: <a href="https://github.com/lace/entente/tree/numpy-bug">https://github.com/lace/entente/tree/numpy-bug</a><br>
Failure in CI: <a href="https://app.circleci.com/pipelines/github/lace/entente/1829/workflows/8973da1a-38f4-46a3-8dc5-63a8a1e4ee17/jobs/5937" rel="nofollow">https://app.circleci.com/pipelines/github/lace/entente/1829/workflows/8973da1a-38f4-46a3-8dc5-63a8a1e4ee17/jobs/5937</a><br>
CI config: <a href="https://github.com/lace/entente/blob/numpy-bug/.circleci/config.yml#L18">https://github.com/lace/entente/blob/numpy-bug/.circleci/config.yml</a></p>
<p dir="auto">Not reproducible on MacOS or Linux native.<br>
Not reproducible on MacOS or Linux with same Docker image.<br>
Not reproducible in Numpy 1.19.2.</p>
<h3 dir="auto">Describe the issue:</h3>
<p dir="auto"><a href="https://numpy.org/devdocs/reference/generated/numpy.lib.format.html" rel="nofollow">NPZ files</a> are zip archives and may contain <em>duplicate</em> entries. While <a href="https://numpy.org/devdocs/reference/generated/numpy.savez.html" rel="nofollow">numpy.savez</a> prevents this by ensuring unique keys on the API level, it is easy to generate a .npz file with duplicate keys by other means, e.g. using <a href="https://docs.python.org/3/library/zipfile.html#zipfile-objects" rel="nofollow">zipfile.ZipFile</a>.</p>
<p dir="auto">When iterating over such a .npz file via <a href="https://github.com/numpy/numpy/blob/c37b6af5577584f6ab0a08a161d9d657741341ea/numpy/lib/npyio.py#L104">NpzFile.items()</a>, duplicate keys are yielded multiple times, but always with the <strong>last value stored</strong>. It is not possible to retrieve previous values stored under that key (at least not using the <code class="notranslate">NpzFile</code> API) although they are contained in the zip archive.</p>
<p dir="auto">I would expect one of these alternative behaviors for <code class="notranslate">NpzFile.items()</code>:</p>
<ol dir="auto">
<li>Yield a duplicate key only <em>once</em> (with the last value stored). This is consistent with the dictionary-like API of <code class="notranslate">NpzFile</code>, but discards content in the zip archive.</li>
<li>Yield the correct values stored with duplicate keys. This is consistent with the content of the zip archive, but somewhat at odds with the dictionary-like API.</li>
</ol>
<p dir="auto">If there is any interest, I can start contributing a PR for either behavior.</p>
<h3 dir="auto">Reproduce the code example:</h3>
<div class="highlight highlight-source-python notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="from zipfile import ZipFile
import numpy as np

# Generate data.npz with duplicate keys

with ZipFile(&quot;data.npz&quot;, &quot;w&quot;) as zf:
    for a in ([1], [2, 2], [3, 3, 3]):
        with zf.open(&quot;duplicate_key.npy&quot;, &quot;w&quot;) as f:
            np.save(f, a)

# Iterating over data.npz with NpzFile.items() yields the last value multiple times

for key, val in np.load(&quot;data.npz&quot;).items():
    print(key, val)

# Output:
# duplicate_key [3 3 3]
# duplicate_key [3 3 3]
# duplicate_key [3 3 3]

# But all three original values are still contained in zip file

with ZipFile(&quot;data.npz&quot;, &quot;r&quot;) as zf:
    for info in zf.infolist():
        with zf.open(info) as f:
            print(info, np.load(f))

# Output:
# &lt;ZipInfo filename='duplicate_key.npy' filemode='?rw-------' file_size=136&gt; [1]
# &lt;ZipInfo filename='duplicate_key.npy' filemode='?rw-------' file_size=144&gt; [2 2]
# &lt;ZipInfo filename='duplicate_key.npy' filemode='?rw-------' file_size=152&gt; [3 3 3]"><pre class="notranslate"><span class="pl-k">from</span> <span class="pl-s1">zipfile</span> <span class="pl-k">import</span> <span class="pl-v">ZipFile</span>
<span class="pl-k">import</span> <span class="pl-s1">numpy</span> <span class="pl-k">as</span> <span class="pl-s1">np</span>

<span class="pl-c"># Generate data.npz with duplicate keys</span>

<span class="pl-k">with</span> <span class="pl-v">ZipFile</span>(<span class="pl-s">"data.npz"</span>, <span class="pl-s">"w"</span>) <span class="pl-k">as</span> <span class="pl-s1">zf</span>:
    <span class="pl-k">for</span> <span class="pl-s1">a</span> <span class="pl-c1">in</span> ([<span class="pl-c1">1</span>], [<span class="pl-c1">2</span>, <span class="pl-c1">2</span>], [<span class="pl-c1">3</span>, <span class="pl-c1">3</span>, <span class="pl-c1">3</span>]):
        <span class="pl-k">with</span> <span class="pl-s1">zf</span>.<span class="pl-en">open</span>(<span class="pl-s">"duplicate_key.npy"</span>, <span class="pl-s">"w"</span>) <span class="pl-k">as</span> <span class="pl-s1">f</span>:
            <span class="pl-s1">np</span>.<span class="pl-en">save</span>(<span class="pl-s1">f</span>, <span class="pl-s1">a</span>)

<span class="pl-c"># Iterating over data.npz with NpzFile.items() yields the last value multiple times</span>

<span class="pl-k">for</span> <span class="pl-s1">key</span>, <span class="pl-s1">val</span> <span class="pl-c1">in</span> <span class="pl-s1">np</span>.<span class="pl-en">load</span>(<span class="pl-s">"data.npz"</span>).<span class="pl-en">items</span>():
    <span class="pl-en">print</span>(<span class="pl-s1">key</span>, <span class="pl-s1">val</span>)

<span class="pl-c"># Output:</span>
<span class="pl-c"># duplicate_key [3 3 3]</span>
<span class="pl-c"># duplicate_key [3 3 3]</span>
<span class="pl-c"># duplicate_key [3 3 3]</span>

<span class="pl-c"># But all three original values are still contained in zip file</span>

<span class="pl-k">with</span> <span class="pl-v">ZipFile</span>(<span class="pl-s">"data.npz"</span>, <span class="pl-s">"r"</span>) <span class="pl-k">as</span> <span class="pl-s1">zf</span>:
    <span class="pl-k">for</span> <span class="pl-s1">info</span> <span class="pl-c1">in</span> <span class="pl-s1">zf</span>.<span class="pl-en">infolist</span>():
        <span class="pl-k">with</span> <span class="pl-s1">zf</span>.<span class="pl-en">open</span>(<span class="pl-s1">info</span>) <span class="pl-k">as</span> <span class="pl-s1">f</span>:
            <span class="pl-en">print</span>(<span class="pl-s1">info</span>, <span class="pl-s1">np</span>.<span class="pl-en">load</span>(<span class="pl-s1">f</span>))

<span class="pl-c"># Output:</span>
<span class="pl-c"># &lt;ZipInfo filename='duplicate_key.npy' filemode='?rw-------' file_size=136&gt; [1]</span>
<span class="pl-c"># &lt;ZipInfo filename='duplicate_key.npy' filemode='?rw-------' file_size=144&gt; [2 2]</span>
<span class="pl-c"># &lt;ZipInfo filename='duplicate_key.npy' filemode='?rw-------' file_size=152&gt; [3 3 3]</span></pre></div>
<h3 dir="auto">Error message:</h3>
<p dir="auto"><em>No response</em></p>
<h3 dir="auto">NumPy/Python version information:</h3>
<p dir="auto">1.21.2 3.8.11 (default, Aug  3 2021, 15:09:35)<br>
[GCC 7.5.0]</p>