diff --git a/.gitignore b/.gitignore index f478dd0..54edb37 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,4 @@ pom.xml.asc .clay* *qmd .clerk +.calva diff --git a/deps.edn b/deps.edn index c75cccf..ad512a6 100644 --- a/deps.edn +++ b/deps.edn @@ -14,5 +14,6 @@ :test {:extra-paths ["test"] :extra-deps {io.github.cognitect-labs/test-runner {:git/tag "v0.5.0" :git/sha "b3fd0d2"}} + :jvm-opts ["-Djava.awt.headless=true"] :main-opts ["-m" "cognitect.test-runner"] :exec-fn cognitect.test-runner.api/test}}} diff --git a/src/clojisr/v1/impl/java_to_clj.clj b/src/clojisr/v1/impl/java_to_clj.clj index 2005cb6..c051c3c 100644 --- a/src/clojisr/v1/impl/java_to_clj.clj +++ b/src/clojisr/v1/impl/java_to_clj.clj @@ -2,9 +2,11 @@ (:require [tech.v3.dataset :as ds] [tech.v3.dataset.column :as col] [tech.v3.datatype :refer [->reader]] - + [tech.v3.tensor :as dtt] [clojisr.v1.impl.protocols :as prot] - [clojisr.v1.impl.common :refer [tsp->reader first-step->java java->column cartesian-product]])) + [clojisr.v1.impl.common :refer [tsp->reader first-step->java java->column cartesian-product]] + [tech.v3.datatype :as dtype] + [ tech.v3.tensor.dimensions :as tdim])) (set! *unchecked-math* :warn-on-boxed) @@ -92,12 +94,28 @@ (apply ds/concat))))) (defn multidim? [obj] (prot/attribute obj "dim")) -(defn multidim->dataset - ([exp] (multidim->dataset exp nil)) - ([exp extra] - (let [dims (fix-dims (prot/attribute exp "dim")) - dimnames (fix-dimnames dims (prot/attribute exp "dimnames"))] - (make-nd exp extra dims dimnames)))) + +(defn- make-tensor [exp dims] + + (def exp exp) + (def dims dims) + + (dtt/construct-tensor (prot/->clj exp) + (tdim/dimensions dims))) + ;; (dtt/->tensor [[7 4 6 8 8 7 5 9 7 8] + ;; [4 1 3 6 5 2 3 5 4 2] + ;; [3 8 5 1 7 9 3 8 5 2]] :datatype :float64)) +(defn multidim->dataset-or-tensor + ([exp] (multidim->dataset-or-tensor exp nil nil)) + ([exp extra] (multidim->dataset-or-tensor exp extra nil)) + ([exp extra options] + + + (let [dims (fix-dims (prot/attribute exp "dim")) + dimnames (fix-dimnames dims (prot/attribute exp "dimnames"))] + (if (:as-tensor options) + (make-tensor exp dims) + (make-nd exp extra dims dimnames))))) (defn mts? [obj] (prot/inherits? obj "mts")) (defn mts->dataset @@ -105,7 +123,7 @@ (let [tsp (-> exp (prot/attribute "tsp") (tsp->reader))] - (multidim->dataset exp [tsp :$time]))) + (multidim->dataset-or-tensor exp [tsp :$time]))) ;; table @@ -153,18 +171,22 @@ (defn java->clj "Perform high level data conversion" - [exp] - (let [exp (first-step->java exp)] - (cond - (data-frame? exp) (data-frame->dataset exp) - (mts? exp) (mts->dataset exp) - (timeseries? exp) (timeseries->dataset exp) - (dist? exp) (dist->dataset exp) - (table? exp) (table->dataset exp) - (multidim? exp) (multidim->dataset exp) - :else (prot/->clj exp)))) - -(defn java->native - [exp] - (prot/->native exp)) + + ([exp options] + ;(println :options options) + (def options options) + (let [exp (first-step->java exp)] + (cond + (data-frame? exp) (data-frame->dataset exp) + (mts? exp) (mts->dataset exp) + (timeseries? exp) (timeseries->dataset exp) + (dist? exp) (dist->dataset exp) + (table? exp) (table->dataset exp) + (multidim? exp) (multidim->dataset-or-tensor exp nil options) + :else (prot/->clj exp)))) + ( [exp] (java->clj exp nil))) + + (defn java->native + [exp] + (prot/->native exp)) diff --git a/src/clojisr/v1/impl/rserve/call.clj b/src/clojisr/v1/impl/rserve/call.clj index f00629b..782f33a 100644 --- a/src/clojisr/v1/impl/rserve/call.clj +++ b/src/clojisr/v1/impl/rserve/call.clj @@ -31,12 +31,18 @@ (defn try-eval-catching-errors [expression ^RConnection r-connection] ;; Using the technique of https://stackoverflow.com/a/40447542/1723677, the way it is used in Rojure. + (def expression expression) (try (let [expression-str (-> expression (string/escape char-escape-string) (->> (format "try(eval(parse(text=\"%s\")),silent=TRUE)"))) ^REXP rexp (locking r-connection (.parseAndEval r-connection expression-str))] + (def rexp rexp) + + (clojure.reflect/reflect rexp) + (.asIntegers rexp) + (if (.inherits rexp "try-error") (do (log/error [::try-eval-catching-errors {:message (format "Error in R evaluating expression: %s. R exception: %s" expression (.asString rexp))}]) diff --git a/src/clojisr/v1/r.clj b/src/clojisr/v1/r.clj index 209e96e..8215e80 100644 --- a/src/clojisr/v1/r.clj +++ b/src/clojisr/v1/r.clj @@ -11,7 +11,8 @@ [clojure.string :as string] [clojisr.v1.util :refer [bracket-data maybe-wrap-backtick]] [clojisr.v1.require :refer [require-r-package]] - [clojisr.v1.engines :refer [engines]]) + [clojisr.v1.engines :refer [engines]] + [tech.v3.tensor :as dtt]) (:import clojisr.v1.robject.RObject)) (defn init [& {:keys [session-args]}] @@ -40,7 +41,7 @@ (defn java->native-clj [java-object] (java2clj/java->native java-object)) -(defn java->clj [java-object] (java2clj/java->clj java-object)) +(defn java->clj [java-object & options] (java2clj/java->clj java-object options)) (defn clj->java [clj-object & {:keys [session-args]}] (let [session (session/fetch-or-make session-args)] @@ -49,7 +50,13 @@ (def clj->java->r (comp java->r clj->java)) (def clj->r clj->java->r) -(defn r->java->clj [r-object] (-> r-object r r->java java2clj/java->clj)) +(defn r->java->clj + ([r-object options] + (-> r-object + (r) + (r->java) + (java2clj/java->clj options))) + ( [r-object] (r->java->clj r-object nil))) (def r->clj r->java->clj) (defn r->java->native-clj [r-object] (-> r r-object r->java java2clj/java->native)) @@ -222,3 +229,45 @@ "Prints help for an R object or function" ([r-object] (println (help r-object))) ([function package] (println (help function package)))) + +(comment + (require-r '[datasets]) + + (require-r '[base]) + + (r "m <- array(seq(1, 10*5*4*7), dim=c(10, 5, 4,7))") + + + ( r) + + (def m (-> (r "m") + ( r->clj {:as-tensor true}))) + + (r "dim(m)") + + ) + +(require '[tech.v3.tensor :as dtt]) + +(r "m[10,5,4,7]") +;;=> [1] 1400 +(dtt/mget m 9 4 3 6) +;;=> 1400 + + +(r "m[1,1,1,1]") +;;=> [1] 1 +(dtt/mget m 0 0 0 0) +;;=> 1 + +(r "m[3,3,3,3]") +;;=> [1] 523 +;; + +;; +(dtt/mget m 2 2 2 2) +;;=> 353 + +(r "print(.MEM$xe6ab3051a83d48f3)") + +(r "capture.output(print(.MEM$x9cd80e7a0ca4471b))") \ No newline at end of file diff --git a/test/clojisr/v1/matrix_test.clj b/test/clojisr/v1/matrix_test.clj new file mode 100644 index 0000000..0fddd64 --- /dev/null +++ b/test/clojisr/v1/matrix_test.clj @@ -0,0 +1,24 @@ +(ns clojisr.v1.matrix-test + (:require + [clojure.test :refer [deftest is]] + [clojisr.v1.r :as r])) + +(r/require-r '[base]) + +(deftest options-are-optional-without + + (is (= (range 5) + (-> + (r.base/matrix (range 5)) + (r/r->clj) + (get 1))))) + + +(deftest options-are-optional-wit + ;; does not crash + (is (= (range 5) + (-> + (r.base/matrix (range 5)) + (r/r->clj {:some-option true}) + (get 1))))) +