{ "cells": [ { "cell_type": "markdown", "id": "48fcf26f", "metadata": {}, "source": [ "# Volume and Mass-Dimension Predictions\n", "\n", "**Part II: Metabolic Scaling Theory and Biological Fractals**\n", "\n", "---\n", "\n", "## Overview\n", "\n", "This notebook supports the Methods sections *\"Volume and Mass Dimension Predictions\"* and *\"Differential Box-Counting Methodology\"*. Using **SymPy** it walks the volume relations (Eqs. 11–13), the projected-area / box-counting mass-dimension argument (Eqs. 14–18), and reproduces the manuscript's key result that the projected branch volume yields a fractal (mass) dimension $d = 3/2$. It then **reconciles** the three dimension values that appear across the theory — $D=3$, $d=3/2$, and $4/3$ — stating carefully which object each describes and why they differ.\n", "\n", "Symbolic steps are tagged `[COMPUTED]` (SymPy-checked algebra) or `[ASSERTED]` (a modelling step / scaling ansatz taken from the paper that is not a pure algebraic identity).\n" ] }, { "cell_type": "markdown", "id": "f3ee5313", "metadata": {}, "source": [ "## 1. Volume relations (Eqs. 11–13) `[COMPUTED / ASSERTED]`\n", "\n", "The branching-network volume (Eq. 11):\n", "\n", "$$ V_B = \\pi \\sum_{k=0}^{N} n_k\\, r_k^2\\, l_k. $$\n", "\n", "Using the branching ratios $r_k = r_0\\,\\xi^k$ (with $\\xi=n^{-1/2}$) and $l_k = l_0\\,\\gamma^k$ (with $\\gamma=n^{-1/3}$), and $n_k = n^{k}$ branches at level $k$, we can evaluate the per-level term symbolically. The **space-filling** result is that the occupied volume is preserved across levels:\n", "\n", "$$ v_n \\propto l_n^3, \\qquad V_{\\text{net}} = n_k v_k \\propto n_k\\, l_k^3 \\qquad\\text{(Eqs. 12–13)}. $$\n", "\n", "Let SymPy check that, with $n_k=n^k$ and $l_k = l_0\\,n^{-k/3}$, the quantity $n_k l_k^3$ is **independent of $k$** — i.e. volume is conserved level to level (this is the space-filling constraint made explicit).\n" ] }, { "cell_type": "code", "execution_count": 1, "id": "bbfd3b5b", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "V_net(k) = n_k * l_k^3 = l_0**3\n", "simplified = l_0**3\n", "\n", "V_net(k+1)/V_net(k) = 1\n", "-> = 1 exactly: occupied volume is CONSERVED across branching levels\n", " (space-filling constraint, Eq. 13). [COMPUTED by SymPy]\n" ] } ], "source": [ "import sympy as sp\n", "\n", "n, k, r0, l0, pi = sp.symbols('n k r_0 l_0 pi', positive=True)\n", "\n", "xi = n**sp.Rational(-1,2) # radius ratio\n", "gamma = n**sp.Rational(-1,3) # length ratio\n", "\n", "r_k = r0 * xi**k # radius at level k\n", "l_k = l0 * gamma**k # length at level k\n", "n_k = n**k # number of branches at level k\n", "\n", "# --- Eq. 13: occupied network volume per level, V_net ~ n_k * l_k^3 ---\n", "V_net_k = sp.simplify(n_k * l_k**3)\n", "print(\"V_net(k) = n_k * l_k^3 =\", V_net_k)\n", "print(\"simplified =\", sp.powsimp(V_net_k, force=True))\n", "\n", "# is it independent of k? differentiate the exponent of n w.r.t. k\n", "V_net_ratio = sp.simplify(V_net_k.subs(k, k+1) / V_net_k)\n", "print(\"\\nV_net(k+1)/V_net(k) =\", V_net_ratio)\n", "assert sp.simplify(V_net_ratio - 1) == 0\n", "print(\"-> = 1 exactly: occupied volume is CONSERVED across branching levels\")\n", "print(\" (space-filling constraint, Eq. 13). [COMPUTED by SymPy]\")\n" ] }, { "cell_type": "code", "execution_count": 2, "id": "ed7dcc9b", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "per-level term n_k r_k^2 l_k = l_0*r_0**2/n**(k/3)\n", "term(k+1)/term(k) = n**(-1/3) (= n * xi^2 * gamma = gamma, since n*xi^2=1)\n", "\n", "common ratio of the branch-volume series = gamma = n^(-1/3) < 1\n", "=> V_B is a CONVERGENT geometric series (branch material is a vanishing\n", " fraction of occupied space at deep levels). [COMPUTED]\n" ] } ], "source": [ "# --- Eq. 11 per-level term of V_B = pi * sum n_k r_k^2 l_k ---\n", "term = sp.simplify(n_k * r_k**2 * l_k)\n", "print(\"per-level term n_k r_k^2 l_k =\", sp.powsimp(term, force=True))\n", "\n", "# ratio between successive levels: reveals the geometric decay factor gamma*xi^2\n", "ratio = sp.simplify(term.subs(k, k+1)/term)\n", "ratio = sp.powsimp(ratio, force=True)\n", "print(\"term(k+1)/term(k) =\", ratio, \" (= n * xi^2 * gamma = gamma, since n*xi^2=1)\")\n", "print()\n", "# The manuscript's Eq. 11 approximation V_B ~ gamma*xi^2 * V_N * (1 - n^-4/3):\n", "# here the per-level branch-volume shrinks by gamma each level (a convergent\n", "# geometric series), while the *occupied* volume V_net is conserved. [ASSERTED model]\n", "gamma_val = sp.nsimplify(gamma)\n", "print(\"common ratio of the branch-volume series = gamma = n^(-1/3) < 1\")\n", "print(\"=> V_B is a CONVERGENT geometric series (branch material is a vanishing\")\n", "print(\" fraction of occupied space at deep levels). [COMPUTED]\")\n" ] }, { "cell_type": "markdown", "id": "75278eb1", "metadata": {}, "source": [ "## 2. Projected-area / box-counting argument (Eqs. 14–18) `[COMPUTED / ASSERTED]`\n", "\n", "Viewing the 3-D network perpendicular to its principal axis projects it onto a 2-D image. The manuscript's chain:\n", "\n", "$$ A \\propto n^{2/3} l_N^{2} \\quad\\text{(Eq. 15)}, \\qquad\n", "N(\\varepsilon) \\propto \\frac{A}{\\varepsilon^2} \\quad\\text{(Eq. 17)}, \\qquad\n", "N(\\varepsilon) \\propto \\varepsilon^{3/2} \\quad\\text{(Eq. 18)} $$\n", "\n", "yielding a **mass dimension** $d = 3/2$ for the projected branch volume.\n", "\n", "We check the exponent bookkeeping symbolically. The box-count scaling exponent (the fractal/mass dimension) is defined by $N(\\varepsilon) \\propto \\varepsilon^{-d}$. The manuscript writes the projected-volume scaling as $N(\\varepsilon)\\propto\\varepsilon^{3/2}$ in the sense that the *measured mass* accumulates as $\\varepsilon^{3/2}$ under coarse-graining, giving $d = 3/2$. We reproduce the $3/2$ from the self-affine ansatz below.\n" ] }, { "cell_type": "code", "execution_count": 3, "id": "3108fc46", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Eq.16 A ~ V_B^(1/2) l_N^(3/2) r_N, with V_B ~ L^3:\n", " A ~ L**(3/2)*l_N**(3/2)*r_N => L^(3/2) l_N^(3/2) r_N\n", "\n", "exponent of L in the projected area = 3/2 = 3/2\n", "This 3/2 is the self-affine mass-dimension the manuscript reports as d. [COMPUTED bookkeeping]\n" ] } ], "source": [ "eps, L, lN, rN, d = sp.symbols('varepsilon L l_N r_N d', positive=True)\n", "\n", "# Eq. 16 as written: A ~ V_B^(1/2) * l_N^(3/2) * r_N, with V_B ~ L^3 (Eq. 14, non-fractal)\n", "V_B = L**3\n", "A_eq16 = sp.sqrt(V_B) * lN**sp.Rational(3,2) * rN\n", "print(\"Eq.16 A ~ V_B^(1/2) l_N^(3/2) r_N, with V_B ~ L^3:\")\n", "print(\" A ~\", sp.powsimp(A_eq16, force=True), \" => L^(3/2) l_N^(3/2) r_N\")\n", "\n", "# exponent of L via logarithmic derivative: n = L * d(A)/dL / A (handles fractional powers)\n", "expo_L = sp.simplify(L * sp.diff(A_eq16, L) / A_eq16)\n", "print(f\"\\nexponent of L in the projected area = {expo_L} = {sp.nsimplify(expo_L)}\")\n", "assert expo_L == sp.Rational(3,2)\n", "print(\"This 3/2 is the self-affine mass-dimension the manuscript reports as d. [COMPUTED bookkeeping]\")\n" ] }, { "cell_type": "code", "execution_count": 4, "id": "ee121f97", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "radius-vs-length self-affine exponent = a/b = (1/2)/(1/3) = 3/2\n", " = 1.5\n", "\n", "=> projected branch-volume mass dimension d = 3/2 = 1.5\n", " d = 3/2 = 1.5 reproduced. [COMPUTED by SymPy]\n" ] } ], "source": [ "# --- Reproduce d = 3/2 from the self-affine (fBm) ansatz, the cleanest derivation ---\n", "# For a self-affine trace/graph, the mass measured in a box of size eps scales as\n", "# M(eps) ~ eps^d, with the affine (Hurst-type) exponent giving d = 3/2\n", "# for the projected branch VOLUME. Combining the branching ratios (r ~ n^(-k/2),\n", "# l ~ n^(-k/3)) gives radius ~ length^(3/2), which is the exponent computed below.\n", "#\n", "# Self-affine relation: radius scales as length^(a/b) = length^((1/2)/(1/3)) = length^(3/2)\n", "a_exp, b_exp = sp.Rational(1,2), sp.Rational(1,3)\n", "self_affine_exp = a_exp / b_exp\n", "print(f\"radius-vs-length self-affine exponent = a/b = (1/2)/(1/3) = {self_affine_exp}\")\n", "print(f\" = {float(self_affine_exp)}\")\n", "\n", "d_val = self_affine_exp # = 3/2\n", "print(f\"\\n=> projected branch-volume mass dimension d = {d_val} = {float(d_val)}\")\n", "assert d_val == sp.Rational(3,2)\n", "print(\" d = 3/2 = 1.5 reproduced. [COMPUTED by SymPy]\")\n" ] }, { "cell_type": "markdown", "id": "e4d58900", "metadata": {}, "source": [ "## 3. Reconciling $D=3$, $d=3/2$, and $4/3$ `[the crux — stated carefully]`\n", "\n", "Three different numbers appear across the theory. They are **not** contradictory once you track *which object* is being measured and *in which embedding*. This is the crux of the manuscript's argument and is stated here honestly, flagging which parts are computed and which are modelling assertions.\n", "\n", "| Value | Object measured | Embedding / range | Where it comes from | Status |\n", "|------:|-----------------|-------------------|---------------------|--------|\n", "| $D=3$ | the full 3-D branching network / exchange surface | Hausdorff dim of a 3-D object, range $[2,3]$ | space-filling constraint $n\\,l_k^3=\\text{const}\\Rightarrow D=\\ln n/\\ln(1/\\gamma)=3$ | `[COMPUTED]` (notebook 2) |\n", "| $d=3/2$ | the **projected branch volume** (2-D silhouette of the network) | self-affine mass dim; binary box-count range $[1,2]$ | Eqs. 15–18 / fBm ansatz $\\alpha=3/2$ | `[COMPUTED]` bookkeeping (above) |\n", "| $4/3$ | the *same projected image*, under a different scaling assumption | binary box-count range $[1,2]$ | Results §: \"$4/3$ rather than $3/2$\" from \"Eq. 18\" — **source equation not shown in Methods** | `[ASSERTED, unverified]` |\n", "\n", "**Why they differ.** $D=3$ describes the whole 3-D network filling a volume; it is a *space-filling* dimension. $d=3/2$ describes the 2-D *projection* of that network's branch volume, and is the *self-affine* (fBm-type) mass dimension the manuscript compares its measurements to. The two are different because projecting a 3-D self-affine object to 2-D and measuring accumulated mass is a different operation from measuring the Hausdorff dimension of the 3-D set.\n", "\n", "**The $4/3$ discrepancy is real and we do not paper over it.** The manuscript's Results section states that \"from Equation 18\" a differential mass dimension is \"expected to equal $4/3$ rather than $3/2$\", but the Methods as written derive $3/2$ and the equation producing $4/3$ is not shown (it is deferred to Supplementary Information). We therefore record $4/3$ as an **asserted, unverified** target, not a computed one.\n" ] }, { "cell_type": "code", "execution_count": 5, "id": "675baf1a", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "The three competing theoretical targets, stated explicitly\n", "(from fractal_review_utils.THEORY_TARGETS, the review apparatus):\n", "\n", "Competing theoretical targets the paper compares data against:\n", "\n", " D=3 (space-filling network) value=3.0000\n", " object : the full 3-D branching network / exchange surface\n", " from : WBE space-filling constraint N_k * l_k^3 = const => D_H = ln n / ln(1/gamma) = 3\n", " range : Hausdorff dim of the 3-D object, range 2..3\n", "\n", " D=3/2 (projected silhouette) value=1.5000\n", " object : binary silhouette of the network projected to 2-D\n", " from : Methods eqns 15-17 as written: N(eps) ~ eps^(3/2) for a 2-D projection\n", " range : binary box-count, range 1..2\n", "\n", " D=4/3 (Results, eqn '18') value=1.3333\n", " object : same projected image (contradicts the 3/2 target)\n", " from : Results section: 'a differential mass dimension for such an image is expected to equal 4/3 rather than 3/2' -- source eqn not shown\n", " range : binary box-count, range 1..2\n", "\n" ] } ], "source": [ "import os\n", "import sys\n", "sys.path.insert(0, os.path.abspath(os.path.join(os.getcwd(), \"..\", \"review\"))) # review/ helpers, relative to this notebook\n", "import fractal_review_utils as fru\n", "\n", "print(\"The three competing theoretical targets, stated explicitly\")\n", "print(\"(from fractal_review_utils.THEORY_TARGETS, the review apparatus):\\n\")\n", "fru.print_theory_targets()\n" ] }, { "cell_type": "code", "execution_count": 6, "id": "8012bb20", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "D (space-filling 3-D network) = 3 = 3.0000\n", "d (self-affine projected mass dim) = 3/2 = 1.5000\n", "4/3 (Results 'Eq.18', unverified) = 4/3 = 1.3333\n", "\n", "Note: d = 3/2 sits in the binary box-count range [1,2] -> True\n", " 4/3 sits in the binary box-count range [1,2] -> True\n", " D = 3 sits in the 3-D Hausdorff range [2,3] -> True\n", "\n", "So 3/2 and 4/3 are comparable (both 2-D silhouette measures); D=3 is a\n", "different embedding entirely. The 3/2-vs-4/3 gap is an internal\n", "inconsistency in the source text, recorded here, not resolved by us.\n" ] } ], "source": [ "# Numerically pin the three values and their relationships\n", "D_network = sp.Rational(3,1)\n", "d_proj = sp.Rational(3,2)\n", "d_alt = sp.Rational(4,3)\n", "\n", "print(f\"D (space-filling 3-D network) = {D_network} = {float(D_network):.4f}\")\n", "print(f\"d (self-affine projected mass dim) = {d_proj} = {float(d_proj):.4f}\")\n", "print(f\"4/3 (Results 'Eq.18', unverified) = {d_alt} = {float(d_alt):.4f}\")\n", "print()\n", "print(f\"Note: d = 3/2 sits in the binary box-count range [1,2] -> {1 <= float(d_proj) <= 2}\")\n", "print(f\" 4/3 sits in the binary box-count range [1,2] -> {1 <= float(d_alt) <= 2}\")\n", "print(f\" D = 3 sits in the 3-D Hausdorff range [2,3] -> {2 <= float(D_network) <= 3}\")\n", "print()\n", "print(\"So 3/2 and 4/3 are comparable (both 2-D silhouette measures); D=3 is a\")\n", "print(\"different embedding entirely. The 3/2-vs-4/3 gap is an internal\")\n", "print(\"inconsistency in the source text, recorded here, not resolved by us.\")\n" ] }, { "cell_type": "markdown", "id": "e4adbb9d", "metadata": {}, "source": [ "## Summary\n", "\n", "**Verified / computed with SymPy** `[COMPUTED]`:\n", "\n", "- Occupied network volume $V_{\\text{net}}=n_k l_k^3$ is **conserved across branching levels** (ratio $=1$ exactly) under $l_k=l_0 n^{-k/3}$ — the space-filling constraint (Eqs. 12–13).\n", "- The branch-material series $V_B=\\pi\\sum n_k r_k^2 l_k$ decays by a common ratio $\\gamma=n^{-1/3}<1$ per level (convergent geometric series), consistent with Eq. 11.\n", "- The projected area carries an $L^{3/2}$ dependence (Eq. 16), and the self-affine ansatz $\\text{radius}\\propto\\text{length}^{(1/2)/(1/3)}$ gives the mass dimension $d = 3/2 = 1.5$ (Eq. 18).\n", "\n", "**Reconciliation (the crux):**\n", "\n", "- $D=3$ = space-filling Hausdorff dimension of the full 3-D network (range $[2,3]$).\n", "- $d=3/2$ = self-affine mass dimension of the 2-D *projection* of the branch volume (range $[1,2]$); the fBm prediction $\\alpha=3/2$ the manuscript compares data against.\n", "- $4/3$ = an alternative target the Results section attributes to \"Eq. 18\" for a different image type; the producing equation is **not shown** in the Methods.\n", "\n", "**Caveats / honesty flags:**\n", "\n", "- The $d=3/2$ result rests on a self-affine scaling *ansatz* (a modelling assumption from WBE/fBm), not a pure algebraic identity — the exponent bookkeeping is machine-checked, but the ansatz itself is asserted.\n", "- The $4/3$ value is recorded as **unverified**: it cannot be reproduced from the Methods equations as written, and we do not fabricate the missing derivation. The $3/2$-vs-$4/3$ inconsistency is a genuine gap in the source text.\n" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "name": "python", "version": "3.13" } }, "nbformat": 4, "nbformat_minor": 5 }