Skip to main content

Cálculo y visualización de β-esqueletos para el modelado de redes de conexión entre sitios arqueológicos.

Project description

PyPI version PyPI - Python Version Linux macOS Intel macOS ARM

Ohtli

Librería para el cálculo y visualización de β-esqueletos para el modelado de redes de conexión entre sitios arqueológicos.

Instalación

pip install ohtli

O en modo desarrollo:

git clone https://github.com/tu-usuario/ohtli.git
cd ohtli
pip install -e ".[dev]"

Uso básico

import numpy as np
from ohtli import adjacency_matrix, build_graph, plot_skeleton
from ohtli import relative_asymmetry, control_value, plot_metric_bars
from ohtli.datasets import load_metro_cdmx

coords = np.array([[0,0],[1,0],[2,0],[1,1]], dtype=float)
names  = ["A", "B", "C", "D"]

# Matriz de adyacencia
adj = adjacency_matrix(coords, beta=1.0)

# Grafo de NetworkX
G = build_graph(coords, beta=2.0, labels=names)

# Visualización
ax, G = plot_skeleton(coords, beta=1.0, labels=names)

# Métricas
ra = relative_asymmetry(G)
cv = control_value(G)
plot_metric_bars(ra, metric_name="Asimetría relativa")

metro  = load_metro_cdmx()
coords = metro["coords"]
names  = metro["names"]
adj    = metro["adj"]
lines  = metro["lines"]

Estructura del paquete

ohtli/
├── geometry.py   # cálculo de lunas y vecindades
├── brute_force.py   # algoritmo fuerza bruta
├── delaunay.py # algoritmos deulaunay
├── datasets.py # datos del metro de la Ciudad de México
├── metrics.py    # asimetría relativa y valor de control
└── viz.py        # visualizaciones

El contraejemplo de Toussaint al algoritmo de Urquhart

Contexto

El algoritmo de Urquhart (1980) propone calcular el grafo de vecindad relativa (RNG, β = 2) en O(n log n) evaluando, para cada arista de la triangulación de Delaunay, únicamente los dos vértices que completan los triángulos que comparten esa arista.

Toussaint (1980) demostró que este algoritmo no es correcto para el RNG mediante el contraejemplo descrito a continuación.


El contraejemplo (Toussaint, Electronics Letters 1980)

Considera cinco puntos a, b, c, d, e con la siguiente disposición:

         d
        /
       / 
      a ─────────── b
                   /
                  c
    
    (e está dentro de la luna de (a,b),
     pero fuera del campo visual de Urquhart)

Condiciones geométricas precisas:

  • a y b tienen la misma coordenada y (están en la misma altura).
  • a y d tienen la misma coordenada x; b y c tienen la misma coordenada x.
  • d tiene coordenada y ligeramente mayor que c, lo que hace que a y c sean vecinos de Voronoi y, por tanto, vecinos de Delaunay.
  • e se encuentra dentro de la luna LUNE(a, b) con β = 2.
  • c y d se encuentran fuera de la luna LUNE(a, b).

La triangulación de Delaunay de estos cinco puntos incluye la arista (a, b). Los únicos vecinos de Delaunay comunes a a y b son c y d.

El algoritmo de Urquhart, al evaluar la arista (a, b), solo prueba c y d. Como ambos están fuera de la luna, concluye que (a, b) ∈ RNG. Pero e — que sí está dentro de la luna — nunca es evaluado, y por tanto la arista se incluye incorrectamente en el grafo resultante.

Resultado más fuerte

Toussaint demuestra que el problema no se resuelve ampliando el conjunto de vecinos probados: cualquier algoritmo que decida la pertenencia de una arista al RNG basándose únicamente en subconjuntos de los vecinos de Delaunay de sus dos vértices está condenado a fallar. Siempre es posible construir una configuración donde el punto invalidador no sea vecino de Delaunay de ninguno de los dos vértices.


¿Qué calcula entonces el algoritmo de Urquhart?

El propio Urquhart reconoció el error en su respuesta (Electronics Letters, 1980) y precisó que su algoritmo calcula un grafo intermedio U tal que:

