<table border="1" cellspacing="0" cellpadding="8">
    <tr>
        <th>Issue</th>
        <td>
            <a href=https://github.com/llvm/llvm-project/issues/54179>54179</a>
        </td>
    </tr>

    <tr>
        <th>Summary</th>
        <td>
            Have conversion take an "is nonzero" predicate
        </td>
    </tr>

    <tr>
      <th>Labels</th>
      <td>
            enhancement,
            mlir:sparse
      </td>
    </tr>

    <tr>
      <th>Assignees</th>
      <td>
      </td>
    </tr>

    <tr>
      <th>Reporter</th>
      <td>
          wrengr
      </td>
    </tr>
</table>

<pre>
    This is primarily for sparse⇒sparse `ConvertOp`, though it also applies to dense⇒sparse `ConvertOp` and `NewOp`.

There is an unintended infelicity with the semantics of the current sparse⇒sparse conversion as implemented by the runtime library. In general, if the source tensor has any dense dimensions then it will be forced to store some zero elements. That's unavoidable, but the problem is that when enumerating the elements of the source tensor we currently do not perform any filtering of these zeros, which means the target tensor will unnecessarily store them since it can't tell them apart from nonzero values.

If all the element types were integral, then we could simply filter them out in the runtime library. However, since we have floating-point types a simple `if (v != 0)` isn't enough. There's a huge range of options for how to parameterize testing for "nonzero" floating-point numbers, so any choice we make is bound to be bad for some user.  So a nice alternative is to hand the decision back to the user to define for us. That is, the conversion op should take an extra argument: a predicate for deciding whether a value counts as "nonzero" or not. Then the runtime library can simply check `if (isNonzero(v))`.

In addition to avoiding the problems around floating-point types, taking a user-defined predicate for what counts as a "nonzero" value has some other nice benefits. For example, we can implement relu as a convert op by using the predicate `[](V v){ return v > 0; }`. And similarly for any other function which filters/selects elements based only on their value.

The big question is how exactly to pass these predicate functions across the boundary from MLIR to C++, since it effectively requires the C++ runtime library to make a callback into MLIR land.  A simple working proposal is to pass the predicate as a C function pointer: `bool(*)(V)`, where the null pointer indicates the always-true function (i.e., the current behavior). This has the limitation that the user can't use closures or other callable objects, but it's not clear to me how we would pass those across the boundary (let alone define them on the MLIR side).
</pre>
<img width="1px" height="1px" alt="" src="http://email.email.llvm.org/o/eJyFVlmP2zYQ_jXyC7HGrryO7Qc_7JEgAdoUaIO8U9LYYpciFR52nF_fb4aW9-gWBQzv6uBw5rvoxnen7bfeRIXPGMygg7EntfNBxVGHSNXHulqvqk1dLlX14frBuwOF9MeI_6v6QaXe532vTFLaRq_0OFpDUSWvOnL_W0Fp1_G9r3SU63l1_Vhd35Xvbz0F4ta0U9kZl8h11CnjdmRNa9JJHU3q0QGpSIN2ybRR-Z3caHMI5NJ_zNFKC9F4pzSGH0ZLA95G8eYky0NGtYGUNU3Q4TRXX5zak6OgLQ9tyibR59CSQl8RkPWaOz2VuVWH5Y53iPyqY4COxlrVEOPbYitAFJMPXAY7_aLgFZU-4lx963Wq6lXE4PrgTacbS7xzk5NsPQaPOwOjk_CqOvIe5PKAFpNxe3lpKjeB8rrf4wUlcN555XxSIwV0N8gcO2MTBa5VlsfSZOQ2jr1pezWQLuOppMOe0qUyD5qdo5ZiLJoqk-LVQUXj0ATwaLXDiLwKr8sjDXqS2gU_oBsnkBy0zRRf6eLLDlqzLydU6TRCdEfRC3jcn3kS5HlOn22HjUH0NFfZ0ANO496n_LM_ElTCdUrLKNTrA_izXjC-Gr257K1LeVE45FHV6wO-bqrFo4JPNqx1E8u85NgyzDH6FZK16vMeLWiHb6DtxyTKYSf2_shSATJ6ICbkFzMYhWR-XtX1GSv897Y3CKKB0GUGL6y2vTdllkE_ibsan52oEdJsdFfsz5LMkcJcqb-wUDlepBk4h_oHWYglPfuX0euoNeKnRrdP_IRvcoGSBDvjRPe4VbSN9WeCXprRjyr2wlXi5uB7-pmCVlBXZp6rxR16GQN1ptWpVOSdOwYDFkC5gBdEM0w6ax-ufA0R1kDqAv-7zLMuJ620PWGcC6Umfp3qrA_MqhD7WpyIlK4zzB-PLuad_Hg2LXoKgvl7QhJU9BMv0QLgVUGvezP2kUF8HlG_GbJAwJEkVHpBRkhsEGM7wxnzCWXop2bViqdJJr-koQpkc6ldKErMDwIyx-eBppb4NFjeV8tHIPNdCTare1RIOTgFJyw-wgaLe1WtHhkxdefEj8bqcD5xWJylzV12reBXUqb4FcB8irB7i3kvudboCGC8QwkvXJpQJn97jqjG7NWPzLbBi9Auuwqzt5x94q4Yzxn3AuZzH0CgRe6VpBO3sEokpX7_7cufvP6hqu_l8_Acb7TboVt4BVsE-pFNoFLi_PK_dIc64kngjXwTI0EWvmxi4TSY8W6KmaMPIhJoavRR27Mhp0FejCEMPjyjKmJDrsFLoKLxHlG5ruo70fP6e9F0CXkqmY0YQd6e16GnUrjso-1Rn-JVCvkZMLHKHCRMDj8fxg0hP41HpG7YfkyDLlUspJB0MQ0L-5Ie0xmR-dS2PmYGEWopSmGc-GRUvvmblTGdkKacnXyitZa0hBCAZtKPDB0nzBkpj8Lv0YsRLPGPGu9oCrByZJTQEFKi6YiHmXXbRbdZbPQsmWRp-5mPiRe5NqUZnGmiemHTC0uzHOy2T2mM4AVSx2ePHze5mbd-wIW1h-nPFSjnaXFpYswcGZ-Wtzerzazf3nyoN11D63W9ajYrve7a5WLxgZabdt2tb1c0A1xk45a9WtfkkN-teAlXDB7-DNawNs4_nHB7-Tgz2_q6rq8X1wt8rZbrebtq6tvF6vZ2tWq7enFT3V7jF5ixc25w7sN-FrbSa5P3EQ-tiUicy0NAb_aOaFsyY6YziAjbI1SyDzOZaisj_QM6XK_v">