Repository: tBuLi/kingdon (language: python)

Issue:
Codegen fails for composite operators when numba=True.
When numba is used, codegen fails for operations that use other operators. For example, inversion requires the geometric product. Because `numba` is already applied in codegen_gp, it's reuse in the computation of the inverse fails. The fix is to delay the application of numbajit to outside of the codegen procedure.


Discussion / hints:

Fix patch:
diff --git a/docs/conf.py b/docs/conf.py
index 738d86b..75a4174 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -39,6 +39,19 @@
     'nbsphinx',
 ]
 
+intersphinx_mapping = {
+    'sympy': ('https://docs.sympy.org/latest', None),
+    'symfit': ('https://symfit.readthedocs.io/en/stable/', None),
+    'python': ('https://docs.python.org/3', None),
+}
+
+nitpick_ignore = [
+    ('py:mod', 'sympy'),
+    ('py:class', 'sympy.core.Symbol'),
+]
+
+autodoc_mock_imports = ["sympy"]
+
 # Add any paths that contain templates here, relative to this directory.
 templates_path = ['_templates']
 
@@ -75,7 +88,7 @@
 # List of patterns, relative to source directory, that match files and
 # directories to ignore when looking for source files.
 # This patterns also effect to html_static_path and html_extra_path
-exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
+exclude_patterns = ['_build', 'build_docs', 'Thumbs.db', '.DS_Store', '**.ipynb_checkpoints']
 
 # The name of the Pygments (syntax highlighting) style to use.
 pygments_style = 'sphinx'
diff --git a/docs/examples/ex_pointcloud.ipynb b/docs/examples/ex_pointcloud.ipynb
index 5101965..e43366a 100644
--- a/docs/examples/ex_pointcloud.ipynb
+++ b/docs/examples/ex_pointcloud.ipynb
@@ -48,7 +48,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 6,
+   "execution_count": 3,
    "id": "091f5c8d",
    "metadata": {},
    "outputs": [],
@@ -62,7 +62,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 7,
+   "execution_count": 4,
    "id": "d742e36d",
    "metadata": {},
    "outputs": [],
@@ -80,7 +80,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 8,
+   "execution_count": 5,
    "id": "1dea6b61",
    "metadata": {},
    "outputs": [
@@ -89,13 +89,13 @@
       "text/plain": [
        "MultiVector(algebra=Algebra(p=2, q=0, r=1, cse=True, precompute='none', numba=False, graded=False, simplify=True), _values=array([[1.        , 1.        , 1.        , 1.        , 1.        ,\n",
        "        1.        , 1.        , 1.        , 1.        , 1.        ],\n",
-       "       [0.54249339, 0.00187754, 0.88934931, 0.14371307, 0.90024069,\n",
-       "        0.16091466, 0.79154578, 0.8069197 , 0.08935312, 0.71837907],\n",
-       "       [0.46277455, 0.50690105, 0.93836961, 0.33780138, 0.21474948,\n",
-       "        0.09130718, 0.67021727, 0.37623112, 0.16948978, 0.31433891]]), _keys=(3, 5, 6))"
+       "       [0.58458649, 0.73592531, 0.11134136, 0.68417876, 0.57177713,\n",
+       "        0.35552796, 0.76742005, 0.21321343, 0.21633547, 0.61716839],\n",
+       "       [0.55369794, 0.08997382, 0.1113173 , 0.64265498, 0.80082692,\n",
+       "        0.24724482, 0.20296079, 0.73196863, 0.62790585, 0.71005653]]), _keys=(3, 5, 6))"
       ]
      },
-     "execution_count": 8,
+     "execution_count": 5,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -116,7 +116,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 9,
+   "execution_count": 6,
    "id": "5e975b48",
    "metadata": {},
    "outputs": [
@@ -126,7 +126,7 @@
      "text": [
       "(1) * 1 + (1) * e13\n",
       "(0.5000000000000001) * 1 + (0.8660254037844386) * e12 + (0.5000000000000001) * e13 + (-0.8660254037844386) * e23\n",
-      "(1.0) * 1 + (0.0) * e12 + (0.0) * e13 + (0.0) * e23\n"
+      "(1.0) * 1\n"
      ]
     }
    ],
@@ -150,7 +150,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 10,
+   "execution_count": 7,
    "id": "7bfb9aa6",
    "metadata": {},
    "outputs": [
@@ -158,9 +158,9 @@
      "name": "stdout",
      "output_type": "stream",
      "text": [
-      "([1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]) * e12 + ([1.88101687 2.16664797 2.12831565 1.95533762 1.48204906 1.73949724\n",
-      " 1.91198636 1.66356202 1.80505835 1.67581711]) * e13 + ([-1.70253249 -1.25262509 -2.21694347 -1.28787096 -1.87394245 -1.21644782\n",
-      " -2.02445395 -1.89602809 -1.10909076 -1.78311012]) * e23\n"
+      "([1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]) * e12 + ([1.93770314 1.46211814 1.77184637 1.94216034 2.1288009  1.76473758\n",
+      " 1.54224206 2.22762334 2.1530351  2.05956487]) * e13 + ([-1.76162814 -1.65760549 -1.18332194 -1.92659531 -1.88520711 -1.45905418\n",
+      " -1.75711176 -1.54669495 -1.4820069  -1.91854545]) * e23\n"
      ]
     }
    ],
