<p dir="auto"><strong>Issue</strong><br>
I am implementing custom dtypes for symbolic types: Variable, Expression, Formula, where the algebra is more or less:</p>
<ul dir="auto">
<li><code class="notranslate">&lt;ExpressionOperand&gt; ::= &lt;Variable&gt; | &lt;Expression&gt;</code></li>
<li><code class="notranslate">&lt;math_op&gt; ::= + | - | * | - | etc...</code></li>
<li><code class="notranslate">&lt;logical_op&gt; ::= == | != | &lt; | &lt;= | etc...</code></li>
<li><code class="notranslate">&lt;ExpressionOperand&gt; &lt;math_op&gt; &lt;ExpressionOperand&gt; -&gt; &lt;Expression&gt;</code></li>
<li><code class="notranslate">&lt;ExpressionOperand&gt; &lt;logical_op&gt; &lt;ExpressionOperand&gt; -&gt; &lt;Formula&gt;</code><br>
I can more or less implement this for most of the operators by augmenting the existing UFuncs using <code class="notranslate">PyUFunc_RegisterLoopForType</code>.</li>
</ul>
<p dir="auto">However, for some builtin operations, like <code class="notranslate">dotfunc</code>, it seems that the C API is rigidly fixed to only providing closed operations for <code class="notranslate">dot</code>, <code class="notranslate">matmul</code>, etc., so I cannot define a meaningful <code class="notranslate">dot</code> produce for <code class="notranslate">Variable</code>, since the result of this operation should produce a <code class="notranslate">Expression</code>:<br>
<a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="308213118" data-permission-text="Title is private" data-url="https://github.com/RobotLocomotion/drake/issues/8452" data-hovercard-type="pull_request" data-hovercard-url="/RobotLocomotion/drake/pull/8452/hovercard?comment_id=379923025&amp;comment_type=issue_comment" href="https://github.com/RobotLocomotion/drake/pull/8452#issuecomment-379923025">RobotLocomotion/drake#8452 (comment)</a></p>
<p dir="auto"><strong>Rambling</strong><br>
I am currently inspecting the source code for v1.11.0 (what I currently have on Ubuntu 16.04 LTS), and it seems that I can use <code class="notranslate">__numpy_ufunc__</code> (the old version of <code class="notranslate">__array_ufunc__</code> which was solidified in v1.13.0, it seems?), because:</p>
<ul dir="auto">
<li><code class="notranslate">array_dot</code> (<code class="notranslate">methods.c</code>) calls <code class="notranslate">PyUFunc_CheckOverride</code> first to check if <code class="notranslate">numpy.core.multiarray.dot</code> can be overridden by a <code class="notranslate">ndarray</code> subclass</li>
<li>If no override is found, it uses <code class="notranslate">PyArray_MatrixProduct2</code>, which then calls <code class="notranslate">dotfunc</code></li>
</ul>
<p dir="auto">TBH, it seems that <code class="notranslate">PyUFunc_CheckOverride</code> could possibly be generalized to see if a compatible user loop has been registered for the <code class="notranslate">PyUFuncObject</code>, if no <code class="notranslate">__array_ufunc__</code> is available.</p>
<p dir="auto">Looking at a recent <code class="notranslate">master</code> (<code class="notranslate">c486d8d</code>), it looks like this functionality is removed from <code class="notranslate">array_dot</code>, so I'm not sure where it's gone now.</p>
<p dir="auto">Seeing <a href="https://docs.scipy.org/doc/numpy-1.13.0/neps/ufunc-overrides.html#id24" rel="nofollow">this footnote</a> in the <code class="notranslate">__array_ufunc__</code> documentation, it seems that <code class="notranslate">matmul</code> is under consideration, but not <code class="notranslate">dot</code>?</p>
<p dir="auto">I am still generally concerned of subclassing <code class="notranslate">ndarray</code> for (a) version considerations (it seems that this feature is still relatively volatile) and (b) forcing downstream users to care about which <code class="notranslate">ndarray</code> subclass they use.<br>
(That being said, is there a way to create a custom <code class="notranslate">dtype</code>, and then indicate which <code class="notranslate">ndarray</code> subclass should be used?)</p>
<p dir="auto">With NumPy master, it is currently impossible to override <code class="notranslate">ndarray @ other</code> if <code class="notranslate">other</code> also implements <code class="notranslate">__array_ufunc__</code>.</p>
<p dir="auto">Consider:</p>
<div class="highlight highlight-source-python notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="import numpy as np

np.__version__  # '1.13.0.dev0+9ef5891'

class OptOut:
    __array_ufunc__ = None
    def __rmatmul__(self, other):
       return 'rmatmul'

class OtherArray:
    def __array_ufunc__(self, *args, **kwargs):
        return 'array_ufunc'
    def __rmatmul__(self, other):
        return 'rmatmul'

array = np.arange(3)
opt_out = OptOut()
other_array = OtherArray()

# OptOut works as expected:
array * opt_out  # TypeError: unsupported operand type(s) for *: 'numpy.ndarray' and 'OptOut'
array @ opt_out  # 'rmatmul'

# But OtherArray does not:
array * other_array  # 'array_ufunc'
array @ other_array  # TypeError: Object arrays are not currently supported"><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">np</span>.<span class="pl-s1">__version__</span>  <span class="pl-c"># '1.13.0.dev0+9ef5891'</span>

<span class="pl-k">class</span> <span class="pl-v">OptOut</span>:
    <span class="pl-s1">__array_ufunc__</span> <span class="pl-c1">=</span> <span class="pl-c1">None</span>
    <span class="pl-k">def</span> <span class="pl-en">__rmatmul__</span>(<span class="pl-s1">self</span>, <span class="pl-s1">other</span>):
       <span class="pl-k">return</span> <span class="pl-s">'rmatmul'</span>

<span class="pl-k">class</span> <span class="pl-v">OtherArray</span>:
    <span class="pl-k">def</span> <span class="pl-en">__array_ufunc__</span>(<span class="pl-s1">self</span>, <span class="pl-c1">*</span><span class="pl-s1">args</span>, <span class="pl-c1">**</span><span class="pl-s1">kwargs</span>):
        <span class="pl-k">return</span> <span class="pl-s">'array_ufunc'</span>
    <span class="pl-k">def</span> <span class="pl-en">__rmatmul__</span>(<span class="pl-s1">self</span>, <span class="pl-s1">other</span>):
        <span class="pl-k">return</span> <span class="pl-s">'rmatmul'</span>

<span class="pl-s1">array</span> <span class="pl-c1">=</span> <span class="pl-s1">np</span>.<span class="pl-en">arange</span>(<span class="pl-c1">3</span>)
<span class="pl-s1">opt_out</span> <span class="pl-c1">=</span> <span class="pl-v">OptOut</span>()
<span class="pl-s1">other_array</span> <span class="pl-c1">=</span> <span class="pl-v">OtherArray</span>()

<span class="pl-c"># OptOut works as expected:</span>
<span class="pl-s1">array</span> <span class="pl-c1">*</span> <span class="pl-s1">opt_out</span>  <span class="pl-c"># TypeError: unsupported operand type(s) for *: 'numpy.ndarray' and 'OptOut'</span>
<span class="pl-s1">array</span> @ <span class="pl-s1">opt_out</span>  <span class="pl-c"># 'rmatmul'</span>

<span class="pl-c"># But OtherArray does not:</span>
<span class="pl-s1">array</span> <span class="pl-c1">*</span> <span class="pl-s1">other_array</span>  <span class="pl-c"># 'array_ufunc'</span>
<span class="pl-s1">array</span> @ <span class="pl-s1">other_array</span>  <span class="pl-c"># TypeError: Object arrays are not currently supported</span></pre></div>
<p dir="auto">This is problematic. We definitely need a way to allow for overriding <code class="notranslate">@</code> from the second argument, whether than means breaking the rules on <code class="notranslate">__array_ufunc__</code> (allowing <code class="notranslate">matmul</code> to be passed even though it isn't a ufunc) or breaking the rules for <code class="notranslate">__matmul__</code> (to call <code class="notranslate">__rmatmul__</code> or allow <code class="notranslate">__rmatmul__</code> to be called by Python, even when <code class="notranslate">__array_ufunc__</code> is implemented on the other argument).</p>