1 import Base
2 import ../shape/core.bend as Shape
3 import ../tensor/core.bend as Core
4 import ../tensor/elementwise_totality.bend as Totality
5 import ./reduce.bend as Reduce
6 import ./reduce_LAWS.bend as ReduceLaws
7 import ./sum_to_size.bend as SumToSize
8
9 def Identity(-input: Core.Dynamic, +target: Shape.Shape, pair: Core.Dynamic & Result<&2, &1, Reduce.Error, Core.Dynamic>) -> Type:
10 (retained, result) = pair
11 match result:
12 case Fail{error}:
13 Empty
14 case Done{output}:
15 {Totality.dimensions(output) == Shape.dimensions(target) : List<&2, U32>} &
16 (@+index: U32 ->
17 {Core.checked_observe_dynamic(output, index) == Core.checked_observe_dynamic(input, index) : Maybe<&2, F32>} &
18 {Core.observe_dynamic(output, index) == Core.observe_dynamic(input, index) : Maybe<&2, F32>})
19
20 type IdentityModel<-input: Core.Dynamic, -target: Shape.Shape,
21 -pair: Core.Dynamic & Result<&2, &1, Reduce.Error, Core.Dynamic>> is Data:
22 IdentityModel{-identity: Identity(input, target, pair)}
23
24 # The contribution owner comes back with the same dimensions and observations.
25 law sum_to_size_retains_input:
26 for input: Core.Dynamic
27 for +target: Shape.Shape
28 ReduceLaws.Retained
29
30 # A target with the contribution's dimensions always succeeds, and the output
31 # has the target's dimensions and the contribution's exact words at every
32 # position: -0.0 stays -0.0, as in PyTorch's autograd.
33 law sum_to_size_identity:
34 for input: Core.Dynamic
35 for +target: Shape.Shape
36 for same: {Totality.dimensions(input) == Shape.dimensions(target) : List<&2, U32>}
37 IdentityModel
38
39 # A target with other dimensions gives exactly the ordered reduce_to result,
40 # so every reduce_to law applies to it.
41 law sum_to_size_reduces:
42 for input: Core.Dynamic
43 for +target: Shape.Shape
44 for differ: {Totality.dimensions(input) != Shape.dimensions(target) : List<&2, U32>}
45 {SumToSize.sum_to_size(input, target) == Reduce.reduce_to(input, target) : Core.Dynamic & Result<&2, &1, Reduce.Error, Core.Dynamic>}