Skip to content

balanced_allocation: float32 truncation breaks balancing for non-integer source IDs #1203

@brendancol

Description

@brendancol

Description

balanced_allocation silently skips its balancing loop when source IDs are non-integer values (e.g. 0.1, 0.3, 0.7).

_allocate_biased casts the allocation array to float32 at line 202. The iteration loop then compares this float32 array against float64 source IDs at line 359:

weights = np.array([
    float(np.sum(fric_weight[alloc == sid]))
    for sid in source_ids
])

NumPy upcasts the float32 alloc to float64 for the comparison, but the round-trip float64 -> float32 -> float64 changes non-integer values:

>>> np.float64(np.float32(0.1)) == np.float64(0.1)
False

alloc == sid produces all False, every weight comes out 0, total == 0, and the loop breaks on the first iteration without ever adjusting biases.

_allocate_from_costs has the same float32 cast at line 161.

Reproduction

import numpy as np
import xarray as xr
from xrspatial.balanced_allocation import balanced_allocation

h, w = 12, 12
data = np.zeros((h, w), dtype=np.float64)
data[2, 6] = 0.1
data[9, 2] = 0.3
data[9, 10] = 0.7

raster = xr.DataArray(data, dims=['y', 'x'], attrs={'res': (1.0, 1.0)})
raster['y'] = np.arange(h, dtype=np.float64)
raster['x'] = np.arange(w, dtype=np.float64)
friction = raster.copy(data=np.ones((h, w)))

result = balanced_allocation(raster, friction, tolerance=0.15)
out = result.values

# Source 0.1 gets 45% instead of ~33%; source 0.7 gets 23% instead of ~33%
for sid in [0.1, 0.3, 0.7]:
    n = np.sum(out == np.float32(sid))
    print(f'{sid}: {n / np.sum(np.isfinite(out)):.1%}')

Expected behavior

Territories should balance to within the requested tolerance regardless of whether source IDs are integers.

Fix

Keep the allocation array as float64 through the iteration loop. Cast to float32 only at the final return. Affects _allocate_biased (line 202) and _allocate_from_costs (line 161).

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingproximity toolsProximity, allocation, direction, cost distance

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions