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.9.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.9.0-py3-none-any.whl (43.3 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: ohtli-0.9.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.9.0.tar.gz
Algorithm Hash digest
SHA256 7b2f073ec8e6d15eba0243c9f578c14dccfb6113af5294a84a1eb9cf0fac8195
MD5 4d7f0a819309740c684a3edd5446db94
BLAKE2b-256 20abab60aa97d4f247f4844314bd0a1065f5b11df2b02b99603c46004e26c7b6

See more details on using hashes here.

File details

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

File metadata

  • Download URL: ohtli-0.9.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.9.0-py3-none-any.whl
Algorithm Hash digest
SHA256 6124660761f2c611d5fb2b7d9892a2e5f8bb764b713010fd5654e6333e9588ab
MD5 05abf8d43e5792fe706f01b960d6f15a
BLAKE2b-256 9074ca36c53099318cb38266c2d70b5b3b7b121b78820577b517236fcec9a836

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