Fork me on GitHub
Jim Strieter05:07:58

I installed Neanderthal yesterday so I can do GPU computations from Clojure. It would be really nice if I could use 16-bit floats since neural nets don't usually require 32-bit precision. Does anyone know if Neanderthal allows that? Hardware is Intel + Nvidia in case it matters.


Yes and no. Neanderthal allows that, but since Nvidia's cuBLAS (and any other) library only supports 32-bit and 64-bit matrix calculations, 16-bit matrix operations are usually not implemented. Additionally, most consumer GPUs don't have general half precision support (AFAIK). Since you are interested in neural networks, maybe the right library for that feature is Deep Diamond. It generally supports half precision, when and where the underlying backend support it. Both Intel and Nvidia DO support 16 bit precision, but only for some operations where that makes sense, and usually only for inference. YMMV, but if you're just starting, I'd recommend not to worry about half precision and use 32bit floats until you reach the point where you absolutely know what you want and why.

Jim Strieter04:07:33

There are 3 reasons, any of which is enough to justify the use of 16-bit floats: 1. Half precision floats require half the RAM, 2. Half as much time to transfer from CPU to GPU, 3. Doubling the precision requires 4x as many cores. Therefore halving the precision should increase throughput by 4x.

Ian Fernandez13:07:57

(#(get % :c (get % :b :not-found)) {:a 1 :b 2})

Ian Fernandez13:07:04

is there any better way to do this?

Ian Fernandez13:07:24

if some get does not work, do other get, else return something


If you don't expect any key to have false or nil value then try ((some-fn :c :b (constantly :nothfound)) {})

Ian Fernandez14:07:30

(let [{:keys [a b]} ...] (or a b))

Gareth Stephenson16:07:09

(def nested-map {:top {:middle {:bottom "hello"}}})
(get-in nested-map [:top :middle :bottom])
(-> nested-map :top :middle :bottom)
Why would I choose one over the other? Purely stylistic?


I would say that get-in is more descriptive as it indicates that you are targeting an associative structure. It also follows the pattern that you would use to manipulate the structure (ref:


If the structure is static, the thread macro performs slightly better. However, sometimes you construct a path to get data, in which case a vector may be more useful.


But I try to avoid nested data structures. Often the nesting is about grouping, in which case namespaced keywords are a better fit.


You may prefer -> when you want to apply a fn to nested element.

Drew Verlee17:07:36

get/get-in is slower then threading but you can pass it a value to return if the key doesn't exist. e.g

(get {} :a "hi")
;; => "hi"

(or (-> {} :a) "hi")
;; => "hi"
i think both are equally readable without the default, with it, slight edge to get-in


(-> {} (:a "hi")) also works

Drew Verlee17:07:32

uh, that's not immediately intuitive to me. here is how that expands:

(let [or__5533__auto__ (-> {} :a)]
  (if or__5533__auto__ or__5533__auto__ (or "hi")))

Drew Verlee17:07:20

err i guess keywords have a not-found argument as well. odd, i don't often use that.


Keywords work the same as get so they also take the default as a second argument.

Ben Lieberman19:07:17

Am I thinking about this incorrectly or is a binding of this form

(loop [[thing1 thing2 & more] thing] ...)
calling first thing second thing and then rest thing and then when it hits a recursion point doing so again


yes except for the last statement, those bindings are only for the initial trip through the loop


You can see for yourself by calling macroexpand on the loop form:


(macroexpand '(loop [[thing1 thing2 & more] thing] ...))

Ben Lieberman19:07:25

but if I call recur with more does it not then follow the same pattern and take the first two items out?


have you tried it?

(loop [[f s & remain] (range 5)]
  (println "first" f "second " s)
  (when (seq remain)
    (recur remain)))
first 0 second  1
first 2 second  3
first 4 second  nil

Ben Lieberman19:07:57

ah so I should use a when yea that makes sense bc I got a NullPtrException trying to do without

Ben Lieberman19:07:13

on a slightly different block of code but similar concept


my when is only about terminating the loop

Ben Lieberman19:07:08

is there a requirement to call seq on remain inside the body like that

Ben Lieberman19:07:17

I thought range was a seq already


i’m just checking when there are more items in the arg


enumerate=> (seq [])

👀 1

in words, it is “if there are any more remaining items, continue looping with those items”

Ben Lieberman20:07:06

is it more common/idiomatic to use seq like that than something like empty?


(source empty?)
(defn empty?
  "Returns true if coll has no items - same as (not (seq coll)).
  Please use the idiom (seq x) rather than (not (empty? x))"
  {:added "1.0"
   :static true}
  [coll] (not (seq coll)))


the seq abstraction is prevalent everywhere. Just embrace it

Ben Lieberman20:07:31

good to know. thanks @U11BV7MTK, enlightening all around

👍 1

pro tip: you don't need to call seq when you've destructed with [f & more]


the "rest" part of that (after the &) is done with a call to clojure.core/next , which you can think of as rest+seq


i always forget whether its destructuring with next or rest so i just play it safe with seq all the time.


but good to remember


when i’m confused I’m not sure where I’d go to confirm that that is the actual behavior

enumerate=> (doc destructure)
I could try it i suppose but I would be worried if that was implementation detail (although it likely would never change)


just macroexpand the loop

👍 1
Ben Lieberman19:07:50

and if not, how might I achieve something like that