Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feature request]fold2 #39

Closed
mars0i opened this issue May 23, 2017 · 2 comments
Closed

[feature request]fold2 #39

mars0i opened this issue May 23, 2017 · 2 comments

Comments

@mars0i
Copy link
Contributor

mars0i commented May 23, 2017

I wrote a compare function in terms of fold2, which seems generally useful. It would probably belong in owl_dense_matrix_generic.ml.

let fold2 f init m1 m2 =
  let rows, cols as dims = shape m1 in
  if dims <> shape m2 then failwith "fold2: matrices have different shapes"
  ;
  let last_col = cols - 1 in
  let apply_f acc i j = 
    f acc (get m1 i j) (get m2 i j)
  in
  let rec loop acc i j =
    if i < rows
    then loop (apply_f acc i j) (i + 1) j
    else if j < last_col         (* don't start on next col if at final col *)
         then loop acc 0 (j + 1) (* start over on next col *)
         else acc
  in loop init 0 0

It's not pretty, and the style probably shows my inexperience, but it's tail recursive. I didn't succeed in coming up with a more elegant purely functional definition. However, I've noticed that existing fold functions in owl use imperative methods internally. I assume that's more efficient, so perhaps you wouldn't want to a version of fold2 similar to this one. I'm not sure what the tradeoffs are between different iter function variants (iteri is defined in owl_dense_matrix_generic.ml, but iter there is defined by the iter in owl_dense_ndarray_generic.ml, which I haven't sorted out fully).

@mars0i
Copy link
Contributor Author

mars0i commented May 23, 2017

Perhaps something like this would be useful, too. (I use it for a special-purpose compare function that I wrote.) It's like fold2, but will abort the loop if the accumulator comes to have a specified stop_val value. Other than the extra parameter, this differs from my fold2 by the addition of a single line of code in the loop function.

(** Fold f over matrices m1 and m2 starting with initial value init, 
    short-circuiting if stop_val is encountered:
    Folds f through all corresponding pairs of elements of matrices m1 
    and m2 by repeatedly applying f acc element_from_m1 element_from_m2,
    where acc is the result of previous applications.  init is the
    initial value for acc.  If f ever returns stop_val, this function
    returns immediately. *)
let short_circuit_fold2 stop_val f init m1 m2 =
  let rows, cols as dims = shape m1 in
  if dims <> shape m2 then failwith "matrices have different shapes"
  ;
  let last_col = cols - 1 in
  let apply_f acc i j = 
    f acc (get m1 i j) (get m2 i j)
  in
  let rec loop acc i j =
    if acc = stop_val then stop_val
    else if i < rows
    then loop (apply_f acc i j) (i + 1) j
    else if j < last_col         (* don't start on next col if at final col *)
         then loop acc 0 (j + 1) (* start over on next col *)
         else acc
  in loop init 0 0

@ryanrhymes
Copy link
Member

Cool, Marshall, many thanks for the code, I will borrow it :) 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants