Source code for policyengine_core.taxscales.single_amount_tax_scale

from __future__ import annotations

import typing

import numpy

from policyengine_core.taxscales import AmountTaxScaleLike

if typing.TYPE_CHECKING:
    NumericalArray = typing.Union[numpy.int_, numpy.float_]


[docs]class SingleAmountTaxScale(AmountTaxScaleLike):
[docs] def calc( self, tax_base: NumericalArray, right: bool = False, ) -> numpy.float_: """ Matches the input amount to a set of brackets and returns the single cell value that fits within that bracket. The returned array preserves the dtype of the bracket amounts. In particular, a scale whose amounts are booleans (``amount_unit: bool`` with ``amount: true``/``false``) returns a boolean array rather than the int64 ``0``/``1`` array that naive sentinel padding would produce. This keeps logical NOT (``~``) working on the result: ``~`` on an int array is a bitwise negation (``~1 == -2`` and ``~0 == -1``, both truthy), whereas ``~`` on a bool array is the intended element-wise logical negation. """ guarded_thresholds = numpy.array([-numpy.inf] + self.thresholds + [numpy.inf]) bracket_indices = numpy.digitize( tax_base, guarded_thresholds, right=right, ) # Build the amounts array from the bracket amounts first, so numpy # infers their natural dtype (bool for boolean brackets, int/float # otherwise), then pad the two out-of-range sentinel cells with a zero # of that same dtype (``False`` for bool, ``0`` for numeric). Padding # with a Python int ``0`` up front, as the previous implementation did, # coerced the whole array to int64 and silently dropped a boolean dtype. # With no amounts, fall back to the historical int64 zero array. if len(self.amounts) == 0: guarded_amounts = numpy.array([0, 0]) else: amounts = numpy.asarray(self.amounts) sentinel = numpy.zeros(1, dtype=amounts.dtype) guarded_amounts = numpy.concatenate([sentinel, amounts, sentinel]) return guarded_amounts[bracket_indices - 1]