5

Consider the following discretization of a region object:

region = DiscretizeRegion[Disk[], MaxCellMeasure -> ∞]
Head[region]
InputForm[region]

which returns something like this:

By all appearances, this has created an object region with Head MeshRegion and which contains a bunch of points, then a Polygon object, and other stuff inside.

I am trying to manipulate regions of this kind using standard mess-with-the-code techniques, but they're proving surprisingly ineffective. Consider the following ways to get at the juicy interior:

  • Replace the head with something else, e.g. via notMeshRegion @@ region,

    which doesn't do anything.

  • Attack it via Part, to get at the inner components of the expression,

    doesn't work, and it returns a Part::partd error.

  • Attempt to replace the head or internals of the expression with something else,

    which leaves it untouched.

What's going on? I might expect this kind of thing to happen with objects with HoldFirst or HoldAll attributes (so their internals are a bit weird and they do some nonstandard handling), but MeshRegion's only attribute is Protected, and under InputForm and FullForm region looks exactly like every other expression. How do I get to those juicy internals?


I thought this would be obvious, since we're all grownups, but just to be clear: I'm not asking about WRI's design reasons for making the language behave like it does, which is obviously a question for WRI instead of this site. I'm asking about what features of the language cause some expressions, which can have intricate an intricate internal structure evident through InputForm or FullForm to make that internal structure unavailable via the normal language tools, where they are represented in the documentation, and how I can use the language to tell whether a given expression will behave this way.

Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
Emilio Pisanty
  • 10,255
  • 1
  • 36
  • 69
  • 2
    check AtomQ @ region – Kuba Jun 06 '17 at 14:38
  • @Kuba That's kind of yet another symptom on the same vein - sure, it's reported as an atom, but it plainly has a ton of internal structure that's being intentionally hidden, and I can't tell what it its about MeshRegion that will cause its output to be thus 'atomized'. – Emilio Pisanty Jun 06 '17 at 14:53
  • 2
    It is WRI which causes it to be atomized, that way you can't mess with content and it can go through internal procedures without too many check points. It is not another symptom, it is the reason and a sign that in order to extract details you need built-in region specific functions. – Kuba Jun 06 '17 at 15:12
  • @Kuba Obviously it is WRI that causes it to be atomized, but that doesn't make your comment particularly helpful. What is it about MeshRegion that indicates that behaviour, and how can I tell, via documentation or otherwise, whether SomeOtherFunction will behave in that way? – Emilio Pisanty Jun 06 '17 at 15:22
  • Sorry, clearly I'm missing the point, but I thought AtomQ is what can tell you that. But I agree an atomic part of the WL could be documented better: comment367234 – Kuba Jun 06 '17 at 15:26
  • @Kuba No, you're probably on the right track, and if it's not well documented then that's what the deal is - and indeed on a closer inspection the MeshRegion docs do have a tiny note on this. I'm happy to accept a more fleshed-out version of your comments as an answer. – Emilio Pisanty Jun 06 '17 at 15:42
  • I don't think I have much more to say than Szabolcs said in the linked answer. Don't you think that is in essence a duplicate? If not let me know what is missing there. – Kuba Jun 07 '17 at 06:38
  • This question was one vote away from being closed. I chose to close it as "already has an answer" and add links, as this seems to me more useful than simply marking it "off-topic" as it was heading for. If anyone has suggestions for other/better links for the header please let me know. – Mr.Wizard Jun 09 '17 at 16:47
  • @Mr.Wizard That could well have waited for the actual closure. The question had been edited and the review showed a change in the trend. I guess there's nothing else to do but I do want to register my displeasure at something that could well have waited for a community decision. – Emilio Pisanty Jun 09 '17 at 17:26
  • @Emilio Since there were four existing close votes my additional vote acted only as a user (community) vote. I voted in an effort to be helpful as I did not think that it should be closed as off-topic. I am sincerely sorry to read of your displeasure. Maybe I don't understand what you intend for this question. You make it clear that this is not about WRI design decisions (and it should therefore not have the language-design tag, so I'll remove that) but the remaining answer seems to be "it is atomic" and that does already have an answer elsewhere. (continued) – Mr.Wizard Jun 09 '17 at 17:40
  • If you wish for your question to be reopened I suggest (as a user, not a community moderator) that you edit it to start with the acknowledgement that MeshRegion is atomic, and then clarify what you actually want answered, as that remains unclear to me. – Mr.Wizard Jun 09 '17 at 17:42
  • @Mr.Wizard That's different to your previous comment. At this stage I don't care that much anymore - the real source of frustration is that the "official" ways to interact with MeshRegions are buggy, unstable, hard to use, and with major changes from version to version. But oh well. – Emilio Pisanty Jun 09 '17 at 17:58

