Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DM-34721: miscellaneous cleanups and union/intersection regions #37

Merged
merged 5 commits into from May 17, 2022

Conversation

TallJimbo
Copy link
Member

No description provided.

This implements DM-15200.  These release() calls were originally a
workaround for a pybind11 bug that has since been fixed upstream.
It's understandable that the general implementation of these operations
doesn't handle this case the way we'd like due to round-off error, and
the old behavior isn't even wrong, given their clearly-documented
limited guarantees.  But it's very nice for unit tests at least to
satisfy "equality is equivalent to CONTAINS and WITHIN".
#else
union { uint64_t u; double d; };
d = item;
buffer.push_back(static_cast<uint8_t>(u));
Copy link
Contributor

@fritzm fritzm May 17, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You might consider a resize() here and then just using array subscripting assignments, to avoid the redundant bounds checking in eight successive push_back() calls. OTOH, there are less than ten of them, so it's probably a shrug :-)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a little worried it could be an accidental pessimization, because the pattern is that lots of functions like this are going to be pushing into the same buffer vector, and I don't actually know if resize is going to (when the capacity isn't big enough) allocate just the amount of extra space needed or do the kind of "allocate more than you did last time" thing that keeps push_back amortized constant-time. Do you happen to know?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. I think you could call capacity() before resize() to avoid the undesired behavior, but I have also just realized the the resize() is going to have to do a default init on all eight slots, so that will have a cost as well.

In any case, for eight items per batch I think it's hard to argue convincingly that there would be much of a win or loss here in the end.

/// Create a vector of Region (or Region-subclass) pointers by copying the
/// regions from a sized Python iterable (e.g. S == py::tuple).
///
/// Note that the pybind11 built-in STL conversions don't work, because they we
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo: "they we"

// within it.
| ((r1 & WITHIN) & (r2 & WITHIN))
// If either operand contains the given region, the union contains it.
| ((r1 & CONTAINS) | (r2 & CONTAINS));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about cases where neither r1 nor r2 individually contain rhs, but the union of r1 and r2 does (for example, two overlapping circles that together contain an ellipse not entirely contained by either one individually?)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're absolute right about the behavior, but we're saved by the limited guarantees made by the Region interface on the behavior; from the docs on Region::relate:

    /// Said another way: if the CONTAINS, WITHIN or DISJOINT bit is set, then
    /// the corresponding spatial relationship between the two regions holds
    /// conclusively. If it is not set, the relationship may or may not
    /// hold.
    ///
    /// These semantics allow for conservative relationship computations. In
    /// particular, a Region may choose to implement `relate` by replacing
    /// itself and/or the argument with a simplified bounding region.

It'd be nice to do better, but I think we'd at least need way for all Region classes to be able to compute some common set of shapes they bound (rather than are bounded by), and that's a lot of hard math for still not being able to formally guarantee any more than we do.

| ((r1 & DISJOINT) | (r2 & DISJOINT))
// If either operand is within the given region, the intersection is
// within it.
| ((r1 & WITHIN) | (r2 & WITHIN));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there are also cases where neither operand is individually within rhs, but the intersection of the operands is (consider two overlapping circles, and rhs an ellipse that surrounds the intersection but extends outside both of the circles)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(other thread on related problem for unions applies here, too)

Copy link
Contributor

@fritzm fritzm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like a nice feature to have. Some questions in comments about whether all cases are properly covered in the relate() methods?

Additionally interesting: compound regions are the first possibly non-convex regions in this library. At first I wondered if this might violate some assumptions elsewhere in the library (e.g. in various pixelization codes) but I didn't see any issues with this offhand...

@TallJimbo
Copy link
Member Author

TallJimbo commented May 17, 2022

compound regions are the first possibly non-convex regions in this library. At first I wondered if this might violate some assumptions elsewhere in the library (e.g. in various pixelization codes) but I didn't see any issues with this offhand

That's a good question, but I don't have a strong enough understanding of what "convex" means for general regions on the sphere to tell if it is qualitatively new in that respect. It certainly does permit non-convex great-circle polygons, which we didn't before, but we could already define Box and Circle objects (those covering more than a hemisphere) with non-great-circle boundaries that would look more concave than convex in some projections (but I'm waving my hands, because they also don't have straight-line edges in any projection I can reason intuitively about).

@TallJimbo TallJimbo merged commit bbfe443 into main May 17, 2022
@TallJimbo TallJimbo deleted the tickets/DM-34721 branch May 17, 2022 16:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
2 participants