<div dir="ltr"><div dir="ltr"><br></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Thu, May 9, 2019 at 2:25 PM Peter Bell <<a href="mailto:PeterBell10@live.co.uk">PeterBell10@live.co.uk</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<div lang="EN-GB">
<div class="gmail-m_2092780952128280331WordSection1">
<p class="MsoNormal">>> Should backends be allowed to add functionality or should they be completely<u></u><u></u></p>
<p class="MsoNormal">>> interchangeable. E.g. unlike fftpack, FFTW has support for long doubles;<u></u><u></u></p>
<p class="MsoNormal">>> should this be exposed to SciPy users?<u></u><u></u></p>
<p class="MsoNormal">><u></u> <u></u></p>
<p class="MsoNormal">> If a user writes backend-specific code, then they may just as well bypass the<u></u><u></u></p>
<p class="MsoNormal">> backend right, and call the other library directly?</p></div></div></blockquote><div><br></div><div>After some thought/discussion I'm going to disagree with myself here.</div><div> <br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div lang="EN-GB"><div class="gmail-m_2092780952128280331WordSection1"><p class="MsoNormal"><u></u><u></u></p>
<p class="MsoNormal"><u></u> <u></u></p>
<p class="MsoNormal">I agree, in fact I quote you in the proposal saying something similar.<u></u><u></u></p>
<p class="MsoNormal"><u></u> <u></u></p>
<p class="MsoNormal">> Although for long double things will work out of the box perhaps, because the<u></u><u></u></p>
<p class="MsoNormal">> dtype of an input array doesn't come back in the API. So the array can be<u></u><u></u></p>
<p class="MsoNormal">> passed straight to the backend.<u></u><u></u></p>
<p class="MsoNormal"><u></u> <u></u></p>
<p class="MsoNormal">This was why I picked it as an example. It doesn't add any new parameters or<u></u><u></u></p>
<p class="MsoNormal">change the function signature at all, but it does change the result of a<u></u><u></u></p>
<p class="MsoNormal">particular call to the API. So it's a very minimal change yet it still breaks<u></u><u></u></p>
<p class="MsoNormal">the interchangeability of backends.<u></u><u></u></p>
</div></div></blockquote><div><br></div><div><div>Peter and I had a very interesting discussion in comments on his
Google doc. It relates to the design question about what a backend is allowed to do (and even higher level, what is a backend for). I'm now relatively sure I
can summarize it well, so I'll give it a try.</div><div><br></div><div>"completely
interchangeable", as Peter uses above, implies that both API and
semantics of the backend are matching 100% with what SciPy does. Peter's
example from the doc (where _dct_func is the backend-provided function)
may help to illustrate that:<br></div><div><br></div><div><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt" id="gmail-docs-internal-guid-22c4b2d2-7fff-015b-a212-dc4e52bed393"><span style="font-size:11pt;font-family:Consolas,sans-serif;color:rgb(0,0,255);background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap"> def</span><span style="font-size:11pt;font-family:Consolas,sans-serif;color:rgb(0,0,0);background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap"> </span><span style="font-size:11pt;font-family:Consolas,sans-serif;color:rgb(163,21,21);background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap">dct</span><span style="font-size:11pt;font-family:Consolas,sans-serif;color:rgb(0,0,0);background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap">(x, type=2, n=None, axis=-1, norm=None, overwrite_x=False):</span></p><div dir="ltr" style="margin-left:0pt"><table style="border:medium none;border-collapse:collapse"><colgroup></colgroup><tbody><tr style="height:0pt"><td style="vertical-align:top;padding:5pt"><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt"><span style="font-size:11pt;font-family:Consolas,sans-serif;color:rgb(0,0,0);background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap"> </span><span style="font-size:11pt;font-family:Consolas,sans-serif;color:rgb(0,0,255);background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap"> </span><span style="font-size:11pt;font-family:Consolas,sans-serif;color:rgb(0,0,0);background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap"> </span><span style="font-size:11pt;font-family:Consolas,sans-serif;color:rgb(0,128,0);background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap"># From current implementation, also converts float16 to float32</span></p><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt"><span style="font-size:11pt;font-family:Consolas,sans-serif;color:rgb(0,0,0);background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap"> </span><span style="font-size:11pt;font-family:Consolas,sans-serif;color:rgb(0,0,255);background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap"> </span><span style="font-size:11pt;font-family:Consolas,sans-serif;color:rgb(0,0,0);background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap"> x = _asfarray(x)</span><span style="font-size:11pt;font-family:Consolas,sans-serif;color:rgb(0,0,0);background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap"><br></span><span style="font-size:11pt;font-family:Consolas,sans-serif;color:rgb(0,0,0);background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap"> </span><span style="font-size:11pt;font-family:Consolas,sans-serif;color:rgb(0,0,255);background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap"> </span><span style="font-size:11pt;font-family:Consolas,sans-serif;color:rgb(0,0,0);background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap"> </span><span style="font-size:11pt;font-family:Consolas,sans-serif;color:rgb(0,0,255);background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap">if</span><span style="font-size:11pt;font-family:Consolas,sans-serif;color:rgb(0,0,0);background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap"> x.dtype not </span><span style="font-size:11pt;font-family:Consolas,sans-serif;color:rgb(0,0,255);background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap">in</span><span style="font-size:11pt;font-family:Consolas,sans-serif;color:rgb(0,0,0);background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap"> (np.float32, np.float64,</span></p><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt"><span style="font-size:11pt;font-family:Consolas,sans-serif;color:rgb(0,0,0);background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap"> </span><span style="font-size:11pt;font-family:Consolas,sans-serif;color:rgb(0,0,255);background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap"> </span><span style="font-size:11pt;font-family:Consolas,sans-serif;color:rgb(0,0,0);background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap"> np.complex64, np.complex128):</span><span style="font-size:11pt;font-family:Consolas,sans-serif;color:rgb(0,0,0);background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap"><br></span><span style="font-size:11pt;font-family:Consolas,sans-serif;color:rgb(0,0,0);background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap"> </span><span style="font-size:11pt;font-family:Consolas,sans-serif;color:rgb(0,0,255);background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap"> </span><span style="font-size:11pt;font-family:Consolas,sans-serif;color:rgb(0,0,0);background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap"> </span><span style="font-size:11pt;font-family:Consolas,sans-serif;color:rgb(0,0,255);background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap">raise</span><span style="font-size:11pt;font-family:Consolas,sans-serif;color:rgb(0,0,0);background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap"> ValueError(</span><span style="font-size:11pt;font-family:Consolas,sans-serif;color:rgb(163,21,21);background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap">"type {} is not supported"</span><span style="font-size:11pt;font-family:Consolas,sans-serif;color:rgb(0,0,0);background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap">.format(x.dtype))</span></p></td></tr></tbody></table></div><span style="font-size:11pt;font-family:Consolas,sans-serif;color:rgb(0,0,0);background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap"> </span><span style="font-size:11pt;font-family:Consolas,sans-serif;color:rgb(0,0,255);background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap"> </span><span style="font-size:11pt;font-family:Consolas,sans-serif;color:rgb(0,0,0);background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap"> </span><span style="font-size:11pt;font-family:Consolas,sans-serif;color:rgb(0,0,255);background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap">return</span><span style="font-size:11pt;font-family:Consolas,sans-serif;color:rgb(0,0,0);background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap"> _dct_func(x, type, n, axis, norm, overwrite_x)</span></div><div><br></div><div>So
that will do for example array coercion and upcasting the same way as the SciPy
implementation. As a consequence, what a backend offers will then be
different performance - speed and/or accuracy.
Let's call this *model 1*.<br></div><div><br></div><div>What my mental
model was when I started commenting on the above example is: a backend
has to have a compatible API, but replaces the implementation of a
function completely. Hence can offer different behavior. The downside is
that a user can't blindly switch backends, reading the docs may be
required. The upside is that it allows for more use cases, like:</div><div>1. Simple differences, like providing a longdouble implementation</div><div>2.
Another input type, e.g. cupy arrays that live on a GPU and executing
the fft on the GPU. With the "completely interchangeable model" this may
still work, but there will be a conversion to-from ndarray on every
function call.<br></div><div>3. Another example of another input type
that otherwise won't work at all: using things that don't fit in memory,
like a large Dask array. <br></div><div>Let's call this *model 2*.</div><div><br></div><div>I
would either way not require _completely_ identical behavior, even if
we'd go with model 1. For example the overwrite_x keyword is an
implementation detail, I think it's fine for any backend to raise an
error if that's True.</div><div><br></div><div>Model 2, which I have a
preference for, is very similar to the numpy __array_function__ and
__array_ufunc__ protocols (in spirit, not implementation). I've tried to
illustrate that in slides 10-11 of [1]. <br></div><div><br></div><div>Both
models 1 and 2 allow faster implementations, like mkl-fft and pyfftw,
to cleanly integrate with SciPy (instead of monkeypatching like pyfftw
does now). Model 2 in addition allows writing code that is generic for
(for example) GPU and distributed implementations, at the cost of a
little less uniformity in behavior.<br></div><div><br></div><div>I hope this made sense. Are there other models of a backend than the above two? Thoughts on what to prefer?</div><div><br></div><div>Cheers,<br></div><div>Ralf<br></div><div><br></div><div>[1] <a href="https://www.slideshare.net/RalfGommers/the-evolution-of-array-computing-in-python">https://www.slideshare.net/RalfGommers/the-evolution-of-array-computing-in-python</a><br></div><div><br></div></div></div></div>