├── deps.edn ├── .gitignore ├── .travis.yml ├── test └── tech │ └── compute │ └── cpu │ ├── tensor_test.clj │ └── driver_test.clj ├── src └── tech │ ├── compute │ ├── cpu │ │ ├── utils.clj │ │ └── driver.clj │ ├── registry.clj │ ├── verify │ │ ├── driver.clj │ │ ├── tensor.clj │ │ └── utils.clj │ ├── context.clj │ ├── driver.clj │ └── tensor.clj │ └── compute.clj ├── project.clj ├── README.md └── LICENSE /deps.edn: -------------------------------------------------------------------------------- 1 | {:paths ["src"] 2 | :deps {org.clojure/clojure {:mvn/version "1.10.1"} 3 | techascent/tech.datatype {:mvn/version "4.51"}}} 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /classes 3 | /checkouts 4 | pom.xml 5 | pom.xml.asc 6 | *.jar 7 | *.class 8 | /.lein-* 9 | /.nrepl-port 10 | .hgignore 11 | .hg/ 12 | .cpcache 13 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: clojure 2 | lein: 2.8.1 3 | before_install: 4 | - curl -O https://download.clojure.org/install/linux-install-1.10.0.403.sh && chmod +x linux-install-1.10.0.403.sh && sudo ./linux-install-1.10.0.403.sh 5 | addons: 6 | apt: 7 | packages: 8 | - libopenblas-dev 9 | - liblapack3 10 | -------------------------------------------------------------------------------- /test/tech/compute/cpu/tensor_test.clj: -------------------------------------------------------------------------------- 1 | (ns tech.compute.cpu.tensor-test 2 | (:require [tech.compute.cpu.driver :as cpu] 3 | [tech.compute.verify.tensor :as verify-tens] 4 | [clojure.test :refer :all])) 5 | 6 | 7 | (deftest clone 8 | (verify-tens/clone (cpu/driver) :float64)) 9 | 10 | 11 | (deftest assign! 12 | (verify-tens/assign! (cpu/driver) :float64)) 13 | -------------------------------------------------------------------------------- /src/tech/compute/cpu/utils.clj: -------------------------------------------------------------------------------- 1 | (ns tech.compute.cpu.utils) 2 | 3 | 4 | (set! *warn-on-reflection* true) 5 | (set! *unchecked-math* true) 6 | 7 | 8 | (defn in-range? 9 | [^long lhs-off ^long lhs-len ^long rhs-off ^long rhs-len] 10 | (or (and (>= rhs-off lhs-off) 11 | (< rhs-off (+ lhs-off lhs-len))) 12 | (and (>= lhs-off rhs-off) 13 | (< lhs-off (+ rhs-off rhs-len))))) 14 | -------------------------------------------------------------------------------- /test/tech/compute/cpu/driver_test.clj: -------------------------------------------------------------------------------- 1 | (ns tech.compute.cpu.driver-test 2 | (:require [tech.compute.cpu.driver :as cpu] 3 | [clojure.test :refer :all] 4 | [tech.compute.verify.utils :refer [def-all-dtype-test 5 | def-double-float-test] :as test-utils] 6 | [tech.compute.verify.driver :as verify-driver])) 7 | 8 | 9 | (use-fixtures :each test-utils/test-wrapper) 10 | 11 | 12 | (deftest simple-stream 13 | (verify-driver/simple-stream (cpu/driver) test-utils/*datatype*)) 14 | -------------------------------------------------------------------------------- /project.clj: -------------------------------------------------------------------------------- 1 | (defproject techascent/tech.compute "4.51-1-SNAPSHOT" 2 | :description "Library designed to provide a generic compute abstraction to allow some level of shared implementation between a cpu, cuda, openCL, webworkers, etc." 3 | :url "http://github.com/tech-ascent/tech.compute" 4 | :license {:name "Eclipse Public License" 5 | :url "http://www.eclipse.org/legal/epl-v10.html"} 6 | :plugins [[lein-tools-deps "0.4.1"]] 7 | :middleware [lein-tools-deps.plugin/resolve-dependencies-with-deps-edn] 8 | :lein-tools-deps/config {:config-files [:install :user :project] 9 | :clojure-executables ["/usr/local/bin/clojure" 10 | "/usr/bin/clojure"]} 11 | :java-source-paths ["java"] 12 | :profiles {:dev {:dependencies [[com.github.fommil.netlib/all "1.1.2" :extension "pom"]]} 13 | :clojure-10 {:dependencies [[org.clojure/clojure "1.10.0"]]}}) 14 | -------------------------------------------------------------------------------- /src/tech/compute/registry.clj: -------------------------------------------------------------------------------- 1 | (ns tech.compute.registry 2 | "Place to store global information about the drivers available to the compute 3 | subystem." 4 | (:require [tech.compute.driver :as drv])) 5 | 6 | 7 | (def ^:dynamic *registered-drivers* (atom {})) 8 | (def ^:dynamic *cpu-driver-name* (atom nil)) 9 | 10 | 11 | (defn- find-driver 12 | [driver-name] 13 | (get @*registered-drivers* driver-name)) 14 | 15 | 16 | (defn driver 17 | [driver-name] 18 | (if-let [retval (find-driver driver-name)] 19 | retval 20 | (throw (ex-info (format "Failed to find driver. Perhaps a require is missing?" ) 21 | {:driver-name driver-name})))) 22 | 23 | 24 | (defn register-driver 25 | [driver] 26 | (swap! *registered-drivers* assoc (drv/driver-name driver) driver) 27 | (drv/driver-name driver)) 28 | 29 | 30 | (defn driver-names 31 | [] 32 | (->> (keys @*registered-drivers*) 33 | set)) 34 | 35 | 36 | ;;The cpu driver has a special place in that it can attach to things that 37 | ;;aren't in the ecosystem. 38 | (defn set-cpu-driver-name! 39 | [driver-name] 40 | (reset! *cpu-driver-name* driver-name)) 41 | 42 | 43 | (defn cpu-driver-name 44 | [] 45 | @*cpu-driver-name*) 46 | 47 | 48 | (defmacro current-ns->keyword 49 | "Use this to name your driver." 50 | [] 51 | `(keyword (str *ns*))) 52 | -------------------------------------------------------------------------------- /src/tech/compute/verify/driver.clj: -------------------------------------------------------------------------------- 1 | (ns tech.compute.verify.driver 2 | (:require [clojure.test :refer :all] 3 | [tech.compute.driver :as drv] 4 | [tech.v2.datatype :as dtype] 5 | [tech.v2.datatype.functional :as dfn] 6 | [tech.compute.verify.utils :as verify-utils] 7 | [tech.compute :as compute] 8 | [tech.compute.context :as compute-ctx])) 9 | 10 | 11 | 12 | (defn simple-stream 13 | [driver datatype] 14 | (verify-utils/with-default-device-and-stream 15 | driver 16 | (let [{:keys [driver device stream]} (compute-ctx/options->context {}) 17 | buf-a (compute/allocate-host-buffer driver 10 datatype) 18 | output-buf-a (compute/allocate-host-buffer driver 10 datatype) 19 | buf-b (compute/allocate-device-buffer device 10 datatype) 20 | input-data (dtype/make-container :typed-buffer datatype (range 10)) 21 | output-data (dtype/make-container :typed-buffer datatype 10)] 22 | (dtype/copy! input-data 0 buf-a 0 10) 23 | (dtype/set-value! buf-a 0 100.0) 24 | (dtype/copy! buf-a 0 output-data 0 10) 25 | (compute/copy-device->device buf-a 0 buf-b 0 10) 26 | (compute/copy-device->device buf-b 0 output-buf-a 0 10) 27 | (compute/sync-with-host stream) 28 | (dtype/copy! output-buf-a 0 output-data 0 10) 29 | (is (dfn/equals [100.0 1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0] 30 | output-data))))) 31 | -------------------------------------------------------------------------------- /src/tech/compute/context.clj: -------------------------------------------------------------------------------- 1 | (ns tech.compute.context 2 | "Compute context allows someone to setup the default driver, device, and stream to 3 | use in function calls." 4 | (:require [tech.compute.registry :as registry] 5 | [tech.compute.driver :as drv])) 6 | 7 | 8 | (def ^:dynamic *context {}) 9 | 10 | 11 | (defn default-driver 12 | [] 13 | (or (:driver *context) 14 | (registry/driver @registry/*cpu-driver-name*))) 15 | 16 | 17 | (defn default-device 18 | [] 19 | (let [retval 20 | (or (:device *context) 21 | (first (drv/get-devices (default-driver))))] 22 | (when-not retval 23 | (throw (Exception. 24 | (format "%s: No devices found" 25 | (drv/driver-name (default-driver)))))) 26 | retval)) 27 | 28 | 29 | (defn default-stream 30 | [] 31 | (or (:stream *context) 32 | (drv/default-stream (default-device)))) 33 | 34 | 35 | (defmacro with-context 36 | [context & body] 37 | `(with-bindings {#'*context ~context} 38 | ~@body)) 39 | 40 | 41 | (defmacro with-merged-context 42 | [context & body] 43 | `(with-context 44 | (merge *context ~context) 45 | ~@body)) 46 | 47 | 48 | (defn default-context 49 | [] 50 | {:driver (default-driver) 51 | :device (default-device) 52 | :stream (default-stream)}) 53 | 54 | 55 | (defn options->context 56 | "Given an options map, return a augmented map that always includes 57 | device, driver, and stream." 58 | [opt-map] 59 | (merge (default-context) opt-map)) 60 | -------------------------------------------------------------------------------- /src/tech/compute/verify/tensor.clj: -------------------------------------------------------------------------------- 1 | (ns tech.compute.verify.tensor 2 | (:require [tech.compute.context :as compute-ctx] 3 | [tech.v2.tensor.impl :as dtt-impl] 4 | [tech.v2.datatype.functional :as dfn] 5 | [tech.v2.datatype :as dtype] 6 | [tech.v2.tensor :as dtt] 7 | [tech.compute.tensor :as ct] 8 | [tech.resource :as resource] 9 | [clojure.test :refer :all])) 10 | 11 | 12 | (defmacro verify-context 13 | [driver datatype & body] 14 | `(resource/stack-resource-context 15 | (compute-ctx/with-context 16 | {:driver ~driver} 17 | (dtt-impl/with-datatype 18 | ~datatype 19 | ~@body)))) 20 | 21 | 22 | (defn clone 23 | [driver datatype] 24 | (verify-context 25 | driver datatype 26 | (let [tensor (ct/->tensor (partition 3 (range 9))) 27 | dev-tens (ct/clone-to-device tensor) 28 | host-tens (ct/clone-to-host dev-tens)] 29 | (is (dfn/equals tensor host-tens)) 30 | (let [sub-tens (dtt/select tensor [0 1] [0 1]) 31 | dev-tens (ct/clone-to-device sub-tens) 32 | host-tens (ct/clone-to-host dev-tens)] 33 | (is (dfn/equals sub-tens host-tens)))))) 34 | 35 | 36 | (defn assign! 37 | [driver datatype] 38 | (verify-context 39 | driver datatype 40 | (let [tensor (ct/->tensor (partition 3 (range 9))) 41 | dev-tens (ct/new-tensor [3 3]) 42 | _ (ct/assign! dev-tens tensor) 43 | host-tens (ct/clone-to-host dev-tens)] 44 | (is (dfn/equals tensor host-tens))))) 45 | -------------------------------------------------------------------------------- /src/tech/compute/verify/utils.clj: -------------------------------------------------------------------------------- 1 | (ns tech.compute.verify.utils 2 | (:require [tech.resource :as resource] 3 | [clojure.test :refer :all] 4 | [tech.v2.datatype.casting :as casting] 5 | [tech.compute :as compute] 6 | [tech.compute.context :as compute-ctx]) 7 | (:import [java.math BigDecimal MathContext])) 8 | 9 | 10 | (defn test-wrapper 11 | [test-fn] 12 | (resource/stack-resource-context 13 | ;;Turn on if you want much slower tests. 14 | (test-fn))) 15 | 16 | 17 | (defmacro with-default-device-and-stream 18 | [driver & body] 19 | `(resource/stack-resource-context 20 | (compute-ctx/with-context 21 | {:driver ~driver}) 22 | ~@body)) 23 | 24 | 25 | (def ^:dynamic *datatype* :float64) 26 | 27 | 28 | (defmacro datatype-list-tests 29 | [datatype-list test-name & body] 30 | `(do 31 | ~@(for [datatype datatype-list] 32 | (do 33 | `(deftest ~(symbol (str test-name "-" (name datatype))) 34 | (with-bindings {#'*datatype* ~datatype} 35 | ~@body)))))) 36 | 37 | 38 | 39 | (defmacro def-double-float-test 40 | [test-name & body] 41 | `(datatype-list-tests [:float64 :float32] ~test-name ~@body)) 42 | 43 | 44 | (defmacro def-int-long-test 45 | [test-name & body] 46 | `(datatype-list-tests [:int32 :uint32 :int64 :uint64] 47 | ~test-name 48 | ~@body)) 49 | 50 | 51 | (defmacro def-all-dtype-test 52 | [test-name & body] 53 | `(datatype-list-tests ~casting/numeric-types ~test-name ~@body)) 54 | 55 | 56 | (defmacro def-all-dtype-exception-unsigned 57 | "Some platforms can detect unsigned errors." 58 | [test-name & body] 59 | `(do 60 | (datatype-list-tests ~casting/host-numeric-types ~test-name ~@body) 61 | (datatype-list-tests ~casting/unsigned-int-types ~test-name 62 | (is (thrown? Throwable 63 | ~@body))))) 64 | -------------------------------------------------------------------------------- /src/tech/compute/driver.clj: -------------------------------------------------------------------------------- 1 | (ns tech.compute.driver 2 | "Base set of protocols required to move information from the host to the device as well as 3 | enable some form of computation on a given device. There is a cpu implementation provided for 4 | reference. 5 | 6 | Base datatypes are defined: 7 | * Driver: Enables enumeration of devices and creation of host buffers. 8 | * Device: Creates streams and device buffers. 9 | * Stream: Stream of execution occuring on the device. 10 | * Event: A synchronization primitive emitted in a stream to notify other 11 | streams that might be blocking.") 12 | 13 | 14 | (defprotocol PDriverProvider 15 | "Get a driver from an object" 16 | (get-driver [impl])) 17 | 18 | 19 | (defprotocol PDeviceProvider 20 | "Get a device from an object." 21 | (get-device [impl])) 22 | 23 | 24 | (defprotocol PStreamProvider 25 | "Get a stream from an object" 26 | (get-stream [impl])) 27 | 28 | 29 | (defprotocol PDriver 30 | "A driver is a generic compute abstraction. Could be a group of threads, could be a 31 | machine on a network or it could be a CUDA or OpenCL driver. A stream is a stream of 32 | execution (analogous to a thread) where subsequent calls are serialized. All buffers 33 | implement a few of the datatype interfaces, at least get-datatype and ecount. Host 34 | buffers are expected to implement enough of the datatype interfaces to allow a copy 35 | operation from generic datatypes into them. This means at least PAccess." 36 | (driver-name [driver] 37 | "A system-unique name for this driver. The keyworded namespace it is implemented in 38 | is ideal.") 39 | (get-devices [driver] 40 | "Get a list of devices accessible to the system.") 41 | (allocate-host-buffer [driver elem-count elem-type options] 42 | "Allocate a host buffer. Transfer from host to device requires data first copied 43 | into a host buffer and then uploaded to a device buffer. 44 | options: 45 | :usage-type 46 | usage-type: #{:one-time :reusable} 47 | Hint to allow implementations to allocate different types of host buffers each 48 | optimized for the desired use case. Default is one-time.") 49 | (acceptable-host-buffer? [driver buffer] 50 | "Will this buffer work as a host buffer?")) 51 | 52 | (defonce host-buffer-usage-types #{:one-time :reusable}) 53 | 54 | 55 | (defprotocol PDevice 56 | (supports-create-stream? [device] 57 | "Does this device support create-stream?") 58 | (default-stream [device] 59 | "All devices must have a default stream whether they support create or not.") 60 | (create-stream [device] 61 | "Create a stream of execution. Streams are indepenent threads of execution. They 62 | can be synchronized with each other and the main thread using events.") 63 | (allocate-device-buffer [device elem-count elem-type options] 64 | "Allocate a device buffer. This is the generic unit of data storage used for 65 | computation. No options at this time.") 66 | (device->device-copy-compatible? [src-device dst-device] 67 | "When two devices differ, it may be possible to copy from src to dest device.") 68 | (acceptable-device-buffer? [device item] 69 | "Do whatever checks necessary to ensure that this item can be used as a device 70 | buffer for this device.")) 71 | 72 | 73 | (defprotocol PStream 74 | "Basic functionality expected of streams. Streams are an abstraction of a stream of 75 | execution and can be synchonized with the host or with each other using events. CPU's 76 | are considered devices." 77 | (copy-device->device [stream dev-a dev-a-off dev-b dev-b-off elem-count] 78 | "copy from one device to another. Used to initiate host->device, device->host, 79 | host->host and device->device copies with memcpy semantics.") 80 | (sync-with-host [stream] 81 | "Block host until stream's queue is finished executing") 82 | (sync-with-stream [src-stream dst-stream] 83 | "Create an event in src-stream's execution queue, then have dst stream wait on 84 | that event. This allows dst-stream to ensure src-stream has reached a certain point 85 | of execution before continuing. Both streams must be of the same driver.")) 86 | -------------------------------------------------------------------------------- /src/tech/compute.clj: -------------------------------------------------------------------------------- 1 | (ns tech.compute 2 | (:require [tech.compute.driver :as drv] 3 | [tech.compute.registry :as registry] 4 | [tech.compute.context :as compute-ctx] 5 | [tech.v2.datatype :as dtype] 6 | [clojure.test :refer :all] 7 | [tech.resource :as resource])) 8 | 9 | 10 | (defn driver-names 11 | "Get the names of the registered drivers." 12 | [] 13 | (registry/driver-names)) 14 | 15 | 16 | (defn driver 17 | "Do a registry lookup to find a driver by its name." 18 | [driver-name] 19 | (registry/driver driver-name)) 20 | 21 | 22 | (defn ->driver 23 | "Generically get a driver from a thing" 24 | [item] 25 | (drv/get-driver item)) 26 | 27 | 28 | (defn ->device 29 | "Generically get a device from a thing" 30 | [item] 31 | (drv/get-device item)) 32 | 33 | 34 | (defn ->stream 35 | "Generically get a stream from a thing" 36 | [item] 37 | (drv/get-stream item)) 38 | 39 | 40 | (defn driver-name 41 | [driver] 42 | (drv/driver-name driver)) 43 | 44 | 45 | (defn get-devices 46 | [driver] 47 | (drv/get-devices driver)) 48 | 49 | 50 | (defn default-device 51 | [driver] 52 | (first (get-devices driver))) 53 | 54 | 55 | (defn allocate-host-buffer 56 | "Allocate a host buffer. Usage type gives a hint as to the 57 | intended usage of the buffer." 58 | [driver elem-count elem-type & {:keys [usage-type] 59 | :or {usage-type :one-time} 60 | :as options}] 61 | (drv/allocate-host-buffer driver elem-count 62 | elem-type (assoc options 63 | :usage-type usage-type))) 64 | 65 | 66 | ;; Device API 67 | 68 | (defn supports-create-stream? 69 | "Does this device support create-stream?" 70 | [device] 71 | (drv/supports-create-stream? device)) 72 | 73 | (defn default-stream 74 | "All devices must have a default stream whether they support create or not." 75 | [device] 76 | (drv/default-stream device)) 77 | 78 | (defn create-stream 79 | "Create a stream of execution. Streams are indepenent threads of execution. They can 80 | be synchronized with each other and the main thread using events." 81 | [device] 82 | (drv/create-stream device)) 83 | 84 | 85 | (defn allocate-device-buffer 86 | "Allocate a device buffer. This is the generic unit of data storage used for 87 | computation. No options at this time." 88 | [device elem-count elem-type & {:as options}] 89 | (drv/allocate-device-buffer device elem-count elem-type options)) 90 | 91 | ;;Stream API 92 | 93 | (defn- check-legal-copy! 94 | [src-buffer src-offset dst-buffer dst-offset elem-count] 95 | (let [src-len (- (dtype/ecount src-buffer) (long src-offset)) 96 | dst-len (- (dtype/ecount dst-buffer) (long dst-offset)) 97 | elem-count (long elem-count)] 98 | (when (> elem-count src-len) 99 | (throw (ex-info "Copy out of range" 100 | {:src-len src-len 101 | :elem-count elem-count}))) 102 | (when (> elem-count dst-len) 103 | (throw (ex-info "Copy out of range" 104 | {:dst-len dst-len 105 | :elem-count elem-count}))))) 106 | 107 | 108 | (defn- provided-or-default-stream 109 | [stream device-buffer] 110 | (or stream 111 | (:stream compute-ctx/*context) 112 | (default-stream (->device device-buffer)))) 113 | 114 | 115 | (defn device->device-copy-compatible? 116 | [src-device dst-device] 117 | (drv/device->device-copy-compatible? src-device dst-device)) 118 | 119 | 120 | (defn copy-device->device 121 | "Copy from one device to another. If no stream is provided then the destination 122 | buffer's device's default stream is used." 123 | [dev-a dev-a-off dev-b dev-b-off elem-count & {:keys [stream] :as options}] 124 | (check-legal-copy! dev-a dev-a-off dev-b dev-b-off elem-count) 125 | (let [{:keys [stream]} (compute-ctx/options->context options)] 126 | (drv/copy-device->device stream dev-a dev-a-off dev-b dev-b-off elem-count))) 127 | 128 | 129 | (defn sync-with-host 130 | "Block host until stream's queue is finished executing" 131 | ([stream] 132 | (drv/sync-with-host stream)) 133 | ([] 134 | (sync-with-host (compute-ctx/default-stream)))) 135 | 136 | (defn sync-with-stream 137 | "Create an event in src-stream's execution queue, then have dst stream wait on that 138 | event. This allows dst-stream to ensure src-stream has reached a certain point of 139 | execution before continuing. Both streams must be of the same driver." 140 | [src-stream dst-stream & [options]] 141 | (drv/sync-with-stream src-stream dst-stream)) 142 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tech.compute 2 | 3 | 4 | [![Clojars Project](https://clojars.org/techascent/tech.compute/latest-version.svg)](https://clojars.org/techascent/tech.compute) 5 | 6 | 7 | Library designed to provide a generic compute abstraction to allow 8 | some level of shared implementation between a cpu, cuda, openCL, 9 | webworkers, etc. design. This system is not specific to neural networks 10 | nor it is specific to doing linear algebra but is rather the basis 11 | to implement any generic algorithm across a range of environments but 12 | targetting environments where there is a distinct transfer step between 13 | the main computing system (called the host) and some external compute 14 | system (called the device). The primitives of this layer are carefully 15 | chosen to be implementable across a wide range of different 'devices' such 16 | that a unified codebase and run unmodified in the various potential runtimes 17 | listed above and this *includes* operations like: 18 | 19 | * Offsetting device buffers to create new buffers. Thus allows efficient implementation 20 | of pooling algorithms or buffer coalescing where the user desires to create 1 large 21 | buffer and the create a series of smaller buffers piecemeal later. Sub-buffers are not 22 | distiguishable to the implementations from the original source buffers. 23 | * Initializing buffers to fixed values. 24 | * Transfer of data between the host and device. 25 | * Overlapping transfer with device compute using multiple 'streams' of execution. 26 | * Synchronizing stream execution with either the host system or with another on-device 27 | stream. 28 | 29 | 30 | There is a generalized underlying compute layer that works across 6 primitive datatypes 31 | in the JVM - bytes, shorts, ints, longs, floats and doubles - along with 4 datatypes 32 | (unsigned versions of the integer types above). This compute layer contains the 33 | protocols that define an interface to a compute driver, either cpu or cuda at this 34 | time. This interface contains ways to move data from the host architecture to the 35 | specific device architecture. 36 | 37 | 38 | Components of the compute abstraction: 39 | 40 | ### [driver.clj](src/tech/compute/driver.clj) 41 | 42 | (ns documentation) 43 | ```clojure 44 | "Base set of protocols required to move information from the host to the device as well as enable some form of computation on a given device. There is a cpu implementation provided for reference. 45 | 46 | Base datatypes are defined: 47 | * Driver: Enables enumeration of devices and creation of host buffers. 48 | * Device: Creates streams and device buffers. 49 | * Stream: Stream of execution occuring on the device. 50 | * Buffer: Lightly typed (java primitive types at this time) heavy on-device buffer. 51 | ``` 52 | 53 | 54 | This layer defines operations you would expect to find on any buffer in a C-based 55 | language such as: 56 | 57 | Buffers allow memcpy, offsetting, upload to device and download from device. 58 | Buffers can be offset to produce a new (shorter) buffer with a different base address. 59 | 60 | 61 | There are two concepts used in this file that are not defined: 62 | 63 | 1. [resource management](https://github.com/tech-ascent/tech.resource) 64 | 2. [datatype](https://github.com/techascent/tech.datatype) 65 | 66 | Resource management allows stack-based resource algorithms similar to the RAII concept 67 | in c++. This is important to ensure that using GPU resources is as pleasant and as 68 | forgiving as possible. 69 | 70 | 71 | The datatype library allows identification and efficient copying of data into packed 72 | sequential buffers, including marshalling copies where we want to say copy a buffer of 73 | bytes into a buffer of floats. This library is very carefully written to allow 74 | efficient upload/download characteristics so client code does not have unnecessary 75 | bottlenecks around moving data to or from a compute device while still allowing users 76 | complete flexibility with regards to their choice of data used in their systems external 77 | to the compute system. 78 | 79 | 80 | This combination of systems (driver, datatype, and resource) are designed to work 81 | together to enable a generic computing abstraction, not specific to linear algebra or 82 | neural networks. Should clojure become a language where people are expressing a wide 83 | range of algorithms on the GPU, CPU, and perhaps in the web then this would be an 84 | appropriate substrate to enable efficient data management for those algorithms. 85 | 86 | 87 | There is one default driver provided for a pure-cpu implementation 88 | 89 | 1. [cpu](src/tech/compute/cpu/driver.clj) 90 | 91 | 92 | ## License 93 | 94 | Copyright © 2018 TechAscent, LLC 95 | 96 | Distributed under the Eclipse Public License either version 1.0 or (at 97 | your option) any later version. 98 | -------------------------------------------------------------------------------- /src/tech/compute/cpu/driver.clj: -------------------------------------------------------------------------------- 1 | (ns tech.compute.cpu.driver 2 | (:require [tech.compute.driver :as drv] 3 | [tech.v2.datatype :as dtype] 4 | [tech.v2.datatype.protocols :as dtype-proto] 5 | [clojure.core.async :as async] 6 | [tech.resource :as resource] 7 | [tech.resource.stack :as stack] 8 | [tech.compute :as compute] 9 | [tech.compute.registry :as registry])) 10 | 11 | 12 | (set! *warn-on-reflection* true) 13 | (set! *unchecked-math* true) 14 | 15 | 16 | (defprotocol PToCPUStream 17 | ;;Setup so other things can masquerade as a cpu stream as long as they 18 | ;;have a conversion to a CPUStream record. 19 | (->cpu-stream [item])) 20 | 21 | 22 | (defrecord CPUDevice [driver-fn device-id error-atom default-stream]) 23 | (defrecord CPUStream [device-fn input-chan exit-chan error-atom] 24 | PToCPUStream 25 | (->cpu-stream [item] item)) 26 | (defrecord CPUDriver [devices error-atom]) 27 | 28 | 29 | (defonce driver-name (registry/current-ns->keyword)) 30 | 31 | 32 | (extend-protocol drv/PDriverProvider 33 | CPUDriver 34 | (get-driver [driver] driver) 35 | CPUDevice 36 | (get-driver [device] ((get device :driver-fn))) 37 | CPUStream 38 | (get-driver [stream] (compute/->driver ((:device-fn stream))))) 39 | 40 | 41 | (extend-protocol drv/PDeviceProvider 42 | CPUDriver 43 | (get-device [driver] (compute/default-device driver)) 44 | CPUDevice 45 | (get-device [device] device) 46 | CPUStream 47 | (get-device [stream] ((:device-fn stream)))) 48 | 49 | 50 | (extend-protocol drv/PStreamProvider 51 | CPUStream 52 | (get-stream [stream] stream)) 53 | 54 | 55 | (extend-type CPUStream 56 | stack/PResource 57 | (release-resource [impl] 58 | (when (.input-chan impl) 59 | (async/close! (.input-chan impl))))) 60 | 61 | 62 | (defn get-memory-info 63 | [] 64 | {:free (.freeMemory (Runtime/getRuntime)) 65 | :total (.totalMemory (Runtime/getRuntime))}) 66 | 67 | 68 | (defn cpu-stream 69 | ([device error-atom] 70 | (let [^CPUStream retval (->CPUStream (constantly device) (async/chan 16) 71 | (async/chan) error-atom)] 72 | (async/thread 73 | (loop [next-val (async/CPUStream (constantly (compute/default-device (driver))) nil nil nil)) 91 | 92 | 93 | (defn is-main-thread-cpu-stream? 94 | [stream] 95 | (let [^CPUStream stream (->cpu-stream stream)] 96 | (not (or (.input-chan stream) 97 | (.exit-chan stream) 98 | (.error-atom stream))))) 99 | 100 | 101 | (defn is-thread-cpu-stream? 102 | [^CPUStream stream] 103 | (not (is-main-thread-cpu-stream? stream))) 104 | 105 | 106 | (defn- check-stream-error-atom 107 | [item] 108 | (when-let [error-atom (:error-atom item)] 109 | (let [error @error-atom] 110 | (when error 111 | (compare-and-set! error-atom error nil) 112 | (throw error))))) 113 | 114 | 115 | (defn check-stream-error 116 | [item] 117 | (check-stream-error-atom (->cpu-stream item))) 118 | 119 | 120 | (defmacro with-stream-dispatch 121 | [stream & body] 122 | `(if (is-thread-cpu-stream? ~stream) 123 | (do 124 | (check-stream-error ~stream) 125 | (let [^CPUStream stream# (->cpu-stream ~stream)] 126 | (async/>!! (.input-chan stream#) 127 | (fn [] ~@body)))) 128 | (do 129 | ~@body))) 130 | 131 | 132 | (defrecord CPUEvent [input-chan]) 133 | 134 | 135 | (extend-type CPUStream 136 | drv/PStream 137 | (copy-host->device [stream host-buffer host-offset 138 | device-buffer device-offset elem-count] 139 | (with-stream-dispatch stream 140 | (dtype/copy! host-buffer host-offset device-buffer device-offset elem-count))) 141 | (copy-device->host [stream device-buffer device-offset host-buffer 142 | host-offset elem-count] 143 | (with-stream-dispatch stream 144 | (dtype/copy! device-buffer device-offset host-buffer host-offset elem-count))) 145 | (copy-device->device [stream dev-a dev-a-off dev-b dev-b-off elem-count] 146 | (with-stream-dispatch stream 147 | (dtype/copy! dev-a dev-a-off dev-b dev-b-off elem-count))) 148 | (sync-with-host [stream] 149 | ;;If main thread cpu stream then we are already syncced 150 | (when-not (is-main-thread-cpu-stream? stream) 151 | (let [^CPUEvent event (->CPUEvent (async/chan))] 152 | (with-stream-dispatch stream 153 | (async/close! (.input-chan event))) 154 | (async/CPUEvent (async/chan))] 157 | (with-stream-dispatch src-stream 158 | (async/close! (.input-chan event))) 159 | (with-stream-dispatch dst-stream 160 | (async/CPUDevice driver-fn dev-number error-atom (atom nil)) 166 | ;;Default cpu stream runs in the main thread of execution 167 | default-stream (->CPUStream (constantly retval) nil nil nil)] 168 | (reset! (:default-stream retval) default-stream) 169 | retval)) 170 | 171 | 172 | (extend-type CPUDevice 173 | drv/PDevice 174 | 175 | (memory-info [impl] 176 | (get-memory-info)) 177 | 178 | (supports-create-stream? [device] true) 179 | 180 | (default-stream [device] @(:default-stream device)) 181 | 182 | (create-stream [impl] 183 | (check-stream-error-atom impl) 184 | (cpu-stream impl (:error-atom impl))) 185 | 186 | (allocate-device-buffer [impl elem-count elem-type options] 187 | (check-stream-error-atom impl) 188 | (dtype/make-container :native-buffer elem-type elem-count options)) 189 | 190 | (acceptable-device-buffer? [device item] 191 | (dtype-proto/convertible-to-writer? item)) 192 | 193 | (device->device-copy-compatible? [src-device dst-device] nil)) 194 | 195 | 196 | (extend-type CPUDriver 197 | drv/PDriver 198 | (driver-name [impl] 199 | driver-name) 200 | 201 | (get-devices [impl] 202 | @(get impl :devices)) 203 | 204 | (allocate-host-buffer [impl elem-count elem-type options] 205 | (check-stream-error-atom impl) 206 | (dtype/make-container :native-buffer elem-type elem-count options)) 207 | 208 | (acceptable-host-buffer? [impl item] 209 | (dtype-proto/convertible-to-writer? item))) 210 | 211 | 212 | (declare default-cpu-stream) 213 | 214 | 215 | (def driver 216 | (memoize 217 | (fn [] 218 | (let [error-atom (atom nil) 219 | retval (->CPUDriver (atom nil) error-atom)] 220 | (reset! (get retval :devices) 221 | (->> (range 1) 222 | (mapv #(make-cpu-device (constantly retval) % error-atom)))) 223 | retval)))) 224 | 225 | 226 | (registry/register-driver (driver)) 227 | (registry/set-cpu-driver-name! (-> (driver) 228 | drv/driver-name)) 229 | 230 | 231 | (extend-type Object 232 | drv/PDriverProvider 233 | (get-driver [impl] (driver)) 234 | drv/PDeviceProvider 235 | (get-device [impl] 236 | (first (drv/get-devices (driver))))) 237 | -------------------------------------------------------------------------------- /src/tech/compute/tensor.clj: -------------------------------------------------------------------------------- 1 | (ns tech.compute.tensor 2 | "Functions for dealing with tensors with the compute system" 3 | (:require [tech.compute.driver :as drv] 4 | [tech.compute.context :as compute-ctx] 5 | [tech.v2.datatype :as dtype] 6 | [tech.v2.datatype.protocols :as dtype-proto] 7 | [tech.v2.tensor.impl :as dtt-impl] 8 | [tech.v2.tensor.dimensions :as dtt-dims] 9 | [tech.v2.tensor :as dtt]) 10 | (:import [tech.v2.tensor.impl Tensor])) 11 | 12 | 13 | (defn new-tensor 14 | ([shape options] 15 | (let [{:keys [device]} (compute-ctx/options->context options) 16 | datatype (dtt-impl/default-datatype (:datatype options)) 17 | ecount (long (apply * shape)) 18 | dev-buf (drv/allocate-device-buffer device ecount datatype options)] 19 | (dtt-impl/construct-tensor dev-buf (dtt-dims/dimensions shape)))) 20 | ([shape] 21 | (new-tensor shape {}))) 22 | 23 | 24 | (defn new-host-tensor 25 | ([shape options] 26 | (let [{:keys [driver]} (compute-ctx/options->context options) 27 | datatype (dtt-impl/default-datatype (:datatype options)) 28 | ecount (long (apply * shape)) 29 | host-buf (drv/allocate-host-buffer driver ecount datatype options)] 30 | (dtt-impl/construct-tensor host-buf (dtt-dims/dimensions shape)))) 31 | ([shape] 32 | (new-host-tensor shape {}))) 33 | 34 | 35 | (defn assign! 36 | "assign rhs to lhs returning lhs" 37 | [lhs rhs & [options]] 38 | (let [lhs (dtt/ensure-tensor lhs) 39 | rhs (dtt/ensure-tensor rhs) 40 | lhs-buf (dtt/tensor->buffer lhs) 41 | rhs-buf (dtt/tensor->buffer rhs) 42 | lhs-shape (dtype/shape lhs) 43 | rhs-shape (dtype/shape rhs) 44 | stream (:stream (compute-ctx/options->context options))] 45 | (when-not (= (dtype/get-datatype lhs) 46 | (dtype/get-datatype rhs)) 47 | (throw (Exception. (format "Cannot assign tensors of different datatypes: %s %s" 48 | (dtype/get-datatype lhs) 49 | (dtype/get-datatype rhs))))) 50 | (when-not (and (= lhs-shape 51 | rhs-shape)) 52 | (throw (Exception. (format "Tensor shapes differ: %s %s" 53 | lhs-shape 54 | rhs-shape)))) 55 | (when-not (and (dtt-impl/simple-dimensions? (dtt/tensor->dimensions lhs)) 56 | (dtt-impl/simple-dimensions? (dtt/tensor->dimensions rhs))) 57 | (throw (Exception. "Both tensors must be 'simple' tensors. 58 | no offset, no transpose, all data must be dense."))) 59 | (drv/copy-device->device stream 60 | rhs-buf 0 61 | lhs-buf 0 62 | (dtype/ecount lhs-buf)) 63 | (when (:sync? options) 64 | (drv/sync-with-host stream)) 65 | lhs)) 66 | 67 | 68 | (defn clone-to-device 69 | "Clone a host tensor to a device. Tensor must have relatively straighforward 70 | dimensions (transpose OK but arbitrary reorder or offset not OK) or :force? 71 | must be specified. 72 | options: 73 | :force? Copy tensor to another buffer if necessary. 74 | :sync? Sync with stream to ensure copy operation is finished before moving forward." 75 | ([input-tens options] 76 | (let [input-tens (dtt/ensure-tensor input-tens) 77 | input-tens-buf (dtt/tensor->buffer input-tens) 78 | {:keys [device stream]} (compute-ctx/options->context options) 79 | datatype (dtype/get-datatype input-tens-buf) 80 | input-tens-buf (if (drv/acceptable-device-buffer? device input-tens-buf) 81 | input-tens-buf 82 | (dtype/make-container :native-buffer 83 | datatype 84 | input-tens-buf 85 | {:unchecked? true})) 86 | n-elems (dtype/ecount input-tens-buf) 87 | dev-buf (drv/allocate-device-buffer device n-elems datatype options)] 88 | (drv/copy-device->device stream 89 | input-tens-buf 0 90 | dev-buf 0 91 | n-elems) 92 | (when (:sync? options) 93 | (drv/sync-with-host stream)) 94 | (dtt-impl/construct-tensor dev-buf (dtt/tensor->dimensions input-tens)))) 95 | ([input-tens] 96 | (clone-to-device input-tens {}))) 97 | 98 | 99 | (defn ensure-device 100 | "Ensure a tensor can be used on a device. Some devices can use CPU tensors." 101 | ([input-tens options] 102 | (let [device (or (:device options) 103 | (compute-ctx/default-device))] 104 | (if (and (drv/acceptable-device-buffer? device input-tens) 105 | (dtt-impl/dims-suitable-for-desc? (dtt-impl/tensor->dimensions 106 | input-tens))) 107 | input-tens 108 | (clone-to-device input-tens)))) 109 | ([input-tens] 110 | (ensure-device input-tens {}))) 111 | 112 | 113 | (defn ->tensor 114 | [data & {:keys [datatype device stream sync?] 115 | :as options}] 116 | (-> (dtt/->tensor data 117 | :container-type :native-buffer 118 | :datatype datatype) 119 | (ensure-device options))) 120 | 121 | 122 | (defn clone-to-host 123 | "Copy this tensor to the host. Synchronized by default." 124 | ([device-tens options] 125 | (let [options (update options 126 | :sync? 127 | #(if (nil? %) true %)) 128 | driver (drv/get-driver device-tens) 129 | {:keys [stream]} (compute-ctx/options->context options) 130 | dev-buf (dtt/tensor->buffer device-tens) 131 | buf-elems (dtype/ecount dev-buf) 132 | host-buf (drv/allocate-host-buffer driver buf-elems 133 | (dtype/get-datatype dev-buf) options)] 134 | (drv/copy-device->device stream 135 | dev-buf 0 136 | host-buf 0 137 | buf-elems) 138 | (when (:sync? options) 139 | (drv/sync-with-host stream)) 140 | (dtt-impl/construct-tensor host-buf (dtt/tensor->dimensions device-tens)))) 141 | ([device-tens] 142 | (clone-to-host device-tens {:sync? true}))) 143 | 144 | 145 | (defn ensure-host 146 | "Ensure this tensor is a 'host' tensor. Synchronized by default." 147 | ([device-tens options] 148 | (let [driver (drv/get-driver device-tens)] 149 | (if (drv/acceptable-host-buffer? driver device-tens) 150 | device-tens 151 | (clone-to-host device-tens options)))) 152 | ([device-tens] 153 | (ensure-host device-tens {}))) 154 | 155 | 156 | (defn ->array 157 | [tens & [datatype]] 158 | (let [datatype (or datatype (dtype/get-datatype tens)) 159 | tens (ensure-host tens)] 160 | (case datatype 161 | :int8 (dtype/->byte-array tens) 162 | :int16 (dtype/->short-array tens) 163 | :int32 (dtype/->int-array tens) 164 | :int64 (dtype/->long-array tens) 165 | :float32 (dtype/->float-array tens) 166 | :float64 (dtype/->double-array tens)))) 167 | 168 | 169 | (defn ->float-array 170 | [tens] 171 | (->array tens :float32)) 172 | 173 | 174 | (defn ->double-array 175 | [tens] 176 | (->array tens :float64)) 177 | 178 | 179 | (defn rows 180 | [tens] 181 | (dtt/rows tens)) 182 | 183 | 184 | (defn columns 185 | [tens] 186 | (dtt/columns tens)) 187 | 188 | 189 | (extend-type Tensor 190 | drv/PDriverProvider 191 | (get-driver [tens] 192 | (drv/get-driver (dtt/tensor->buffer tens))) 193 | drv/PDeviceProvider 194 | (get-device [tens] 195 | (drv/get-device (dtt/tensor->buffer tens)))) 196 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC 2 | LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM 3 | CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. 4 | 5 | 1. DEFINITIONS 6 | 7 | "Contribution" means: 8 | 9 | a) in the case of the initial Contributor, the initial code and 10 | documentation distributed under this Agreement, and 11 | 12 | b) in the case of each subsequent Contributor: 13 | 14 | i) changes to the Program, and 15 | 16 | ii) additions to the Program; 17 | 18 | where such changes and/or additions to the Program originate from and are 19 | distributed by that particular Contributor. A Contribution 'originates' from 20 | a Contributor if it was added to the Program by such Contributor itself or 21 | anyone acting on such Contributor's behalf. Contributions do not include 22 | additions to the Program which: (i) are separate modules of software 23 | distributed in conjunction with the Program under their own license 24 | agreement, and (ii) are not derivative works of the Program. 25 | 26 | "Contributor" means any person or entity that distributes the Program. 27 | 28 | "Licensed Patents" mean patent claims licensable by a Contributor which are 29 | necessarily infringed by the use or sale of its Contribution alone or when 30 | combined with the Program. 31 | 32 | "Program" means the Contributions distributed in accordance with this 33 | Agreement. 34 | 35 | "Recipient" means anyone who receives the Program under this Agreement, 36 | including all Contributors. 37 | 38 | 2. GRANT OF RIGHTS 39 | 40 | a) Subject to the terms of this Agreement, each Contributor hereby grants 41 | Recipient a non-exclusive, worldwide, royalty-free copyright license to 42 | reproduce, prepare derivative works of, publicly display, publicly perform, 43 | distribute and sublicense the Contribution of such Contributor, if any, and 44 | such derivative works, in source code and object code form. 45 | 46 | b) Subject to the terms of this Agreement, each Contributor hereby grants 47 | Recipient a non-exclusive, worldwide, royalty-free patent license under 48 | Licensed Patents to make, use, sell, offer to sell, import and otherwise 49 | transfer the Contribution of such Contributor, if any, in source code and 50 | object code form. This patent license shall apply to the combination of the 51 | Contribution and the Program if, at the time the Contribution is added by the 52 | Contributor, such addition of the Contribution causes such combination to be 53 | covered by the Licensed Patents. The patent license shall not apply to any 54 | other combinations which include the Contribution. No hardware per se is 55 | licensed hereunder. 56 | 57 | c) Recipient understands that although each Contributor grants the licenses 58 | to its Contributions set forth herein, no assurances are provided by any 59 | Contributor that the Program does not infringe the patent or other 60 | intellectual property rights of any other entity. Each Contributor disclaims 61 | any liability to Recipient for claims brought by any other entity based on 62 | infringement of intellectual property rights or otherwise. As a condition to 63 | exercising the rights and licenses granted hereunder, each Recipient hereby 64 | assumes sole responsibility to secure any other intellectual property rights 65 | needed, if any. For example, if a third party patent license is required to 66 | allow Recipient to distribute the Program, it is Recipient's responsibility 67 | to acquire that license before distributing the Program. 68 | 69 | d) Each Contributor represents that to its knowledge it has sufficient 70 | copyright rights in its Contribution, if any, to grant the copyright license 71 | set forth in this Agreement. 72 | 73 | 3. REQUIREMENTS 74 | 75 | A Contributor may choose to distribute the Program in object code form under 76 | its own license agreement, provided that: 77 | 78 | a) it complies with the terms and conditions of this Agreement; and 79 | 80 | b) its license agreement: 81 | 82 | i) effectively disclaims on behalf of all Contributors all warranties and 83 | conditions, express and implied, including warranties or conditions of title 84 | and non-infringement, and implied warranties or conditions of merchantability 85 | and fitness for a particular purpose; 86 | 87 | ii) effectively excludes on behalf of all Contributors all liability for 88 | damages, including direct, indirect, special, incidental and consequential 89 | damages, such as lost profits; 90 | 91 | iii) states that any provisions which differ from this Agreement are offered 92 | by that Contributor alone and not by any other party; and 93 | 94 | iv) states that source code for the Program is available from such 95 | Contributor, and informs licensees how to obtain it in a reasonable manner on 96 | or through a medium customarily used for software exchange. 97 | 98 | When the Program is made available in source code form: 99 | 100 | a) it must be made available under this Agreement; and 101 | 102 | b) a copy of this Agreement must be included with each copy of the Program. 103 | 104 | Contributors may not remove or alter any copyright notices contained within 105 | the Program. 106 | 107 | Each Contributor must identify itself as the originator of its Contribution, 108 | if any, in a manner that reasonably allows subsequent Recipients to identify 109 | the originator of the Contribution. 110 | 111 | 4. COMMERCIAL DISTRIBUTION 112 | 113 | Commercial distributors of software may accept certain responsibilities with 114 | respect to end users, business partners and the like. While this license is 115 | intended to facilitate the commercial use of the Program, the Contributor who 116 | includes the Program in a commercial product offering should do so in a 117 | manner which does not create potential liability for other Contributors. 118 | Therefore, if a Contributor includes the Program in a commercial product 119 | offering, such Contributor ("Commercial Contributor") hereby agrees to defend 120 | and indemnify every other Contributor ("Indemnified Contributor") against any 121 | losses, damages and costs (collectively "Losses") arising from claims, 122 | lawsuits and other legal actions brought by a third party against the 123 | Indemnified Contributor to the extent caused by the acts or omissions of such 124 | Commercial Contributor in connection with its distribution of the Program in 125 | a commercial product offering. The obligations in this section do not apply 126 | to any claims or Losses relating to any actual or alleged intellectual 127 | property infringement. In order to qualify, an Indemnified Contributor must: 128 | a) promptly notify the Commercial Contributor in writing of such claim, and 129 | b) allow the Commercial Contributor to control, and cooperate with the 130 | Commercial Contributor in, the defense and any related settlement 131 | negotiations. The Indemnified Contributor may participate in any such claim 132 | at its own expense. 133 | 134 | For example, a Contributor might include the Program in a commercial product 135 | offering, Product X. That Contributor is then a Commercial Contributor. If 136 | that Commercial Contributor then makes performance claims, or offers 137 | warranties related to Product X, those performance claims and warranties are 138 | such Commercial Contributor's responsibility alone. Under this section, the 139 | Commercial Contributor would have to defend claims against the other 140 | Contributors related to those performance claims and warranties, and if a 141 | court requires any other Contributor to pay any damages as a result, the 142 | Commercial Contributor must pay those damages. 143 | 144 | 5. NO WARRANTY 145 | 146 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON 147 | AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER 148 | EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR 149 | CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A 150 | PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the 151 | appropriateness of using and distributing the Program and assumes all risks 152 | associated with its exercise of rights under this Agreement , including but 153 | not limited to the risks and costs of program errors, compliance with 154 | applicable laws, damage to or loss of data, programs or equipment, and 155 | unavailability or interruption of operations. 156 | 157 | 6. DISCLAIMER OF LIABILITY 158 | 159 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY 160 | CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, 161 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION 162 | LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 163 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 164 | ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE 165 | EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY 166 | OF SUCH DAMAGES. 167 | 168 | 7. GENERAL 169 | 170 | If any provision of this Agreement is invalid or unenforceable under 171 | applicable law, it shall not affect the validity or enforceability of the 172 | remainder of the terms of this Agreement, and without further action by the 173 | parties hereto, such provision shall be reformed to the minimum extent 174 | necessary to make such provision valid and enforceable. 175 | 176 | If Recipient institutes patent litigation against any entity (including a 177 | cross-claim or counterclaim in a lawsuit) alleging that the Program itself 178 | (excluding combinations of the Program with other software or hardware) 179 | infringes such Recipient's patent(s), then such Recipient's rights granted 180 | under Section 2(b) shall terminate as of the date such litigation is filed. 181 | 182 | All Recipient's rights under this Agreement shall terminate if it fails to 183 | comply with any of the material terms or conditions of this Agreement and 184 | does not cure such failure in a reasonable period of time after becoming 185 | aware of such noncompliance. If all Recipient's rights under this Agreement 186 | terminate, Recipient agrees to cease use and distribution of the Program as 187 | soon as reasonably practicable. However, Recipient's obligations under this 188 | Agreement and any licenses granted by Recipient relating to the Program shall 189 | continue and survive. 190 | 191 | Everyone is permitted to copy and distribute copies of this Agreement, but in 192 | order to avoid inconsistency the Agreement is copyrighted and may only be 193 | modified in the following manner. The Agreement Steward reserves the right to 194 | publish new versions (including revisions) of this Agreement from time to 195 | time. No one other than the Agreement Steward has the right to modify this 196 | Agreement. The Eclipse Foundation is the initial Agreement Steward. The 197 | Eclipse Foundation may assign the responsibility to serve as the Agreement 198 | Steward to a suitable separate entity. Each new version of the Agreement will 199 | be given a distinguishing version number. The Program (including 200 | Contributions) may always be distributed subject to the version of the 201 | Agreement under which it was received. In addition, after a new version of 202 | the Agreement is published, Contributor may elect to distribute the Program 203 | (including its Contributions) under the new version. Except as expressly 204 | stated in Sections 2(a) and 2(b) above, Recipient receives no rights or 205 | licenses to the intellectual property of any Contributor under this 206 | Agreement, whether expressly, by implication, estoppel or otherwise. All 207 | rights in the Program not expressly granted under this Agreement are 208 | reserved. 209 | 210 | This Agreement is governed by the laws of the State of New York and the 211 | intellectual property laws of the United States of America. No party to this 212 | Agreement will bring a legal action under this Agreement more than one year 213 | after the cause of action arose. Each party waives its rights to a jury trial 214 | in any resulting litigation. 215 | --------------------------------------------------------------------------------