Re: Deciding on tensor parameters

Cem Bassoy via ublas <[email protected]> Thu, 13 Sep 2018 20:06:04 +0200
Newsgroups gmane.comp.lib.boost.ublas
Message-ID <CADrR+FuKdZhgRwm9tpzWrq6m3ZC=1sUHJab7ddcVmfgr4vOYag@mail.gmail.com>
--===============4064730647738210319==
Content-Type: multipart/alternative; boundary="00000000000043563b0575c490db"

--00000000000043563b0575c490db
Content-Type: text/plain; charset="UTF-8"

Am Do., 13. Sep. 2018 um 18:12 Uhr schrieb Stefan Seefeld via ublas <
[email protected]>:

> Hi Cem,
>
> thanks for sending this out !
>
> On 2018-09-13 11:34 AM, Cem Bassoy via ublas wrote:
>
> The GSOC 2018 project with the title "Adding tensor support " has been
> succefully completed. Boost.uBlas may support tensors in future. The
> code, project and documentation can be found here
> <https://github.com/BoostGSoC18/tensor> and here
> <https://github.com/BoostGSoC18/tensor/wiki/Documentation>.
>
> The tensor template class is parametrized in terms of data type, storage
> format (first- or last-order), storage type (e.g. std::vector or
> std::array):
>
>
> (Minor nit-pick: it's a class template. There is no such thing as
> "template classes" in C++ :-). I know the existing ublas docs are full of
> that spelling...)
>
>
Actually there is something a template class which is said to be an
instantiation of a class template, see https://isocpp.org/wiki/faq/templates
.
However, I used it wrong here :-)



> template<class T, class F=first_order, class A=std::vector<T,std::allocator<T>>>
>
>
> class tensor;
>
>
> An instance of a tensor template class has dynamic rank (number of
> dimensions) and  dimensions using a shape class that holds the data. It
> is a adaptor of std::vector where the rank is the size of it:
>
> // {3,4,2} could be runtime variables of an integer type.
>
> auto A = tensor<float>{make_shape(3,4,2)};
> ------------------------------------------
>
> I am thinking to redesign the tensor template class where  the rank is a
> compile time parameter:
>
> template<class T, std::size_t N, class F=first_order<N>, class A=std::vector<T,std::allocator<T>>>class tensor;
>
>
> An instance of a tensor template class could be generated as follows:
>
> // {3,4,2} could be runtime variables of an integer type.auto A = tensor<float,3>(make_shape(3,4,2));
>
>
> This instantiation could be definitely improved. However, having a static
> rank has the following advantages and disadvantages:
>
> -------------
>
> *Advantages*:
>
>    1. improving runtime behavior about 30% to 5 % of basic tensor
>    operations ( depends according to my findings on the length of the inner
>    most loop ).
>    2. ability to statically distinguish between different tensor types at
>    compile time. tensor<float,3> is a different type than tensor<float,4>. If
>    so, why not setting matrix as an alias:
>
>
> template <class type, class format, class storage>
> using matrix = tensor<type,2,format,storage>.
>
> We would only need to specify and implement one data structure ' tensor '
> and if needed  provide optimized functions for matrices. This simplifies
> the maintenance.
>
>
> A big advantage (which has been my main motivation for pushing for this
> solution) is that such a scenario would be fully in line with the existing
> Boost.uBLAS API, so your work becomes a natural extension of what we
> already have.
>

I think, just the contrary is the case. There would be no extension to the
old dense matrix class template, as the new alias template would replace
the old one because we cannot have the identifier in the same namespace. In
the above case, we need to port all vector and matrix functions for the new
tensor type. The vector and matrix class templates are not alias templates
but are distinct class templates. If I am not mistaken, adding the tensor
as a class template as it is right now would be the uBLAS way.



>
> Alternatively, if you keep the rank a runtime parameter, you are basically
> proposing an entirely new API, which means that Boost.uBLAS users will have
> to decide whether to use the old or the new API, which I'm afraid will
> result in a fragmentation of the community. Likewise, many existing
> operations only support existing vector and matrix types, so maintainers
> will have more work to do to support both APIs.
>
> That, to me as library maintainer, is a very high cost, so I'm reluctant
> to such a change, even if the proposed API with runtime ranks is otherwise
> sound.
>

Yes agree with you on that point.



>
> Also there might be advantages in terms of subtensor and iterator support.
> However implementing them will be harder.
>
> ---------
> *Disadvantages*:
>
>    1. The implementations become more complicated especially for tensor
>    multiplications and tensor reshaping.
>
>
> I have worked on a BLAS library with compile-time constant ranks. And
> while capturing parameters such as ranks in the type system itself can
> indeed be a bit of a challenge, I think it's definitely doable, and may
> even lead to clearer code down the road.
>