@@ -171,28 +171,30 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 11,
+   "execution_count": 8,
    "id": "d1f304ab",
    "metadata": {},
    "outputs": [
     {
      "data": {
       "text/plain": [
-       "<matplotlib.legend.Legend at 0x24a29099c90>"
+       "<matplotlib.legend.Legend at 0x235ee61ff10>"
       ]
      },
-     "execution_count": 11,
+     "execution_count": 8,
      "metadata": {},
      "output_type": "execute_result"
     },
     {
      "data": {
-      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAiMAAAGdCAYAAADAAnMpAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjYuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/av/WaAAAACXBIWXMAAA9hAAAPYQGoP6dpAAAwCUlEQVR4nO3de3jU1YH/8c8kQAImGQxILjZAFAVivEAiEipeH7kuXtZWWxXjFu1CqywiFRH7ILprxKWKikKxFGpprT4NUFlYFFtulVhEgkIDohiBH05EQCcBJIHk/P6ImWXI5DKTmTkzmffreeahc+Z8Z858+db5cL7n4jDGGAEAAFgSZ7sBAAAgthFGAACAVYQRAABgFWEEAABYRRgBAABWEUYAAIBVhBEAAGAVYQQAAFjVwXYDWqOurk5ffPGFkpOT5XA4bDcHAAC0gjFGVVVVyszMVFxc0/0fURFGvvjiC2VlZdluBgAACMD+/fv1ve99r8nXoyKMJCcnS6r/MikpKZZbAwAAWqOyslJZWVme3/GmREUYabg1k5KSQhgBACDKtDTEggGsAADAKsIIAACwijACAACsiooxIwCAyGaM0alTp1RbW2u7KQij+Ph4dejQoc3LbhBGAABtUlNTI5fLpePHj9tuCizo0qWLMjIy1KlTp4DfgzACAAhYXV2dysvLFR8fr8zMTHXq1InFKWOEMUY1NTX66quvVF5ergsuuKDZhc2aQxgBAASspqZGdXV1ysrKUpcuXWw3B2HWuXNndezYUXv37lVNTY0SExMDeh8GsAIA2izQfxEj+gXj756eEaA5dbXS3k3S0S+lpDSp1xApLt52qwCgXSHKAk0pe1Oakyv97l+k4nH1f87JrS8HELMef/xxXXbZZX4dc80112jSpEnW2xGp6BkBfCl7U3rjbknGu7zSVV9+26tSzo1WmgbArilTpuiBBx7w65ilS5eqY8eOIWpR9KNnBDhTXa20eqoaBRHp/8pWP1JfD0DMaFhLJSkpSd26dfPr2NTU1BY3i4tlhBHgTHs3SZVfNFPBSJUH6usBCJraOqOSPYf1l20HVLLnsGrrfP2DILiqq6s1ceJE9ejRQ4mJibryyiv1/vvvS5LWrVsnh8Oht956S/n5+UpISNDGjRsb3R45deqUJk6cqK5du6pbt26aOnWqCgsLdfPNN3vqnHmbpnfv3nrqqaf0k5/8RMnJyerZs6cWLFjg1bapU6fqwgsvVJcuXXTeeefpl7/8pU6ePBnK02ENYQQ409Evg1sPQItW73Dpyll/049feU//8adt+vEr7+nKWX/T6h2ukH7uww8/rOLiYv3ud7/T1q1b1adPHw0fPlxHjhzxqlNUVKSdO3fqkksuafQes2bN0h/+8ActWrRI7777riorK7V8+fIWP/tXv/qV8vPzVVpaqp/97GeaMGGCdu3a5Xk9OTlZixcvVllZmZ5//nm98soreu6554LyvSNN7IaRulqpfKO0/c/1f9LljgZJacGtB6BZq3e4NGHJVrncJ7zKK9wnNGHJ1pAFkmPHjmnevHn67//+b40cOVI5OTl65ZVX1LlzZy1cuNBT74knntANN9yg888/3+ftmRdffFHTpk3TLbfcon79+mnu3Lnq2rVri58/atQo/exnP1OfPn00depUde/eXevWrfO8/thjj2nIkCHq3bu3xowZo4ceekhvvPFGML56xInNAaxlb9aPCTi9Kz4lUxoxi0GJqJ++m5JZP1jV57gRR/3rvYaEu2VAu1NbZzRzRVmTI7QckmauKNMNOemKjwvuyq579uzRyZMn9f3vf99T1rFjRw0aNEg7d+7U5ZdfLknKz89v8j3cbre+/PJLDRo0yFMWHx+vvLw81dXVNfv5p/eyOBwOpaen6+DBg56yP//5z5ozZ44+/fRTHT16VKdOnVJKSorf3zMaxF7PSMMsiTPHBDTMkmDaJuLi64OppPr/FJ7uu+cjnma9ESAINpcfadQjcjojyeU+oc3lR5qsEyhj6iPQmcvXG2O8ys4666wW38vXe7TkzNk1DofDE2Dee+89/ehHP9LIkSP1P//zPyotLdX06dNVU1PT4vtGo9gKI8ySQGvl3Fg/fTclw7s8JZNpvUAQHaxqOogEUs8fffr0UadOnfT3v//dU3by5Elt2bJF/fv3b9V7OJ1OpaWlafPmzZ6y2tpalZaWtqlt7777rnr16qXp06crPz9fF1xwgfbu3dum94xksXWbxp9ZEtlDw9YsRKicG6V+o1mBFQihHsmt28uktfX8cdZZZ2nChAn6xS9+odTUVPXs2VPPPPOMjh8/rnHjxunDDz9s1fs88MADKioqUp8+fdSvXz+9+OKL+vrrr9u0YWCfPn20b98+/elPf9Lll1+ulStXatmyZQG/X6SLrTDCLAn4Ky6eYAqE0KDsVGU4E1XhPtHUCC2lOxM1KDs1JJ//9NNPq66uTmPHjlVVVZXy8/P11ltv6eyzz271e0ydOlUVFRW6++67FR8fr5/+9KcaPny44uMD/4fLTTfdpAcffFD333+/qqurNXr0aP3yl7/U448/HvB7RjKHac2NLcsqKyvldDrldrvbNninfGP9kt4tKfwffoAAoBVOnDih8vJyZWdnB7xja8NsGsn7JnpDv8K8uwZqRG5Go+MiVV1dnfr376/bbrtNTz75pO3mhFxz10Brf79ja8xIwyyJRoMSGziklHOZJQEAYTQiN0Pz7hqodKf3D1m6MzEqgsjevXv1yiuvaPfu3dq+fbsmTJig8vJy3XHHHbabFjVi6zZNwyyJN+5WfSDxkcGZJQEAYTciN0M35KRrc/kRHaw6oR7J9bdmgj2dNxTi4uK0ePFiTZkyRcYY5ebm6p133mn1IFjEWhiR/m+WhM91Rp5mlgQAWBIf51DB+f7t+RIJsrKy9O6779puRlSLvTAiMUsCAIAIEpthRGKWBAAAESK2BrACAICIQxgBAABWEUYAAIBVhBEAAGAVYQQAgDDYtWuXBg8erMTERF122WW2m9Mqn3/+uRwOh7Zt2xbSz4nd2TQAgJh2zTXX6LLLLtOcOXPC8nkzZszQWWedpY8//lhJSUlh+cxoQRgBAESGutqIWv/JGKPa2lp16BCcn8o9e/Zo9OjR6tWrV8DvUVNTo06dOgWlPZGE2zQAAPvK3pTm5NZvZlo8rv7PObn15SFwzz33aP369Xr++eflcDjkcDi0ePFiORwOvfXWW8rPz1dCQoI2btyoPXv26KabblJaWpqSkpJ0+eWX65133vF6v969e+upp57ST37yEyUnJ6tnz55asGCB53WHw6EPPvhATzzxhBwOh2f33e3bt+u6665T586d1a1bN/30pz/V0aNHvdp58803q6ioSJmZmbrwwgs9t07eeOMNDR06VJ07d9bll1+u3bt36/3331d+fr6SkpI0YsQIffXVV17tXLRokfr376/ExET169dPL7/8stfrmzdv1oABA5SYmKj8/HyVlpYG+cw3wUQBt9ttJBm32227KQCA03z77bemrKzMfPvtt4G/yT//YswMpzEzUs54OOsf//xLkFr7f7755htTUFBg7rvvPuNyuYzL5TLvvPOOkWQuueQS8/bbb5tPP/3UHDp0yGzbts3Mnz/ffPTRR2b37t1m+vTpJjEx0ezdu9fzfr169TKpqanmpZdeMp988okpKioycXFxZufOncYYY1wul7nooovMQw89ZFwul6mqqjLHjh0zmZmZ5l//9V/N9u3bzV//+leTnZ1tCgsLPe9bWFhokpKSzNixY82OHTvM9u3bTXl5uZFk+vXrZ1avXm3KysrM4MGDzcCBA80111xj/v73v5utW7eaPn36mPHjx3vea8GCBSYjI8MUFxebzz77zBQXF5vU1FSzePFiY4wxR48eNeecc465/fbbzY4dO8yKFSvMeeedZySZ0tLSJs9lc9dAa3+//QojTz31lMnPzzdJSUnmnHPOMTfddJPZtWtXi8etW7fODBw40CQkJJjs7Gwzb948fz6WMAIAEarNYaT2lDG/6ucjiJwWSH7Vv75ekF199dXmP/7jPzzP165daySZ5cuXt3hsTk6OefHFFz3Pe/XqZe666y7P87q6OtOjRw+v37tLL73UzJgxw/N8wYIF5uyzzzZHjx71lK1cudLExcWZiooKY0x9GElLSzPV1dWeOg1h5De/+Y2n7LXXXjOSzF//+ldPWVFRkenbt6/neVZWlvnjH//o9T2efPJJU1BQYIwx5te//rVJTU01x44d87w+b968sIQRv27TrF+/Xj//+c/13nvvac2aNTp16pSGDRumY8eONXlMeXm5Ro0apaFDh6q0tFSPPvqoJk6cqOLi4sC6cgAA7cfeTd6bljZipMoD9fXCJD8/3+v5sWPH9PDDDysnJ0ddu3ZVUlKSdu3apX379nnVu+SSSzz/2+FwKD09XQcPHmzyc3bu3KlLL71UZ511lqfs+9//vurq6vTxxx97yi6++GKf40RO/7y0tDRP3dPLGj7/q6++0v79+zVu3DglJSV5Hv/5n/+pPXv2eLWnS5cunvcoKChosv3B5NeonNWrV3s9X7RokXr06KEPPvhAV111lc9j5s+fr549e3pGK/fv319btmzR7NmzdeuttwbWagBA+3D0y+DWC4LTw4Ek/eIXv9Bbb72l2bNnq0+fPurcubN+8IMfqKamxqtex44dvZ47HA7V1dU1+TnGGDkcDp+vnV5+Znt8fV5D/TPLGj6/4c9XXnlFV1xxhdf7xMfHe9pjS5uGCLvdbklSampqk3VKSko0bNgw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guHw4cPav3+/149vJAvl+YmVa6egoEBut1ubN2/WoEGDJEn/+Mc/5Ha7NWTIkFZ/XrRdO7506tRJeXl5WrNmjW655RZP+Zo1a3TTTTf5PKagoEArVqzwKnv77beVn5+vjh07hrS94RTIufGltLQ0qq+RYAr6tRPQsFf47cCBA6ZPnz7muuuuM//v//0/43K5PI/T9e3b1yxdutTz/OmnnzZOp9MsXbrUbN++3fz4xz82GRkZprKyMtxfIaT27t1rSktLzcyZM01SUpIpLS01paWlpqqqylPn9HNTVVVlHnroIbNp0yZTXl5u1q5dawoKCsy5557b7s6NMf6fH2Ni59oZMWKEueSSS0xJSYkpKSkxF198sfmXf/kXrzqxcu386U9/Mh07djQLFy40ZWVlZtKkSeass84yn3/+uTHGmEceecSMHTvWU/+zzz4zXbp0MQ8++KApKyszCxcuNB07djR//vOfbX2FkPH33Dz33HNm2bJlZvfu3WbHjh3mkUceMZJMcXGxra8QUlVVVZ7/rkgyzz77rCktLTV79+41xoT+2iGMhMmiRYuMJJ+P00kyixYt8jyvq6szM2bMMOnp6SYhIcFcddVVZvv27WFufegVFhb6PDdr16711Dn93Bw/ftwMGzbMnHPOOaZjx46mZ8+eprCw0Ozbt8/OFwgxf8+PMbFz7Rw+fNjceeedJjk52SQnJ5s777yz0XTMWLp2XnrpJdOrVy/TqVMnM3DgQLN+/XrPa4WFhebqq6/2qr9u3TozYMAA06lTJ9O7d28zb968MLc4fPw5N7NmzTLnn3++SUxMNGeffba58sorzcqVKy20OjwapjKf+SgsLDTGhP7acRjz3YgTAAAAC5hNAwAArCKMAAAAqwgjAADAKsIIAACwijACAACsIowAAACrCCMAAMAqwggAALCKMAIAAKwijAAAAKsIIwAAwCrCCAAAsOr/A3lFil65iAWoAAAAAElFTkSuQmCC\n",
+      "image/png": "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\n",
       "text/plain": [
-       "<Figure size 640x480 with 1 Axes>"
+       "<Figure size 432x288 with 1 Axes>"
       ]
      },
-     "metadata": {},
+     "metadata": {
+      "needs_background": "light"
+     },
      "output_type": "display_data"
     }
    ],
@@ -212,7 +214,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 13,
+   "execution_count": 9,
    "id": "aefb3cbd",
    "metadata": {},
    "outputs": [
@@ -237,7 +239,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 14,
+   "execution_count": 10,
    "id": "7e72e068",
    "metadata": {
     "scrolled": false
@@ -249,10 +251,10 @@
        "\\begin{align}q_{12}(p_{12}; R, R_{12}) & = p_{12} \\left(R^{2} + R_{12}^{2}\\right)\\\\q_{13}(p_{12}, p_{13}, p_{23}; R, R_{12}, R_{13}, R_{23}) & = R^{2} p_{13} + 2 R R_{12} p_{23} - 2 R R_{23} p_{12} - R_{12}^{2} p_{13} + 2 R_{12} R_{13} p_{12}\\\\q_{23}(p_{12}, p_{13}, p_{23}; R, R_{12}, R_{13}, R_{23}) & = R^{2} p_{23} - 2 R R_{12} p_{13} + 2 R R_{13} p_{12} - R_{12}^{2} p_{23} + 2 R_{12} R_{23} p_{12}\\end{align}"
       ],
       "text/plain": [
-       "<symfit.core.models.Model at 0x24a2994aec0>"
+       "<symfit.core.models.Model at 0x235eecf96d0>"
       ]
      },
-     "execution_count": 14,
+     "execution_count": 10,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -273,7 +275,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 15,
+   "execution_count": 11,
    "id": "c8d31d48",
    "metadata": {},
    "outputs": [
@@ -281,26 +283,25 @@
      "data": {
       "text/plain": [
        "{'p12': array([1., 1., 1., 1., 1., 1., 1., 1., 1., 1.]),\n",
-       " 'p13': array([0.54249339, 0.00187754, 0.88934931, 0.14371307, 0.90024069,\n",
-       "        0.16091466, 0.79154578, 0.8069197 , 0.08935312, 0.71837907]),\n",
-       " 'p23': array([0.46277455, 0.50690105, 0.93836961, 0.33780138, 0.21474948,\n",
-       "        0.09130718, 0.67021727, 0.37623112, 0.16948978, 0.31433891]),\n",
+       " 'p13': array([0.58458649, 0.73592531, 0.11134136, 0.68417876, 0.57177713,\n",
+       "        0.35552796, 0.76742005, 0.21321343, 0.21633547, 0.61716839]),\n",
+       " 'p23': array([0.55369794, 0.08997382, 0.1113173 , 0.64265498, 0.80082692,\n",
+       "        0.24724482, 0.20296079, 0.73196863, 0.62790585, 0.71005653]),\n",
        " 'q12': array([1., 1., 1., 1., 1., 1., 1., 1., 1., 1.]),\n",
-       " 'q13': array([1.88101687, 2.16664797, 2.12831565, 1.95533762, 1.48204906,\n",
-       "        1.73949724, 1.91198636, 1.66356202, 1.80505835, 1.67581711]),\n",
-       " 'q23': array([-1.70253249, -1.25262509, -2.21694347, -1.28787096, -1.87394245,\n",
-       "        -1.21644782, -2.02445395, -1.89602809, -1.10909076, -1.78311012])}"
+       " 'q13': array([1.93770314, 1.46211814, 1.77184637, 1.94216034, 2.1288009 ,\n",
+       "        1.76473758, 1.54224206, 2.22762334, 2.1530351 , 2.05956487]),\n",
+       " 'q23': array([-1.76162814, -1.65760549, -1.18332194, -1.92659531, -1.88520711,\n",
+       "        -1.45905418, -1.75711176, -1.54669495, -1.4820069 , -1.91854545])}"
       ]
      },
-     "execution_count": 15,
+     "execution_count": 11,
      "metadata": {},
      "output_type": "execute_result"
     }
    ],
    "source": [
-    "p_data_dict = {p_var[k].name: p[k] for k in p_var.keys()}\n",
-    "q_data_dict = {q_var[k].name: q[k] for k in q_var.keys()}\n",
-    "datadict = p_data_dict | q_data_dict\n",
+    "datadict = {p_var[k].name: p[k] for k in p_var.keys()}\n",
+    "datadict.update({q_var[k].name: q[k] for k in q_var.keys()})\n",
     "datadict"
    ]
   },