MST ⊆ RNG ⊆ U ⊆ GG ⊆ DT

donde GG es el Grafo de Gabriel. El algoritmo de Urquhart es exacto para el Grafo de Gabriel (β = 1) pero solo aproximado para el RNG (β = 2) y cualquier otro β ≠ 1.


Verificación estadística con ohtli

El siguiente código muestra que urquhart_approximate produce aristas extra (falsas) en comparación con beta_skeleton_delaunay (exacto) para β ≠ 1:

import numpy as np
from ohtli.delaunay import beta_skeleton_delaunay, urquhart_approximate

np.random.seed(42)
extra_edges = 0
trials = 100

for _ in range(trials):
    coords = np.random.rand(20, 2) * 10
    G_exact  = beta_skeleton_delaunay(coords, beta=2.0)
    G_approx = urquhart_approximate(coords, beta=2.0, warn=False)
    extra_edges += len(set(G_approx.edges()) - set(G_exact.edges()))

print(f"Aristas falsas en {trials} conjuntos: {extra_edges} total")
print(f"Promedio por conjunto: {extra_edges / trials:.2f}")
# Resultado típico: ~0.25 aristas falsas por conjunto de 20 puntos

Para β = 1 (Grafo de Gabriel) el resultado es exacto:

for _ in range(trials):
    coords = np.random.rand(20, 2) * 10
    G_exact  = beta_skeleton_delaunay(coords, beta=1.0)
    G_approx = urquhart_approximate(coords, beta=1.0, warn=False)
    assert set(G_exact.edges()) == set(G_approx.edges()), "Diferencia inesperada"

print("Gabriel: exacto en todos los casos ✓")

Cuándo usar cada función

Función Complejidad β soportado Corrección
beta_skeleton_delaunay O(n²) β ≥ 1 Exacta
urquhart_approximate O(n log n) β ≥ 1 Exacta solo para β = 1 (GG)
build_graph (fuerza bruta) O(n³) cualquier β Exacta

Referencia

  • Kirkpatrick, D. G. y Radke, J. D. (1985). A Framework for Computational Morphology. Computational Geometry, pp. 217-248.
  • Urquhart, R. B. (1980). Algorithms for Computation of Relative Neighbourhood Graph. Electronics Letters, 16(14), 556–557.
  • Toussaint, G. T. (1980). Comment: Algorithms for Computing Relative Neighbourhood Graph. Electronics Letters, 16(22), 860.
  • Urquhart, R. B. (1980). Reply. Electronics Letters, 16(22), 860–861.
  • Toussaint, G. T. (1980). The Relative Neighbourhood Graph of a Finite Planar Set. Pattern Recognition, 12(4), 261–268.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ohtli-0.10.0.tar.gz (41.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ohtli-0.10.0-py3-none-any.whl (43.3 kB view details)

Uploaded Python 3

File details

Details for the file ohtli-0.10.0.tar.gz.

File metadata

  • Download URL: ohtli-0.10.0.tar.gz
  • Upload date:
  • Size: 41.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.9

File hashes

Hashes for ohtli-0.10.0.tar.gz
Algorithm Hash digest
SHA256 c1a0466764cdfb9799fbee6722ac7991d2d14b2607e5d4521b4c07d2109b39c3
MD5 6086c81c442d593b8b5eeaffd96bb8ec
BLAKE2b-256 0d1959f47d5cf759109cb334d646dbfe26617846c340306a44a4ffbfd2d6d141

See more details on using hashes here.

File details

Details for the file ohtli-0.10.0-py3-none-any.whl.

File metadata

  • Download URL: ohtli-0.10.0-py3-none-any.whl
  • Upload date:
  • Size: 43.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.9

File hashes

Hashes for ohtli-0.10.0-py3-none-any.whl
Algorithm Hash digest
SHA256 a23f1870a99c617227effb8e17671aa29a12b5de08735b65c972481414b12bcf
MD5 ba16bcd7fbad6419ce4cd37cd7dc96bb
BLAKE2b-256 0d833d557cc05a38bde80160217f49a9988ddf4dc1bec94cf4730500364039c6

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page