Expressing barycentric coordinates symbolically in GEM#230
Expressing barycentric coordinates symbolically in GEM#230
Conversation
…ts of different sizes
…ion to handle nested tensor-product cells + added tests
…ation works properly for symbolic points
…ds to work on different array shapes
…xpressions representing point sets
…rately with singleton contractions
…ons work on GEM tensor expressions
…_barycentric_coordinates in tp cells
| key = (key,) | ||
|
|
||
| # Expand ellipsis -> fill remaining dimensions with slice(None) | ||
| if Ellipsis in key: |
There was a problem hiding this comment.
Should we move the Ellipsis suport to gem.view?
There was a problem hiding this comment.
To me, gem.view looks like a lower-level operation which accepts one slice per each dimension to return a (new) tensor with the restricted view. The ellipsis, on the other hand, is more a syntactic convenience for indexing, mimicking that of NumPy. It basically just creates under hood the list of slices that view then accepts to produce the resulting tensor view. I would personally keep it in __getitem__.
There was a problem hiding this comment.
Update: gem.view returns a ComponentTensor with FlexiblyIndexed node which apparently gem's evaluator cannot evaluate. Instead of calling gem.view when doing slice indexing, an alternative is to build a ListTensor from the components directly:
Lines 131 to 146 in 76e63f7
This seems to work with evaluate.
|
I think we should discuss a protocol to support symbolic computations with FIAT. FIAT is written in numpy, so one approach is to assume that all inputs and outputs are (or can be casted as) numpy arrays. This is the approach taken in #172. In particular, let's take as an example the main part of that PR, which is Lines 148 to 151 in 5c231cc There, finat converts a tensor-valued GEM expression (a list of coordinates of shape However, this means that we will have expanded expressions that involve the individual coordinates, that prevent GEM contractions along the coordinate |
|
To support GEM tensor expressions in barycentric computations, the main idea was to substitute NumPy operations with equivalent tensor operations that have implementations in both NumPy and GEM. This resulted in barycentric coordinate computations supporting inputs that are both NumPy arrays and GEM Nodes. Where array-style manipulations were required, equivalent functionality was introduced directly in GEM. An example of that is array slicing which appears in The only explicit GEM dependency added to FIAT appears in fiat/FIAT/reference_element.py Lines 1637 to 1646 in ddf6eb4 This raises a design question: whether it is acceptable to keep this GEM dependency in FIAT (since barycentric computations are inherently tied to reference cell geometry, which lives in FIAT), or whether it should instead be routed through FInAT to preserve the traditional separation between FIAT and GEM. Introducing an indirection layer in FInAT to dispatch back into FIAT for geometry-related symbolic operations would be a far more complex approach, at least in the context of the barycentric coordinate computation. |
pbrubeck
left a comment
There was a problem hiding this comment.
Ideally we would like to support any input that can be casted into numpy.ndarray, we often work with points as list of tuples, mainly because numpy types are not immutable.
| """Returns the barycentric coordinates of a list of points on the complex.""" | ||
| if len(points) == 0: | ||
|
|
||
| if isinstance(points, numpy.ndarray) and len(points) == 0: |
There was a problem hiding this comment.
| if isinstance(points, numpy.ndarray) and len(points) == 0: | |
| if numpy.size(points) == 0: |
| this will have shape ``(npoints, 2*d)``. | ||
| """ | ||
|
|
||
| if isinstance(points, numpy.ndarray) and len(points) == 0: |
There was a problem hiding this comment.
| if isinstance(points, numpy.ndarray) and len(points) == 0: | |
| if numpy.size(points) == 0: |
| (triangle_x_interval, [0.25, 0.25, 0.5]), | ||
| (quadrilateral_x_interval, [0.25, 0.25, 0.5])]) | ||
| def test_tp_axis_bary_coords(cell, point, epsilon=1e-12): | ||
| point = np.asarray(point) |
There was a problem hiding this comment.
| point = np.asarray(point) |
| (hexahedron, [0.3, 0.4, 0.0]), | ||
| (hexahedron, [0.3, 0.4, 1.0]),]) | ||
| def test_hypercube_bary_coords_are_in_facet_order(cell, point, epsilon=1e-12): | ||
| point = np.asarray(point) |
There was a problem hiding this comment.
| point = np.asarray(point) |
| import gem | ||
| from gem.interpreter import evaluate | ||
|
|
||
| point = np.asarray(point) |
There was a problem hiding this comment.
| point = np.asarray(point) |
This PR introduces new features that enable the barycentric coordinates of points expressed in cell-local reference coordinates to be expressed symbolically in GEM.
Previously, FIAT’s
compute_barycentric_coordinatesroutines assumed that input points are NumPy arrays and performed eager NumPy operations such asdotand array slicing. Passing agem.Nodeobject resulted in NumPy doing object manipulations rather than constructing meaningful GEM expressions.Broadly two sets of changes were made:
In
FIAT.reference_element.py: new methods were introduced to enable the computation of barycentric coordinates on tensor product cells which previously did not exist. Additionally, all operations within those methods support both the case where the input array of points is a NumPy array or a GEM tensor expression. Some operations such as slicing in the case of tensor product cells required introducinggemoperations which makes FIAT depended on GEM. These could potentially be removed if the equivalent NumPy operations can be re-implemented in GEM directly.In
gem.gem.py: minor changes were made to__matmul__. First, the method now supports multiplication between a GEM tensor and a NumPy array. Second, due to some compilation issues raised by loopy, a special case was introduced to handle tensor contractions over singleton dimensions. This essentially replaces the index summation with direct indexing which eliminates the need to build trivial contraction loops (iterating over indices of extent 1).