1

I've perused the docs and demo notebooks in the awkward1 repo (and it's entirely possible I missed something obvious), but I've strayed into unfamiliar territory and would like some help.

Suppose I have points in a polar coordinate frame that I provide to users as an array of records using the awkward1 library. Some users may prefer to work with the points in a Cartesian coordinate frame, so I can accommodate them by adding behaviors, and the following example works.

import awkward1 as ak
import numpy as np


class Polar2Cartesian(object):
    """Mixin class to convert from polar to Cartesian."""
    @property
    def x(self):
        return self.r * np.cos(self.theta)

    # And similarly for y, but this is just an example


# Register the behaviors with awkward
class Point(ak.Record, Polar2Cartesian):
    pass

class PointArray(ak.Array, Polar2Cartesian):
    pass

ak.behavior['Point'] = Point
ak.behavior['*', 'Point'] = PointArray

# Define the points
r = np.array([2.53, 0.29, 3.18])
theta = np.array([np.pi/4, np.pi, -np.pi/3])
points = ak.zip({'r': r, 'theta': theta}, with_name='Point')

# x-component of the first point
points[0].x == r[0] * np.cos(theta[0]) # True

# x-components of all points
points.x == r * np.cos(theta) # <Array [True, True, True] type='3 * bool'>

Now suppose the conversion must use a Numba-compiled function. I've defined one in the following example, but in principle it could come from a third-party library over which I have no control. The following works for individual records, but raises a TypingError over the array of records.

import awkward1 as ak
import numba as nb
import numpy as np


@nb.njit
def cos(x):
    """A contrived example appears."""
    return np.cos(x)

class Polar2Cartesian(object):
    """Mixin class to convert from polar to Cartesian."""
    @property
    def x(self):
        return self.r * cos(self.theta) # uses jitted function here

    # And similarly for y, but this is just an example

# Define and register the behaviors like before, and create
# the same array of Points as in the previous example

# This still works
points[0].x == r[0] * np.cos(theta[0]) # True

# This now raises a TypingError
points.x == r * np.cos(theta)

Parsing the traceback reveals the reason for the TypingError:

TypingError: can't resolve ufunc cos for types (awkward1.ArrayView(awkward1.NumpyArrayType(array(float64, 1d, A), none, {}), None, ()),)

That's fair, as it would be unreasonable to expect Numba to know how to use this type without help from the developer.

A workaround I have found is to call np.asarray() within the definition of Polar2Cartesian.x, i.e. cos(np.asarray(self.theta)). Although cos returns a NumPy array, Polar2Cartesian.x ends up becoming an awkward Array due to the subsequent multiplication with self.r. In general, one could convert the return value to an awkward record or array as needed.

But is this solution "awkward1-approved", or should I be going the route where I provide typer and lower functions for theta and x as additional behaviors? If the latter, could someone walk me through how to correctly write the typer and lower functions?

At this point, I wonder if the scope of my question title is too broad. If someone has a better suggestion, please let me know.

0 Answers0