feat: initial release
Assisted-by: GLM 5.3 Flash
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package core
|
||||
|
||||
// Discrete differences: the workhorse behind finite-difference
|
||||
// derivatives and signal detrending, along any single axis.
|
||||
|
||||
// Diff takes the successive differences along one axis, order times:
|
||||
// order 1 is out[i] = a[i+1] − a[i] along the axis, order 2 applies
|
||||
// it again, and so on. The axis shrinks by order; the axis must
|
||||
// therefore hold more elements than the order, and axis must name one
|
||||
// of the array's axes. Complex arrays are fine: differences carry no
|
||||
// ordering assumption. Int arrays keep their dtype, because the
|
||||
// difference of two int64 is the int64 difference; everything else
|
||||
// produces float.
|
||||
func Diff(a *Array, order, axis int) (*Array, error) {
|
||||
if order < 1 {
|
||||
return nil, errf("Diff: the order must be at least 1, got %d", order)
|
||||
}
|
||||
if axis < 0 || axis >= a.NDim() {
|
||||
return nil, errf("Diff: axis %d is outside the %d axes of shape %s", axis, a.NDim(), shapeText(a.Shape()))
|
||||
}
|
||||
if a.Shape()[axis] <= order {
|
||||
return nil, errf("Diff: axis %d holds %d elements, more than the order %d is needed",
|
||||
axis, a.Shape()[axis], order)
|
||||
}
|
||||
cur := a
|
||||
for range order {
|
||||
next, err := diffOnce(cur, axis)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cur = next
|
||||
}
|
||||
return cur, nil
|
||||
}
|
||||
|
||||
// diffOnce applies one round of differences along the axis. Int
|
||||
// differences stay int64 and complex ones stay complex; every other
|
||||
// dtype produces float, the only route that used to widen the int side
|
||||
// through float64 and round neighbours above 2^53 together.
|
||||
//
|
||||
// The output is walked run by run: for one position along the trailing
|
||||
// dimensions the two neighbours sit a fixed stride apart, so a run of
|
||||
// the output is a plain elementwise subtraction with the dtype
|
||||
// dispatched once, not a per-element coordinate fold.
|
||||
func diffOnce(a *Array, axis int) (*Array, error) {
|
||||
if !a.isContiguous() {
|
||||
// A strided view's payload window is not the run the walk needs,
|
||||
// so reduce it to a dense copy first; the elements, and with
|
||||
// them the differences, are the ones the accessors returned.
|
||||
a = a.materialise()
|
||||
}
|
||||
shape := a.Shape()
|
||||
outShape := append([]int{}, shape...)
|
||||
outShape[axis]--
|
||||
dt := Float
|
||||
switch a.dt {
|
||||
case Complex:
|
||||
dt = Complex
|
||||
case Int:
|
||||
dt = Int
|
||||
}
|
||||
out := &Array{shape: outShape, dt: dt}
|
||||
out.alloc(out.Len())
|
||||
tail := 1
|
||||
for d := axis + 1; d < len(shape); d++ {
|
||||
tail *= shape[d]
|
||||
}
|
||||
head := 1
|
||||
for d := range axis {
|
||||
head *= shape[d]
|
||||
}
|
||||
n := shape[axis]
|
||||
switch dt {
|
||||
case Int:
|
||||
diffRuns(out.ints, a.ints[:a.Len()], head, n, tail)
|
||||
case Complex:
|
||||
diffRuns(out.complexes, a.complexes[:a.Len()], head, n, tail)
|
||||
default:
|
||||
// The source widens exactly as FloatAt widens it; a float64
|
||||
// source is read in place.
|
||||
diffRuns(out.floats, floatPayload(a), head, n, tail)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// diffRuns fills dst with the successive differences of src along an
|
||||
// axis of n elements that steps by tail elements, for each of head
|
||||
// outer positions.
|
||||
func diffRuns[T int64 | float64 | complex128](dst, src []T, head, n, tail int) {
|
||||
for h := range head {
|
||||
base := h * n * tail
|
||||
dstBase := h * (n - 1) * tail
|
||||
for i := range tail {
|
||||
s, d := base+i, dstBase+i
|
||||
for k := range n - 1 {
|
||||
dst[d+k*tail] = src[s+(k+1)*tail] - src[s+k*tail]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user