@@ -16,6 +16,9 @@ bool va_arg_is_present(va_list args) {
1616
1717Tensor GradFn_mean (Tensor self , int i );
1818Tensor GradFn_sum (Tensor self , int i );
19+ Tensor GradFn_max_all (Tensor self , int i );
20+ Tensor GradFn_min_all (Tensor self , int i );
21+ Tensor GradFn_reduce_dim (Tensor self , int i );
1922
2023Tensor Tensor_mean_all (Tensor self ) {
2124 float total = 0.0f ;
@@ -67,6 +70,155 @@ Tensor Tensor_sum_dim(Tensor self, int dim) {
6770 return res ;
6871}
6972
73+ Tensor Tensor_max_all (Tensor self ) {
74+ bool requires_grad = !cten_is_eval () && (self .node != NULL );
75+ Tensor res = Tensor_new ((TensorShape ){1 , 0 , 0 , 0 }, requires_grad );
76+
77+ if (self .data -> numel == 0 ) cten_assert (false, "max on empty tensor" );
78+ float max_val = self .data -> flex [0 ];
79+ for (int i = 1 ; i < self .data -> numel ; i ++ ) {
80+ if (self .data -> flex [i ] > max_val ) {
81+ max_val = self .data -> flex [i ];
82+ }
83+ }
84+ res .data -> flex [0 ] = max_val ;
85+
86+ if (requires_grad ) {
87+ res .node -> grad_fn = GradFn_max_all ;
88+ res .node -> inputs [0 ] = self ;
89+ res .node -> n_inputs = 1 ;
90+ res .node -> name = "MaxAll" ;
91+ }
92+ return res ;
93+ }
94+
95+ TensorMaxMinResult Tensor_max_dim (Tensor self , int dim ) {
96+ int ndim = TensorShape_dim (self .shape );
97+ dim = TensorShape_asdim (self .shape , dim );
98+
99+ TensorShape out_shape = {0 };
100+ int out_shape_len = 0 ;
101+ for (int i = 0 ; i < ndim ; i ++ ) {
102+ if (i != dim ) out_shape [out_shape_len ++ ] = self .shape [i ];
103+ }
104+
105+ bool requires_grad = !cten_is_eval () && (self .node != NULL );
106+ Tensor values = Tensor_new (out_shape , requires_grad );
107+ Tensor indices = Tensor_new (out_shape , false);
108+
109+ int dim_size = self .shape [dim ];
110+ for (int i = 0 ; i < values .data -> numel ; ++ i ) {
111+ float best_val = - INFINITY ;
112+ int best_idx = -1 ;
113+
114+ for (int j = 0 ; j < dim_size ; ++ j ) {
115+ int in_linear_idx = 0 , stride = 1 , out_i_rem = i , out_idx_tracker = out_shape_len - 1 ;
116+ for (int k = ndim - 1 ; k >= 0 ; -- k ) {
117+ int current_dim_idx ;
118+ if (k == dim ) {
119+ current_dim_idx = j ;
120+ } else {
121+ int dim_k = out_shape [out_idx_tracker -- ];
122+ current_dim_idx = out_i_rem % dim_k ;
123+ out_i_rem /= dim_k ;
124+ }
125+ in_linear_idx += current_dim_idx * stride ;
126+ stride *= self .shape [k ];
127+ }
128+ float current_val = self .data -> flex [in_linear_idx ];
129+ if (current_val > best_val ) { best_val = current_val ; best_idx = j ; }
130+ }
131+ values .data -> flex [i ] = best_val ;
132+ indices .data -> flex [i ] = (float )best_idx ;
133+ }
134+
135+ if (requires_grad ) {
136+ values .node -> grad_fn = GradFn_reduce_dim ;
137+ values .node -> inputs [0 ] = self ;
138+ values .node -> inputs [1 ] = indices ;
139+ values .node -> n_inputs = 2 ;
140+ values .node -> name = "MaxDim" ;
141+ }
142+
143+ TensorMaxMinResult result = {values , indices };
144+ return result ;
145+ }
146+
147+ Tensor Tensor_min_all (Tensor self ) {
148+ bool requires_grad = !cten_is_eval () && (self .node != NULL );
149+ Tensor res = Tensor_new ((TensorShape ){1 , 0 , 0 , 0 }, requires_grad );
150+
151+ if (self .data -> numel == 0 ) cten_assert (false, "min on empty tensor" );
152+ float min_val = self .data -> flex [0 ];
153+ for (int i = 1 ; i < self .data -> numel ; i ++ ) {
154+ if (self .data -> flex [i ] < min_val ) {
155+ min_val = self .data -> flex [i ];
156+ }
157+ }
158+ res .data -> flex [0 ] = min_val ;
159+
160+ if (requires_grad ) {
161+ res .node -> grad_fn = GradFn_min_all ;
162+ res .node -> inputs [0 ] = self ;
163+ res .node -> n_inputs = 1 ;
164+ res .node -> name = "MinAll" ;
165+ }
166+ return res ;
167+ }
168+
169+ TensorMaxMinResult Tensor_min_dim (Tensor self , int dim ) {
170+ int ndim = TensorShape_dim (self .shape );
171+ dim = TensorShape_asdim (self .shape , dim );
172+
173+ TensorShape out_shape = {0 };
174+ int out_shape_len = 0 ;
175+ for (int i = 0 ; i < ndim ; i ++ ) {
176+ if (i != dim ) out_shape [out_shape_len ++ ] = self .shape [i ];
177+ }
178+
179+ bool requires_grad = !cten_is_eval () && (self .node != NULL );
180+ Tensor values = Tensor_new (out_shape , requires_grad );
181+ Tensor indices = Tensor_new (out_shape , false);
182+
183+ int dim_size = self .shape [dim ];
184+ for (int i = 0 ; i < values .data -> numel ; ++ i ) {
185+ float best_val = INFINITY ;
186+ int best_idx = -1 ;
187+
188+ for (int j = 0 ; j < dim_size ; ++ j ) {
189+ int in_linear_idx = 0 , stride = 1 , out_i_rem = i , out_idx_tracker = out_shape_len - 1 ;
190+ for (int k = ndim - 1 ; k >= 0 ; -- k ) {
191+ int current_dim_idx ;
192+ if (k == dim ) {
193+ current_dim_idx = j ;
194+ } else {
195+ int dim_k = out_shape [out_idx_tracker -- ];
196+ current_dim_idx = out_i_rem % dim_k ;
197+ out_i_rem /= dim_k ;
198+ }
199+ in_linear_idx += current_dim_idx * stride ;
200+ stride *= self .shape [k ];
201+ }
202+ float current_val = self .data -> flex [in_linear_idx ];
203+ if (current_val < best_val ) { best_val = current_val ; best_idx = j ; }
204+ }
205+ values .data -> flex [i ] = best_val ;
206+ indices .data -> flex [i ] = (float )best_idx ;
207+ }
208+
209+ if (requires_grad ) {
210+ values .node -> grad_fn = GradFn_reduce_dim ;
211+ values .node -> inputs [0 ] = self ;
212+ values .node -> inputs [1 ] = indices ;
213+ values .node -> n_inputs = 2 ;
214+ values .node -> name = "MinDim" ;
215+ }
216+
217+ TensorMaxMinResult result = {values , indices };
218+ return result ;
219+ }
220+
221+
70222void cten_assert (bool cond , const char * fmt , ...) {
71223 if (!cond ) {
72224 va_list args ;
@@ -91,7 +243,6 @@ void cten_assert_dim(const char* title, int a, int b) {
91243 cten_assert (a == b , "%s: %d != %d" , title , a , b );
92244}
93245
94-
95246bool cten_elemwise_broadcast (Tensor * a , Tensor * b ) {
96247 Tensor orig_a = * a ;
97248 Tensor orig_b = * b ;
@@ -366,4 +517,4 @@ Tensor Tensor_unsqueeze(Tensor self, int dim) {
366517 memcpy (res .shape , new_shape , sizeof (TensorShape ));
367518
368519 return res ;
369- }
520+ }
0 commit comments