@@ -314,7 +315,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 16,
+   "execution_count": 12,
    "id": "97427a69",
    "metadata": {},
    "outputs": [],
@@ -327,7 +328,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 17,
+   "execution_count": 13,
    "id": "e9b9e56e",
    "metadata": {},
    "outputs": [
@@ -337,24 +338,24 @@
      "text": [
       "\n",
       "Parameter Value        Standard Deviation\n",
-      "R         5.108861e-01 1.055367e-02\n",
-      "R12       8.596484e-01 7.296990e-03\n",
-      "R13       4.858532e-01 1.842247e-02\n",
-      "R23       -8.692481e-01 9.055921e-03\n",
+      "R         5.039768e-01 1.392512e-02\n",
+      "R12       8.637172e-01 9.139166e-03\n",
+      "R13       4.932221e-01 2.474936e-02\n",
+      "R23       -8.682665e-01 1.074836e-02\n",
       "Status message         Optimization terminated successfully\n",
-      "Number of iterations   24\n",
-      "Objective              <symfit.core.objectives.LeastSquares object at 0x0000024A2994BD00>\n",
-      "Minimizer              <symfit.core.minimizers.SLSQP object at 0x0000024A29993790>\n",
+      "Number of iterations   22\n",
+      "Objective              <symfit.core.objectives.LeastSquares object at 0x00000235EE213070>\n",
+      "Minimizer              <symfit.core.minimizers.SLSQP object at 0x00000235EECFEAC0>\n",
       "\n",
       "Goodness of fit qualifiers:\n",
-      "chi_squared            0.006075606510390592\n",
-      "objective_value        0.003037803255195296\n",
-      "r_squared              0.9965636446731153\n",
+      "chi_squared            0.0071557500481054595\n",
+      "objective_value        0.0035778750240527298\n",
+      "r_squared              0.9936522373747326\n",
       "\n",
       "Constraints:\n",
       "--------------------\n",
       "Question: R**2 + R12**2 - 1 == 0?\n",
-      "Answer:   4.747247039915692e-11\n",
+      "Answer:   6.727698398378834e-10\n",
       "\n",
       "\n"
      ]
@@ -375,7 +376,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 18,
+   "execution_count": 14,
    "id": "2f072bec",
    "metadata": {},
    "outputs": [
@@ -383,8 +384,8 @@
      "name": "stdout",
      "output_type": "stream",
      "text": [
-      "(0.5108861009186112) * 1 + (0.8596484118147669) * e12 + (0.48585315153371916) * e13 + (-0.8692480906253773) * e23\n",
-      "(1.0000000000474725) * 1 + (0.0) * e12 + (0.0) * e13 + (5.551115123125783e-17) * e23\n"
+      "(0.5039767984432324) * 1 + (0.8637171917379435) * e12 + (0.49322210183114706) * e13 + (-0.8682664685672835) * e23\n",
+      "(1.0000000006727698) * 1\n"
      ]
     }
    ],
@@ -396,7 +397,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 19,
+   "execution_count": 15,
    "id": "8d0e25ed",
    "metadata": {},
    "outputs": [],
@@ -406,28 +407,30 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 20,
+   "execution_count": 16,
    "id": "d8059b46",
    "metadata": {},
    "outputs": [
     {
      "data": {
       "text/plain": [
-       "<matplotlib.legend.Legend at 0x24a2994bdc0>"
+       "<matplotlib.legend.Legend at 0x235eee31700>"
       ]
      },
-     "execution_count": 20,
+     "execution_count": 16,
      "metadata": {},
      "output_type": "execute_result"
     },
     {
      "data": {
-      "image/png": "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/fffN//85z/N4cOHzbZt28zixYvN559/bnbv3m2mT59uwsLCTGFhoef9EhMTTdeuXc0LL7xgvvrqKzNr1iwTFBRkdu7caYwxpqioyPTt29c8/PDDpqioyJSVlZljx46Z+Ph485Of/MRs377dfPDBByYpKcmMHz/e877jx483nTt3NuPGjTM7duww27dvNwUFBUaS6d27t1m7dq3Jz883gwYNMqmpqWbIkCHm73//u8nNzTU9e/Y0EydO9LzXkiVLTFxcnMnMzDRff/21yczMNF27djXLly83xhhz9OhRExUVZW655RazY8cOs3r1anPuuecaSSYvL6/Z/wxO1dCx1Njfb5/CyFNPPWXS0tJM586dTVRUlLnuuuvMrl27Trtddna2SU1NNaGhoSYpKcksWrTIl48ljABAMzvjMOI6YcwzvesIIicFkmf6VPdrZldddZX51a9+5Xm+YcMGI8m88847p902OTnZPP/8857niYmJ5vbbb/c8d7vdJjo62ut36uKLLzYzZszwPF+yZIk566yzzNGjRz1t7733ngkKCjLFxcXGmOowEhMTYyorKz19asLIH/7wB0/bG2+8YSSZDz74wNM2a9Ys06tXL8/zhIQE8/rrr3t9jyeffNKkp6cbY4x56aWXTNeuXc2xY8c8ry9atKhVhRGfTtNs3LhR9957rz755BOtX79eJ06c0IgRI3Ts2LF6tykoKNCYMWM0ePBg5eXl6dFHH9UDDzygzMzMpg3lAACsV/ix981GazFS6YHqfgGSlpbm9fzYsWN65JFHlJycrC5duqhz587atWuX9u3b59Xvoosu8vy/zWZTbGysSkpK6v2cnTt36uKLL1anTp08bZdffrncbre+/PJLT9uFF15Y5zyRkz8vJibG0/fktprPP3TokPbv368JEyaoc+fOnsfvfvc77dmzx6ue8PBwz3ukp6fXW39L5NPsnrVr13o9X7ZsmaKjo7V161ZdeeWVdW6zePFide/e3TPruU+fPtqyZYvmzp2rG2+8sWlVAwCsdfRg8/ZrBieHA0n69a9/rXXr1mnu3Lnq2bOnOnbsqJtuuklVVVVe/UJCQrye22w2ud3uej/HGCObzVbnaye3n1pPXZ9X0//UtprPr/nvyy+/rEsvvdTrfex2u6ee1u6Mpho7nU5JUteuXevts3nzZo0YMcKrbeTIkVq6dKmO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M0v+sdT7WlZgBQC0eoSR1izILiUNtroKAADOCKdpAACApQgjAADAUoQRAABgKcIIAACwFGEEAABYijACAAAsRRgBAACWIowAAABLEUYAAIClCCMAAMBShBEAAGApwggAALAUYQQAAFiKMAIAACxFGAEAAJYijAAAAEsRRgAAgKUIIwAAwFKEEQAAYCnCCAAAsBRhBAAAWIowAgAALEUYAQAAliKMAAAASxFGAACApQgjAADAUoQRAABgKcIIAACwFGEEAABYijACAAAsRRgBAACWIowAAABLEUYAAIClCCMAAMBShBEAAGApwggAALAUYQQAAFiKMAIAACxFGAEAAJYijAAAAEsRRgAAgKUIIwAAwFKEEQAAYCnCCAAAsBRhBAAAWIowAgAALEUYAQAAliKMAAAASxFGAACApQgjAADAUoQRAABgKcIIAACwFGEEAABYijACAAAsRRgBAACWIowAAABLEUYAAIClCCMAAMBShBEAAGApwggAALAUYQQAAFiKMAIAACxFGAEAAJbyOYx8+OGHGjt2rOLj42Wz2fTOO++cdpuNGzeqf//+CgsL07nnnqvFixc3pVYAANAG+RxGjh07posvvlgLFy5sVP+CggKNGTNGgwcPVl5enh599FE98MADyszM9LlYAADQ9gT7usHo0aM1evToRvdfvHixunfvrvnz50uS+vTpoy1btmju3Lm68cYbff14AADQxvh9zsjmzZs1YsQIr7aRI0dqy5YtOn78uL8/HgAAtHA+j4z4qri4WDExMV5tMTExOnHihA4fPqy4uLha21RWVqqystLzvLS01N9lAgAAiwTkahqbzeb13BhTZ3uNWbNmyeFweB4JCQl+rxEAAFjD72EkNjZWxcXFXm0lJSUKDg5Wt27d6txm2rRpcjqdnsf+/fv9XSYAALCI30/TpKena/Xq1V5t77//vtLS0hQSElLnNqGhoQoNDfV3aQAAoAXweWTk6NGj2rZtm7Zt2yap+tLdbdu2ad++fZKqRzXuuOMOT/+JEyeqsLBQGRkZ2rlzp1555RUtXbpUkydPbp5vAAAAWjWfR0a2bNmioUOHep5nZGRIksaPH6/ly5erqKjIE0wkKSkpSWvWrNFDDz2kF154QfHx8Xruuee4rBcAAEiSbKZmNmkLVlpaKofDIafTqcjISKvLAQAAjdDY32/uTQMAACxF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\n",
+      "image/png": "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\n",
       "text/plain": [
-       "<Figure size 640x480 with 1 Axes>"
+       "<Figure size 432x288 with 1 Axes>"
       ]
      },
-     "metadata": {},
+     "metadata": {
+      "needs_background": "light"
+     },
      "output_type": "display_data"
     }
    ],