Yes I agree.


>
>
>
>
>
>    1. With static rank the interfaces are harder to use (setting the rank
>    as a template parameter).
>
>
>
>
> That depends on the use case. It simply means that you have to think about
> the rank slightly differently, while writing code.
>
(It could simply mean that you have to drag along an additional template
> parameter, if you want to write generic code. But as I mentioned above,
> this could arguably lead to clearer code, so I consider this a feature, not
> a bug. :-) )
>

Yes I also do not consider it as a bug :-). For us as library designers,
C++ 'experts', this might be nice feature.
But 'normal' users, not library designers, especially those who are used to
matlab, python, octave, scilab etc. are not used to not worry about
template parameters. There still might be a way to elegantly instanstiate
tensors. However, programming and implementing tensor algorithms becomes
definitely harder with template specialization or with if constexpr.



>
>
>    1. The number of contracted dimensions must be known at compile time.
>    Therefore, implementing some tensor algorithms would only be possible with
>    template specialization instead of simple for loops. Making algorithms
>    becomes more difficult.
>
>
> Right.
>

> Although Eigen and Boost.MultiArray decided  for compile time, it might be
> a critical point for uBLAS.
>
> I am working on this right now and also I am trying to suppprt p! number
> of linear storage formats as a compile time parameter if p is the rank of
> the tensor. Actually unit-testing becomes very hard as I am not able to use
> fixtures so easily. Supporting static and dynamic rank will be a
> maintenance nightmare.
>
>
> Yeah, the parameter space to cover grows exponentially. But that is true
> no matter whether the rank is determined at compile-time or at runtime. The
> difference is only in whether you use normal functions or meta-functions to
> compute derived ranks, storage formats, et al.
>

Well yes, I experienced difficulty not only in covering the parameter space
but also in setting up the unit tests with fixtures when using template
functions. Well but that might not be the main issue here.

Cheers
C

--00000000000043563b0575c490db
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable

<div dir=3D"ltr"><br><br><div class=3D"gmail_quote"><div dir=3D"ltr">Am Do.=
, 13. Sep. 2018 um 18:12=C2=A0Uhr schrieb Stefan Seefeld via ublas &lt;<a h=
ref=3D"mailto:[email protected]">[email protected]</a>&gt;:<br></di=
v><blockquote class=3D"gmail_quote" style=3D"margin:0 0 0 .8ex;border-left:=
1px #ccc solid;padding-left:1ex">
 =20
   =20
 =20
  <div text=3D"#000000" bgcolor=3D"#FFFFFF">
    <p>Hi Cem,</p>
    <p>thanks for sending this out !<br>
    </p>
    <br>
    <div class=3D"m_2777987025696287046moz-cite-prefix">On 2018-09-13 11:34=
 AM, Cem Bassoy via
      ublas wrote:<br>
    </div>
    <blockquote type=3D"cite">
     =20
      <div dir=3D"ltr">
        <div><font size=3D"2">The GSOC 2018 project with the title &quot;Ad=
ding
            tensor support &quot; has been succefully completed. <font size=
=3D"2"><font size=3D"2">Boost.uBlas may support tensors in
                future.</font></font> The code, project and
            documentation can be found <a href=3D"https://github.com/BoostG=
SoC18/tensor" target=3D"_blank">here</a> and <a href=3D"https://github.com/=
BoostGSoC18/tensor/wiki/Documentation" target=3D"_blank">here</a>.<br>
          </font></div>
        <div><font size=3D"2"><br>
          </font></div>
        <div><font size=3D"2">The tensor template class is parametrized in
            terms of d</font><font size=3D"2">ata type,</font><font size=3D=
"2"> storage format (first- or last-order)</font><font size=3D"2">, storage=
 type (e.g. std::vector or std::array):</font></div>
      </div>
    </blockquote>
    <br>
    (Minor nit-pick: it&#39;s a class template. There is no such thing as
    &quot;template classes&quot; in C++ :-). I know the existing ublas docs=
 are
    full of that spelling...)<br>
    <br></div></blockquote><div><br></div><div>Actually there is something =
a template class which is said to be an instantiation of a class template, =
see <a href=3D"https://isocpp.org/wiki/faq/templates">https://isocpp.org/wi=
ki/faq/templates</a>.</div><div>However, I used it wrong here :-)<br></div>=
<div><br></div><div>=C2=A0</div><blockquote class=3D"gmail_quote" style=3D"=
margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div text=3D=
"#000000" bgcolor=3D"#FFFFFF">
    <blockquote type=3D"cite">
      <div dir=3D"ltr">
        <div><span style=3D"font-family:monospace,monospace"><span class=3D=
