3#include "AttributeComputerDomain.hpp"
4#include "AttributeComputerFamily.hpp"
5#include "../detail/AttributeKernelSupport.hpp"
6#include "../../trees/detail/CommittedTreeAccess.hpp"
7#include "../../trees/detail/TreeTraversalDetail.hpp"
8#include "../../trees/MorphologicalTree.hpp"
9#include "../../utils/Contract.hpp"
18namespace mmcfilters::attributes::computers {
23struct TreeTopologyRequest {
28 bool numChildren =
false;
29 bool numSiblings =
false;
30 bool numDescendants =
false;
31 bool numLeafDescendants =
false;
32 bool leafRatio =
false;
34 bool avgChildHeight =
false;
37 [[nodiscard]]
bool any() const noexcept {
38 return height || depth || isLeaf || isRoot || numChildren || numSiblings || numDescendants || numLeafDescendants || leafRatio || balance ||
43 [[nodiscard]]
bool needsHeight() const noexcept {
return height || balance || avgChildHeight; }
45 [[nodiscard]]
bool needsDescendantCounts() const noexcept {
return numDescendants || numLeafDescendants || leafRatio; }
52 [[nodiscard]]
static TreeTopologyRequest from(std::span<const Attribute> requestedAttributes) {
53 return {.height = requestsAttribute(requestedAttributes, SubtreeHeight),
54 .depth = requestsAttribute(requestedAttributes, DepthNode),
55 .isLeaf = requestsAttribute(requestedAttributes, IsLeafNode),
56 .isRoot = requestsAttribute(requestedAttributes, IsRootNode),
57 .numChildren = requestsAttribute(requestedAttributes, NumChildrenNode),
58 .numSiblings = requestsAttribute(requestedAttributes, NumSiblingsNode),
59 .numDescendants = requestsAttribute(requestedAttributes, NumDescendantsNode),
60 .numLeafDescendants = requestsAttribute(requestedAttributes, NumLeafDescendantsNode),
61 .leafRatio = requestsAttribute(requestedAttributes, LeafRatioNode),
62 .balance = requestsAttribute(requestedAttributes, BalanceNode),
63 .avgChildHeight = requestsAttribute(requestedAttributes, AvgChildHeightNode)};
74template <std::
floating_po
int Real>
75inline void computeTreeTopology(
const AttributeComputeContext<Real>& context,
const TreeTopologyRequest& request) {
80 const int stride = context.attrNames.NUM_ATTRIBUTES;
81 const auto offsetOf = [&](Attribute attribute) {
return context.attrNames.indexMap.find(attribute)->
second; };
82 const int heightOffset = request.height ? offsetOf(SubtreeHeight) : 0;
83 const int depthOffset = request.depth ? offsetOf(DepthNode) : 0;
84 const int isLeafOffset = request.isLeaf ? offsetOf(IsLeafNode) : 0;
85 const int isRootOffset = request.isRoot ? offsetOf(IsRootNode) : 0;
86 const int numChildrenOffset = request.numChildren ? offsetOf(NumChildrenNode) : 0;
87 const int numSiblingsOffset = request.numSiblings ? offsetOf(NumSiblingsNode) : 0;
88 const int numDescendantsOffset = request.numDescendants ? offsetOf(NumDescendantsNode) : 0;
89 const int numLeafDescendantsOffset = request.numLeafDescendants ? offsetOf(NumLeafDescendantsNode) : 0;
90 const int leafRatioOffset = request.leafRatio ? offsetOf(LeafRatioNode) : 0;
91 const int balanceOffset = request.balance ? offsetOf(BalanceNode) : 0;
92 const int avgChildHeightOffset = request.avgChildHeight ? offsetOf(AvgChildHeightNode) : 0;
93 const auto outputIndex = [&](
NodeId node,
int offset) {
return static_cast<std::size_t
>(node * stride + offset); };
95 const int numNodeSlots = context.tree.numInternalNodeSlots();
96 std::vector<Real> heightStorage(request.needsHeight() && !request.height ?
static_cast<std::size_t
>(numNodeSlots) : 0, Real{0});
97 std::vector<Real> numDescendantStorage(request.needsDescendantCounts() && !request.numDescendants ?
static_cast<std::size_t
>(numNodeSlots) : 0,
99 std::vector<Real> numLeafDescendantStorage(
100 request.needsDescendantCounts() && !request.numLeafDescendants ?
static_cast<std::size_t
>(numNodeSlots) : 0, Real{0});
101 std::vector<Real> minimumChildHeight(request.balance ?
static_cast<std::size_t
>(numNodeSlots) : 0, Real{0});
103 const auto heightAt = [&](
NodeId node) -> Real& {
104 return request.height ? context.buffer[outputIndex(node, heightOffset)] : heightStorage[
static_cast<std::size_t
>(node)];
106 const auto numDescendantsAt = [&](
NodeId node) -> Real& {
107 return request.numDescendants ? context.buffer[outputIndex(node, numDescendantsOffset)]
108 : numDescendantStorage[
static_cast<std::size_t
>(node)];
110 const auto numLeafDescendantsAt = [&](
NodeId node) -> Real& {
111 return request.numLeafDescendants ? context.buffer[outputIndex(node, numLeafDescendantsOffset)]
112 : numLeafDescendantStorage[
static_cast<std::size_t
>(node)];
115 const NodeId root = context.tree.root();
116 ::mmcfilters::detail::kernel::traversePostOrder(
119 const bool isRoot = node == root;
120 const NodeId parent = isRoot ? InvalidNode : ::mmcfilters::detail::CommittedTreeAccess::nodeParent(context.tree, node);
121 const int numChildren = ::mmcfilters::detail::CommittedTreeAccess::numChildren(context.tree, node);
122 const bool isLeaf = numChildren == 0;
125 context.buffer[outputIndex(node, depthOffset)] =
126 isRoot ? Real{0} : context.buffer[outputIndex(parent, depthOffset)] + Real{1};
128 if (request.needsHeight()) {
129 heightAt(node) = Real{0};
131 if (request.needsDescendantCounts()) {
132 numDescendantsAt(node) = Real{0};
133 numLeafDescendantsAt(node) = isLeaf ? Real{1} : Real{0};
136 context.buffer[outputIndex(node, isLeafOffset)] = isLeaf ? Real{1} : Real{0};
138 context.buffer[outputIndex(node, isRootOffset)] = isRoot ? Real{1} : Real{0};
139 if (request.numChildren)
140 context.buffer[outputIndex(node, numChildrenOffset)] =
static_cast<Real
>(numChildren);
141 if (request.numSiblings)
142 context.buffer[outputIndex(node, numSiblingsOffset)] =
143 isRoot ? Real{0} :
static_cast<Real
>(::mmcfilters::detail::CommittedTreeAccess::numChildren(context.tree, parent) - 1);
144 if (request.leafRatio)
145 context.buffer[outputIndex(node, leafRatioOffset)] = Real{0};
146 if (request.balance) {
147 minimumChildHeight[
static_cast<std::size_t
>(node)] = std::numeric_limits<Real>::infinity();
148 context.buffer[outputIndex(node, balanceOffset)] = Real{0};
150 if (request.avgChildHeight)
151 context.buffer[outputIndex(node, avgChildHeightOffset)] = Real{0};
154 if (request.needsDescendantCounts()) {
155 numDescendantsAt(parent) += numDescendantsAt(child) + Real{1};
156 numLeafDescendantsAt(parent) += numLeafDescendantsAt(child);
158 if (request.needsHeight()) {
159 const Real childHeight = heightAt(child);
160 heightAt(parent) = std::max(heightAt(parent), childHeight + Real{1});
161 if (request.balance) {
162 Real& minimumHeight = minimumChildHeight[
static_cast<std::size_t
>(parent)];
163 minimumHeight = std::min(minimumHeight, childHeight);
165 if (request.avgChildHeight) {
166 context.buffer[outputIndex(parent, avgChildHeightOffset)] += childHeight;
171 const int numChildren = ::mmcfilters::detail::CommittedTreeAccess::numChildren(context.tree, node);
172 if (request.leafRatio) {
173 const Real descendantCount = numDescendantsAt(node);
174 context.buffer[outputIndex(node, leafRatioOffset)] =
175 descendantCount > Real{0}
176 ? ::mmcfilters::attributes::numeric::safeDivide(numLeafDescendantsAt(node), descendantCount + Real{1})
179 if (numChildren != 0) {
180 if (request.balance) {
181 context.buffer[outputIndex(node, balanceOffset)] =
182 heightAt(node) - Real{1} - minimumChildHeight[
static_cast<std::size_t
>(node)];
184 if (request.avgChildHeight) {
185 context.buffer[outputIndex(node, avgChildHeightOffset)] = ::mmcfilters::attributes::numeric::safeDivide(
186 context.buffer[outputIndex(node, avgChildHeightOffset)],
static_cast<Real
>(numChildren));
194template <std::
floating_po
int Real>
195inline void validateTreeTopologyContext(
const AttributeComputeContext<Real>& context) {
196 requireAttributeBufferShape(context.tree, context.buffer, context.attrNames);
197 requireRequestedAttributeColumns(context);
230 static constexpr std::string_view familyName =
"tree-topology";
233 static constexpr AttributeComputerFamily family = AttributeComputerFamily::TreeTopology;
236 static constexpr AttributeComputerDomain domain = AttributeComputerDomain::Topology;
241 inline static constexpr std::array<Attribute, 11> producedAttributes{SubtreeHeight, DepthNode, IsLeafNode, IsRootNode,
242 NumChildrenNode, NumSiblingsNode, NumDescendantsNode, NumLeafDescendantsNode,
243 LeafRatioNode, BalanceNode, AvgChildHeightNode};
258 const detail::TreeTopologyRequest
request = detail::TreeTopologyRequest::from(
context.requestedAttributes);
int NodeId
Node identifier type used throughout the project.
#define MMCFILTERS_CONTRACT_CHECKED_ONLY(...)
Executes validation statements only when defensive checks are enabled.
Layout object that maps scalar attributes to flat-buffer offsets.
Mutable connected-subset tree on a finite pixel domain.
Computes structural descriptors that depend only on the tree topology.
static void computeUnitRows(const UnitAttributeComputeContext< Real > &context)
Materializes topology descriptors for one-pixel unit supports.
static void compute(const AttributeComputeContext< Real > &context)
Computes the requested topology descriptors.
Owning result for one computed scalar attribute layout and buffer.
std::vector< Real > second
Flat per-node attribute buffer indexed through first.