@@ -465,7 +468,7 @@
  ],
  "metadata": {
   "kernelspec": {
-   "display_name": "Python 3 (ipykernel)",
+   "display_name": "Python 3",
    "language": "python",
    "name": "python3"
   },
@@ -479,7 +482,7 @@
    "name": "python",
    "nbconvert_exporter": "python",
    "pygments_lexer": "ipython3",
-   "version": "3.10.6"
+   "version": "3.8.10"
   }
  },
  "nbformat": 4,
diff --git a/kingdon/algebra.py b/kingdon/algebra.py
index 9093162..a96e633 100644
--- a/kingdon/algebra.py
+++ b/kingdon/algebra.py
@@ -15,9 +15,10 @@ def cached_property(func):
 from kingdon.codegen import (
     codegen_gp, codegen_conj, codegen_cp, codegen_ip, codegen_op, codegen_div,
     codegen_rp, codegen_acp, codegen_proj, codegen_sp, codegen_lc, codegen_inv,
-    codegen_rc, codegen_normsq, codegen_outerexp
+    codegen_rc, codegen_normsq,
+    codegen_outerexp, codegen_outersin, codegen_outercos, codegen_outertan,
 )
-from kingdon.operator_dict import OperatorDict
+from kingdon.operator_dict import OperatorDict, UnaryOperatorDict
 from kingdon.matrixreps import matrix_rep
 from kingdon.multivector import MultiVector
 # from kingdon.module_builder import predefined_modules
@@ -48,21 +49,24 @@ class Algebra:
     signature: list = field(init=False, repr=False, compare=False)
 
     # Clever dictionaries that cache previously symbolically optimized lambda functions between elements.
-    gp: OperatorDict = operation_field(metadata={'codegen': codegen_gp})  # geometric product dict
-    conj: OperatorDict = operation_field(metadata={'codegen': codegen_conj})  # conjugation dict
-    cp: OperatorDict = operation_field(metadata={'codegen': codegen_cp})  # commutator product dict
-    acp: OperatorDict = operation_field(metadata={'codegen': codegen_acp})  # anti-commutator product dict
-    ip: OperatorDict = operation_field(metadata={'codegen': codegen_ip})  # inner product dict
-    sp: OperatorDict = operation_field(metadata={'codegen': codegen_sp})  # Scalar product dict
+    gp: OperatorDict = operation_field(metadata={'codegen': codegen_gp})  # geometric product
+    conj: OperatorDict = operation_field(metadata={'codegen': codegen_conj})  # conjugation
+    cp: OperatorDict = operation_field(metadata={'codegen': codegen_cp})  # commutator product
+    acp: OperatorDict = operation_field(metadata={'codegen': codegen_acp})  # anti-commutator product
+    ip: OperatorDict = operation_field(metadata={'codegen': codegen_ip})  # inner product
+    sp: OperatorDict = operation_field(metadata={'codegen': codegen_sp})  # Scalar product
     lc: OperatorDict = operation_field(metadata={'codegen': codegen_lc})  # left-contraction
     rc: OperatorDict = operation_field(metadata={'codegen': codegen_rc})  # right-contraction
-    op: OperatorDict = operation_field(metadata={'codegen': codegen_op})  # exterior product dict
-    rp: OperatorDict = operation_field(metadata={'codegen': codegen_rp})  # regressive product dict
-    proj: OperatorDict = operation_field(metadata={'codegen': codegen_proj})  # projection dict
-    div: OperatorDict = operation_field(metadata={'codegen': codegen_div})  # division dict
-    inv: OperatorDict = operation_field(metadata={'codegen': codegen_inv})  # inverse dict
-    normsq: OperatorDict = operation_field(metadata={'codegen': codegen_normsq})  # norm squared dict
-    outerexp: OperatorDict = operation_field(metadata={'codegen': codegen_outerexp})
+    op: OperatorDict = operation_field(metadata={'codegen': codegen_op})  # exterior product
+    rp: OperatorDict = operation_field(metadata={'codegen': codegen_rp})  # regressive product
+    proj: OperatorDict = operation_field(metadata={'codegen': codegen_proj})  # projection
+    div: OperatorDict = operation_field(metadata={'codegen': codegen_div})  # division
+    inv: UnaryOperatorDict = operation_field(metadata={'codegen': codegen_inv})  # inverse
+    normsq: UnaryOperatorDict = operation_field(metadata={'codegen': codegen_normsq})  # norm squared
+    outerexp: UnaryOperatorDict = operation_field(metadata={'codegen': codegen_outerexp})
+    outersin: UnaryOperatorDict = operation_field(metadata={'codegen': codegen_outersin})
+    outercos: UnaryOperatorDict = operation_field(metadata={'codegen': codegen_outercos})
+    outertan: UnaryOperatorDict = operation_field(metadata={'codegen': codegen_outertan})
 
     # Mappings from binary to canonical reps. e.g. 0b01 = 1 <-> 'e1', 0b11 = 3 <-> 'e12'.
     canon2bin: dict = field(init=False, repr=False, compare=False)
@@ -99,7 +103,7 @@ def __post_init__(self):
         # Prepare OperatorDict's
         operators = (f for f in fields(self) if 'codegen' in f.metadata)
         for f in operators:
-            setattr(self, f.name, OperatorDict(name=f.name, codegen=f.metadata['codegen'], algebra=self))
+            setattr(self, f.name, f.type(name=f.name, codegen=f.metadata['codegen'], algebra=self))
 
     def __len__(self):
         return 2 ** self.d
diff --git a/kingdon/codegen.py b/kingdon/codegen.py
index 0b5d438..1dcb47a 100644
--- a/kingdon/codegen.py
+++ b/kingdon/codegen.py
@@ -1,25 +1,44 @@
+from __future__ import annotations
+
 from itertools import product, combinations, groupby
 from collections import namedtuple
+from typing import NamedTuple, Callable, Tuple
 from functools import reduce
 import operator
 import linecache
 import warnings
 
-from sympy import simplify, sympify, Add, Mul
+from sympy import simplify, sympify, Add, Mul, Symbol
 from sympy.utilities.lambdify import lambdify
-from numba import njit
 
 
-TermTuple = namedtuple('TermTuple', ['key_out', 'keys_in', 'sign', 'values_in', 'termstr'])
-TermTuple.__doc__ = """
-TermTuple represents a single monomial in a product of multivectors.
+class TermTuple(NamedTuple):
+    """
+    TermTuple represents a single monomial in a product of multivectors.
 
-:param key_out: is the basis blade to which this monomial belongs.
-:param keys_in: are the input basis blades in this monomial.
-:param sign: Sign of the monomial.
-:param values_in: Input values. Typically sympy symbols.
-:param termstr: The string representation of this monomial, e.g. '-x*y'.
-"""
+    :param key_out: is the basis blade to which this monomial belongs.
+    :param keys_in: are the input basis blades in this monomial.
+    :param sign: Sign of the monomial.
+    :param values_in: Input values. Typically, tuple of :class:`~sympy.core.Symbol`.
+    :param termstr: The string representation of this monomial, e.g. '-x*y'.
+    """
+    key_out: int
+    keys_in: Tuple[int]
+    sign: int
+    values_in: Tuple[Symbol]
+    termstr: str
+
+
+class CodegenOutput(NamedTuple):
+    """
+    Output of a codegen function.
+
+    :param keys_out: tuple with the output blades in binary rep.
+    :param func: callable that takes (several) sequence(s) of values
+        returns a tuple of :code:`len(keys_out)`.
+    """
+    keys_out: Tuple[int]
+    func: Callable
 
 
 def term_tuple(items, sign_func, keyout_func=operator.xor):
@@ -95,7 +114,7 @@ def codegen_gp(x, y, symbolic=False):
     if symbolic:
         return x.fromkeysvalues(x.algebra, keys_out, func(x.values(), y.values()))
 
-    return keys_out, func
+    return CodegenOutput(keys_out, func)
 
 
 def codegen_conj(x, y):
@@ -119,7 +138,7 @@ def codegen_cp(x, y, symbolic=False):
     if symbolic:
         return x.fromkeysvalues(algebra, keys_out, func(x.values(), y.values()))
 
-    return keys_out, func
+    return CodegenOutput(keys_out, func)
 
 
 def codegen_acp(x, y, symbolic=False):
@@ -134,7 +153,7 @@ def codegen_acp(x, y, symbolic=False):
     if symbolic:
         return x.fromkeysvalues(algebra, keys_out, func(x.values(), y.values()))
 
-    return keys_out, func
+    return CodegenOutput(keys_out, func)
 
 
 def codegen_ip(x, y, diff_func=abs, symbolic=False):
@@ -152,7 +171,7 @@ def codegen_ip(x, y, diff_func=abs, symbolic=False):
     if symbolic:
         return x.fromkeysvalues(algebra, keys_out, func(x.values(), y.values()))
 
-    return keys_out, func
+    return CodegenOutput(keys_out, func)
 
 
 def codegen_lc(x, y):
@@ -209,7 +228,7 @@ def codegen_op(x, y, symbolic=False):
     if symbolic:
         return x.fromkeysvalues(algebra, keys_out, func(x.values(), y.values()))
 
-    return keys_out, func
+    return CodegenOutput(keys_out, func)
 
 
 def codegen_rp(x, y):
@@ -253,14 +272,14 @@ def codegen_inv(x, symbolic=False):
     but it can be expensive to compute.
     In the future this should be extended to use dedicated solutions for known cases.
     """
-    k = 2 ** ((x.algebra.d + 1) // 2)
+    n = 2 ** ((x.algebra.d + 1) // 2)
     x_i = x
     if x.grades == (0,):
         adj_x = 1
     else:
-        for i in range(1, k + 1):
-            # Sympify ratio to keep the ratios exact and avoid floating point errors.
-            c_i = (sympify(k) / i) * x_i[0] if x_i[0] else x_i[0]
+        n = sympify(n)  # Sympify ratio to keep the ratios exact and avoid floating point errors.
+        for i in range(1, n + 1):
+            c_i = (n / i) * x_i[0] if x_i[0] else x_i[0]
             adj_x = (x_i - c_i)
             if x.algebra.simplify:
                 keys, values = zip(*((k, simp_expr) for k, expr in adj_x.items() if (simp_expr := simplify(expr))))
@@ -327,27 +346,48 @@ def codegen_outerexp(x, asterms=False):
     L = reduce(operator.add, Ws)
     return _func_builder(dict(L.items()), x, name_base='outerexp')
 
+def codegen_outersin(x):
+    odd_Ws = codegen_outerexp(x, asterms=True)[1::2]
+    outersin = reduce(operator.add, odd_Ws)
+    return _func_builder(outersin, x, name_base='outersin')
+
+
+def codegen_outercos(x):
+    even_Ws = codegen_outerexp(x, asterms=True)[0::2]
+    outercos = reduce(operator.add, even_Ws)
+    return _func_builder(outercos, x, name_base='outercos')
+
+
+def codegen_outertan(x):
+    Ws = codegen_outerexp(x, asterms=True)
+    even_Ws, odd_Ws = Ws[0::2], Ws[1::2]
+    outercos = reduce(operator.add, even_Ws)
+    outersin = reduce(operator.add, odd_Ws)
+    outertan = outersin / outercos
+    return _func_builder(outertan, x, name_base='outertan')
+
 
 def _lambdify_binary(x, y, x_bin_y):
     xy_symbols = [list(x.values()), list(y.values())]
     func = lambdify(xy_symbols, list(x_bin_y.values()), cse=x.algebra.cse)
-    return tuple(x_bin_y.keys()), njit(func) if x.algebra.numba else func
+    return CodegenOutput(tuple(x_bin_y.keys()), func)
 
 
 def _lambdify_unary(x, x_unary):
     func = lambdify([list(x.values())], list(x_unary.values()), cse=x.algebra.cse)
-    return tuple(x_unary.keys()), njit(func) if x.algebra.numba else func
+    return CodegenOutput(tuple(x_unary.keys()), func)
 
 
 def _lambdify_mv(free_symbols, mv):
     # TODO: Numba wants a tuple in the line below, but simpy only produces a
     #  list as output if this is a list, not a tuple. See if we can solve this.
     func = lambdify(free_symbols, list(mv.values()), cse=mv.algebra.cse)
-    return tuple(mv.keys()), njit(func) if mv.algebra.numba else func
+    return CodegenOutput(tuple(mv.keys()), func)
+
 
 def _func_builder(res_vals: dict, *mvs, name_base: str):
     """