"m_2777987025696287046gmail-pl-k">template</span>&lt;<span class=3D"m_27779=
87025696287046gmail-pl-k">class</span> <span class=3D"m_2777987025696287046=
gmail-pl-en">T</span>,
            <span class=3D"m_2777987025696287046gmail-pl-k">class</span> <s=
pan class=3D"m_2777987025696287046gmail-pl-en">F</span>=3Dfirst_order, <spa=
n class=3D"m_2777987025696287046gmail-pl-k">class</span> <span class=3D"m_2=
777987025696287046gmail-pl-en">A</span>=3Dstd::vector&lt;T,std::allocator&l=
t;T&gt;&gt;&gt;
          </span></div>
        <div>
          <pre><span style=3D"font-family:monospace,monospace"><span class=
=3D"m_2777987025696287046gmail-pl-k">class</span> <span class=3D"m_27779870=
25696287046gmail-pl-en">tensor</span>;</span></pre>
        </div>
        <div><font size=3D"2"><br>
          </font></div>
        <div><font size=3D"2">An instance of a tensor template class has </=
font><font size=3D"2">dynamic rank (number of dimensions) and=C2=A0 dimensi=
ons
            using a shape class that </font>holds the data. It is a
          adaptor of std::vector where the rank is the size of it: <br>
        </div>
        <div><br>
        </div>
        <div>
          <pre><span style=3D"font-family:monospace,monospace"><font size=
=3D"2"><span class=3D"m_2777987025696287046gmail-pl-k">// {3,4,2} could be =
runtime variables of an integer type.
</span></font></span></pre>
          <pre><span style=3D"font-family:monospace,monospace"><font size=