2 Answers2

6

Not an answer to your question how to test for this behaviour, but I thought you might find it useful to have a way to programatically access the internal structure of the MeshRegion you used as an example.

This works as follows:

deAtomized = ToExpression[
                  StringReplace[
                      ToBoxes[InputForm@region][[1, 1]], 
                      "MeshRegion" -> "meshregion"]
             ];

Now you have access to the internal structure, and you can use Part on deAtomized like here:

Graphics[
  {
    FaceForm[], EdgeForm[Black], 
    GraphicsComplex[deAtomized[[1]], deAtomized[[2]]]
  }
]

enter image description here

deAtomized /. meshregion -> MeshRegion 

generates the atomized verion again.

Sjoerd C. de Vries
  • 65,815
  • 14
  • 188
  • 323
2

As pointed out in the comments, MeshRegion objects are "atomized", which is not particularly well described in the documentation, as mentioned in an earlier question and more importantly this other one. This can be brought to the fore via

AtomQ[region]
(*True*)

where AtomQ is a function which

yields True if expr is an expression which cannot be divided into subexpressions, and yields False otherwise.

or in more detail

  • You can use AtomQ in a recursive procedure to tell when you have reached the bottom of the tree corresponding to an expression.
  • AtomQ gives True for symbols, numbers, strings, and other raw objects, such as sparse arrays.
  • AtomQ gives True for any object whose subparts cannot be accessed using functions like Map.

This is briefly mentioned in the documentation for MeshRegion, which states

MeshRegion is always converted to an optimized representation and treated as raw by functions like AtomQ for purposes of pattern matching.

That still does not explain why MMA is unwilling to break up the expression when explicitly told to do so (via e.g. Applying a different head to it), though in that sense it does behave like some other 'atomic' expressions, such as

f @@ Rational[2, 3]
(*2/3*)

but not others, such as

f @@ SparseArray[{{1, 1} -> 1, {1, 2} -> 3}]

(which returns, for some reason, an object of the type f[SparseArray[...]], but with dimensions {2} instead of {1,2} as the initial array).

The bottom line, it seems, is that this is for performance reasons which are not particularly well documented.

Emilio Pisanty
  • 10,255
  • 1
  • 36
  • 69
  • "That still does not explain why MMA is unwilling to break up the expression when explicitly told to do so (via e.g. Applying a different head to it)" --- This is the typical behavior of atomic expressions in Mathematica as I noted in Scan vs. Map vs. Apply. SparseArray is specially overloaded to behave (more like) a normal List expression, including Apply. – Mr.Wizard Jun 09 '17 at 17:04
  • @Mr.Wizard Frankly, I don't think the documentation is quite at the level where it can fend off accusations of "doesn't explain". If I run Sjoerd's deAtomized on my region I get a LeafCount of over 600; if such an object is "atomic" then it would frankly need to be the very first thing in the Details section if it wanted to not be misleading. Similarly, region has a Head and the docs for Apply says that it will replace that Head - with no mention at all of atomic objects in the details section. Call it a documentation bug if you want, but this isn't up to scratch, I think. – Emilio Pisanty Jun 09 '17 at 17:22
  • I do not disagree. The documentation seems increasingly lacking in some fundamental areas such as atomic objects. – Mr.Wizard Jun 09 '17 at 17:43