-    Build a Python functions for the product between given multivectors.
+    Build a Python function for the product between given multivectors.
 
     :param res_vals: Dict to be converted into a function. The keys correspond to the basis blades in binary,
         while the values are strings to be converted into source code.
@@ -375,4 +415,4 @@ def _func_builder(res_vals: dict, *mvs, name_base: str):
     # Add the generated code to linecache such that it is inspect-safe.
     linecache.cache[func_name] = (len(func_source), None, func_source.splitlines(True), func_name)
     func = func_locals[func_name]
-    return tuple(res_vals.keys()), njit(func) if mvs[0].algebra.numba else func
+    return CodegenOutput(tuple(res_vals.keys()), func)
diff --git a/kingdon/multivector.py b/kingdon/multivector.py
index 9488233..be5b50f 100644
--- a/kingdon/multivector.py
+++ b/kingdon/multivector.py
@@ -1,3 +1,4 @@
+import operator
 from collections.abc import Mapping
 from dataclasses import dataclass, field
 from functools import reduce, cached_property
@@ -40,7 +41,7 @@ def __new__(cls, algebra, values=None, keys=None, *, name=None, grades=None, sym
 
         # Construct a new MV on the basis of the kind of input we received.
         if isinstance(values, Mapping):
-            keys, values = zip(*values.items())
+            keys, values = zip(*values.items()) if values else (tuple(), tuple())
         elif len(values) == len(algebra) and not keys:
             keys = tuple(range(len(values)))
         elif len(values) == len(algebra.indices_for_grades[grades]) and not keys:
@@ -160,9 +161,14 @@ def __add__(self, other):
                 vals[k] = v
         return self.fromkeysvalues(self.algebra, tuple(vals.keys()), tuple(vals.values()))
 
+    __radd__ = __add__
+
     def __sub__(self, other):
         return self + (-other)
 
+    def __rsub__(self, other):
+        return other + (-self)
+
     def __truediv__(self, other):
         return self.algebra.div(self, other)
 
@@ -173,6 +179,12 @@ def __str__(self):
         else:
             return '0'
 
+    def __format__(self, format_spec):
+        if format_spec == 'keys_binary':
+            iden = '_'.join(''.join('1' if i in self.keys() else '0' for i in bin_blades)
+                            for bin_blades in self.algebra.indices_for_grade.values())
+            return iden
+
     def __getitem__(self, item):
         if isinstance(item, tuple):
             key, slicing = item
@@ -202,7 +214,7 @@ def __contains__(self, item):
 
     @cached_property
     def free_symbols(self):
-        return reduce(lambda tot, x: tot | x, (v.free_symbols for v in self.values()))
+        return reduce(operator.or_, (v.free_symbols for v in self.values() if hasattr(v, "free_symbols")))
 
     @cached_property
     def _callable(self):
@@ -291,6 +303,14 @@ def __pow__(self, power, modulo=None):
     def outerexp(self):
         return self.algebra.outerexp(self)
 
+    def outersin(self):
+        return self.algebra.outersin(self)
+
+    def outercos(self):
+        return self.algebra.outercos(self)
+
+    def outertan(self):
+        return self.algebra.outertan(self)
 
     def dual(self, kind='auto'):
         """
@@ -334,28 +354,3 @@ def undual(self, kind='auto'):
             raise Exception('Cannot select a suitable undual in auto mode for this algebra.')
         else:
             raise ValueError(f'No undual found for kind={kind}.')
-
-
-# class GradedMultiplication:
-#     def _binary_operation(self, other, func_dictionary, codegen):
-#         """ Helper function for all multiplication types such as gp, sp, cp etc. """
-#         if self.algebra != other.algebra:
-#             raise AlgebraError("Cannot multiply elements of different algebra's.")
-#
-#         keys_in = (self.algebra.indices_for_grades[self.grades],
-#                    self.algebra.indices_for_grades[other.grades])
-#         if keys_in not in func_dictionary:
-#             x = self.algebra.multivector(vals={ek: Symbol(f'a{self.algebra.bin2canon[ek][1:]}')
-#                                                for ek in keys_in[0]})
-#             y = self.algebra.multivector(vals={ek: Symbol(f'b{self.algebra.bin2canon[ek][1:]}')
-#                                                for ek in keys_in[1]})
-#             keys_out, func = func_dictionary[keys_in] = codegen(x, y)
-#         else:
-#             keys_out, func = func_dictionary[keys_in]
-#
-#         args = chain((self.vals.get(i, 0) for i in keys_in[0]),
-#                      (other.vals.get(i, 0) for i in keys_in[1]))
-#         res_vals = defaultdict(int, {k: v for k, v in zip(keys_out, func(*args))
-#                                      if (True if v.__class__ is not Expr else simplify(v))})
-#
-#         return self.algebra.mvfromtrusted(vals=res_vals)
diff --git a/kingdon/operator_dict.py b/kingdon/operator_dict.py
index 3cac065..8bb94cb 100644
--- a/kingdon/operator_dict.py
+++ b/kingdon/operator_dict.py
@@ -1,8 +1,9 @@
 from dataclasses import dataclass, field
 from collections.abc import Mapping
-from typing import Callable
+from typing import Callable, Tuple
 import string
 
+from numba import njit
 from sympy import Symbol, Expr, simplify
 
 from kingdon.multivector import MultiVector
@@ -14,6 +15,17 @@ class AlgebraError(Exception):
 
 @dataclass
 class OperatorDict(Mapping):
+    """
+    A dict-like object which performs codegen of a particular operator,
+    and caches the result for future use. For example, to generate the geometric product,
+    we create an OperatorDict as follows::
+
+        alg = Algebra(3, 0, 1)
+        gp = OperatorDict('gp', codegen=codegen_gp, algebra=alg)
+
+    Here, :code:`codegen_gp` is a function that outputs the keys of the result, and a callable that
+    produces the corresponding values. See :class:`~kingdon.codegen.CodegenOutput` for more info.
+    """
     name: str
     codegen: Callable
     algebra: "Algebra"
@@ -22,35 +34,107 @@ class OperatorDict(Mapping):
     def __len__(self):
         return len(self.operator_dict)
 
-    def __getitem__(self, keys_in):
+    def __getitem__(self, keys_in: Tuple[Tuple[int]]):
         if keys_in not in self.operator_dict:
-            mvs = []
-            for name, keys in zip(string.ascii_lowercase, keys_in):
-                vals = tuple(Symbol(f'{name}{self.algebra.bin2canon[ek][1:]}') for ek in keys)
-                mvs.append(MultiVector.fromkeysvalues(self.algebra, keys=keys, values=vals))
-            self.operator_dict[keys_in] = self.codegen(*mvs)
+            # Make symbolic multivectors for each set of keys and generate the code.
+            mvs = tuple(
+                MultiVector.fromkeysvalues(
+                    self.algebra, keys=keys,
+                    values=tuple(Symbol(f'{name}{self.algebra.bin2canon[ek][1:]}') for ek in keys))
+                for name, keys in zip(string.ascii_lowercase, keys_in)
+            )
+            keys_out, func = self.codegen(*mvs)
+            self.operator_dict[keys_in] = (keys_out, func, njit(func))
         return self.operator_dict[keys_in]
 
     def __iter__(self):
         return iter(self.operator_dict)
 
     def __call__(self, *mvs):
+        if len(mvs) == 2:
+            return self._call_binary(*mvs)
+
         # Make sure all inputs are multivectors. If an input is not, assume its scalar.
         mvs = [mv if isinstance(mv, MultiVector) else MultiVector.fromkeysvalues(self.algebra, (0,), (mv,))
                for mv in mvs]
         if any((mvs[0].algebra != mv.algebra) for mv in mvs[1:]):
             raise AlgebraError("Cannot multiply elements of different algebra's.")
+
         keys_in = tuple(mv.keys() for mv in mvs)
         values_in = tuple(mv.values() for mv in mvs)
-        keys_out, func = self[keys_in]
-        values = func(*values_in)
+        keys_out, func, numba_func = self[keys_in]
+        issymbolic = any(mv.issymbolic for mv in mvs)
+        if issymbolic or not mvs[0].algebra.numba:
+            values_out = func(*values_in)
+        else:
+            values_out = numba_func(*values_in)
+
+        if issymbolic and self.algebra.simplify:
+            # Keep only symbolically non-zero elements.
+            keysvalues = tuple(filter(
+                lambda kv: True if not isinstance(kv[1], Expr) else simplify(kv[1]),
+                zip(keys_out, values_out)
+            ))
+            keys_out, values_out = zip(*keysvalues) if keysvalues else (tuple(), tuple())
+
+        return MultiVector.fromkeysvalues(self.algebra, keys=keys_out, values=values_out)
+
+    def _call_binary(self, mv1, mv2):
+        """ Specialization for binary operators. """
+        # Make sure all inputs are multivectors. If an input is not, assume its scalar.
+        mv1 = mv1 if isinstance(mv1, MultiVector) else MultiVector.fromkeysvalues(self.algebra, (0,), (mv1,))
+        mv2 = mv2 if isinstance(mv2, MultiVector) else MultiVector.fromkeysvalues(self.algebra, (0,), (mv2,))
+        # Check is written to be fast, not readable. Typically, the first check is true.
+        if not (mv1.algebra is mv2.algebra or mv1.algebra == mv2.algebra):
+            raise AlgebraError("Cannot multiply elements of different algebra's.")
+
+        keys_out, func, numba_func = self[mv1.keys(), mv2.keys()]
+        issymbolic = (mv1.issymbolic or mv2.issymbolic)
+        if issymbolic or not mv1.algebra.numba:
+            values_out = func(mv1.values(), mv2.values())
+        else:
+            values_out = numba_func(mv1.values(), mv2.values())
+
+        if issymbolic and self.algebra.simplify:
+            # Keep only symbolically non-zero elements.
+            keysvalues = tuple(filter(
+                lambda kv: True if not isinstance(kv[1], Expr) else simplify(kv[1]),
+                zip(keys_out, values_out)
+            ))
+            keys_out, values_out = zip(*keysvalues) if keysvalues else (tuple(), tuple())
+
+        return MultiVector.fromkeysvalues(self.algebra, keys=keys_out, values=values_out)
+
+
+class UnaryOperatorDict(OperatorDict):
+    """
+    Specialization of OperatorDict for unary operators. In the
+    case of unary operators, we can do away with all of the overhead that is necessary for
+    operators that act on multiple multivectors.
+    """
+    def __getitem__(self, keys_in: Tuple[Tuple[int]]):
+        if keys_in not in self.operator_dict:
+            vals = tuple(Symbol(f'a{self.algebra.bin2canon[ek][1:]}') for ek in keys_in)
+            mv = MultiVector.fromkeysvalues(self.algebra, keys=keys_in, values=vals)
+            keys_out, func = self.codegen(mv)
+            self.operator_dict[keys_in] = (keys_out, func, njit(func))
+        return self.operator_dict[keys_in]
+
+    def __call__(self, mv):
+        keys_out, func, numba_func = self[mv.keys()]
+
+        issymbolic = mv.issymbolic
+        if issymbolic or not mv.algebra.numba:
+            values_out = func(mv.values())
+        else:
+            values_out = numba_func(mv.values())
 
-        if self.algebra.simplify and any(mv.issymbolic for mv in mvs):
+        if issymbolic and self.algebra.simplify:
             # Keep only symbolically non-zero elements.
             keysvalues = tuple(filter(
                 lambda kv: True if not isinstance(kv[1], Expr) else simplify(kv[1]),
-                zip(keys_out, values)
+                zip(keys_out, values_out)
             ))
-            keys_out, values = zip(*keysvalues) if keysvalues else (tuple(), tuple())
+            keys_out, values_out = zip(*keysvalues) if keysvalues else (tuple(), tuple())
 
-        return MultiVector.fromkeysvalues(self.algebra, keys=keys_out, values=values)
+        return MultiVector.fromkeysvalues(self.algebra, keys=keys_out, values=values_out)


Test patch:
diff --git a/tests/test_kingdon.py b/tests/test_kingdon.py
index 6394939..b446921 100644
--- a/tests/test_kingdon.py
+++ b/tests/test_kingdon.py
@@ -427,7 +427,7 @@ def test_projection():
 def test_outerexp(R6):
     B = R6.bivector(name='B')
     LB = B.outerexp()
-    LB_exact = B + (B ^ B) / 2 + (B ^ B ^ B) / 6 + 1
+    LB_exact = 1 + B + (B ^ B) / 2 + (B ^ B ^ B) / 6
 
     diff = LB - LB_exact
     for val in diff.values():
@@ -435,11 +435,23 @@ def test_outerexp(R6):
 
     v = R6.vector(name='v')
     Lv = v.outerexp()
-    Lv_exact = v + 1
+    Lv_exact = 1 + v
     diff = Lv - Lv_exact
     for val in diff.values():
         assert val == 0
 
+def test_outertrig(R6):
+    alg = Algebra(6)
+    B = alg.bivector(name='B', keys=(0b110000, 0b1100, 0b11))
+    sB = B.outersin()
+    cB = B.outercos()
+
+    sB_exact = B + (B ^ B ^ B) / sympify(6)
+    cB_exact = sympify(1) + (B ^ B) / sympify(2)
+
+    for diff in [sB - sB_exact, cB - cB_exact]:
+        assert all(v == 0 for v in diff.values())
+
 
 def test_indexing():
     alg = Algebra(4)
diff --git a/tests/test_operator_dict.py b/tests/test_operator_dict.py
index 0258310..9a38b38 100644
--- a/tests/test_operator_dict.py
+++ b/tests/test_operator_dict.py
@@ -2,7 +2,7 @@
 
 import pytest
 
-from kingdon.operator_dict import OperatorDict
+from kingdon.operator_dict import OperatorDict, UnaryOperatorDict
 from kingdon.codegen import codegen_gp, codegen_inv
 from kingdon import Algebra
 
@@ -20,8 +20,8 @@ def test_operator_dict():
     assert len(gp) == 1  # size of gp has grown by one
     assert (x.keys(), y.keys()) in gp
 
-    inv = OperatorDict('inv', codegen=codegen_inv, algebra=alg)
+    inv = UnaryOperatorDict('inv', codegen=codegen_inv, algebra=alg)
     assert len(inv) == 0
     xinv = inv(x)
     assert len(inv) == 1
-    assert (x.keys(),) in inv
+    assert x.keys() in inv