=3D"2"><span class=3D"m_2777987025696287046gmail-pl-k">auto</span> A =3D te=
nsor&lt;<span class=3D"m_2777987025696287046gmail-pl-k">float</span>&gt;{<s=
pan class=3D"m_2777987025696287046gmail-pl-c1">make_shape(3</span>,<span cl=
ass=3D"m_2777987025696287046gmail-pl-c1">4</span>,<span class=3D"m_27779870=
25696287046gmail-pl-c1">2</span>)};

</font></span><span style=3D"font-family:arial,helvetica,sans-serif"><font =
size=3D"2">---------------------
<font size=3D"2">---------------------</font></font></span><span style=3D"f=
ont-family:monospace,monospace"><font size=3D"2">
</font></span></pre>
        </div>
        <div><font size=3D"2">I am thinking to redesign the tensor
            template class where=C2=A0 the rank is a compile time parameter=
:<br>
          </font></div>
        <div><font size=3D"2"><br>
          </font></div>
        <div>
          <pre><span style=3D"font-family:monospace,monospace"><span class=
=3D"m_2777987025696287046gmail-pl-k">template</span>&lt;<span class=3D"m_27=
77987025696287046gmail-pl-k">class</span> <span class=3D"m_2777987025696287=
046gmail-pl-en">T</span>, std::size_t N, <span class=3D"m_27779870256962870=
46gmail-pl-k">class</span> <span class=3D"m_2777987025696287046gmail-pl-en"=
>F</span>=3Dfirst_order&lt;N&gt;, <span class=3D"m_2777987025696287046gmail=
-pl-k">class</span> <span class=3D"m_2777987025696287046gmail-pl-en">A</spa=
n>=3Dstd::vector&lt;T,std::allocator&lt;T&gt;&gt;&gt;
<span class=3D"m_2777987025696287046gmail-pl-k">class</span> <span class=3D=
"m_2777987025696287046gmail-pl-en">tensor</span>;

</span></pre>
          <pre><span style=3D"font-family:arial,helvetica,sans-serif">An in=
stance of a tensor template class could be generated as follows:</span><spa=
n style=3D"font-family:monospace,monospace">
</span></pre>
          <pre><span style=3D"font-family:monospace,monospace"><span style=
=3D"font-family:monospace,monospace"><font size=3D"2"><span class=3D"m_2777=
987025696287046gmail-pl-k">// {3,4,2} could be runtime variables of an inte=
ger type.
</span></font></span><span style=3D"font-family:monospace,monospace"><font =
size=3D"2"><span class=3D"m_2777987025696287046gmail-pl-k">auto</span> A =
=3D tensor&lt;<span class=3D"m_2777987025696287046gmail-pl-k">float</span>,=
3&gt;(make_shape(<span class=3D"m_2777987025696287046gmail-pl-c1">3</span>,=
<span class=3D"m_2777987025696287046gmail-pl-c1">4</span>,<span class=3D"m_=
2777987025696287046gmail-pl-c1">2))</span>;</font></span></span></pre>
        </div>
        <div dir=3D"auto"><font size=3D"2"><br>
          </font> </div>
        <div><font size=3D"2">This instantiation could be definitely
            improved. However, having a static rank has the following
            advantages and disadvantages:<br>
          </font></div>
        <div><font size=3D"2"><br>
          </font></div>
        <div dir=3D"auto"><font size=3D"2">-------------</font></div>
        <div dir=3D"auto"><font size=3D"2"><br>
          </font> </div>
        <div dir=3D"auto"><font size=3D"2"><b>Advantages</b>:</font></div>
        <div dir=3D"auto">
          <ol>
            <li><font size=3D"2">improving runtime behavior about 30% to 5
                % of basic tensor operations ( depends according to my
                findings on the length of the inner most loop ).</font></li=
>
            <li><font size=3D"2">ability to statically distinguish between
                different tensor types at compile time.
                tensor&lt;float,3&gt; is a different type than
                tensor&lt;float,4&gt;. If so, why not setting matrix as
                an alias:</font> </li>
          </ol>
        </div>
        <div dir=3D"auto"><span style=3D"font-family:monospace,monospace"><=
font size=3D"2"><br>
            </font></span> </div>
        <span style=3D"font-family:monospace,monospace"> </span>
        <div dir=3D"auto"><span style=3D"font-family:monospace,monospace"><=
font size=3D"2">template &lt;class type, class format, class
              storage&gt;</font></span></div>
        <span style=3D"font-family:monospace,monospace"> </span>
        <div dir=3D"auto"><span style=3D"font-family:monospace,monospace"><=
font size=3D"2">using matrix =3D
              tensor&lt;type,2,format,storage&gt;.</font></span></div>
        <div dir=3D"auto"><font size=3D"2"><br>
          </font> </div>
        <div dir=3D"auto"><font size=3D"2">We would only need to specify an=
d
            implement one data structure &#39; tensor &#39; and if needed
            =C2=A0provide optimized functions for matrices. This simplifies
            the maintenance. <br>
          </font></div>
      </div>
    </blockquote>
    <br>
    A big advantage (which has been my main motivation for pushing for
    this solution) is that such a scenario would be fully in line with
    the existing Boost.uBLAS API, so your work becomes a natural
    extension of what we already have.<br></div></blockquote><div><br></div=
><div>I think, just the contrary is the case. There would be no extension t=
o the old dense matrix class template, as the new alias template would repl=
ace the old one because we cannot have the identifier in the same namespace=
. In the above case, we need to port all vector and matrix functions for th=
e new tensor type. The vector and matrix class templates are not alias temp=
lates but are distinct class templates. If I am not mistaken, adding the te=
nsor as a class template as it is right now would be the uBLAS way.<br></di=
v><div><br></div><div>=C2=A0</div><blockquote class=3D"gmail_quote" style=
=3D"margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div tex=
t=3D"#000000" bgcolor=3D"#FFFFFF">
    <br>
    Alternatively, if you keep the rank a runtime parameter, you are
    basically proposing an entirely new API, which means that
    Boost.uBLAS users will have to decide whether to use the old or the
    new API, which I&#39;m afraid will result in a fragmentation of the
    community. Likewise, many existing operations only support existing
    vector and matrix types, so maintainers will have more work to do to
    support both APIs.<br>
    <br>
    That, to me as library maintainer, is a very high cost, so I&#39;m
    reluctant to such a change, even if the proposed API with runtime
    ranks is otherwise sound.<br></div></blockquote><div><br></div><div>Yes=
 agree with you on that point.<br></div><div><br></div><div>=C2=A0</div><bl=
ockquote class=3D"gmail_quote" style=3D"margin:0 0 0 .8ex;border-left:1px #=
ccc solid;padding-left:1ex"><div text=3D"#000000" bgcolor=3D"#FFFFFF">
    <br>
    <blockquote type=3D"cite">
      <div dir=3D"ltr">
        <div dir=3D"auto"><font size=3D"2">Also there might be advantages i=
n
            terms of subtensor and iterator support. However
            implementing them will be harder.=C2=A0</font></div>
        <div dir=3D"auto"><font size=3D"2"><br>
          </font> </div>
        <div dir=3D"auto"><font size=3D"2">---------</font></div>
        <div dir=3D"auto"><font size=3D"2"><b>Disadvantages</b>:</font></di=
v>
        <div dir=3D"auto">
          <ol>
            <li><font size=3D"2">The implementations become more
                complicated especially for tensor multiplications and
                tensor reshaping.</font></li>
          </ol>
        </div>
      </div>
    </blockquote>
    <br>
    I have worked on a BLAS library with compile-time constant ranks.
    And while capturing parameters such as ranks in the type system
    itself can indeed be a bit of a challenge, I think it&#39;s definitely
    doable, and may even lead to clearer code down the road.<br></div></blo=
ckquote><div><br></div><div>Yes I agree.<br></div><div>=C2=A0</div><blockqu=
ote class=3D"gmail_quote" style=3D"margin:0px 0px 0px 0.8ex;border-left:1px=
 solid rgb(204,204,204);padding-left:1ex"><div>
    <br>
    <br>
    <blockquote type=3D"cite">
      <div dir=3D"ltr">
        <div dir=3D"auto">
          <ol>
           =20
            </ol><ol><li><font size=3D"2">With static rank the interfaces a=
re
                harder to use (setting the rank as a template
                parameter). </font></li></ol></div><div dir=3D"auto"><ol>
          </ol>
        </div>
      </div>
    </blockquote>
    <br>
    That depends on the use case. It simply means that you have to think
    about the rank slightly differently, while writing code. <br></div></bl=
ockquote><blockquote class=3D"gmail_quote" style=3D"margin:0 0 0 .8ex;borde=
r-left:1px #ccc solid;padding-left:1ex"><div text=3D"#000000" bgcolor=3D"#F=
FFFFF">
    (It could simply mean that you have to drag along an additional
    template parameter, if you want to write generic code. But as I
    mentioned above, this could arguably lead to clearer code, so I
    consider this a feature, not a bug. :-) )<br></div></blockquote><div><b=
r></div><div>Yes I also do not consider it as a bug :-). For us as library =
designers, C++ &#39;experts&#39;, this might be nice feature.</div><div>But=
 &#39;normal&#39; users, not library designers, especially those who are us=
ed to matlab, python, octave, scilab etc. are not used to not worry about t=
emplate parameters. There still might be a way to elegantly instanstiate te=
nsors. However, programming and implementing tensor algorithms becomes defi=
nitely harder with template specialization or with if constexpr.<br></div><=
div><br></div><div>=C2=A0</div><blockquote class=3D"gmail_quote" style=3D"m=
argin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div>
    <br>
    <blockquote type=3D"cite">
      <div dir=3D"ltr">
        <div dir=3D"auto">
          <ol>
            <li><font size=3D"2">The number of contracted dimensions must
                be known at compile time. Therefore, implementing some
                tensor algorithms would only be possible with template
                specialization instead of simple for loops. Making
                algorithms becomes more difficult.<br>
              </font></li>
          </ol>
        </div>
      </div>
    </blockquote>
    <br>
    Right. <br></div></blockquote><blockquote class=3D"gmail_quote" style=
=3D"margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div tex=
t=3D"#000000" bgcolor=3D"#FFFFFF">
    <br>
    <blockquote type=3D"cite">
      <div dir=3D"ltr">
        <div dir=3D"auto">
          <div><font size=3D"2">Although Eigen and Boost.MultiArray
              decided=C2=A0 for compile time, it might be a critical point
              for uBLAS.</font><font size=3D"2"> <br>
            </font></div>
          <div><font size=3D"2"><br>
            </font></div>
          <div><font size=3D"2">I am working on this right now and also I
              am trying to suppprt p! number of linear storage formats
              as a compile time parameter if p is the rank of the
              tensor. Actually unit-testing becomes very hard as I am
              not able to use fixtures so easily. Supporting static and
              dynamic rank will be a maintenance nightmare.</font></div>
        </div>
      </div>
    </blockquote>
    <br>
    Yeah, the parameter space to cover grows exponentially. But that is
    true no matter whether the rank is determined at compile-time or at
    runtime. The difference is only in whether you use normal functions
    or meta-functions to compute derived ranks, storage formats, et al.<br>=
</div></blockquote><div><br></div><div>Well yes, I experienced difficulty n=
ot only in covering the parameter space but also in setting up the unit tes=
ts with fixtures when using template functions. Well but that might not be =
the main issue here.<br></div><div>=C2=A0</div><div>Cheers</div><div>C<br><=
/div></div></div>

--00000000000043563b0575c490db--

--===============4064730647738210319==
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: inline