├── .gitignore ├── .gitmodules ├── .travis.yml ├── LICENSE ├── README.md ├── deps.edn ├── dockerfiles └── Dockerfile ├── docs ├── css │ └── default.css ├── gc.html ├── highlight │ ├── highlight.min.js │ └── solarized-light.css ├── index.html ├── js │ ├── jquery.min.js │ └── page_effects.js ├── libjulia-clj.java-api.html ├── libjulia-clj.julia.html └── signals.html ├── examples └── fractal │ ├── README.md │ ├── deps.edn │ └── src │ └── julia_fractals.clj ├── resources ├── clj_julia_helper.jl └── symbols.txt ├── scripts ├── activate-julia ├── activate-openj9 ├── build-docker ├── deploy ├── local-julia-env ├── openj9-java ├── remote-repl └── run-docker ├── src └── libjulia_clj │ ├── impl │ ├── base.clj │ ├── collections.clj │ ├── ffi.clj │ ├── gc.clj │ └── protocols.clj │ ├── java_api.clj │ ├── julia.clj │ └── modules │ ├── Base.clj │ ├── Core.clj │ └── LinearAlgebra.clj ├── test └── libjulia_clj │ ├── api_test.clj │ └── julia_test.clj └── topics ├── gc.md ├── images └── julia.png └── signals.md /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /classes 3 | /checkouts 4 | profiles.clj 5 | pom.xml 6 | pom.xml.asc 7 | *.jar 8 | *.class 9 | /.lein-* 10 | .nrepl-port 11 | .hgignore 12 | .hg/ 13 | julia-1.5.3* 14 | julia 15 | openj9 16 | .cpcache 17 | *.png 18 | julia-1.* 19 | *.asc -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cnuernber/libjulia-clj/42685f6bc13d141522783efbf827d70c4d895336/.gitmodules -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: julia 2 | 3 | os: 4 | - linux 5 | - osx 6 | - windows 7 | 8 | dist: focal 9 | 10 | 11 | julia: 12 | - 1.5.3 13 | 14 | env: 15 | - JULIA_COPY_STACKS=0 16 | - JULIA_COPY_STACKS=1 17 | 18 | before_install: 19 | - wget https://raw.githubusercontent.com/technomancy/leiningen/stable/bin/lein 20 | - chmod a+x lein 21 | - export JULIA_HOME=$TRAVIS_HOME/julia 22 | - echo $TRAVIS_OS_NAME 23 | - if [ "$TRAVIS_OS_NAME" = "windows" ]; then choco install jdk8; fi 24 | - if [ "$TRAVIS_OS_NAME" = "windows" ]; then export PATH=$PATH:"/C/Program Files/Java/jdk1.8.0_211/bin"; fi 25 | - if [ "$TRAVIS_OS_NAME" = "windows" ]; then java -version; fi 26 | - if [ "$TRAVIS_OS_NAME" = "windows" ]; then export JULIA_HOME="/c/julia/"; fi 27 | - echo $($TRAVIS_BUILD_DIR/lein) 28 | 29 | 30 | script: 31 | - $TRAVIS_BUILD_DIR/lein test 32 | 33 | notifications: 34 | email: false 35 | 36 | jobs: 37 | allow_failures: 38 | - os: windows 39 | 40 | cache: 41 | directories: 42 | - "$HOME/.m2" 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2021 Chris Nuernberger 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # julia-clj 2 | 3 | [![Clojars Project](https://img.shields.io/clojars/v/com.cnuernber/libjulia-clj.svg)](https://clojars.org/cnuernber/libjulia-clj) 4 | [![travis integration](https://travis-ci.com/cnuernber/libjulia-clj.svg?branch=master)](https://travis-ci.com/cnuernber/libjulia-clj) 5 | 6 | 7 | * [API docs](https://cnuernber.github.io/libjulia-clj/) 8 | * [In-Depth Example](https://github.com/cnuernber/kmeans-mnist) 9 | * We now support julia-1.7.X so you can use it with the latest Julia. 10 | 11 | ## Usage 12 | 13 | Install julia and set JULIA_HOME: 14 | 15 | ```console 16 | scripts/activate-julia 17 | ``` 18 | 19 | In your repl, load the julia base namespace and initialize the system. 20 | 21 | ```clojure 22 | user> (require '[libjulia-clj.julia :as julia]) 23 | nil 24 | user> (julia/initialize!) 25 | 07:07:06.228 [nREPL-session-e1c7b4a4-54f4-4298-80bb-972e83b902ff] INFO libjulia-clj.impl.base - Attempting to initialize Julia at /home/chrisn/dev/cnuernber/libjulia-clj/julia-1.5.3/lib/libjulia.so 26 | 07:07:07.121 [nREPL-session-e1c7b4a4-54f4-4298-80bb-972e83b902ff] INFO tech.v3.jna.base - Library /home/chrisn/dev/cnuernber/libjulia-clj/julia-1.5.3/lib/libjulia.so found at [:system "/home/chrisn/dev/cnuernber/libjulia-clj/julia-1.5.3/lib/libjulia.so"] 27 | :ok 28 | user> (def ones-fn (julia/jl "Base.ones")) 29 | #'user/ones-fn 30 | user> (ones-fn 3 4) 31 | [1.0 1.0 1.0 1.0; 1.0 1.0 1.0 1.0; 1.0 1.0 1.0 1.0] 32 | user> (def julia-ary *1) 33 | #'user/julia-ary 34 | user> (require '[tech.v3.tensor :as dtt]) 35 | nil 36 | user> (dtt/ensure-tensor julia-ary) 37 | #tech.v3.tensor[3 4] 38 | [[1.000 1.000 1.000 1.000] 39 | [1.000 1.000 1.000 1.000] 40 | [1.000 1.000 1.000 1.000]] 41 | user> (def clj-tens *1) 42 | #'user/clj-tens 43 | user> (dtt/mset! clj-tens 0 25) 44 | #tech.v3.tensor[3 4] 45 | [[25.00 25.00 25.00 25.00] 46 | [1.000 1.000 1.000 1.000] 47 | [1.000 1.000 1.000 1.000]] 48 | user> julia-ary 49 | [25.0 25.0 25.0 25.0; 1.0 1.0 1.0 1.0; 1.0 1.0 1.0 1.0] 50 | ``` 51 | 52 | ## Something Fun 53 | 54 | ```clojure 55 | user> (require '[tech.v3.libs.buffered-image :as bufimg]) 56 | nil 57 | user> (require '[tech.v3.datatype :as dtype]) 58 | nil 59 | user> (def fract-width 1920) 60 | (def fract-height 1080) 61 | (def i1 0.31) 62 | (def i2 -0.6) 63 | (def d 11) 64 | (def zoom-factor 0.2) 65 | #'user/fract-width#'user/fract-height#'user/i1#'user/i2#'user/d#'user/zoom-factor 66 | user> (def julia-code 67 | "function juliaSet(i1,i2,d,zoomFactor,imgWidth,imgHeight) 68 | # Allocating a widthxheight matrix as our Clojure client is row-major 69 | matrix = Array{UInt8}(undef,imgWidth,imgHeight) 70 | icomp = Complex{Float64}(i1,i2) 71 | Threads.@threads for i in CartesianIndices(matrix) 72 | ## Julia has 1-based indexing... 73 | pos = complex(((i[1]-1) - (0.5 * imgWidth)) / (zoomFactor * imgWidth), 74 | ((i[2]-1) - (0.5 * imgHeight)) / (zoomFactor * imgHeight)) 75 | 76 | for c in (1:d) pos = (pos * pos) + icomp end 77 | absval = abs(pos) 78 | if (absval != NaN && absval < (d-1)) 79 | matrix[i] = 255 80 | else 81 | matrix[i] = 0 82 | end 83 | end 84 | return matrix 85 | end") 86 | #'user/julia-code 87 | user> (def fractal-fn (julia/jl julia-code)) 88 | #'user/fractal-fn 89 | user> (defn jl-fractal 90 | [] 91 | (-> (fractal-fn i1 i2 d zoom-factor fract-width fract-height) 92 | (dtt/ensure-tensor) 93 | ;;Julia is column-major so our image comes out widthxheight 94 | ;;datatype is row major. 95 | (dtt/transpose [1 0]) 96 | ;;The tensor library *knows* the original was transposed so transposing the result 97 | ;;back into row-major means the memory can be read in order and thus 98 | ;;the copy operation below is one large memcopy into a jvm byte array. 99 | (dtype/copy! (bufimg/new-image fract-height fract-width :byte-gray)))) 100 | #'user/jl-fractal 101 | user> (jl-fractal) 102 | #object[java.awt.image.BufferedImage 0x4d63b28f "BufferedImage@4d63b28f: type = 10 ColorModel: #pixelBits = 8 numComponents = 1 color space = java.awt.color.ICC_ColorSpace@2703464d transparency = 1 has alpha = false isAlphaPre = false ByteInterleavedRaster: width = 1920 height = 1080 #numDataElements 1 dataOff[0] = 0"] 103 | ;; Roughly 1920*1080*11*3, or 68428800 complex number operations 104 | user> (time (def ignored (jl-fractal))) 105 | "Elapsed time: 31.487044 msecs" 106 | #'user/ignored 107 | user> (bufimg/save! (jl-fractal) "julia.png") 108 | true 109 | ``` 110 | 111 | ![julia-img](topics/images/julia.png) 112 | 113 | 114 | ## License 115 | 116 | Copyright © 2021 Chris Nuernberger 117 | 118 | * [MIT](LICENSE) 119 | -------------------------------------------------------------------------------- /deps.edn: -------------------------------------------------------------------------------- 1 | {:paths ["src"] 2 | :deps {org.clojure/clojure {:mvn/version "1.10.2" :scope "provided"} 3 | cnuernber/dtype-next {:mvn/version "8.062"} 4 | net.java.dev.jna/jna {:mvn/version "5.10.0"}} 5 | :aliases 6 | {:jdk-17 7 | {:jvm-opts ["--add-modules" "jdk.incubator.foreign" 8 | "--enable-native-access=ALL-UNNAMED"]} 9 | :test 10 | {:extra-deps {com.cognitect/test-runner 11 | {:git/url "https://github.com/cognitect-labs/test-runner" 12 | :sha "209b64504cb3bd3b99ecfec7937b358a879f55c1"} 13 | ch.qos.logback/logback-classic {:mvn/version "1.1.3"}} 14 | :extra-paths ["test"] 15 | :main-opts ["-m" "cognitect.test-runner"]} 16 | :depstar 17 | {:replace-deps {com.github.seancorfield/depstar {:mvn/version "2.0.193"}} 18 | :ns-default hf.depstar 19 | :exec-fn hf.depstar/jar 20 | :exec-args {:group-id "com.cnuernber" 21 | :artifact-id "libjulia-clj" 22 | :version "1.000-beta-8" 23 | :aot true 24 | :compile-ns [libjulia-clj.java-api] 25 | :sync-pom true 26 | :jar "target/libjulia-clj.jar"}} 27 | 28 | :deploy 29 | {:replace-deps {slipset/deps-deploy {:mvn/version "0.1.5"}} 30 | :exec-fn deps-deploy.deps-deploy/deploy 31 | :exec-args {:installer :remote 32 | :sign-releases? true 33 | :artifact "target/libjulia-clj.jar"}} 34 | 35 | :install 36 | {:replace-deps {slipset/deps-deploy {:mvn/version "0.1.5"}} 37 | :exec-fn deps-deploy.deps-deploy/deploy 38 | :exec-args {:installer :local 39 | :artifact "target/libjulia-clj.jar"}} 40 | :codox 41 | {:extra-deps {codox-theme-rdash/codox-theme-rdash {:mvn/version "0.1.2"} 42 | com.cnuernber/codox {:mvn/version "1.001"} 43 | cider/cider-nrepl {:mvn/version "0.26.0"}} 44 | :extra-paths ["test" "resources"] 45 | :exec-fn codox.main/-main 46 | :exec-args {:arg-paths [[:aliases :depstar :exec-args]] 47 | :description "Julia bindings for Clojure and the JVM" 48 | :metadata {:doc/format :markdown} 49 | :themes [:rdash] 50 | :source-paths ["src"] 51 | :output-path "docs" 52 | :doc-paths ["topics"] 53 | :source-uri "https://github.com/cnuernber/libjulia-clj/blob/master/{filepath}#L{line}" 54 | :namespaces [libjulia-clj.julia 55 | libjulia-clj.java-api]}}} 56 | } 57 | -------------------------------------------------------------------------------- /dockerfiles/Dockerfile: -------------------------------------------------------------------------------- 1 | # We will use Ubuntu for our image 2 | FROM ubuntu:latest 3 | 4 | # Updating Ubuntu packages 5 | 6 | ARG CLOJURE_TOOLS_VERSION=1.10.1.507 7 | 8 | 9 | RUN apt-get -qq update && apt-get -qq -y install curl wget bzip2 openjdk-8-jdk-headless \ 10 | && curl -o install-clojure https://download.clojure.org/install/linux-install-${CLOJURE_TOOLS_VERSION}.sh \ 11 | && chmod +x install-clojure \ 12 | && ./install-clojure && rm install-clojure \ 13 | && wget https://raw.githubusercontent.com/technomancy/leiningen/stable/bin/lein \ 14 | && chmod a+x lein \ 15 | && mv lein /usr/bin \ 16 | && apt-get -qq -y autoremove \ 17 | && apt-get autoclean \ 18 | && rm -rf /var/lib/apt/lists/* /var/log/dpkg.log 19 | 20 | 21 | ARG USERID 22 | ARG GROUPID 23 | ARG USERNAME 24 | 25 | RUN groupadd -g $GROUPID $USERNAME 26 | RUN useradd -u $USERID -g $GROUPID $USERNAME 27 | RUN mkdir /home/$USERNAME && chown $USERNAME:$USERNAME /home/$USERNAME 28 | USER $USERNAME 29 | 30 | WORKDIR /home/$USERNAME 31 | 32 | RUN wget https://julialang-s3.julialang.org/bin/linux/x64/1.5/julia-1.5.3-linux-x86_64.tar.gz \ 33 | && tar -xvzf julia-1.5.3-linux-x86_64.tar.gz 34 | 35 | ENV PATH /home/$USERNAME/julia-1.5.3/bin:$PATH 36 | ENV JULIA_HOME /home/$USERNAME/julia-1.5.3 37 | ENV LD_LIBRARY_PATH /home/$USERNAME/julia-1.5.3/lib 38 | ENV JULIA_COPY_STACKS=yes 39 | 40 | RUN lein -v -------------------------------------------------------------------------------- /docs/css/default.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css?family=PT+Sans'); 2 | 3 | body { 4 | font-family: 'PT Sans', Helvetica, sans-serif; 5 | font-size: 14px; 6 | } 7 | 8 | a { 9 | color: #337ab7; 10 | text-decoration: none; 11 | } 12 | 13 | a:hover { 14 | color: #30426a; 15 | text-decoration: underline; 16 | } 17 | 18 | pre, code { 19 | font-family: Monaco, DejaVu Sans Mono, Consolas, monospace; 20 | font-size: 9pt; 21 | margin: 15px 0; 22 | } 23 | 24 | h1 { 25 | font-weight: normal; 26 | font-size: 29px; 27 | margin: 10px 0 2px 0; 28 | padding: 0; 29 | } 30 | 31 | h2 { 32 | font-weight: normal; 33 | font-size: 25px; 34 | } 35 | 36 | h3 > a:hover { 37 | text-decoration: none; 38 | } 39 | 40 | .document h1, .namespace-index h1 { 41 | font-size: 32px; 42 | margin-top: 12px; 43 | } 44 | 45 | #header, #content, .sidebar { 46 | position: fixed; 47 | } 48 | 49 | #header { 50 | top: 0; 51 | left: 0; 52 | right: 0; 53 | height: 22px; 54 | color: #f5f5f5; 55 | padding: 5px 7px; 56 | } 57 | 58 | #content { 59 | top: 32px; 60 | right: 0; 61 | bottom: 0; 62 | overflow: auto; 63 | background: #fff; 64 | color: #333; 65 | padding: 0 18px; 66 | } 67 | 68 | .sidebar { 69 | position: fixed; 70 | top: 32px; 71 | bottom: 0; 72 | overflow: auto; 73 | } 74 | 75 | .sidebar.primary { 76 | background: #30426a; 77 | border-right: solid 1px #cccccc; 78 | left: 0; 79 | width: 250px; 80 | color: white; 81 | font-size: 110%; 82 | } 83 | 84 | .sidebar.secondary { 85 | background: #f2f2f2; 86 | border-right: solid 1px #d7d7d7; 87 | left: 251px; 88 | width: 200px; 89 | font-size: 110%; 90 | } 91 | 92 | #content.namespace-index, #content.document { 93 | left: 251px; 94 | } 95 | 96 | #content.namespace-docs { 97 | left: 452px; 98 | } 99 | 100 | #content.document { 101 | padding-bottom: 10%; 102 | } 103 | 104 | #header { 105 | background: #2d3e63; 106 | box-shadow: 0 0 8px rgba(0, 0, 0, 0.4); 107 | z-index: 100; 108 | } 109 | 110 | #header h1 { 111 | margin: 0; 112 | padding: 0; 113 | font-size: 18px; 114 | font-weight: lighter; 115 | text-shadow: -1px -1px 0px #333; 116 | } 117 | 118 | #header h1 .project-version { 119 | font-weight: normal; 120 | } 121 | 122 | .project-version { 123 | padding-left: 0.15em; 124 | } 125 | 126 | #header a, .sidebar a { 127 | display: block; 128 | text-decoration: none; 129 | } 130 | 131 | #header a { 132 | color: #f5f5f5; 133 | } 134 | 135 | .sidebar.primary, .sidebar.primary a { 136 | color: #b2bfdc; 137 | } 138 | 139 | .sidebar.primary a:hover { 140 | color: white; 141 | } 142 | 143 | .sidebar.secondary, .sidebar.secondary a { 144 | color: #738bc0; 145 | } 146 | 147 | .sidebar.secondary a:hover { 148 | color: #2d3e63; 149 | } 150 | 151 | #header h2 { 152 | float: right; 153 | font-size: 9pt; 154 | font-weight: normal; 155 | margin: 4px 3px; 156 | padding: 0; 157 | color: #bbb; 158 | } 159 | 160 | #header h2 a { 161 | display: inline; 162 | } 163 | 164 | .sidebar h3 { 165 | margin: 0; 166 | padding: 10px 13px 0 13px; 167 | font-size: 19px; 168 | font-weight: lighter; 169 | } 170 | 171 | .sidebar.primary h3.no-link { 172 | text-transform: uppercase; 173 | font-size: 12px; 174 | color: #738bc0; 175 | } 176 | 177 | .sidebar.secondary h3 a { 178 | text-transform: uppercase; 179 | font-size: 12px; 180 | color: #2d3e63; 181 | } 182 | 183 | .sidebar ul { 184 | padding: 7px 0 6px 0; 185 | margin: 0; 186 | } 187 | 188 | .sidebar ul.index-link { 189 | padding-bottom: 4px; 190 | } 191 | 192 | .sidebar li { 193 | display: block; 194 | vertical-align: middle; 195 | } 196 | 197 | .sidebar li a, .sidebar li .no-link { 198 | border-left: 3px solid transparent; 199 | padding: 0 10px; 200 | white-space: nowrap; 201 | } 202 | 203 | .sidebar li .inner { 204 | display: inline-block; 205 | padding-top: 7px; 206 | height: 24px; 207 | } 208 | 209 | .sidebar li a, .sidebar li .tree { 210 | height: 31px; 211 | } 212 | 213 | .depth-1 .inner { padding-left: 2px; } 214 | .depth-2 .inner { padding-left: 6px; } 215 | .depth-3 .inner { padding-left: 20px; } 216 | .depth-4 .inner { padding-left: 34px; } 217 | .depth-5 .inner { padding-left: 48px; } 218 | .depth-6 .inner { padding-left: 62px; } 219 | 220 | .sidebar li .tree { 221 | display: block; 222 | float: left; 223 | position: relative; 224 | top: -10px; 225 | margin: 0 4px 0 0; 226 | padding: 0; 227 | } 228 | 229 | .sidebar li.depth-1 .tree { 230 | display: none; 231 | } 232 | 233 | .sidebar li .tree .top, .sidebar li .tree .bottom { 234 | display: block; 235 | margin: 0; 236 | padding: 0; 237 | width: 7px; 238 | } 239 | 240 | .sidebar li .tree .top { 241 | border-left: 1px solid #aaa; 242 | border-bottom: 1px solid #aaa; 243 | height: 19px; 244 | } 245 | 246 | .sidebar li .tree .bottom { 247 | height: 22px; 248 | } 249 | 250 | .sidebar li.branch .tree .bottom { 251 | border-left: 1px solid #aaa; 252 | } 253 | 254 | .sidebar.primary li.current a { 255 | border-left: 3px solid #e99d1a; 256 | color: white; 257 | } 258 | 259 | .sidebar.secondary li.current a { 260 | border-left: 3px solid #2d3e63; 261 | color: #33a; 262 | } 263 | 264 | .namespace-index h2 { 265 | margin: 30px 0 0 0; 266 | } 267 | 268 | .namespace-index h3 { 269 | font-size: 16px; 270 | font-weight: bold; 271 | margin-bottom: 0; 272 | letter-spacing: 0.05em; 273 | border-bottom: solid 1px #ddd; 274 | max-width: 680px; 275 | background-color: #fafafa; 276 | padding: 0.5em; 277 | } 278 | 279 | .namespace-index .topics { 280 | padding-left: 30px; 281 | margin: 11px 0 0 0; 282 | } 283 | 284 | .namespace-index .topics li { 285 | padding: 5px 0; 286 | } 287 | 288 | .namespace-docs h3 { 289 | font-size: 18px; 290 | font-weight: bold; 291 | } 292 | 293 | .public h3 { 294 | margin: 0; 295 | float: left; 296 | } 297 | 298 | .usage { 299 | clear: both; 300 | } 301 | 302 | .public { 303 | margin: 0; 304 | border-top: 1px solid #e0e0e0; 305 | padding-top: 14px; 306 | padding-bottom: 6px; 307 | } 308 | 309 | .public:last-child { 310 | margin-bottom: 20%; 311 | } 312 | 313 | .members .public:last-child { 314 | margin-bottom: 0; 315 | } 316 | 317 | .members { 318 | margin: 15px 0; 319 | } 320 | 321 | .members h4 { 322 | color: #555; 323 | font-weight: normal; 324 | font-variant: small-caps; 325 | margin: 0 0 5px 0; 326 | } 327 | 328 | .members .inner { 329 | padding-top: 5px; 330 | padding-left: 12px; 331 | margin-top: 2px; 332 | margin-left: 7px; 333 | border-left: 1px solid #bbb; 334 | } 335 | 336 | #content .members .inner h3 { 337 | font-size: 12pt; 338 | } 339 | 340 | .members .public { 341 | border-top: none; 342 | margin-top: 0; 343 | padding-top: 6px; 344 | padding-bottom: 0; 345 | } 346 | 347 | .members .public:first-child { 348 | padding-top: 0; 349 | } 350 | 351 | h4.type, 352 | h4.dynamic, 353 | h4.added, 354 | h4.deprecated { 355 | float: left; 356 | margin: 3px 10px 15px 0; 357 | font-size: 15px; 358 | font-weight: bold; 359 | font-variant: small-caps; 360 | } 361 | 362 | .public h4.type, 363 | .public h4.dynamic, 364 | .public h4.added, 365 | .public h4.deprecated { 366 | font-size: 13px; 367 | font-weight: bold; 368 | margin: 3px 0 0 10px; 369 | } 370 | 371 | .members h4.type, 372 | .members h4.added, 373 | .members h4.deprecated { 374 | margin-top: 1px; 375 | } 376 | 377 | h4.type { 378 | color: #717171; 379 | } 380 | 381 | h4.dynamic { 382 | color: #9933aa; 383 | } 384 | 385 | h4.added { 386 | color: #508820; 387 | } 388 | 389 | h4.deprecated { 390 | color: #880000; 391 | } 392 | 393 | .namespace { 394 | margin-bottom: 30px; 395 | } 396 | 397 | .namespace:last-child { 398 | margin-bottom: 10%; 399 | } 400 | 401 | .index { 402 | padding: 0; 403 | font-size: 80%; 404 | margin: 15px 0; 405 | line-height: 1.6em; 406 | } 407 | 408 | .index * { 409 | display: inline; 410 | } 411 | 412 | .index p { 413 | padding-right: 3px; 414 | } 415 | 416 | .index li { 417 | padding-right: 5px; 418 | } 419 | 420 | .index ul { 421 | padding-left: 0; 422 | } 423 | 424 | .type-sig { 425 | clear: both; 426 | color: #088; 427 | } 428 | 429 | .type-sig pre { 430 | padding-top: 10px; 431 | margin: 0; 432 | } 433 | 434 | .usage code { 435 | display: block; 436 | color: #008; 437 | margin: 2px 0; 438 | } 439 | 440 | .usage code:first-child { 441 | padding-top: 10px; 442 | } 443 | 444 | p { 445 | margin: 15px 0; 446 | } 447 | 448 | .public p:first-child, .public pre.plaintext { 449 | margin-top: 12px; 450 | } 451 | 452 | .doc { 453 | margin: 0 0 26px 0; 454 | clear: both; 455 | } 456 | 457 | .public .doc { 458 | margin: 0; 459 | } 460 | 461 | .namespace-index { 462 | font-size: 120%; 463 | } 464 | 465 | .namespace-index .doc { 466 | margin-bottom: 20px; 467 | } 468 | 469 | .namespace-index .namespace .doc { 470 | margin-bottom: 10px; 471 | } 472 | 473 | .markdown p, .markdown li, .markdown dt, .markdown dd, .markdown td { 474 | line-height: 1.6em; 475 | } 476 | 477 | .markdown h2 { 478 | font-weight: normal; 479 | font-size: 25px; 480 | } 481 | 482 | #content .markdown h3 { 483 | font-size: 20px; 484 | } 485 | 486 | .markdown h4 { 487 | font-size: 15px; 488 | } 489 | 490 | .doc, .public, .namespace .index { 491 | max-width: 680px; 492 | overflow-x: visible; 493 | } 494 | 495 | .markdown pre > code { 496 | display: block; 497 | padding: 10px; 498 | } 499 | 500 | .markdown pre > code, .src-link a { 501 | border: 1px solid #e4e4e4; 502 | border-radius: 2px; 503 | } 504 | 505 | .src-link a { 506 | background: #f6f6f6; 507 | } 508 | 509 | .markdown code:not(.hljs) { 510 | color: #c7254e; 511 | background-color: #f9f2f4; 512 | border-radius: 4px; 513 | font-size: 90%; 514 | padding: 2px 4px; 515 | } 516 | 517 | pre.deps { 518 | display: inline-block; 519 | margin: 0 10px; 520 | border: 1px solid #e4e4e4; 521 | border-radius: 2px; 522 | padding: 10px; 523 | background-color: #f6f6f6; 524 | } 525 | 526 | .markdown hr { 527 | border-style: solid; 528 | border-top: none; 529 | color: #ccc; 530 | } 531 | 532 | .doc ul, .doc ol { 533 | padding-left: 30px; 534 | } 535 | 536 | .doc table { 537 | border-collapse: collapse; 538 | margin: 0 10px; 539 | } 540 | 541 | .doc table td, .doc table th { 542 | border: 1px solid #dddddd; 543 | padding: 4px 6px; 544 | } 545 | 546 | .doc table th { 547 | background: #f2f2f2; 548 | } 549 | 550 | .doc dl { 551 | margin: 0 10px 20px 10px; 552 | } 553 | 554 | .doc dl dt { 555 | font-weight: bold; 556 | margin: 0; 557 | padding: 3px 0; 558 | border-bottom: 1px solid #ddd; 559 | } 560 | 561 | .doc dl dd { 562 | padding: 5px 0; 563 | margin: 0 0 5px 10px; 564 | } 565 | 566 | .doc abbr { 567 | border-bottom: 1px dotted #333; 568 | font-variant: none; 569 | cursor: help; 570 | } 571 | 572 | .src-link { 573 | margin-bottom: 15px; 574 | } 575 | 576 | .src-link a { 577 | font-size: 70%; 578 | padding: 1px 4px; 579 | text-decoration: none; 580 | color: #5555bb; 581 | background-color: #f6f6f6; 582 | } 583 | 584 | blockquote { 585 | opacity: 0.6; 586 | border-left: solid 2px #ddd; 587 | margin-left: 0; 588 | padding-left: 1em; 589 | } 590 | 591 | /* Responsiveness Theme */ 592 | 593 | @media (max-device-width: 480px) { 594 | .sidebar { 595 | display:none; 596 | } 597 | 598 | #content { 599 | position: relative; 600 | left: initial !important; 601 | top: 110px; 602 | padding: 0 1em; 603 | } 604 | 605 | #header { 606 | display: flex; 607 | flex-direction: column-reverse; 608 | height: 100px; 609 | } 610 | 611 | #header > h1 { 612 | font-size: 52px; 613 | } 614 | 615 | #header h2 { 616 | float: none; 617 | font-size: 20px; 618 | } 619 | 620 | .namespace-index > h1 { 621 | display: none; 622 | } 623 | 624 | .public, .doc, .namespace > .index, .namespace > .doc, .namespace > h3 { 625 | max-width: initial; 626 | } 627 | 628 | .doc { 629 | text-align: justify; 630 | } 631 | 632 | .public { 633 | padding-top: 2em; 634 | padding-bottom: 2em; 635 | } 636 | 637 | .public > h3 { 638 | font-size: 300%; 639 | } 640 | 641 | .public > h4.type, .public > h4.added, .public > h4.deprecated { 642 | font-size: 150%; 643 | margin-top: 1em; 644 | } 645 | 646 | pre > code { 647 | font-size: 200%; 648 | } 649 | } 650 | -------------------------------------------------------------------------------- /docs/gc.html: -------------------------------------------------------------------------------- 1 | 3 | JVM/Julia Garbage Collection Integration

JVM/Julia Garbage Collection Integration

4 |

Both the JVM and julia rely on garbage collection in order to decided 5 | when and how to cleanup objects. This means that we need a mechanism 6 | to ensure that objects visible across language boundaries are not cleaned 7 | up prematurely.

8 |

Julia Objects in the JVM

9 |

When a new julia object is returned from a function we first check 10 | if this is a primitive or atomic type - numbers, symbols, strings. 11 | If so, we convert the value into the JVM immediately. Else we 12 | return a JVM object and root the julia object in a datastructure 13 | we declared to julia.

14 |

The user has a choice to link the Julia object to the JVM's GC mechanism 15 | such that when the JVM decided the object is no longer reachable we will 16 | then unroot the Julia object. This is default and requires the user 17 | to periodically call 18 | cycle-gc! 19 | in order to unroot objects as the JVM's callback happens in another thread and 20 | thus we can only mark objects that should be unrooted automatically. The 21 | gc based unrooting needs to be cooperative at this point to ensure it happens 22 | in whichever thread is currently using Julia.

23 |

There is also a stack based mechanism, 24 | with-stack-context 25 | by which we can ensure that Julia objects rooted within a given stack 26 | scope are unrooted when programmatic flow exits that scope either 27 | normally or via an exception.

28 |

JVM Objects in Julia

29 |
    30 |
  • TODO - not sure if this is very necessary or the best way to handle this. 31 | Currently you can pass functions to Julia but this isn't very fleshed 32 | out and it is most likely not bullet-proof. If they are called from tasks 33 | then there is a decent chance they will be silently ignored. Most likely 34 | you, from the JVM side, will need to ensure they do not leave JVM scope 35 | while you think they are in scope in Julia.
  • 36 |
37 |
-------------------------------------------------------------------------------- /docs/highlight/highlight.min.js: -------------------------------------------------------------------------------- 1 | /*! highlight.js v9.6.0 | BSD3 License | git.io/hljslicense */ 2 | !function(e){var n="object"==typeof window&&window||"object"==typeof self&&self;"undefined"!=typeof exports?e(exports):n&&(n.hljs=e({}),"function"==typeof define&&define.amd&&define([],function(){return n.hljs}))}(function(e){function n(e){return e.replace(/[&<>]/gm,function(e){return I[e]})}function t(e){return e.nodeName.toLowerCase()}function r(e,n){var t=e&&e.exec(n);return t&&0===t.index}function a(e){return k.test(e)}function i(e){var n,t,r,i,o=e.className+" ";if(o+=e.parentNode?e.parentNode.className:"",t=B.exec(o))return R(t[1])?t[1]:"no-highlight";for(o=o.split(/\s+/),n=0,r=o.length;r>n;n++)if(i=o[n],a(i)||R(i))return i}function o(e,n){var t,r={};for(t in e)r[t]=e[t];if(n)for(t in n)r[t]=n[t];return r}function u(e){var n=[];return function r(e,a){for(var i=e.firstChild;i;i=i.nextSibling)3===i.nodeType?a+=i.nodeValue.length:1===i.nodeType&&(n.push({event:"start",offset:a,node:i}),a=r(i,a),t(i).match(/br|hr|img|input/)||n.push({event:"stop",offset:a,node:i}));return a}(e,0),n}function c(e,r,a){function i(){return e.length&&r.length?e[0].offset!==r[0].offset?e[0].offset"}function u(e){l+=""}function c(e){("start"===e.event?o:u)(e.node)}for(var s=0,l="",f=[];e.length||r.length;){var g=i();if(l+=n(a.substr(s,g[0].offset-s)),s=g[0].offset,g===e){f.reverse().forEach(u);do c(g.splice(0,1)[0]),g=i();while(g===e&&g.length&&g[0].offset===s);f.reverse().forEach(o)}else"start"===g[0].event?f.push(g[0].node):f.pop(),c(g.splice(0,1)[0])}return l+n(a.substr(s))}function s(e){function n(e){return e&&e.source||e}function t(t,r){return new RegExp(n(t),"m"+(e.cI?"i":"")+(r?"g":""))}function r(a,i){if(!a.compiled){if(a.compiled=!0,a.k=a.k||a.bK,a.k){var u={},c=function(n,t){e.cI&&(t=t.toLowerCase()),t.split(" ").forEach(function(e){var t=e.split("|");u[t[0]]=[n,t[1]?Number(t[1]):1]})};"string"==typeof a.k?c("keyword",a.k):E(a.k).forEach(function(e){c(e,a.k[e])}),a.k=u}a.lR=t(a.l||/\w+/,!0),i&&(a.bK&&(a.b="\\b("+a.bK.split(" ").join("|")+")\\b"),a.b||(a.b=/\B|\b/),a.bR=t(a.b),a.e||a.eW||(a.e=/\B|\b/),a.e&&(a.eR=t(a.e)),a.tE=n(a.e)||"",a.eW&&i.tE&&(a.tE+=(a.e?"|":"")+i.tE)),a.i&&(a.iR=t(a.i)),null==a.r&&(a.r=1),a.c||(a.c=[]);var s=[];a.c.forEach(function(e){e.v?e.v.forEach(function(n){s.push(o(e,n))}):s.push("self"===e?a:e)}),a.c=s,a.c.forEach(function(e){r(e,a)}),a.starts&&r(a.starts,i);var l=a.c.map(function(e){return e.bK?"\\.?("+e.b+")\\.?":e.b}).concat([a.tE,a.i]).map(n).filter(Boolean);a.t=l.length?t(l.join("|"),!0):{exec:function(){return null}}}}r(e)}function l(e,t,a,i){function o(e,n){var t,a;for(t=0,a=n.c.length;a>t;t++)if(r(n.c[t].bR,e))return n.c[t]}function u(e,n){if(r(e.eR,n)){for(;e.endsParent&&e.parent;)e=e.parent;return e}return e.eW?u(e.parent,n):void 0}function c(e,n){return!a&&r(n.iR,e)}function g(e,n){var t=N.cI?n[0].toLowerCase():n[0];return e.k.hasOwnProperty(t)&&e.k[t]}function h(e,n,t,r){var a=r?"":y.classPrefix,i='',i+n+o}function p(){var e,t,r,a;if(!E.k)return n(B);for(a="",t=0,E.lR.lastIndex=0,r=E.lR.exec(B);r;)a+=n(B.substr(t,r.index-t)),e=g(E,r),e?(M+=e[1],a+=h(e[0],n(r[0]))):a+=n(r[0]),t=E.lR.lastIndex,r=E.lR.exec(B);return a+n(B.substr(t))}function d(){var e="string"==typeof E.sL;if(e&&!x[E.sL])return n(B);var t=e?l(E.sL,B,!0,L[E.sL]):f(B,E.sL.length?E.sL:void 0);return E.r>0&&(M+=t.r),e&&(L[E.sL]=t.top),h(t.language,t.value,!1,!0)}function b(){k+=null!=E.sL?d():p(),B=""}function v(e){k+=e.cN?h(e.cN,"",!0):"",E=Object.create(e,{parent:{value:E}})}function m(e,n){if(B+=e,null==n)return b(),0;var t=o(n,E);if(t)return t.skip?B+=n:(t.eB&&(B+=n),b(),t.rB||t.eB||(B=n)),v(t,n),t.rB?0:n.length;var r=u(E,n);if(r){var a=E;a.skip?B+=n:(a.rE||a.eE||(B+=n),b(),a.eE&&(B=n));do E.cN&&(k+=C),E.skip||(M+=E.r),E=E.parent;while(E!==r.parent);return r.starts&&v(r.starts,""),a.rE?0:n.length}if(c(n,E))throw new Error('Illegal lexeme "'+n+'" for mode "'+(E.cN||"")+'"');return B+=n,n.length||1}var N=R(e);if(!N)throw new Error('Unknown language: "'+e+'"');s(N);var w,E=i||N,L={},k="";for(w=E;w!==N;w=w.parent)w.cN&&(k=h(w.cN,"",!0)+k);var B="",M=0;try{for(var I,j,O=0;;){if(E.t.lastIndex=O,I=E.t.exec(t),!I)break;j=m(t.substr(O,I.index-O),I[0]),O=I.index+j}for(m(t.substr(O)),w=E;w.parent;w=w.parent)w.cN&&(k+=C);return{r:M,value:k,language:e,top:E}}catch(T){if(T.message&&-1!==T.message.indexOf("Illegal"))return{r:0,value:n(t)};throw T}}function f(e,t){t=t||y.languages||E(x);var r={r:0,value:n(e)},a=r;return t.filter(R).forEach(function(n){var t=l(n,e,!1);t.language=n,t.r>a.r&&(a=t),t.r>r.r&&(a=r,r=t)}),a.language&&(r.second_best=a),r}function g(e){return y.tabReplace||y.useBR?e.replace(M,function(e,n){return y.useBR&&"\n"===e?"
":y.tabReplace?n.replace(/\t/g,y.tabReplace):void 0}):e}function h(e,n,t){var r=n?L[n]:t,a=[e.trim()];return e.match(/\bhljs\b/)||a.push("hljs"),-1===e.indexOf(r)&&a.push(r),a.join(" ").trim()}function p(e){var n,t,r,o,s,p=i(e);a(p)||(y.useBR?(n=document.createElementNS("http://www.w3.org/1999/xhtml","div"),n.innerHTML=e.innerHTML.replace(/\n/g,"").replace(//g,"\n")):n=e,s=n.textContent,r=p?l(p,s,!0):f(s),t=u(n),t.length&&(o=document.createElementNS("http://www.w3.org/1999/xhtml","div"),o.innerHTML=r.value,r.value=c(t,u(o),s)),r.value=g(r.value),e.innerHTML=r.value,e.className=h(e.className,p,r.language),e.result={language:r.language,re:r.r},r.second_best&&(e.second_best={language:r.second_best.language,re:r.second_best.r}))}function d(e){y=o(y,e)}function b(){if(!b.called){b.called=!0;var e=document.querySelectorAll("pre code");w.forEach.call(e,p)}}function v(){addEventListener("DOMContentLoaded",b,!1),addEventListener("load",b,!1)}function m(n,t){var r=x[n]=t(e);r.aliases&&r.aliases.forEach(function(e){L[e]=n})}function N(){return E(x)}function R(e){return e=(e||"").toLowerCase(),x[e]||x[L[e]]}var w=[],E=Object.keys,x={},L={},k=/^(no-?highlight|plain|text)$/i,B=/\blang(?:uage)?-([\w-]+)\b/i,M=/((^(<[^>]+>|\t|)+|(?:\n)))/gm,C="
",y={classPrefix:"hljs-",tabReplace:null,useBR:!1,languages:void 0},I={"&":"&","<":"<",">":">"};return e.highlight=l,e.highlightAuto=f,e.fixMarkup=g,e.highlightBlock=p,e.configure=d,e.initHighlighting=b,e.initHighlightingOnLoad=v,e.registerLanguage=m,e.listLanguages=N,e.getLanguage=R,e.inherit=o,e.IR="[a-zA-Z]\\w*",e.UIR="[a-zA-Z_]\\w*",e.NR="\\b\\d+(\\.\\d+)?",e.CNR="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",e.BNR="\\b(0b[01]+)",e.RSR="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",e.BE={b:"\\\\[\\s\\S]",r:0},e.ASM={cN:"string",b:"'",e:"'",i:"\\n",c:[e.BE]},e.QSM={cN:"string",b:'"',e:'"',i:"\\n",c:[e.BE]},e.PWM={b:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|like)\b/},e.C=function(n,t,r){var a=e.inherit({cN:"comment",b:n,e:t,c:[]},r||{});return a.c.push(e.PWM),a.c.push({cN:"doctag",b:"(?:TODO|FIXME|NOTE|BUG|XXX):",r:0}),a},e.CLCM=e.C("//","$"),e.CBCM=e.C("/\\*","\\*/"),e.HCM=e.C("#","$"),e.NM={cN:"number",b:e.NR,r:0},e.CNM={cN:"number",b:e.CNR,r:0},e.BNM={cN:"number",b:e.BNR,r:0},e.CSSNM={cN:"number",b:e.NR+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",r:0},e.RM={cN:"regexp",b:/\//,e:/\/[gimuy]*/,i:/\n/,c:[e.BE,{b:/\[/,e:/\]/,r:0,c:[e.BE]}]},e.TM={cN:"title",b:e.IR,r:0},e.UTM={cN:"title",b:e.UIR,r:0},e.METHOD_GUARD={b:"\\.\\s*"+e.UIR,r:0},e});hljs.registerLanguage("clojure",function(e){var t={"builtin-name":"def defonce cond apply if-not if-let if not not= = < > <= >= == + / * - rem quot neg? pos? delay? symbol? keyword? true? false? integer? empty? coll? list? set? ifn? fn? associative? sequential? sorted? counted? reversible? number? decimal? class? distinct? isa? float? rational? reduced? ratio? odd? even? char? seq? vector? string? map? nil? contains? zero? instance? not-every? not-any? libspec? -> ->> .. . inc compare do dotimes mapcat take remove take-while drop letfn drop-last take-last drop-while while intern condp case reduced cycle split-at split-with repeat replicate iterate range merge zipmap declare line-seq sort comparator sort-by dorun doall nthnext nthrest partition eval doseq await await-for let agent atom send send-off release-pending-sends add-watch mapv filterv remove-watch agent-error restart-agent set-error-handler error-handler set-error-mode! error-mode shutdown-agents quote var fn loop recur throw try monitor-enter monitor-exit defmacro defn defn- macroexpand macroexpand-1 for dosync and or when when-not when-let comp juxt partial sequence memoize constantly complement identity assert peek pop doto proxy defstruct first rest cons defprotocol cast coll deftype defrecord last butlast sigs reify second ffirst fnext nfirst nnext defmulti defmethod meta with-meta ns in-ns create-ns import refer keys select-keys vals key val rseq name namespace promise into transient persistent! conj! assoc! dissoc! pop! disj! use class type num float double short byte boolean bigint biginteger bigdec print-method print-dup throw-if printf format load compile get-in update-in pr pr-on newline flush read slurp read-line subvec with-open memfn time re-find re-groups rand-int rand mod locking assert-valid-fdecl alias resolve ref deref refset swap! reset! set-validator! compare-and-set! alter-meta! reset-meta! commute get-validator alter ref-set ref-history-count ref-min-history ref-max-history ensure sync io! new next conj set! to-array future future-call into-array aset gen-class reduce map filter find empty hash-map hash-set sorted-map sorted-map-by sorted-set sorted-set-by vec vector seq flatten reverse assoc dissoc list disj get union difference intersection extend extend-type extend-protocol int nth delay count concat chunk chunk-buffer chunk-append chunk-first chunk-rest max min dec unchecked-inc-int unchecked-inc unchecked-dec-inc unchecked-dec unchecked-negate unchecked-add-int unchecked-add unchecked-subtract-int unchecked-subtract chunk-next chunk-cons chunked-seq? prn vary-meta lazy-seq spread list* str find-keyword keyword symbol gensym force rationalize"},r="a-zA-Z_\\-!.?+*=<>&#'",n="["+r+"]["+r+"0-9/;:]*",a="[-+]?\\d+(\\.\\d+)?",o={b:n,r:0},s={cN:"number",b:a,r:0},i=e.inherit(e.QSM,{i:null}),c=e.C(";","$",{r:0}),d={cN:"literal",b:/\b(true|false|nil)\b/},l={b:"[\\[\\{]",e:"[\\]\\}]"},m={cN:"comment",b:"\\^"+n},p=e.C("\\^\\{","\\}"),u={cN:"symbol",b:"[:]{1,2}"+n},f={b:"\\(",e:"\\)"},h={eW:!0,r:0},y={k:t,l:n,cN:"name",b:n,starts:h},b=[f,i,m,p,c,u,l,s,d,o];return f.c=[e.C("comment",""),y,h],h.c=b,l.c=b,{aliases:["clj"],i:/\S/,c:[f,i,m,p,c,u,l,s,d]}});hljs.registerLanguage("clojure-repl",function(e){return{c:[{cN:"meta",b:/^([\w.-]+|\s*#_)=>/,starts:{e:/$/,sL:"clojure"}}]}}); -------------------------------------------------------------------------------- /docs/highlight/solarized-light.css: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Orginal Style from ethanschoonover.com/solarized (c) Jeremy Hull 4 | 5 | */ 6 | 7 | .hljs { 8 | display: block; 9 | overflow-x: auto; 10 | padding: 0.5em; 11 | background: #fdf6e3; 12 | color: #657b83; 13 | } 14 | 15 | .hljs-comment, 16 | .hljs-quote { 17 | color: #93a1a1; 18 | } 19 | 20 | /* Solarized Green */ 21 | .hljs-keyword, 22 | .hljs-selector-tag, 23 | .hljs-addition { 24 | color: #859900; 25 | } 26 | 27 | /* Solarized Cyan */ 28 | .hljs-number, 29 | .hljs-string, 30 | .hljs-meta .hljs-meta-string, 31 | .hljs-literal, 32 | .hljs-doctag, 33 | .hljs-regexp { 34 | color: #2aa198; 35 | } 36 | 37 | /* Solarized Blue */ 38 | .hljs-title, 39 | .hljs-section, 40 | .hljs-name, 41 | .hljs-selector-id, 42 | .hljs-selector-class { 43 | color: #268bd2; 44 | } 45 | 46 | /* Solarized Yellow */ 47 | .hljs-attribute, 48 | .hljs-attr, 49 | .hljs-variable, 50 | .hljs-template-variable, 51 | .hljs-class .hljs-title, 52 | .hljs-type { 53 | color: #b58900; 54 | } 55 | 56 | /* Solarized Orange */ 57 | .hljs-symbol, 58 | .hljs-bullet, 59 | .hljs-subst, 60 | .hljs-meta, 61 | .hljs-meta .hljs-keyword, 62 | .hljs-selector-attr, 63 | .hljs-selector-pseudo, 64 | .hljs-link { 65 | color: #cb4b16; 66 | } 67 | 68 | /* Solarized Red */ 69 | .hljs-built_in, 70 | .hljs-deletion { 71 | color: #dc322f; 72 | } 73 | 74 | .hljs-formula { 75 | background: #eee8d5; 76 | } 77 | 78 | .hljs-emphasis { 79 | font-style: italic; 80 | } 81 | 82 | .hljs-strong { 83 | font-weight: bold; 84 | } 85 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 3 | 1.000-beta-8

1.000-beta-8

Julia bindings for Clojure and the JVM.

Topics

Namespaces

libjulia-clj.java-api

Java Users - Use the -aot postfixed version of the jar on clojars. Then import 4 | libjulia_clj.java_api. Each method in this namespace is exposed as a public static 5 | method on the java_api class so for instance -initialize is exposed as:

6 |

libjulia-clj.julia

Public API for Julia functionality. Initialize! must be called before any other functions 7 | and there are a choice of garbgage collection mechanisms (see topic in docs). 8 | is probably a bad idea to call Julia from multiple threads so access to julia 9 | should be from one thread or protected via a mutex.

10 |
-------------------------------------------------------------------------------- /docs/js/page_effects.js: -------------------------------------------------------------------------------- 1 | function visibleInParent(element) { 2 | var position = $(element).position().top 3 | return position > -50 && position < ($(element).offsetParent().height() - 50) 4 | } 5 | 6 | function hasFragment(link, fragment) { 7 | return $(link).attr("href").indexOf("#" + fragment) != -1 8 | } 9 | 10 | function findLinkByFragment(elements, fragment) { 11 | return $(elements).filter(function(i, e) { return hasFragment(e, fragment)}).first() 12 | } 13 | 14 | function scrollToCurrentVarLink(elements) { 15 | var elements = $(elements); 16 | var parent = elements.offsetParent(); 17 | 18 | if (elements.length == 0) return; 19 | 20 | var top = elements.first().position().top; 21 | var bottom = elements.last().position().top + elements.last().height(); 22 | 23 | if (top >= 0 && bottom <= parent.height()) return; 24 | 25 | if (top < 0) { 26 | parent.scrollTop(parent.scrollTop() + top); 27 | } 28 | else if (bottom > parent.height()) { 29 | parent.scrollTop(parent.scrollTop() + bottom - parent.height()); 30 | } 31 | } 32 | 33 | function setCurrentVarLink() { 34 | $('.secondary a').parent().removeClass('current') 35 | $('.anchor'). 36 | filter(function(index) { return visibleInParent(this) }). 37 | each(function(index, element) { 38 | findLinkByFragment(".secondary a", element.id). 39 | parent(). 40 | addClass('current') 41 | }); 42 | scrollToCurrentVarLink('.secondary .current'); 43 | } 44 | 45 | var hasStorage = (function() { try { return localStorage.getItem } catch(e) {} }()) 46 | 47 | function scrollPositionId(element) { 48 | var directory = window.location.href.replace(/[^\/]+\.html$/, '') 49 | return 'scroll::' + $(element).attr('id') + '::' + directory 50 | } 51 | 52 | function storeScrollPosition(element) { 53 | if (!hasStorage) return; 54 | localStorage.setItem(scrollPositionId(element) + "::x", $(element).scrollLeft()) 55 | localStorage.setItem(scrollPositionId(element) + "::y", $(element).scrollTop()) 56 | } 57 | 58 | function recallScrollPosition(element) { 59 | if (!hasStorage) return; 60 | $(element).scrollLeft(localStorage.getItem(scrollPositionId(element) + "::x")) 61 | $(element).scrollTop(localStorage.getItem(scrollPositionId(element) + "::y")) 62 | } 63 | 64 | function persistScrollPosition(element) { 65 | recallScrollPosition(element) 66 | $(element).scroll(function() { storeScrollPosition(element) }) 67 | } 68 | 69 | function sidebarContentWidth(element) { 70 | var widths = $(element).find('.inner').map(function() { return $(this).innerWidth() }) 71 | return Math.max.apply(Math, widths) 72 | } 73 | 74 | function calculateSize(width, snap, margin, minimum) { 75 | if (width == 0) { 76 | return 0 77 | } 78 | else { 79 | return Math.max(minimum, (Math.ceil(width / snap) * snap) + (margin * 2)) 80 | } 81 | } 82 | 83 | function resizeSidebars() { 84 | var primaryWidth = sidebarContentWidth('.primary') 85 | var secondaryWidth = 0 86 | 87 | if ($('.secondary').length != 0) { 88 | secondaryWidth = sidebarContentWidth('.secondary') 89 | } 90 | 91 | // snap to grid 92 | primaryWidth = calculateSize(primaryWidth, 32, 13, 160) 93 | secondaryWidth = calculateSize(secondaryWidth, 32, 13, 160) 94 | 95 | $('.primary').css('width', primaryWidth) 96 | $('.secondary').css('width', secondaryWidth).css('left', primaryWidth + 1) 97 | 98 | if (secondaryWidth > 0) { 99 | $('#content').css('left', primaryWidth + secondaryWidth + 2) 100 | } 101 | else { 102 | $('#content').css('left', primaryWidth + 1) 103 | } 104 | } 105 | 106 | $(window).ready(resizeSidebars) 107 | $(window).ready(setCurrentVarLink) 108 | $(window).ready(function() { persistScrollPosition('.primary')}) 109 | $(window).ready(function() { 110 | $('#content').scroll(setCurrentVarLink) 111 | $(window).resize(setCurrentVarLink) 112 | $(window).resize(resizeSidebars) 113 | }) 114 | -------------------------------------------------------------------------------- /docs/libjulia-clj.java-api.html: -------------------------------------------------------------------------------- 1 | 3 | libjulia-clj.java-api documentation

libjulia-clj.java-api

Java Users - Use the -aot postfixed version of the jar on clojars. Then import 4 | libjulia_clj.java_api. Each method in this namespace is exposed as a public static 5 | method on the java_api class so for instance -initialize is exposed as:

6 |
  java_api.initialize(options);
 7 | 
8 |

-arrayToJVM

(-arrayToJVM jlary)

Returns a map with three keys - shape, datatype, and data. Shape is an integer array 9 | that is the reverse of the julia shape, datatype is a string denoting one of the supported 10 | datatypes, and data is a primitive array of data.

11 |

-createArray

(-createArray datatype shape data)

Return julia array out of the tuple of datatype, shape, and data.

12 |
    13 |
  • datatype - must be one of the strings `"int8" "uint8" "int16" "uin16" 15 | "int32" "uint32" "int64" "uint64" "float32" "float64".
  • 16 |
  • shape - an array or implementation of java.util.List that specifies the row-major 17 | shape intended of the data. Note that Julia is column-major so this data will appear 18 | transposed when printed via Julia.
  • 19 |
  • data may be a java array or an implementation of java.util.List. Ideally data is 20 | of the same datatype as data.
  • 21 |
22 |

-initialize

(-initialize options)

Initialize the julia interpreter. See documentation for libjulia-clj.julia/initialize!. 23 | Options may be null or must be a map of string->value for one of the supported initialization 24 | values. JULIA_HOME can be set by the user by setting the key "julia-home" in the 25 | options map to the desired value and this will supercede the environment variable 26 | version.

27 |

Example:

28 |
  (japi/-initialize (jvm-map/hash-map {"n-threads" 8}))
29 | 
30 |

-inJlContext

(-inJlContext fn)

Execute a function in a context where all julia objects created will be released 31 | just after the function returns. The function must return pure JVM data - it cannot 32 | return a reference to a julia object.

33 |

-namedTuple

(-namedTuple data)

Create a julia named tuple. This is required for calling keyword functions. The 34 | path for calling keyword functions looks something like:

35 |
    36 |
  • data - must be an implementation of java.util.Map with strings as keys.
  • 37 |
38 |
(let [add-fn (jl "function teste(a;c = 1.0, b = 2.0)
39 |     a+b+c
40 | end")
41 |           kwfunc (jl "Core.kwfunc")
42 |           add-kwf (kwfunc add-fn)]
43 |       (is (= 38.0 (add-kwf (jl/named-tuple {'b 10 'c 20})
44 |                            add-fn
45 |                            8.0)))
46 |       (is (= 19.0 (add-kwf (jl/named-tuple {'b 10})
47 |                            add-fn
48 |                            8.0)))
49 |       (is (= 11.0 (add-kwf (jl/named-tuple)
50 |                            add-fn
51 |                            8.0)))
52 | 
53 |       (is (= 38.0 (add-fn 8.0 :b 10 :c 20)))
54 |       (is (= 19.0 (add-fn 8 :b 10)))
55 |       (is (= 11.0 (add-fn 8))))
56 | 
57 |

-runString

(-runString data)

Run a string in Julia returning a jvm object if the return value is simple or 58 | a julia object if not. The returned object will have a property overloaded 59 | toString method for introspection.

60 |

requires*

-------------------------------------------------------------------------------- /docs/libjulia-clj.julia.html: -------------------------------------------------------------------------------- 1 | 3 | libjulia-clj.julia documentation

libjulia-clj.julia

Public API for Julia functionality. Initialize! must be called before any other functions 4 | and there are a choice of garbgage collection mechanisms (see topic in docs). 5 | is probably a bad idea to call Julia from multiple threads so access to julia 6 | should be from one thread or protected via a mutex.

7 |

Example:

8 |
user> (require '[libjulia-clj.julia :as julia])
  9 | nil
 10 | user> (julia/initialize!)
 11 | :ok
 12 | user> (def ones-fn (julia/jl "Base.ones"))
 13 | Nov 27, 2020 12:39:39 PM clojure.tools.logging$eval6611$fn__6614 invoke
 14 | INFO: Rooting address  0x00007F3E092D6E40
 15 | #'user/ones-fn
 16 | user> (def jl-ary (ones-fn 3 4))
 17 | 
 18 | Nov 27, 2020 12:40:02 PM clojure.tools.logging$eval6611$fn__6614 invoke
 19 | INFO: Rooting address  0x00007F3DFF9C92A0
 20 | #'user/jl-ary
 21 | user> jl-ary
 22 | [1.0 1.0 1.0 1.0; 1.0 1.0 1.0 1.0; 1.0 1.0 1.0 1.0]
 23 | user> (type jl-ary)
 24 | libjulia_clj.impl.base.JuliaArray
 25 | user> (require '[tech.v3.tensor :as dtt])
 26 | nil
 27 | user> ;;zero-copy
 28 | user> (def tens (dtt/as-tensor jl-ary))
 29 | #'user/tens
 30 | user> tens
 31 | #tech.v3.tensor<float64>[3 4]
 32 | [[1.000 1.000 1.000 1.000]
 33 |  [1.000 1.000 1.000 1.000]
 34 |  [1.000 1.000 1.000 1.000]]
 35 | user> (dtt/mset! tens 0 25)
 36 | #tech.v3.tensor<float64>[3 4]
 37 | [[25.00 25.00 25.00 25.00]
 38 |  [1.000 1.000 1.000 1.000]
 39 |  [1.000 1.000 1.000 1.000]]
 40 | user> jl-ary
 41 | [25.0 25.0 25.0 25.0; 1.0 1.0 1.0 1.0; 1.0 1.0 1.0 1.0]
 42 | 
43 |

->array

(->array tens)

Create a new dense julia array that is the 'transpose' of the input tensor. 44 | Transposing ensures the memory alignment matches as Julia is column-major 45 | while datatype is row-major.

46 |

apply-tuple-type

(apply-tuple-type & args)

Create a new Julia tuple type from a sequence of types.

47 |

apply-type

(apply-type jl-type & args)

Create a new Julia type from an existing type and a sequence of other 48 | types.

49 |

base-ary-type*

Resolves to the base julia array datatype

50 |

call-function

(call-function fn-handle args options)(call-function fn-handle args)

Call a function. The result will be marshalled back to the jvm and if necessary, 51 | rooted. This method is normally not necessary but useful if you would like to 52 | use keywords in your argument list or specify to avoid rooting the result.

53 |

call-function-kw

(call-function-kw fn-handle args kw-args options)(call-function-kw fn-handle args kw-args)(call-function-kw fn-handle args)

Call a julia function and pass in keyword arguments. This is useful if you 54 | would like to specify the arglist and kw-argmap. Normally this is done for 55 | you.

56 |

cycle-gc!

(cycle-gc!)

Call periodically to release rooted Julia objects. We root return values if they 57 | aren't primitives and then hook them to the jvm GC such that they get put in a queue 58 | once they aren't reachable by the program. This call clears that queue and unroots 59 | them thus notifying the Julia GC that they aren't reachable any more.

60 |

In the future point this may be done for you.

61 |

initialize!

(initialize! {:keys [julia-library-path], :as options})(initialize!)

Initialize julia optionally providing an explicit path which will override 62 | the julia library search mechanism.

63 |

Currently the search mechanism is:

64 |

user-path->JULIA_HOME->"julia"

65 |

Returns :ok on success else exception.

66 |

Options:

67 |
    68 |
  • :julia-home - Explicitly declare the env var julia-home.
  • 69 |
  • :n-threads - Set to -1 to set to n-cpus. Defaults to nil which means single threaded 70 | unless the JULIA_NUM_THREADS environment variable is set. Note that this has implications 71 | for application stability - see the signals.md topic.
  • 72 |
  • :signals-enabled? - Users do not usually need to set this. This allows users to disable 73 | all of Julia's signal handling most likely leading to a crash. See the signals.md topic.
  • 74 |
75 |

jl

(jl str-data options)(jl str-data)

Eval a string in julia returning the result. If the result is callable in Julia, 76 | the result will be callable in Clojure.

77 |

jl-undef*

Resolves to the julia undef type

78 |

named-tuple

(named-tuple value-map)(named-tuple)

Create a julia named tuple from a map of values. The keys of the map must be 79 | keywords or symbols. A new named tuple type will be created and instantiated.

80 |

new-array

(new-array shape datatype)(new-array shape)

Create a new, uninitialized dense julia array. Because Julia is column major 81 | while tech.v3.datatype is row major, the returned array's size will be the 82 | reverse of dtype/shape as that keeps the same in memory alignment of data.

83 |

set-julia-gc-root-log-level!

(set-julia-gc-root-log-level! log-level)

Set the log level to use when rooting/unrooting julia objects. We automatically 84 | root julia objects in a julia id dictionary that we expose to JVM objects. 85 | This code isn't yet perfect, so sometimes it is worthwhile to log all 86 | root/unroot operations.

87 |

Valid log levels are valid log levels for 88 | clojure/tools.logging.

89 |

struct

(struct struct-type & args)

Instantiate a Julia struct type (tuple, NamedTuple, etc). Use this after 90 | apply-tuple-type in order to create an instance of your new type.

91 |

tuple

(tuple & args)

Create a julia tuple from a set of arguments. The tuple type will be the 92 | same datatype as the argument types.

93 |

This function converts the arguments to julia, calls apply-tuple-type 94 | on the resulting argument types, and then instantiates an instance of 95 | the given newly created tuple type.

96 |

typeof

(typeof item)

Get the julia type of an item.

97 |

with-stack-context

macro

(with-stack-context & body)

Run code in which all objects created within this context will be released once 98 | the stack unwinds where released means unrooted and thus potentially available to 99 | the next julia garbage collection run.

100 |
-------------------------------------------------------------------------------- /docs/signals.html: -------------------------------------------------------------------------------- 1 | 3 | Julia, The JVM, and Signals

Julia, The JVM, and Signals

4 |

Julia and the JVM both rely on an operating concept called signals 5 | which are a simple method of IPC. If you aren't familiar with them 6 | it probably isn't necessary to get familiar right now but it is necessary 7 | in order to use libjulia-clj for you to understand how the signal mechanism 8 | in these two systems interact and what happens when they interact poorly.

9 |

Two Worlds Collide

10 |

When using both Julia and the JVM in the same process you are likely to run into 11 | instances where, during their normal course of operation their respective usage of 12 | signals conflict. For instance, the JVM uses SIGSEGV during it's normal course of 13 | operation and if the Julia handler for SIGSEGV is installed then things like a 14 | normal JVM garbage collection run can cause the process to unceremoniously exit:

15 |
user> (require '[libjulia-clj.julia :as julia])
16 | nil
17 | user> (julia/initialize! {:signals-enabled? true})
18 | 16:14:40.838 [nRepl-session-926e4a65-853b-40b4-a182-0f4b8a0cdfa3] INFO libjulia-clj.impl.base - Attempting to initialize Julia at /home/chrisn/dev/cnuernber/libjulia-clj/julia-1.5.3/lib/libjulia.so
19 | 16:14:40.875 [nRepl-session-926e4a65-853b-40b4-a182-0f4b8a0cdfa3] INFO tech.v3.jna.base - Library /home/chrisn/dev/cnuernber/libjulia-clj/julia-1.5.3/lib/libjulia.so found at [:system "/home/chrisn/dev/cnuernber/libjulia-clj/julia-1.5.3/lib/libjulia.so"]
20 | 16:14:40.881 [nRepl-session-926e4a65-853b-40b4-a182-0f4b8a0cdfa3] INFO libjulia-clj.impl.jna - Julia startup options: n-threads null, signals? true
21 | :ok
22 | user> (System/gc)
23 | 
24 | *** Closed on Mon Dec 14 16:14:45 2020 ***
25 | 
26 |

Julia has an option to disable its use of signals but this results in a crash as it 27 | requires the use of at least SIGINT in order to manage garbage collection in 28 | multithreaded code.

29 |

If we simply disable Julia's use of signals then single-threaded code works fine. 30 | Multithreaded code, however, will eventually crash without warning:

31 |
user> (require '[libjulia-clj.julia :as julia])
32 | nil
33 | user> (julia/initialize! {:n-threads 8 :signals-enabled? false})
34 | 16:28:10.854 [nRepl-session-a6713b61-bf94-4492-bcbb-7cc7e44c2a4f] INFO libjulia-clj.impl.base - Attempting to initialize Julia at /home/chrisn/dev/cnuernber/libjulia-clj/julia-1.5.3/lib/libjulia.so
35 | 16:28:10.908 [nRepl-session-a6713b61-bf94-4492-bcbb-7cc7e44c2a4f] INFO tech.v3.jna.base - Library /home/chrisn/dev/cnuernber/libjulia-clj/julia-1.5.3/lib/libjulia.so found at [:system "/home/chrisn/dev/cnuernber/libjulia-clj/julia-1.5.3/lib/libjulia.so"]
36 | 16:28:10.925 [nRepl-session-a6713b61-bf94-4492-bcbb-7cc7e44c2a4f] INFO libjulia-clj.impl.jna - Julia startup options: n-threads 8, signals? false
37 | :ok
38 | user> (System/gc)
39 | nil
40 | user> (julia/eval-string "Threads.@threads for i in 1:1000; zeros(1024, 1024) .+ zeros(1024, 1024); end")
41 | 
42 | *** Closed on Mon Dec 14 16:28:25 2020 ***
43 | 
44 |

The Work-Around For Now

45 |

The JVM has a facility for signal chaining. This allows us to launch the JVM 46 | in a particular way where installs a handler that listens for entities attempting 47 | to install a signal handler and it records these handlers. When a signal occurse, 48 | it can detect whether it came from the JVM or from an outside entity and thus route 49 | the correct signal to the correct handler as required.

50 |

Using this facility is fairly simple, setup an environment variable LD_PRELOAD that 51 | forces the operating system to load a shared library that exports functions that 52 | allow the JVM to chain signals as opposed to simply handling them.

53 |

If we modify our example from before with this pathway we can successfully run 54 | our example:

55 |
chrisn@chrisn-lt-01:~/dev/cnuernber/libjulia-clj$ find /usr -name "*jsig*"
56 | /usr/lib/jvm/java-11-openjdk-amd64/lib/server/libjsig.so
57 | /usr/lib/jvm/java-11-openjdk-amd64/lib/libjsig.so
58 | /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/server/libjsig.so
59 | /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/libjsig.so
60 | chrisn@chrisn-lt-01:~/dev/cnuernber/libjulia-clj$ export LD_PRELOAD=/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/libjsig.so
61 | 
62 |
user> (require '[libjulia-clj.julia :as julia])
63 | nil
64 | user> ;;Signals are automatically enabled if n-threads has a value
65 | user> (julia/initialize! {:n-threads 8})
66 | 16:37:40.695 [nRepl-session-447a06c6-bf23-4338-9618-34e0f841c82b] INFO libjulia-clj.impl.base - Attempting to initialize Julia at /home/chrisn/dev/cnuernber/libjulia-clj/julia-1.5.3/lib/libjulia.so
67 | 16:37:40.741 [nRepl-session-447a06c6-bf23-4338-9618-34e0f841c82b] INFO tech.v3.jna.base - Library /home/chrisn/dev/cnuernber/libjulia-clj/julia-1.5.3/lib/libjulia.so found at [:system "/home/chrisn/dev/cnuernber/libjulia-clj/julia-1.5.3/lib/libjulia.so"]
68 | 16:37:40.747 [nRepl-session-447a06c6-bf23-4338-9618-34e0f841c82b] INFO libjulia-clj.impl.jna - Julia startup options: n-threads 8, signals? true
69 | :ok
70 | user> (julia/eval-string "Threads.@threads for i in 1:1000; zeros(1024, 1024) .+ zeros(1024, 1024); end")
71 | nil
72 | 
73 |

So, for now, we have to setup some nonstandard JVM state in order to use Julia to 74 | it's full potential. Still, it is pretty amazing that the chaining facility exists 75 | and that it works as advertised and at least we have a solid pathway forward in 76 | order to using Julia from the JVM or vice versa.

77 |
78 | -------------------------------------------------------------------------------- /examples/fractal/README.md: -------------------------------------------------------------------------------- 1 | # Simple Julia Example 2 | 3 | Ensure you have julia installed and JULIA_HOME exported to point to your 4 | installation directory. If you are on a linux system, from the top level 5 | run: 6 | 7 | ```console 8 | (base) chrisn@chrisn-lt3:~/dev/cnuernber/libjulia-clj/examples/fractal$ cd ../../ 9 | (base) chrisn@chrisn-lt3:~/dev/cnuernber/libjulia-clj$ source scripts/activate-julia 10 | (base) chrisn@chrisn-lt3:~/dev/cnuernber/libjulia-clj$ cd examples/fractal/ 11 | (base) chrisn@chrisn-lt3:~/dev/cnuernber/libjulia-clj/examples/fractal$ 12 | ``` 13 | 14 | 15 | ```console 16 | 17 | Downloading: com/cnuernber/libjulia-clj/1.000-beta-1/libjulia-clj-1.000-beta-1.pom from clojars 18 | Downloading: com/cnuernber/libjulia-clj/1.000-beta-1/libjulia-clj-1.000-beta-1.jar from clojars 19 | Clojure 1.10.3 20 | (require '[julia-fractals :as jf]) 21 | nil 22 | (jf/jl-fractal) 23 | Oct 25, 2021 12:14:59 PM clojure.tools.logging$eval3221$fn__3224 invoke 24 | INFO: Reference thread starting 25 | Oct 25, 2021 12:14:59 PM clojure.tools.logging$eval3221$fn__3224 invoke 26 | INFO: julia library arguments: ["--handle-signals=no" "--threads" "1"] 27 | #object[java.awt.image.BufferedImage 0x31ee5a9 "BufferedImage@31ee5a9: type = 10 ColorModel: #pixelBits = 8 numComponents = 1 color space = java.awt.color.ICC_ColorSpace@67add4c9 transparency = 1 has alpha = false isAlphaPre = false ByteInterleavedRaster: width = 1920 height = 1080 #numDataElements 1 dataOff[0] = 0"] 28 | (def img *1) 29 | #'user/img 30 | (require '[tech.v3.libs.buffered-image :as bufimg]) 31 | nil 32 | (bufimg/save! img "test.png") 33 | true 34 | ``` 35 | -------------------------------------------------------------------------------- /examples/fractal/deps.edn: -------------------------------------------------------------------------------- 1 | {:paths ["src"] 2 | :deps {com.cnuernber/libjulia-clj {:mvn/version "1.000-beta-3"}}} 3 | -------------------------------------------------------------------------------- /examples/fractal/src/julia_fractals.clj: -------------------------------------------------------------------------------- 1 | (ns julia-fractals 2 | (:require [tech.v3.tensor :as dtt] 3 | [tech.v3.datatype :as dtype] 4 | [tech.v3.libs.buffered-image :as bufimg] 5 | [libjulia-clj.julia :as julia]) 6 | (:import [org.apache.commons.math3.complex Complex])) 7 | 8 | (set! *unchecked-math* :warn-on-boxed) 9 | 10 | 11 | (def fract-width 1920) 12 | (def fract-height 1080) 13 | (def i1 0.31) 14 | (def i2 -0.6) 15 | (def d 11) 16 | (def zoom-factor 0.2) 17 | 18 | 19 | (defn fractal 20 | [] 21 | (let [icomp (Complex. i1 i2) 22 | ;;type out all the variables to avoid boxing 23 | zoom-factor (double zoom-factor) 24 | img-width (double fract-width) 25 | img-height (double fract-height) 26 | d (long d) 27 | hw (* 0.5 (double fract-width)) 28 | hh (* 0.5 (double fract-height)) 29 | zw (* zoom-factor img-width) 30 | zh (* zoom-factor img-height)] 31 | (-> (dtt/typed-compute-tensor 32 | :int64 33 | [img-height img-width] 34 | [y x] 35 | (loop [pos (Complex. (-> (- x hw) (/ zw)) (-> (- y hh) (/ zh))) 36 | c 0] 37 | (if (< c d) 38 | (recur (-> (.multiply pos pos) (.add icomp)) (inc c)) 39 | (let [val (.abs pos)] 40 | (if (and (Double/isFinite val) (< val (dec d))) 255 0))))) 41 | (dtype/copy! (bufimg/new-image img-height img-width :byte-gray))))) 42 | 43 | 44 | (comment 45 | 46 | (-> (fractal) 47 | (bufimg/save! "clj-fract.png")) 48 | 49 | (time (fractal)) 50 | ;;202 ms 51 | ) 52 | 53 | 54 | (def julia-code 55 | "function juliaSet(i1,i2,d,zoomFactor,imgWidth,imgHeight) 56 | # Allocating a widthxheight matrix as our Clojure client is row-major 57 | matrix = Array{UInt8}(undef,imgWidth,imgHeight) 58 | icomp = Complex{Float64}(i1,i2) 59 | Threads.@threads for i in CartesianIndices(matrix) 60 | ## Julia has 1-based indexing... 61 | pos = complex(((i[1]-1) - (0.5 * imgWidth)) / (zoomFactor * imgWidth), 62 | ((i[2]-1) - (0.5 * imgHeight)) / (zoomFactor * imgHeight)) 63 | 64 | for c in (1:d) pos = (pos * pos) + icomp end 65 | absval = abs(pos) 66 | if (absval != NaN && absval < (d-1)) 67 | matrix[i] = 255 68 | else 69 | matrix[i] = 0 70 | end 71 | end 72 | return matrix 73 | end") 74 | 75 | 76 | (def jl-fn* (delay (do (julia/initialize!) 77 | (julia/jl julia-code)))) 78 | 79 | 80 | (defn jl-fractal 81 | [] 82 | (let [jl-fn @jl-fn*] 83 | (-> (jl-fn i1 i2 d zoom-factor fract-width fract-height) 84 | (dtt/ensure-tensor) 85 | (dtype/copy! (bufimg/new-image fract-height fract-width :byte-gray))))) 86 | 87 | 88 | (comment 89 | 90 | (-> (jl-fractal) 91 | (bufimg/save! "jl-fract.png")) 92 | 93 | (time (jl-fractal)) 94 | ;;34 ms 95 | ) 96 | -------------------------------------------------------------------------------- /resources/clj_julia_helper.jl: -------------------------------------------------------------------------------- 1 | module clj_julia_helper 2 | "Ruthlessly stolen from pyjulia" 3 | 4 | 5 | """ 6 | num_utf8_trailing(d::Vector{UInt8}) 7 | 8 | If `d` ends with an incomplete UTF8-encoded character, return the number of trailing incomplete bytes. 9 | Otherwise, return `0`. 10 | 11 | Taken from IJulia.jl. 12 | """ 13 | function num_utf8_trailing(d::Vector{UInt8}) 14 | i = length(d) 15 | # find last non-continuation byte in d: 16 | while i >= 1 && ((d[i] & 0xc0) == 0x80) 17 | i -= 1 18 | end 19 | i < 1 && return 0 20 | c = d[i] 21 | # compute number of expected UTF-8 bytes starting at i: 22 | n = c <= 0x7f ? 1 : c < 0xe0 ? 2 : c < 0xf0 ? 3 : 4 23 | nend = length(d) + 1 - i # num bytes from i to end 24 | return nend == n ? 0 : nend 25 | end 26 | 27 | function pipe_stream(sender::IO, receiver, buf::IO = IOBuffer()) 28 | receiver("begin pipe stream") 29 | try 30 | while !eof(sender) 31 | receiver("in loop") 32 | nb = bytesavailable(sender) 33 | write(buf, read(sender, nb)) 34 | 35 | # Taken from IJulia.send_stream: 36 | d = take!(buf) 37 | n = num_utf8_trailing(d) 38 | dextra = d[end-(n-1):end] 39 | resize!(d, length(d) - n) 40 | s = String(copy(d)) 41 | 42 | write(buf, dextra) 43 | receiver(s) # check isvalid(String, s)? 44 | end 45 | catch e 46 | receiver("error detected") 47 | if !isa(e, InterruptException) 48 | rethrow() 49 | end 50 | pipe_stream(sender, receiver, buf) 51 | end 52 | end 53 | 54 | const read_stdout = Ref{Base.PipeEndpoint}() 55 | const read_stderr = Ref{Base.PipeEndpoint}() 56 | 57 | function pipe_std_outputs(out_receiver, err_receiver) 58 | global readout_task 59 | global readerr_task 60 | global out_rec = out_receiver 61 | global err_rec = err_receiver 62 | read_stdout[], = redirect_stdout() 63 | readout_task = @task pipe_stream(read_stdout[], out_receiver) 64 | read_stderr[], = redirect_stderr() 65 | readerr_task = @task pipe_stream(read_stderr[], err_receiver) 66 | (readout_task,readerr_task) 67 | end 68 | 69 | end 70 | -------------------------------------------------------------------------------- /resources/symbols.txt: -------------------------------------------------------------------------------- 1 | 00000000000435e0 T jl_ 2 | 00000000000fc060 T jl_abs_float 3 | 00000000000fbf40 T jl_abs_float_withtype 4 | 00000000004f2cf8 B jl_abstractarray_type 5 | 00000000004f2d90 B jl_abstractslot_type 6 | 00000000004f2c00 B jl_abstractstring_type 7 | 00000000000fabd0 T jl_add_float 8 | 00000000000fa960 T jl_add_int 9 | 000000000013c550 T jl_add_optimization_passes 10 | 00000000000fa980 T jl_add_ptr 11 | 00000000000ef250 T jl_add_standard_imports 12 | 00000000000fe5c0 T jl_alignment 13 | 00000000000d87b0 T jl_alloc_array_1d 14 | 00000000000d8b50 T jl_alloc_array_2d 15 | 00000000000d8f10 T jl_alloc_array_3d 16 | 00000000000d9550 T jl_alloc_string 17 | 00000000000f8920 T jl_alloc_svec 18 | 00000000000f87f0 T jl_alloc_svec_uninit 19 | 00000000000d9a10 T jl_alloc_vec_any 20 | 00000000000fb890 T jl_and_int 21 | 00000000004f2ab8 B jl_an_empty_string 22 | 00000000004f2ac0 B jl_an_empty_vec_any 23 | 00000000004f2d68 B jl_anytuple_type 24 | 00000000004f2d58 B jl_anytuple_type_type 25 | 00000000004f2dd0 B jl_any_type 26 | 00000000000d9a30 T jl_apply_array_type 27 | 00000000000b7c00 T jl_apply_generic 28 | 00000000000a9a50 T jl_apply_tuple_type 29 | 00000000000ab100 T jl_apply_tuple_type_v 30 | 00000000000ab2a0 T jl_apply_type 31 | 00000000000ab6b0 T jl_apply_type1 32 | 00000000000ab710 T jl_apply_type2 33 | 0000000000041610 T jl_argument_datatype 34 | 00000000004f2b50 B jl_argumenterror_type 35 | 000000000010f660 T jl_argument_method_table 36 | 00000000004f2c20 B jl_array_any_type 37 | 00000000000dbd70 T jl_array_cconvert_cstring 38 | 00000000000dba00 T jl_array_copy 39 | 00000000000dbd40 T jl_array_data_owner 40 | 00000000000db0f0 T jl_array_del_at 41 | 00000000000db520 T jl_array_del_beg 42 | 00000000000db730 T jl_array_del_end 43 | 00000000001106c0 T jl_array_eltype 44 | 00000000000d9e60 T jl_array_grow_at 45 | 00000000000dace0 T jl_array_grow_beg 46 | 00000000000daa30 T jl_array_grow_end 47 | 00000000004f2c10 B jl_array_int32_type 48 | 00000000000d9b90 T jl_array_isassigned 49 | 00000000000fcbc0 T jl_arraylen 50 | 0000000000040f40 T jl_array_ptr 51 | 00000000000dbc80 T jl_array_ptr_1d_append 52 | 00000000000dbbf0 T jl_array_ptr_1d_push 53 | 00000000000dbb00 T jl_array_ptr_copy 54 | 00000000001106e0 T jl_array_rank 55 | 00000000000d9ad0 T jl_arrayref 56 | 00000000000d9bf0 T jl_arrayset 57 | 00000000001106f0 T jl_array_size 58 | 00000000000db7e0 T jl_array_sizehint 59 | 00000000004f2c18 B jl_array_symbol_type 60 | 00000000000d9480 T jl_array_to_string 61 | 00000000004f2c38 B jl_array_type 62 | 00000000004f2c30 B jl_array_typename 63 | 00000000000d7d70 T jl_array_typetagdata 64 | 00000000004f2c28 B jl_array_uint8_type 65 | 00000000000d9e00 T jl_arrayunset 66 | 00000000000fb930 T jl_ashr_int 67 | 00000000001110f0 T jl_astaggedvalue 68 | 00000000000d4e30 T jl_atexit_hook 69 | 00000000000ffba0 T jl_backtrace_from_here 70 | 000000000050cf60 B jl_base_module 71 | 00000000000ef220 T jl_base_relative_to 72 | 00000000000ceb40 T jl_binding_owner 73 | 00000000000cf840 T jl_binding_resolved_p 74 | 00000000000fa270 T jl_bitcast 75 | 00000000004f2ce8 B jl_bool_type 76 | 00000000004f2d00 B jl_bottom_type 77 | 00000000000cf6b0 T jl_boundp 78 | 0000000000040740 T jl_bounds_error 79 | 00000000000408c0 T jl_bounds_error_int 80 | 0000000000040960 T jl_bounds_error_ints 81 | 0000000000040930 T jl_bounds_error_tuple_int 82 | 00000000004f2a90 B jl_boundserror_type 83 | 0000000000040830 T jl_bounds_error_unboxed_int 84 | 00000000000407b0 T jl_bounds_error_v 85 | 00000000000f6a20 T jl_box_bool 86 | 00000000000f6630 T jl_box_char 87 | 00000000000f6250 T jl_box_float32 88 | 00000000000f62e0 T jl_box_float64 89 | 00000000000f6330 T jl_box_int16 90 | 00000000000f6390 T jl_box_int32 91 | 00000000000f6570 T jl_box_int64 92 | 00000000000f6690 T jl_box_int8 93 | 00000000000f6510 T jl_box_slotnumber 94 | 00000000000f64b0 T jl_box_ssavalue 95 | 00000000000f63f0 T jl_box_uint16 96 | 00000000000f6450 T jl_box_uint32 97 | 00000000000f65d0 T jl_box_uint64 98 | 00000000000f66a0 T jl_box_uint8 99 | 00000000000f62a0 T jl_box_voidpointer 100 | 0000000000043680 T jl_breakpoint 101 | 00000000000fb950 T jl_bswap_int 102 | 00000000004f2d10 B jl_builtin_type 103 | 0000000000110710 T jl_call 104 | 00000000001108a0 T jl_call0 105 | 00000000001109c0 T jl_call1 106 | 0000000000110b00 T jl_call2 107 | 0000000000110c50 T jl_call3 108 | 00000000000b9050 T jl_call_in_typeinf_world 109 | 000000000010ac00 T jl_calloc 110 | 00000000000d2f90 T jl_capture_interp_frame 111 | 00000000000fc350 T jl_ceil_llvm 112 | 00000000000fc190 T jl_ceil_llvm_withtype 113 | 00000000000fa660 T jl_cglobal 114 | 00000000000fa920 T jl_cglobal_auto 115 | 00000000004f2ce0 B jl_char_type 116 | 00000000000d04a0 T jl_checked_assignment 117 | 00000000000fbde0 T jl_checked_sadd_int 118 | 00000000000fbea0 T jl_checked_sdiv_int 119 | 00000000000fbe60 T jl_checked_smul_int 120 | 00000000000fbee0 T jl_checked_srem_int 121 | 00000000000fbe20 T jl_checked_ssub_int 122 | 00000000000fbe00 T jl_checked_uadd_int 123 | 00000000000fbec0 T jl_checked_udiv_int 124 | 00000000000fbe80 T jl_checked_umul_int 125 | 00000000000fbf00 T jl_checked_urem_int 126 | 00000000000fbe40 T jl_checked_usub_int 127 | 00000000000d0bd0 T jl_clear_implicit_imports 128 | 0000000000050ce0 T jl_clear_malloc_data 129 | 00000000001be560 T jl_clock_now 130 | 00000000000f21a0 T jl_close_uv 131 | 000000000010f0d0 T jl_code_for_staged 132 | 00000000004f2b68 B jl_code_info_type 133 | 00000000004f2b70 B jl_code_instance_type 134 | 0000000000133890 T jl_compile_extern_c 135 | 00000000000b8a40 T jl_compile_hint 136 | 00000000000e4b90 T jl_compress_argnames 137 | 00000000000e4c60 T jl_compress_ir 138 | 00000000000a84c0 T jl_compute_fieldtypes 139 | 00000000000c1e40 T jl_copy_ast 140 | 000000000010f350 T jl_copy_code_info 141 | 00000000000fc070 T jl_copysign_float 142 | 000000000050cf68 B jl_core_module 143 | 000000000012a360 T jl_cpuid 144 | 000000000012a380 T jl_cpuidex 145 | 0000000000111200 T jl_cpu_pause 146 | 00000000000d4200 T jl_cpu_threads 147 | 0000000000111210 T jl_cpu_wake 148 | 00000000001223a0 i jl_crc32c 149 | 00000000001221d0 T jl_crc32c_sw 150 | 000000000013cff0 T jl_create_native 151 | 00000000000ee270 T jl_create_system_image 152 | 00000000000d99e0 T jl_cstr_to_string 153 | 00000000000fb9b0 T jl_ctlz_int 154 | 00000000000fb980 T jl_ctpop_int 155 | 00000000000fb9e0 T jl_cttz_int 156 | 00000000001103a0 T jl_current_exception 157 | 00000000004f2d20 B jl_datatype_type 158 | 00000000004fade0 B jl_debug_method_invalidation 159 | 00000000000d05f0 T jl_declare_constant 160 | 00000000004a1ea0 D jl_default_cgparams 161 | 00000000004a1ee0 D jl_default_debug_info_kind 162 | 00000000000cf6d0 T jl_defines_or_exports_p 163 | 00000000004f2cf0 B jl_densearray_type 164 | 00000000000cfc40 T jl_deprecate_binding 165 | 00000000004f2aa8 B jl_diverror_exception 166 | 00000000000fae70 T jl_div_float 167 | 00000000000d38e0 T jl_dlclose 168 | 000000000050cfb0 B jl_dl_handle 169 | 00000000000d3540 T jl_dlopen 170 | 00000000000d3920 T jl_dlsym 171 | 00000000001306a0 T jl_dump_compiles 172 | 0000000000154260 T jl_dump_fptr_asm 173 | 000000000014b350 T jl_dump_function_ir 174 | 000000000012b350 T jl_dump_host_cpu 175 | 0000000000140d80 T jl_dump_llvm_asm 176 | 0000000000135e90 T jl_dump_method_asm 177 | 00000000000c8f50 T jl_egal 178 | 0000000000040b40 T jl_eh_restore_state 179 | 00000000004f2c60 B jl_emptysvec 180 | 00000000004f2c68 B jl_emptytuple 181 | 00000000004f2d60 B jl_emptytuple_type 182 | 00000000000feea0 T jl_enqueue_task 183 | 0000000000040ac0 T jl_enter_handler 184 | 00000000000fe530 T jl_enter_threaded_region 185 | 00000000000d4230 T jl_environ 186 | 0000000000040a40 T jl_eof_error 187 | 00000000000fb310 T jl_eq_float 188 | 00000000000fb250 T jl_eq_int 189 | 00000000000cac30 T jl_eqtable_get 190 | 00000000000cacb0 T jl_eqtable_nextind 191 | 00000000000cac60 T jl_eqtable_pop 192 | 00000000000cabb0 T jl_eqtable_put 193 | 00000000000d41d0 T jl_errno 194 | 0000000000040290 T jl_error 195 | 00000000004f2b58 B jl_errorexception_type 196 | 0000000000040430 T jl_errorf 197 | 0000000000110540 T jl_eval_string 198 | 0000000000110400 T jl_exception_clear 199 | 00000000000404d0 T jl_exceptionf 200 | 00000000001103e0 T jl_exception_occurred 201 | 0000000000040ca0 T jl_excstack_state 202 | 000000000050cfa0 B jl_exe_handle 203 | 00000000000f2fc0 T jl_exit 204 | 0000000000111f10 T jl_exit_on_sigint 205 | 00000000000fe540 T jl_exit_threaded_region 206 | 00000000000c4b20 T jl_expand 207 | 000000000010efa0 T jl_expand_and_resolve 208 | 00000000000c5950 T jl_expand_stmt 209 | 00000000000c5250 T jl_expand_stmt_with_loc 210 | 00000000000c4420 T jl_expand_with_loc 211 | 00000000000c4b30 T jl_expand_with_loc_warn 212 | 00000000004f2bf0 B jl_expr_type 213 | 0000000000133ad0 T jl_extern_c 214 | 00000000000c6e30 T jl_f__abstracttype 215 | 00000000004f2c48 B jl_false 216 | 00000000000c73d0 T jl_f_applicable 217 | 00000000000c8940 T jl_f__apply 218 | 00000000000c8910 T jl_f__apply_iterate 219 | 00000000000c8970 T jl_f__apply_latest 220 | 00000000000c89c0 T jl_f__apply_pure 221 | 00000000000c77c0 T jl_f_apply_type 222 | 00000000000c6b40 T jl_f_arrayref 223 | 00000000000c6c00 T jl_f_arrayset 224 | 00000000000c5ea0 T jl_f_arraysize 225 | 00000000000cafb0 T jl_f_const_arrayref 226 | 00000000000c9d10 T jl_f__equiv_typedef 227 | 00000000000c74b0 T jl_f__expr 228 | 00000000000c7350 T jl_f_fieldtype 229 | 00000000000c5fa0 T jl_f_getfield 230 | 00000000000f77d0 T jl_field_index 231 | 00000000000f8460 T jl_field_isdefined 232 | 00000000000c71c0 T jl_f_ifelse 233 | 00000000004f1f78 D jl_filename 234 | 000000000010b460 T jl_finalize 235 | 0000000000103690 T jl_finalize_th 236 | 00000000000a5f80 T jl_find_free_typevars 237 | 00000000000c7020 T jl_f_intrinsic_call 238 | 00000000000c6800 T jl_f_invoke 239 | 00000000000c8c30 T jl_f_invoke_kwsorter 240 | 0000000000041600 T jl_first_argument_datatype 241 | 00000000000c95e0 T jl_f_is 242 | 00000000000c7240 T jl_f_isa 243 | 00000000000c66f0 T jl_f_isdefined 244 | 00000000000c5bc0 T jl_f_issubtype 245 | 00000000000fbf20 T jl_flipsign_int 246 | 00000000004f2c98 B jl_float16_type 247 | 00000000004f2c90 B jl_float32_type 248 | 00000000004f2c88 B jl_float64_type 249 | 00000000004f2c80 B jl_floatingpoint_type 250 | 00000000000fc530 T jl_floor_llvm 251 | 00000000000fc360 T jl_floor_llvm_withtype 252 | 0000000000041410 T jl_flush_cstdio 253 | 00000000000fb040 T jl_fma_float 254 | 00000000000cfb70 T jl_f_new_module 255 | 00000000000c7170 T jl_f_nfields 256 | 00000000000f23a0 T jl_forceclose_uv 257 | 000000000003e880 T jl_format_filename 258 | 00000000000fbc80 T jl_fpext 259 | 00000000000fb610 T jl_fpiseq 260 | 00000000000fb740 T jl_fpislt 261 | 00000000000c6f00 T jl_f__primitivetype 262 | 00000000000fbae0 T jl_fptosi 263 | 00000000000fbb00 T jl_fptoui 264 | 00000000000ae820 T jl_fptr_args 265 | 00000000000ae810 T jl_fptr_const_return 266 | 00000000000d2aa0 T jl_fptr_interpret_call 267 | 00000000000ae830 T jl_fptr_sparam 268 | 00000000000fbb20 T jl_fptrunc 269 | 000000000010ac40 T jl_free 270 | 000000000010cba0 T jl_free_stack 271 | 00000000000f2790 T jl_fs_chmod 272 | 00000000000f27e0 T jl_fs_chown 273 | 00000000000f2990 T jl_fs_close 274 | 00000000000c6130 T jl_f_setfield 275 | 00000000000c8ab0 T jl_f__setsuper 276 | 00000000000c5960 T jl_f_sizeof 277 | 00000000000f2890 T jl_fs_read 278 | 00000000000f2900 T jl_fs_read_byte 279 | 00000000000f2620 T jl_fs_rename 280 | 00000000000f26a0 T jl_fs_sendfile 281 | 00000000000f2740 T jl_fs_symlink 282 | 00000000000d3bd0 T jl_fstat 283 | 00000000000c6cd0 T jl_f__structtype 284 | 00000000000f25b0 T jl_fs_unlink 285 | 00000000000c7430 T jl_f_svec 286 | 00000000000f2830 T jl_fs_write 287 | 00000000000c7300 T jl_f_throw 288 | 00000000000d39a0 T jl_ftruncate 289 | 00000000000c7610 T jl_f_tuple 290 | 00000000000c5cc0 T jl_f_typeassert 291 | 00000000000c9640 T jl_f__typebody 292 | 00000000000c7130 T jl_f_typeof 293 | 00000000000caf40 T jl_f__typevar 294 | 00000000004f2d18 B jl_function_type 295 | 000000000010b430 T jl_gc_add_finalizer 296 | 0000000000103520 T jl_gc_add_finalizer_th 297 | 00000000001033f0 T jl_gc_add_ptr_finalizer 298 | 000000000010a690 T jl_gc_alloc 299 | 000000000010b4f0 T jl_gc_alloc_0w 300 | 000000000010b520 T jl_gc_alloc_1w 301 | 000000000010b550 T jl_gc_alloc_2w 302 | 000000000010b580 T jl_gc_alloc_3w 303 | 000000000010b4c0 T jl_gc_allocobj 304 | 000000000010b790 T jl_gc_alloc_typed 305 | 0000000000109ea0 T jl_gc_big_alloc 306 | 0000000000109ac0 T jl_gc_collect 307 | 000000000010b610 T jl_gc_conservative_gc_support_enabled 308 | 000000000010aa50 T jl_gc_counted_calloc 309 | 000000000010aad0 T jl_gc_counted_free_with_size 310 | 000000000010a9d0 T jl_gc_counted_malloc 311 | 000000000010ab20 T jl_gc_counted_realloc_with_old_size 312 | 0000000000109a40 T jl_gc_diff_total_bytes 313 | 0000000000109860 T jl_gc_enable 314 | 000000000010b5b0 T jl_gc_enable_conservative_gc_support 315 | 0000000000102e90 T jl_gc_enable_finalizers 316 | 000000000010b780 T jl_gc_external_obj_hdr_size 317 | 000000000010b800 T jl_gc_find_taggedvalue_pool 318 | 0000000000109900 T jl_gc_get_total_bytes 319 | 000000000010b620 T jl_gc_internal_obj_base_ptr 320 | 00000000001098e0 T jl_gc_is_enabled 321 | 0000000000109ab0 T jl_gc_live_bytes 322 | 000000000010acd0 T jl_gc_managed_malloc 323 | 000000000010ad70 T jl_gc_managed_realloc 324 | 0000000000104260 T jl_gc_mark_queue_obj 325 | 00000000001043a0 T jl_gc_mark_queue_objarray 326 | 000000000010b770 T jl_gc_max_internal_obj_size 327 | 000000000010b490 T jl_gc_new_weakref 328 | 000000000010a0f0 T jl_gc_new_weakref_th 329 | 0000000000109990 T jl_gc_num 330 | 0000000000109f90 T jl_gc_pool_alloc 331 | 0000000000103f30 T jl_gc_queue_multiroot 332 | 0000000000103d80 T jl_gc_queue_root 333 | 0000000000111190 T jl_gc_safe_enter 334 | 00000000001111b0 T jl_gc_safe_leave 335 | 00000000001111e0 T jl_gc_safepoint 336 | 000000000010b7a0 T jl_gc_schedule_foreign_sweepfunc 337 | 0000000000102ca0 T jl_gc_set_cb_notify_external_alloc 338 | 0000000000102d90 T jl_gc_set_cb_notify_external_free 339 | 0000000000102bb0 T jl_gc_set_cb_post_gc 340 | 0000000000102ac0 T jl_gc_set_cb_pre_gc 341 | 00000000001028e0 T jl_gc_set_cb_root_scanner 342 | 00000000001029d0 T jl_gc_set_cb_task_scanner 343 | 0000000000109a70 T jl_gc_sync_total_bytes 344 | 0000000000109980 T jl_gc_total_hrtime 345 | 0000000000111140 T jl_gc_unsafe_enter 346 | 0000000000111170 T jl_gc_unsafe_leave 347 | 0000000000100ab0 T jl_gdblookup 348 | 00000000000fd5a0 T jl_generating_output 349 | 000000000010f530 T jl_generic_function_def 350 | 00000000000d33a0 T jl_gensym 351 | 00000000000f3310 T jl_getaddrinfo 352 | 00000000000d4360 T jl_getallocationgranularity 353 | 00000000000d43e0 T jl_get_ARCH 354 | 0000000000100160 T jl_get_backtrace 355 | 00000000000cdd80 T jl_get_binding 356 | 00000000000cddb0 T jl_get_binding_for_method_def 357 | 00000000000d0080 T jl_get_binding_or_error 358 | 00000000000cdbe0 T jl_get_binding_wr 359 | 000000000003e8e0 T jl_get_cfunction_trampoline 360 | 000000000012b4f0 T jl_get_cpu_name 361 | 00000000000d74e0 T jl_get_current_task 362 | 000000000003cbe0 T jl_get_default_sysimg_path 363 | 0000000000100200 T jl_get_excstack 364 | 0000000000111220 T jl_get_fenv_consts 365 | 0000000000110e20 T jl_get_field 366 | 00000000000f8540 T jl_get_field_offset 367 | 0000000000111120 T jl_get_fieldtypes 368 | 000000000013b6f0 T jl_get_function_id 369 | 00000000000d0110 T jl_get_global 370 | 0000000000110fb0 T jl_get_image_file 371 | 00000000000b96b0 T jl_get_invoke_lambda 372 | 000000000003e030 T jl_get_JIT 373 | 0000000000110fa0 T jl_get_julia_bin 374 | 0000000000110f90 T jl_get_julia_bindir 375 | 0000000000040a80 T jl_get_keyword_sorter 376 | 00000000000b9d10 T jl_get_kwsorter 377 | 000000000013b7f0 T jl_get_llvm_context 378 | 000000000013c560 T jl_get_llvmf_defn 379 | 000000000013b7d0 T jl_get_llvm_function 380 | 000000000013b7b0 T jl_get_llvm_module 381 | 0000000000048190 T jl_get_LLVM_VERSION 382 | 00000000000b3b00 T jl_get_method_inferred 383 | 00000000000cf8f0 T jl_get_module_binding 384 | 00000000000cdd90 T jl_get_module_of_binding 385 | 00000000000cdb60 T jl_get_module_optlevel 386 | 00000000000f3370 T jl_getnameinfo 387 | 00000000000d6670 T jl_get_next_task 388 | 00000000000f7670 T jl_get_nth_field 389 | 00000000000f7eb0 T jl_get_nth_field_checked 390 | 00000000000f7e60 T jl_get_nth_field_noalloc 391 | 00000000000d4350 T jl_getpagesize 392 | 00000000000f2fe0 T jl_getpid 393 | 00000000000fe0b0 i jl_get_ptls_states 394 | w jl_get_ptls_states_static 395 | 00000000000d3370 T jl_get_root_symbol 396 | 00000000000a67e0 T jl_get_size 397 | 00000000000b7070 T jl_get_spec_lambda 398 | 00000000000d78c0 T jl_get_task_tid 399 | 00000000001be540 T jl_gettimeofday 400 | 00000000000b23d0 T jl_get_tls_world_age 401 | 00000000000d43d0 T jl_get_UNAME 402 | 00000000000b23c0 T jl_get_world_counter 403 | 000000000012b7a0 T jl_get_zero_subnormals 404 | 00000000000b90a0 T jl_gf_invoke_lookup 405 | 0000000000111010 T jl_git_branch 406 | 0000000000111080 T jl_git_commit 407 | 00000000000f25a0 T jl_global_event_loop 408 | 00000000004f2be8 B jl_globalref_type 409 | 00000000004f2bd8 B jl_gotonode_type 410 | 0000000000120ee0 T jl_has_empty_intersection 411 | 00000000000a5e80 T jl_has_free_typevars 412 | 00000000000f35a0 T jl_has_so_reuseport 413 | 00000000000a6110 T jl_has_typevar 414 | 00000000000a6400 T jl_has_typevar_from_unionall 415 | 00000000000d4220 T jl_hrtime 416 | 00000000001bd220 T jl_id_char 417 | 00000000001bcfa0 T jl_id_start_char 418 | 00000000000ca7d0 T jl_idtable_rehash 419 | 00000000004fc008 B jl_incomplete_sym 420 | 00000000000f15d0 T jl_infer_thunk 421 | 00000000004f2b30 B jl_initerror_type 422 | 00000000000e4a40 T jl_init_restored_modules 423 | 00000000001104a0 T jl_init__threading 424 | 0000000000110420 T jl_init_with_image__threading 425 | 00000000001124f0 T jl_install_sigint_handler 426 | 00000000000aade0 T jl_instantiate_type_in_env 427 | 00000000000aa690 T jl_instantiate_unionall 428 | 00000000004f2cc8 B jl_int16_type 429 | 00000000004f2cb8 B jl_int32_type 430 | 00000000004f2ca8 B jl_int64_type 431 | 00000000004f2cd8 B jl_int8_type 432 | 00000000004f2a98 B jl_interrupt_exception 433 | 0000000000120ed0 T jl_intersect_types 434 | 00000000000fe520 T jl_in_threaded_region 435 | 00000000000cb0d0 T jl_intrinsic_name 436 | 00000000004f2ba0 B jl_intrinsic_type 437 | 00000000000b74d0 T jl_invoke 438 | 00000000000b6bc0 T jl_invoke_api 439 | 00000000000f1d30 T jl_iolock_begin 440 | 00000000000f1d40 T jl_iolock_end 441 | 00000000000d4060 T jl_ios_buffer_n 442 | 00000000000d39f0 T jl_ios_fd 443 | 00000000000d40c0 T jl_ios_get_nbyte_int 444 | 00000000000e4b00 T jl_ir_flag_inferred 445 | 00000000000e4b30 T jl_ir_flag_inlineable 446 | 00000000000e4b60 T jl_ir_flag_pure 447 | 00000000000e51f0 T jl_ir_nslots 448 | 00000000000e5220 T jl_ir_slotflag 449 | 00000000001208d0 T jl_isa 450 | 00000000000b4120 T jl_isa_compileable_sig 451 | 00000000000cfc70 T jl_is_binding_deprecated 452 | 00000000000d4340 T jl_is_char_signed 453 | 00000000000cfc20 T jl_is_const 454 | 0000000000110f70 T jl_is_debugbuild 455 | 00000000000414e0 T jl_is_identifier 456 | 00000000000cef30 T jl_is_imported 457 | 0000000000110270 T jl_is_initialized 458 | 00000000000b23e0 T jl_is_in_pure_context 459 | 00000000000f43a0 T jl_islayout_inline 460 | 0000000000110f80 T jl_is_memdebug 461 | 00000000001206b0 T jl_is_not_broken_subtype 462 | 00000000000c3420 T jl_is_operator 463 | 00000000000d78b0 T jl_is_task_started 464 | 00000000000cdbd0 T jl_istopmod 465 | 00000000000c3540 T jl_is_unary_and_binary_operator 466 | 00000000000c34b0 T jl_is_unary_operator 467 | 00000000000fb560 T jl_le_float 468 | 00000000004f2b20 B jl_lineinfonode_type 469 | 000000000050f650 B jl_lineno 470 | 00000000004f2be0 B jl_linenumbernode_type 471 | 00000000000c1780 T jl_lisp_prompt 472 | 00000000004f21a8 B jl_LLVMContext 473 | 000000000014cde0 T jl_LLVMCreateDisasm 474 | 000000000014cdf0 T jl_LLVMDisasmInstruction 475 | 0000000000127fd0 T jl_LLVMFlipSign 476 | 00000000004f2b00 B jl_llvmpointer_type 477 | 00000000004f2af8 B jl_llvmpointer_typename 478 | 0000000000127aa0 T jl_LLVMSMod 479 | 00000000000f1700 T jl_load 480 | 00000000000f1710 T jl_load_ 481 | 000000000003f800 T jl_load_and_lookup 482 | 00000000000d35a0 T jl_load_dynamic_library 483 | 00000000004f2b38 B jl_loaderror_type 484 | 00000000000c3400 T jl_load_file_string 485 | 00000000000f1680 T jl_load_rewrite 486 | 00000000000c2d40 T jl_load_rewrite_file_string 487 | 0000000000100400 T jl_lookup_code_address 488 | 00000000000d39b0 T jl_lseek 489 | 00000000000fb910 T jl_lshr_int 490 | 00000000000d3b10 T jl_lstat 491 | 00000000000fb4b0 T jl_lt_float 492 | 00000000000c36e0 T jl_macroexpand 493 | 00000000000c3d80 T jl_macroexpand1 494 | 000000000050cf70 B jl_main_module 495 | 000000000010abc0 T jl_malloc 496 | 000000000010cfd0 T jl_malloc_stack 497 | 00000000000b68c0 T jl_matching_methods 498 | 00000000000d43f0 T jl_maxrss 499 | 00000000004f2a88 B jl_memory_exception 500 | 000000000010f670 T jl_method_def 501 | 00000000004f2b40 B jl_methoderror_type 502 | 00000000000b4a20 T jl_method_instance_add_backedge 503 | 00000000004f2b78 B jl_method_instance_type 504 | 00000000000b4c60 T jl_method_table_add_backedge 505 | 00000000000b4f40 T jl_method_table_disable 506 | 000000000010f650 T jl_method_table_for 507 | 00000000000b51a0 T jl_method_table_insert 508 | 00000000004f2b98 B jl_method_type 509 | 00000000000b3700 T jl_methtable_lookup 510 | 00000000004f2b90 B jl_methtable_type 511 | 00000000000d39d0 T jl_mmap 512 | 00000000000d0b60 T jl_module_build_id 513 | 00000000000cf4e0 T jl_module_export 514 | 00000000000cf790 T jl_module_exports_p 515 | 00000000000ced00 T jl_module_globalref 516 | 00000000000cf0a0 T jl_module_import 517 | 00000000000d0b40 T jl_module_name 518 | 00000000000d0860 T jl_module_names 519 | 00000000000d0b50 T jl_module_parent 520 | 00000000004f2b60 B jl_module_type 521 | 00000000000cf0b0 T jl_module_use 522 | 00000000000cf0c0 T jl_module_using 523 | 00000000000d0620 T jl_module_usings 524 | 00000000000d0b70 T jl_module_uuid 525 | 00000000000fb150 T jl_muladd_float 526 | 00000000000fad90 T jl_mul_float 527 | 00000000000fa9e0 T jl_mul_int 528 | 00000000004f2ad0 B jl_namedtuple_type 529 | 00000000004f2ad8 B jl_namedtuple_typename 530 | 00000000000d4310 T jl_native_alignment 531 | 00000000000d3a00 T jl_nb_available 532 | 00000000000fb3e0 T jl_ne_float 533 | 00000000000fabc0 T jl_neg_float 534 | 00000000000faaa0 T jl_neg_float_withtype 535 | 00000000000fa930 T jl_neg_int 536 | 00000000000fb270 T jl_ne_int 537 | 00000000000d8660 T jl_new_array 538 | 00000000000f73f0 T jl_new_bits 539 | 000000000010e7a0 T jl_new_code_info_uninit 540 | 00000000000f6d90 T jl_new_datatype 541 | 00000000000f72f0 T jl_new_foreign_type 542 | 000000000010e730 T jl_new_method_instance_uninit 543 | 00000000000f4050 T jl_new_method_table 544 | 000000000010f410 T jl_new_method_uninit 545 | 00000000000cfa10 T jl_new_module 546 | 00000000000f7220 T jl_new_primitivetype 547 | 00000000000f6a40 T jl_new_struct 548 | 00000000000f7910 T jl_new_structt 549 | 00000000000f7390 T jl_new_struct_uninit 550 | 00000000000f8290 T jl_new_structv 551 | 00000000000d7250 T jl_new_task 552 | 00000000000f4170 T jl_new_typename_in 553 | 00000000000cad90 T jl_new_typevar 554 | 00000000004f2ba8 B jl_newvarnode_type 555 | 00000000000f3400 T jl_next_from_addrinfo 556 | 00000000000d66a0 T jl_no_exc_handler 557 | 00000000004f2c58 B jl_nothing 558 | 00000000004f2ae8 B jl_nothing_type 559 | 00000000000fba10 T jl_not_int 560 | 0000000000520f38 B jl_n_threads 561 | 00000000004f2c78 B jl_number_type 562 | 00000000000c9d70 T jl_object_id 563 | 00000000000ca4a0 T jl_object_id_ 564 | 00000000001170b0 T jl_obvious_subtype 565 | 00000000000c35d0 T jl_operator_precedence 566 | 00000000001bd7e0 T jl_op_suffix_char 567 | 00000000004a1de0 D jl_options 568 | 00000000000fb8b0 T jl_or_int 569 | 00000000000c19e0 T jl_parse_all 570 | 00000000000c1ac0 T jl_parse_input_line 571 | 000000000003cbf0 T jl_parse_opts 572 | 00000000000c1ad0 T jl_parse_string 573 | 00000000000d4380 T jl_pathname_for_handle 574 | 00000000000d9330 T jl_pchar_to_array 575 | 00000000000d9380 T jl_pchar_to_string 576 | 00000000004f2bc0 B jl_phicnode_type 577 | 00000000004f2bc8 B jl_phinode_type 578 | 00000000004f2bd0 B jl_pinode_type 579 | 00000000000fa380 T jl_pointerref 580 | 00000000000fa4e0 T jl_pointerset 581 | 00000000004f2b10 B jl_pointer_type 582 | 00000000004f2b08 B jl_pointer_typename 583 | 0000000000040c40 T jl_pop_handler 584 | 00000000000ee420 T jl_preload_sysimg_so 585 | 00000000000f1720 T jl_prepend_cwd 586 | 00000000000f2f30 T jl_printf 587 | 00000000000f2020 T jl_process_events 588 | 0000000000112d20 T jl_profile_clear_data 589 | 0000000000112d10 T jl_profile_delay_nsec 590 | 0000000000112ce0 T jl_profile_get_data 591 | 0000000000112c70 T jl_profile_init 592 | 0000000000112d30 T jl_profile_is_running 593 | 0000000000112cf0 T jl_profile_len_data 594 | 0000000000112d00 T jl_profile_maxlen_data 595 | 0000000000111f20 T jl_profile_start_timer 596 | 0000000000112040 T jl_profile_stop_timer 597 | 00000000000d9aa0 T jl_ptrarrayref 598 | 00000000000d8310 T jl_ptr_to_array 599 | 00000000000d8130 T jl_ptr_to_array_1d 600 | 00000000000d39c0 T jl_pwrite 601 | 00000000000f3760 T jl_queue_work 602 | 00000000004f2bb0 B jl_quotenode_type 603 | 00000000000d43c0 T jl_raise_debugger 604 | 00000000004f2a80 B jl_readonlymemory_exception 605 | 00000000000d3e10 T jl_readuntil 606 | 00000000000e3ae0 T jl_read_verify_header 607 | 000000000010ac70 T jl_realloc 608 | 00000000004f2b18 B jl_ref_type 609 | 00000000000b2400 T jl_register_newmeth_tracer 610 | 00000000000faf50 T jl_rem_float 611 | 0000000000112500 T jl_repl_raise_sigtstp 612 | 00000000000d7dd0 T jl_reshape_array 613 | 0000000000040cd0 T jl_restore_excstack 614 | 00000000000e7400 T jl_restore_incremental 615 | 00000000000e73c0 T jl_restore_incremental_from_buf 616 | 00000000000ee510 T jl_restore_system_image 617 | 00000000000ee490 T jl_restore_system_image_data 618 | 00000000000d71a0 T jl_rethrow 619 | 00000000000d7200 T jl_rethrow_other 620 | 00000000000b3760 T jl_rettype_inferred 621 | 00000000000fc870 T jl_rint_llvm 622 | 00000000000fc6e0 T jl_rint_llvm_withtype 623 | 000000000050cfa8 B jl_RTLD_DEFAULT_handle 624 | 00000000000ee1d0 T jl_running_on_valgrind 625 | 00000000000f2a90 T jl_safe_printf 626 | 00000000000e5940 T jl_save_incremental 627 | 00000000000ee2e0 T jl_save_system_image 628 | 00000000000d4370 T jl_SC_CLK_TCK 629 | 00000000000faa00 T jl_sdiv_int 630 | 0000000000110290 T jl_set_ARGS 631 | 00000000000cf990 T jl_set_const 632 | 00000000000d41e0 T jl_set_errno 633 | 00000000000d05c0 T jl_set_global 634 | 00000000000cdbb0 T jl_set_istopmod 635 | 00000000000b3860 T jl_set_method_inferred 636 | 00000000000cdb40 T jl_set_module_nospecialize 637 | 00000000000cdb50 T jl_set_module_optlevel 638 | 00000000000d0b80 T jl_set_module_uuid 639 | 00000000000d6650 T jl_set_next_task 640 | 0000000000040f60 T jl_set_nth_field 641 | 00000000000fe060 T jl_set_ptls_states_getter 642 | 00000000000ee380 T jl_set_sysimg_so 643 | 00000000000fe730 T jl_set_task_tid 644 | 00000000000b8650 T jl_set_typeinf_func 645 | 000000000012b7b0 T jl_set_zero_subnormals 646 | 00000000000fba60 T jl_sext_int 647 | 00000000000fb8f0 T jl_shl_int 648 | 0000000000110f10 T jl_sigatomic_begin 649 | 0000000000110f30 T jl_sigatomic_end 650 | 00000000007cda24 B jl_signal_pending 651 | 00000000004f2c70 B jl_signed_type 652 | 00000000000d71e0 T jl_sig_throw 653 | 00000000004f2d78 B jl_simplevector_type 654 | 00000000000fbaa0 T jl_sitofp 655 | 00000000000d39e0 T jl_sizeof_ios_t 656 | 000000000003dee0 T jl_sizeof_jl_options 657 | 00000000000d3990 T jl_sizeof_mode_t 658 | 00000000000d3980 T jl_sizeof_off_t 659 | 00000000000d3a40 T jl_sizeof_stat 660 | 00000000000d3a10 T jl_sizeof_uv_fs_t 661 | 00000000000fb2d0 T jl_sle_int 662 | 00000000004f2d88 B jl_slotnumber_type 663 | 00000000000fb290 T jl_slt_int 664 | 00000000000faa80 T jl_smod_int 665 | 00000000000f33f0 T jl_sockaddr_from_addrinfo 666 | 00000000000f3430 T jl_sockaddr_host4 667 | 00000000000f3440 T jl_sockaddr_host6 668 | 00000000000f3410 T jl_sockaddr_is_ip4 669 | 00000000000f3420 T jl_sockaddr_is_ip6 670 | 00000000000f3450 T jl_sockaddr_port4 671 | 00000000000f3460 T jl_sockaddr_port6 672 | 00000000000f3470 T jl_sockaddr_set_port 673 | 00000000000f2470 T jl_spawn 674 | 00000000000b2520 T jl_specializations_get_linfo 675 | 00000000000b36d0 T jl_specializations_lookup 676 | 00000000000fca10 T jl_sqrt_llvm 677 | 00000000000fcbb0 T jl_sqrt_llvm_fast 678 | 00000000000fca20 T jl_sqrt_llvm_fast_withtype 679 | 00000000000fc880 T jl_sqrt_llvm_withtype 680 | 00000000000faa40 T jl_srem_int 681 | 00000000004f2d98 B jl_ssavalue_type 682 | 00000000004f2ab0 B jl_stackovf_exception 683 | 00000000000d3a50 T jl_stat 684 | 00000000000d3d10 T jl_stat_blksize 685 | 00000000000d3d20 T jl_stat_blocks 686 | 00000000000d3d60 T jl_stat_ctime 687 | 00000000000d3c90 T jl_stat_dev 688 | 00000000000d3ce0 T jl_stat_gid 689 | 0000000000043590 T jl_static_show 690 | 0000000000041640 T jl_static_show_func_sig 691 | 00000000000d3ca0 T jl_stat_ino 692 | 00000000000d3cb0 T jl_stat_mode 693 | 00000000000d3d30 T jl_stat_mtime 694 | 00000000000d3cc0 T jl_stat_nlink 695 | 00000000000d3cf0 T jl_stat_rdev 696 | 00000000000d3d00 T jl_stat_size 697 | 00000000000d3cd0 T jl_stat_uid 698 | 0000000000041490 T jl_stderr_obj 699 | 00000000000d4300 T jl_stderr_stream 700 | 00000000000d42e0 T jl_stdin_stream 701 | 0000000000041440 T jl_stdout_obj 702 | 00000000000d42f0 T jl_stdout_stream 703 | 00000000000f45f0 T jl_stored_inline 704 | 0000000000110700 T jl_string_ptr 705 | 00000000000d80b0 T jl_string_to_array 706 | 00000000004f2bf8 B jl_string_type 707 | 00000000001c4150 T jl_strtod_c 708 | 00000000001c41b0 T jl_strtof_c 709 | 00000000000facb0 T jl_sub_float 710 | 00000000000fa9a0 T jl_sub_int 711 | 00000000000fa9c0 T jl_sub_ptr 712 | 0000000000041210 T jl_substrtod 713 | 00000000000413e0 T jl_substrtof 714 | 00000000001170c0 T jl_subtype 715 | 000000000011bac0 T jl_subtype_env 716 | 0000000000117060 T jl_subtype_env_size 717 | 00000000000f8850 T jl_svec 718 | 00000000000f86c0 T jl_svec1 719 | 00000000000f8740 T jl_svec2 720 | 00000000000f8970 T jl_svec_copy 721 | 00000000000f89f0 T jl_svec_fill 722 | 00000000000d7000 T jl_switch 723 | 00000000000d7180 T jl_switchto 724 | 00000000000d3260 T jl_symbol 725 | 00000000000d3290 T jl_symbol_lookup 726 | 00000000000d3320 T jl_symbol_n 727 | 0000000000040f30 T jl_symbol_name 728 | 00000000004f2da0 B jl_symbol_type 729 | 00000000000d33e0 T jl_tagged_gensym 730 | 00000000000d3d90 T jl_take_buffer 731 | 00000000000ff240 T jl_task_get_next 732 | 00000000000d6580 T jl_task_stack_buffer 733 | 00000000004f2ac8 B jl_task_type 734 | 00000000000f2ff0 T jl_tcp_bind 735 | 00000000000f3490 T jl_tcp_connect 736 | 00000000000f3120 T jl_tcp_getpeername 737 | 00000000000f3060 T jl_tcp_getsockname 738 | 00000000000f3560 T jl_tcp_quickack 739 | 00000000000f35b0 T jl_tcp_reuseport 740 | 00000000000fe140 T jl_threadid 741 | 00000000000d4420 T jl_threading_enabled 742 | 00000000000d6ac0 T jl_throw 743 | 000000000010a9b0 T jl_throw_out_of_memory_error 744 | 00000000000405f0 T jl_too_few_args 745 | 0000000000040620 T jl_too_many_args 746 | 00000000000f1100 T jl_toplevel_eval 747 | 00000000000f12f0 T jl_toplevel_eval_in 748 | 000000000050cf58 B jl_top_module 749 | 00000000004f2c50 B jl_true 750 | 00000000000fba40 T jl_trunc_int 751 | 00000000000fc6d0 T jl_trunc_llvm 752 | 00000000000fc540 T jl_trunc_llvm_withtype 753 | 0000000000041070 T jl_try_substrtod 754 | 0000000000041240 T jl_try_substrtof 755 | 00000000000f3740 T jl_tty_set_mode 756 | 00000000000a9b00 T jl_tupletype_fill 757 | 00000000004f2d70 B jl_tuple_typename 758 | 00000000004f2d38 B jl_tvar_type 759 | 0000000000040a90 T jl_typeassert 760 | 00000000004f2d80 B jl_typedslot_type 761 | 0000000000040700 T jl_type_error 762 | 0000000000040650 T jl_type_error_rt 763 | 00000000004f2b48 B jl_typeerror_type 764 | 00000000000ba1a0 T jl_typeinf_begin 765 | 00000000000ba270 T jl_typeinf_end 766 | 0000000000121ca0 T jl_type_intersection 767 | 0000000000121cb0 T jl_type_intersection_with_env 768 | 00000000004f2b88 B jl_typemap_entry_type 769 | 00000000004f2b80 B jl_typemap_level_type 770 | 00000000000f5fd0 T jl_typemax_uint 771 | 0000000000121ea0 T jl_type_morespecific 772 | 0000000000121f40 T jl_type_morespecific_no_subtype 773 | 0000000000110680 T jl_typename_str 774 | 00000000004f2da8 B jl_typename_type 775 | 0000000000111110 T jl_typeof 776 | 00000000004f2d08 B jl_typeofbottom_type 777 | 00000000001106b0 T jl_typeof_str 778 | 000000000011ec00 T jl_types_equal 779 | 0000000000056080 T jl_type_to_llvm 780 | 00000000004f2dc8 B jl_type_type 781 | 00000000004f2dc0 B jl_type_typename 782 | 00000000004f2c40 B jl_typetype_type 783 | 00000000000a7160 T jl_type_union 784 | 00000000000a79c0 T jl_type_unionall 785 | 00000000000faa20 T jl_udiv_int 786 | 00000000000f31e0 T jl_udp_bind 787 | 00000000000f3250 T jl_udp_send 788 | 00000000004f2cc0 B jl_uint16_type 789 | 00000000004f2cb0 B jl_uint32_type 790 | 00000000004f2ca0 B jl_uint64_type 791 | 00000000004f2cd0 B jl_uint8_type 792 | 00000000000fbac0 T jl_uitofp 793 | 00000000000fb2f0 T jl_ule_int 794 | 00000000000fb2b0 T jl_ult_int 795 | 00000000000f6210 T jl_unbox_bool 796 | 00000000000f6220 T jl_unbox_float32 797 | 00000000000f6230 T jl_unbox_float64 798 | 00000000000f61b0 T jl_unbox_int16 799 | 00000000000f61d0 T jl_unbox_int32 800 | 00000000000f61f0 T jl_unbox_int64 801 | 00000000000f6190 T jl_unbox_int8 802 | 00000000000f61c0 T jl_unbox_uint16 803 | 00000000000f61e0 T jl_unbox_uint32 804 | 00000000000f6200 T jl_unbox_uint64 805 | 00000000000f61a0 T jl_unbox_uint8 806 | 00000000000f6240 T jl_unbox_voidpointer 807 | 00000000000e58d0 T jl_uncompress_argname_n 808 | 00000000000e5250 T jl_uncompress_argnames 809 | 00000000000e5390 T jl_uncompress_ir 810 | 0000000000040720 T jl_undefined_var_error 811 | 00000000004f2aa0 B jl_undefref_exception 812 | 00000000004f2b28 B jl_undefvarerror_type 813 | 00000000004f2d28 B jl_unionall_type 814 | 00000000004f2d30 B jl_uniontype_type 815 | 00000000004f2bb8 B jl_upsilonnode_type 816 | 00000000000faa60 T jl_urem_int 817 | 00000000000f2450 T jl_uv_associate_julia_struct 818 | 00000000000f1f80 T jl_uv_buf_base 819 | 00000000000f1f90 T jl_uv_buf_len 820 | 00000000000f1fa0 T jl_uv_buf_set_base 821 | 00000000000f1fb0 T jl_uv_buf_set_len 822 | 00000000000f1fc0 T jl_uv_connect_handle 823 | 00000000000f2460 T jl_uv_disassociate_julia_struct 824 | 00000000000f1fd0 T jl_uv_file_handle 825 | 00000000000f1e10 T jl_uv_flush 826 | 00000000000d3a30 T jl_uv_fs_t_path 827 | 00000000000d3a20 T jl_uv_fs_t_ptr 828 | 00000000000f3870 T jl_uv_handle 829 | 00000000000f2000 T jl_uv_handle_data 830 | 00000000000f3730 T jl_uv_handle_type 831 | 00000000000f32e0 T jl_uv_interface_addresses 832 | 00000000000f32f0 T jl_uv_interface_address_is_internal 833 | 00000000000f3300 T jl_uv_interface_address_sockaddr 834 | 00000000000f1f70 T jl_uv_process_data 835 | 00000000000f1f60 T jl_uv_process_pid 836 | 00000000000f2e60 T jl_uv_putb 837 | 00000000000f2e80 T jl_uv_putc 838 | 00000000000f2c10 T jl_uv_puts 839 | 00000000000f1fe0 T jl_uv_req_data 840 | 00000000000f1ff0 T jl_uv_req_set_data 841 | 00000000000f32d0 T jl_uv_sizeof_interface_address 842 | 00000000004f1f68 D jl_uv_stderr 843 | 000000000050cf90 B jl_uv_stdin 844 | 00000000004f1f70 D jl_uv_stdout 845 | 00000000000f35f0 T jl_uv_unix_fd_is_watched 846 | 00000000000f29d0 T jl_uv_write 847 | 00000000000f2bc0 T jl_uv_writecb 848 | 00000000000f2010 T jl_uv_write_handle 849 | 0000000000111100 T jl_valueof 850 | 0000000000040f50 T jl_value_ptr 851 | 00000000004f2d48 B jl_vararg_type 852 | 00000000004f2d40 B jl_vararg_typename 853 | 00000000004f2d50 B jl_vecelement_typename 854 | 0000000000110ff0 T jl_ver_is_release 855 | 0000000000110fc0 T jl_ver_major 856 | 0000000000110fd0 T jl_ver_minor 857 | 0000000000110fe0 T jl_ver_patch 858 | 0000000000111000 T jl_ver_string 859 | 0000000000040320 T jl_vexceptionf 860 | 00000000004f2ae0 B jl_voidpointer_type 861 | 00000000004f2af0 B jl_void_type 862 | 00000000000f2ed0 T jl_vprintf 863 | 00000000000ff050 T jl_wakeup_thread 864 | 00000000004f2c08 B jl_weakref_type 865 | 00000000004a1ef0 D jl_world_counter 866 | 00000000000fb8d0 T jl_xor_int 867 | 0000000000110dc0 T jl_yield 868 | 00000000000fba80 T jl_zext_int 869 | -------------------------------------------------------------------------------- /scripts/activate-julia: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ ! -e julia-1.7.0-rc2 ]; then 4 | wget https://julialang-s3.julialang.org/bin/linux/x64/1.7/julia-1.7.0-rc2-linux-x86_64.tar.gz 5 | tar -xvzf julia-1.7.0-rc2-linux-x86_64.tar.gz 6 | rm julia-1.7.0-rc2-linux-x86_64.tar.gz 7 | fi 8 | 9 | export JULIA_HOME=$(pwd)/julia-1.7.0-rc2 10 | export PATH=$PATH:$JULIA_HOME/bin -------------------------------------------------------------------------------- /scripts/activate-openj9: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | OPENJ9_VERSION="jdk8u275-b01" 4 | OPENJ9_VERSION_NODASH="8u275b01" 5 | J9_VERSION="0.23.0" 6 | J9_TARNAME="OpenJDK8U-jdk_x64_linux_openj9_$OPENJ9_VERSION_NODASH_openj9-$J9_VERSION.tar.gz" 7 | JAVA_PATH=$(pwd)/openj9/$OPENJ9_VERSION/bin 8 | 9 | if [ ! -e "$JAVA_PATH/java" ]; then 10 | mkdir -p openj9 11 | pushd openj9 12 | wget "https://github.com/AdoptOpenJDK/openjdk8-binaries/releases/download/$OPENJ9_VERSION_openj9-$J9_VERSION/$J9_TARNAME" 13 | tar -xvzf $J9_TARNAME 14 | popd 15 | fi 16 | 17 | export LD_PRELOAD=$(pwd)/openj9/$OPENJ9_VERSION/jre/lib/amd64/j9vm/libjsig.so 18 | export PATH=$JAVA_PATH:$PATH 19 | 20 | -------------------------------------------------------------------------------- /scripts/build-docker: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | 6 | pushd dockerfiles 7 | docker build -t julia-clj-docker -f Dockerfile --build-arg USERID=$(id -u) --build-arg GROUPID=$(id -u) --build-arg USERNAME=$USER . 8 | popd 9 | -------------------------------------------------------------------------------- /scripts/deploy: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | rm -rf classes 6 | 7 | source scripts/local-julia-env 8 | 9 | clj -M:test 10 | clj -X:codox 11 | clj -X:depstar 12 | clj -X:deploy 13 | -------------------------------------------------------------------------------- /scripts/local-julia-env: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source scripts/activate-julia 4 | export LD_PRELOAD=/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/server/libjsig.so 5 | -------------------------------------------------------------------------------- /scripts/openj9-java: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source scripts/activate-openj9 4 | java $@ -------------------------------------------------------------------------------- /scripts/remote-repl: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export JULIA_COPY_STACKS=yes 4 | 5 | lein update-in :dependencies conj \[nrepl\ \"0.6.0\"\]\ 6 | -- update-in :plugins conj \[cider/cider-nrepl\ \"0.22.4\"\]\ 7 | -- repl :headless :host localhost 8 | 9 | -------------------------------------------------------------------------------- /scripts/run-docker: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | scripts/build-docker 6 | 7 | ## Need this for julia magic 8 | export JULIA_COPY_STACKS=yes 9 | 10 | docker run --rm -it -u $(id -u):$(id -g) \ 11 | -v /$HOME/.m2:/home/$USER/.m2 \ 12 | -v $(pwd)/:/julia-clj \ 13 | --net=host -w /julia-clj \ 14 | julia-clj-docker lein update-in :dependencies conj \[nrepl\ \"0.6.0\"\]\ 15 | -- update-in :plugins conj \[cider/cider-nrepl\ \"0.22.4\"\]\ 16 | -- repl :headless :host localhost 17 | -------------------------------------------------------------------------------- /src/libjulia_clj/impl/base.clj: -------------------------------------------------------------------------------- 1 | (ns libjulia-clj.impl.base 2 | (:require [tech.v3.datatype :as dtype] 3 | [tech.v3.datatype.ffi :as dt-ffi] 4 | [tech.v3.datatype.ffi.size-t :as ffi-size-t] 5 | [tech.v3.datatype.ffi.ptr-value :as ffi-ptr-value] 6 | [tech.v3.datatype.protocols :as dtype-proto] 7 | [tech.v3.datatype.errors :as errors] 8 | [tech.v3.datatype.casting :as casting] 9 | [tech.v3.datatype.pprint :as dtype-pp] 10 | [tech.v3.datatype.native-buffer :as native-buffer] 11 | [tech.v3.tensor.dimensions.analytics :as dims-analytics] 12 | [tech.v3.tensor :as dtt] 13 | [tech.v3.resource :as resource] 14 | [libjulia-clj.impl.gc :as gc] 15 | [libjulia-clj.impl.ffi :as julia-ffi] 16 | [libjulia-clj.impl.protocols :as julia-proto] 17 | [com.github.ztellman.primitive-math :as pmath] 18 | [clojure.tools.logging :as log]) 19 | (:import [tech.v3.datatype.ffi Pointer] 20 | [java.nio.file Paths] 21 | [java.util Map Iterator NoSuchElementException] 22 | [clojure.lang IFn Symbol Keyword]) 23 | (:refer-clojure :exclude [struct])) 24 | 25 | 26 | (set! *warn-on-reflection* true) 27 | 28 | 29 | (defonce jvm-julia-roots* (atom nil)) 30 | 31 | 32 | (declare call-function raw-call-function call-function-kw) 33 | 34 | 35 | (defn initialize-julia-root-map! 36 | [] 37 | (errors/when-not-error 38 | (nil? @jvm-julia-roots*) 39 | "Attempt to initialize julia root map twice") 40 | (let [refmap (julia-ffi/jl_eval_string "jvm_refs = IdDict()") 41 | set-index! (julia-ffi/jl_get_function (julia-ffi/jl_base_module) "setindex!") 42 | delete! (julia-ffi/jl_get_function (julia-ffi/jl_base_module) "delete!") 43 | haskey (julia-ffi/jl_get_function (julia-ffi/jl_base_module) "haskey")] 44 | (reset! jvm-julia-roots* 45 | {:jvm-refs refmap 46 | :set-index! set-index! 47 | :delete! delete! 48 | :haskey haskey}))) 49 | 50 | (defonce julia-gc-root-log-level* (atom nil)) 51 | 52 | (defn root-ptr! 53 | "Root a pointer and set a GC hook that will unroot it when the time comes. 54 | Defaults to :auto track type" 55 | ([^Pointer value options] 56 | (when-not (or (:unrooted? options) 57 | (== 0 (julia-ffi/jl_gc_is_enabled))) 58 | (let [{:keys [jvm-refs set-index! delete! haskey]} @jvm-julia-roots* 59 | native-value (.address value) 60 | untracked-value (Pointer. native-value) 61 | ;;Eventually we will turn off default logging... 62 | log-level (:log-level options @julia-gc-root-log-level*)] 63 | ;;We do not root pointers twice; that could cause a crash when 64 | ;;dereferencing 65 | (if-not (julia-proto/julia->jvm 66 | (julia-ffi/jl_call2 haskey jvm-refs untracked-value) 67 | {}) 68 | (do 69 | (when log-level 70 | (log/logf log-level "Rooting address 0x%016X - %s" 71 | native-value (julia-ffi/jl-ptr->typename value))) 72 | (julia-ffi/jl_call3 set-index! jvm-refs untracked-value value) 73 | (gc/track value (fn [] 74 | (when log-level 75 | (log/logf log-level 76 | "Unrooting address 0x%016X" 77 | native-value)) 78 | (julia-ffi/jl_call2 delete! jvm-refs untracked-value)))) 79 | ;;already rooted 80 | value)))) 81 | ([value] 82 | (root-ptr! value nil))) 83 | 84 | 85 | (defn combine-paths 86 | ^String [src-path & args] 87 | (-> (Paths/get (str src-path) ^"[java.lang.String;" 88 | (into-array String 89 | (map str args))) 90 | (.toString))) 91 | 92 | 93 | (defonce module-functions* (atom nil)) 94 | 95 | 96 | (defn initialize-module-functions! 97 | [] 98 | (let [basemod (julia-ffi/jl_base_module) 99 | coremod (julia-ffi/jl_core_module)] 100 | (reset! module-functions* 101 | {:sprint (julia-ffi/jl_get_function basemod "sprint") 102 | :showerror (julia-ffi/jl_get_function basemod "showerror") 103 | :catch-backtrace (julia-ffi/jl_get_function basemod "catch_backtrace") 104 | :dump (julia-ffi/jl_get_function basemod "dump") 105 | :print (julia-ffi/jl_get_function basemod "print") 106 | :methods (julia-ffi/jl_get_function basemod "methods") 107 | :length (julia-ffi/jl_get_function basemod "length") 108 | :names (julia-ffi/jl_get_function basemod "names") 109 | :kwfunc (julia-ffi/jl_get_function coremod "kwfunc") 110 | :isempty (julia-ffi/jl_get_function basemod "isempty") 111 | :setindex! (julia-ffi/jl_get_function basemod "setindex!") 112 | :getindex (julia-ffi/jl_get_function basemod "getindex") 113 | :fieldnames (julia-ffi/jl_get_function basemod "fieldnames") 114 | :getfield (julia-ffi/jl_get_function basemod "getfield") 115 | :keys (julia-ffi/jl_get_function basemod "keys") 116 | :values (julia-ffi/jl_get_function basemod "values") 117 | :haskey (julia-ffi/jl_get_function basemod "haskey") 118 | :get (julia-ffi/jl_get_function basemod "get") 119 | :append! (julia-ffi/jl_get_function basemod "append!") 120 | :delete! (julia-ffi/jl_get_function basemod "delete!") 121 | :iterate (julia-ffi/jl_get_function basemod "iterate") 122 | :pairs (julia-ffi/jl_get_function basemod "pairs") 123 | :eltype (julia-ffi/jl_get_function basemod "eltype")}))) 124 | 125 | 126 | (defn module-fn 127 | [fn-name & args] 128 | (if-let [fn-val (get @module-functions* fn-name)] 129 | (call-function fn-val args) 130 | (errors/throwf "Failed to find module function %s" fn-name))) 131 | 132 | 133 | (defn sprint-last-error 134 | ^String [exc] 135 | (when exc 136 | (let [{:keys [sprint showerror catch-backtrace]} @module-functions* 137 | bt (julia-ffi/jl_call0 catch-backtrace)] 138 | (-> (julia-ffi/jl_call3 sprint showerror exc bt) 139 | (julia-proto/julia->jvm nil))))) 140 | 141 | 142 | (defn check-last-error 143 | [] 144 | (when-let [exc (julia-ffi/jl_exception_occurred)] 145 | (errors/throwf "Julia error:\n%s" (or (sprint-last-error exc) 146 | "unable to print error")))) 147 | 148 | 149 | (defn jl-obj->str 150 | ^String [jl-ptr] 151 | (when jl-ptr 152 | (let [{:keys [sprint print]} @module-functions*] 153 | (-> (julia-ffi/jl_call2 sprint print jl-ptr) 154 | (julia-proto/julia->jvm nil))))) 155 | 156 | 157 | (defn windows-os? 158 | [] 159 | (.contains (.toLowerCase (System/getProperty "os.name")) 160 | "windows")) 161 | 162 | 163 | (defn initialize! 164 | "Initialize julia optionally providing an explicit path which will override 165 | the julia library search mechanism. 166 | 167 | Currently the search mechanism is: 168 | 169 | [user-path]->JULIA_HOME->\"julia\" 170 | 171 | Returns :ok on success else exception. 172 | 173 | Options: 174 | 175 | * `:julia-home` - Explicitly declare the env var julia-home. 176 | * `:n-threads` - Set to -1 to set to n-cpus. Defaults to nil which means single threaded 177 | unless the JULIA_NUM_THREADS environment variable is set. Note that this has implications 178 | for application stability - see the signals.md topic. 179 | * `:signals-enabled?` - Users do not usually need to set this. This allows users to disable 180 | all of Julia's signal handling most likely leading to a crash. See the signals.md topic." 181 | ([{:keys [julia-library-path] 182 | :as options}] 183 | (julia-ffi/initialize! options) 184 | (reset! julia-ffi/julia-library-path* julia-library-path) 185 | (when-not (== 1 (julia-ffi/jl_is_initialized)) 186 | ;;The JVM uses SIGSEGV signals during it's normal course of 187 | ;;operation. Without disabling Julia's signal handling this will 188 | ;;cause an instantaneous and unceremonious exit :-). 189 | (julia-ffi/init-process-options options) 190 | (julia-ffi/jl_init__threading) 191 | (julia-ffi/initialize-type-map!) 192 | (initialize-julia-root-map!) 193 | (initialize-module-functions!)) 194 | :ok) 195 | ([] 196 | (initialize! nil))) 197 | 198 | 199 | (def ptr-dtype (ffi-size-t/lower-ptr-type :pointer)) 200 | 201 | 202 | (defn module-symbol-names 203 | [module] 204 | (let [names-fn (:names @module-functions*) 205 | names-ary (julia-ffi/jl_call1 names-fn module) 206 | ary-data (native-buffer/wrap-address (ffi-ptr-value/ptr-value names-ary) 207 | 16 ptr-dtype :little-endian nil) 208 | data-ptr (ary-data 0) 209 | ;;If julia is compiled with STORE_ARRAY_LENGTH 210 | data-len (ary-data 1) 211 | data (native-buffer/wrap-address data-ptr 212 | (* data-len 213 | (casting/numeric-byte-width ptr-dtype)) 214 | ptr-dtype :little-endian nil)] 215 | (-> (dtype/emap (fn [^long sym-data] 216 | (-> (Pointer. sym-data) 217 | (julia-ffi/jl_symbol_name))) 218 | :string data) 219 | (dtype/clone)))) 220 | 221 | 222 | (deftype GenericJuliaObject [^Pointer handle gc-obj] 223 | julia-proto/PToJulia 224 | (->julia [item] handle) 225 | dt-ffi/PToPointer 226 | (convertible-to-pointer? [this] true) 227 | (->pointer [this] handle) 228 | Object 229 | (toString [this] 230 | (jl-obj->str handle))) 231 | 232 | (dtype-pp/implement-tostring-print GenericJuliaObject) 233 | 234 | 235 | (defn args->pos-kw-args 236 | "Utility function that, given a list of arguments, separates them 237 | into positional and keyword arguments. Throws an exception if the 238 | keyword argument is not followed by any more arguments." 239 | [arglist] 240 | (loop [args arglist 241 | pos-args [] 242 | kw-args nil 243 | found-kw? false] 244 | (if-not (seq args) 245 | [pos-args kw-args] 246 | (let [arg (first args) 247 | [pos-args kw-args args found-kw?] 248 | (if (keyword? arg) 249 | (if-not (seq (rest args)) 250 | (throw (Exception. 251 | (format "Keyword arguments must be followed by another arg: %s" 252 | (str arglist)))) 253 | [pos-args (assoc kw-args arg (first (rest args))) 254 | (drop 2 args) true]) 255 | (if found-kw? 256 | (throw (Exception. 257 | (format "Positional arguments are not allowed after keyword arguments: %s" 258 | arglist))) 259 | [(conj pos-args (first args)) 260 | kw-args 261 | (rest args) found-kw?]))] 262 | (recur args pos-args kw-args found-kw?))))) 263 | 264 | 265 | (defmacro ^:private impl-callable-julia-object 266 | [] 267 | `(deftype ~'CallableJuliaObject [~'handle ~'kw-fn-handle ~'gc-obj] 268 | julia-proto/PToJulia 269 | (->julia [item] ~'handle) 270 | julia-proto/PJuliaKWFn 271 | (kw-fn [item] ~'kw-fn-handle) 272 | dt-ffi/PToPointer 273 | (convertible-to-pointer? [this#] true) 274 | (->pointer [this#] ~'handle) 275 | Object 276 | (toString [this] 277 | (jl-obj->str ~'handle)) 278 | IFn 279 | ~@(->> (range 16) 280 | (map (fn [idx] 281 | (let [argsyms (->> (range idx) 282 | (mapv (fn [arg-idx] 283 | (symbol (str "arg-" arg-idx)))))] 284 | `(invoke ~(vec (concat ['this] argsyms)) 285 | (let [[pos-args# kw-args#] (args->pos-kw-args ~argsyms)] 286 | (if (nil? kw-args#) 287 | (call-function ~'handle ~argsyms nil) 288 | (call-function-kw ~'handle pos-args# kw-args#)))))))) 289 | (applyTo [this# argseq#] 290 | (let [[pos-args# kw-args#] (args->pos-kw-args argseq#)] 291 | (if (nil? kw-args#) 292 | (call-function ~'handle argseq# nil) 293 | (call-function-kw ~'kw-fn-handle ~'handle pos-args# kw-args#)))))) 294 | 295 | 296 | (impl-callable-julia-object) 297 | 298 | 299 | (dtype-pp/implement-tostring-print CallableJuliaObject) 300 | 301 | (defn jl-obj-callable? 302 | [jl-obj] 303 | (when jl-obj 304 | (let [{:keys [methods length]} @module-functions*] 305 | (try 306 | (not= 0 (long (call-function length 307 | [(raw-call-function methods [jl-obj])]))) 308 | (catch Throwable e false))))) 309 | 310 | 311 | (defn kw-fn 312 | ([jl-fn options] 313 | (let [{:keys [kwfunc]} @module-functions*] 314 | (call-function kwfunc [jl-fn] options))) 315 | ([jl-fn] 316 | (kw-fn jl-fn nil))) 317 | 318 | 319 | (extend-type Object 320 | julia-proto/PJuliaKWFn 321 | (kw-fn [item] 322 | (kw-fn item))) 323 | 324 | 325 | (defmethod julia-proto/julia->jvm :default 326 | [julia-val options] 327 | (when julia-val 328 | ;;Do not root when someone asks us not to or when 329 | ;;we have explicitly disabled the julia gc. 330 | (root-ptr! julia-val options) 331 | (if (jl-obj-callable? julia-val) 332 | (CallableJuliaObject. julia-val (kw-fn julia-val {:unrooted? true}) 333 | (:gc-obj options)) 334 | (GenericJuliaObject. julia-val 335 | (:gc-obj options))))) 336 | 337 | 338 | (extend-protocol julia-proto/PToJulia 339 | Boolean 340 | (->julia [item] (julia-ffi/jl_box_bool (if item 1 0))) 341 | Byte 342 | (->julia [item] (julia-ffi/jl_box_int8 item)) 343 | Short 344 | (->julia [item] (julia-ffi/jl_box_int16 item)) 345 | Integer 346 | (->julia [item] (julia-ffi/jl_box_int32 item)) 347 | Long 348 | (->julia [item] (julia-ffi/jl_box_int64 item)) 349 | Float 350 | (->julia [item] (julia-ffi/jl_box_float32 item)) 351 | Double 352 | (->julia [item] (julia-ffi/jl_box_float64 item)) 353 | String 354 | (->julia [item] (julia-ffi/jl_cstr_to_string item)) 355 | Symbol 356 | (->julia [item] (julia-ffi/jl_symbol (name item))) 357 | Keyword 358 | (->julia [item] (julia-ffi/jl_symbol (name item))) 359 | Pointer 360 | (->julia [item] item)) 361 | 362 | (defmethod julia-proto/julia->jvm :boolean 363 | [julia-val options] 364 | (if (== 0 (julia-ffi/jl_unbox_bool julia-val)) 365 | false 366 | true)) 367 | 368 | 369 | (defmethod julia-proto/julia->jvm :uint8 370 | [julia-val options] 371 | (pmath/byte->ubyte (julia-ffi/jl_unbox_uint8 julia-val))) 372 | 373 | (defmethod julia-proto/julia->jvm :uint16 374 | [julia-val options] 375 | (pmath/short->ushort (julia-ffi/jl_unbox_uint16 julia-val))) 376 | 377 | (defmethod julia-proto/julia->jvm :uint32 378 | [julia-val options] 379 | (pmath/int->uint (julia-ffi/jl_unbox_uint32 julia-val))) 380 | 381 | (defmethod julia-proto/julia->jvm :uint64 382 | [julia-val options] 383 | (julia-ffi/jl_unbox_uint64 julia-val)) 384 | 385 | 386 | (defmethod julia-proto/julia->jvm :int8 387 | [julia-val options] 388 | (julia-ffi/jl_unbox_int8 julia-val)) 389 | 390 | (defmethod julia-proto/julia->jvm :int16 391 | [julia-val options] 392 | (julia-ffi/jl_unbox_int16 julia-val)) 393 | 394 | (defmethod julia-proto/julia->jvm :int32 395 | [julia-val options] 396 | (julia-ffi/jl_unbox_int32 julia-val)) 397 | 398 | (defmethod julia-proto/julia->jvm :int64 399 | [julia-val options] 400 | (julia-ffi/jl_unbox_int64 julia-val)) 401 | 402 | (defmethod julia-proto/julia->jvm :float64 403 | [julia-val options] 404 | (julia-ffi/jl_unbox_float64 julia-val)) 405 | 406 | (defmethod julia-proto/julia->jvm :float32 407 | [julia-val options] 408 | (julia-ffi/jl_unbox_float32 julia-val)) 409 | 410 | (defmethod julia-proto/julia->jvm :string 411 | [julia-val options] 412 | (julia-ffi/jl_string_ptr julia-val)) 413 | 414 | (defmethod julia-proto/julia->jvm :jl-nothing-type 415 | [julia-val options] 416 | nil) 417 | 418 | 419 | (defn jvm-args->julia 420 | [args] 421 | (mapv #(if % 422 | (julia-proto/->julia %) 423 | (julia-ffi/jl_nothing)) args)) 424 | 425 | 426 | #_(defn lookup-julia-type 427 | "Lookup a julia type from a clojure type keyword." 428 | [clj-type-kwd] 429 | (if-let [retval (get-in @julia-ffi/julia-typemap* [:typename->typeid clj-type-kwd])] 430 | (julia-proto/julia->jvm retval {:unrooted? true}) 431 | (errors/throwf "Failed to find julia type %s" clj-type-kwd))) 432 | 433 | 434 | (defn jvm-args->julia-types 435 | [args] 436 | (mapv #(if % 437 | (julia-proto/->julia %) 438 | (julia-ffi/jl_nothing)) 439 | args)) 440 | 441 | 442 | (defn jvm-args->julia-symbols 443 | [args] 444 | (mapv #(cond (string? %) (julia-ffi/jl_symbol %) 445 | (or (keyword? %) (symbol? %)) (julia-proto/->julia %) 446 | :else (errors/throwf "%s is not convertible to a julia symbol" %)) 447 | args)) 448 | 449 | 450 | (defn raw-call-function 451 | "Call the function. We disable the Julia GC when marshalling arguments but 452 | the GC is enabled for the actual julia function call. The result is returned 453 | to the user as a Pointer." 454 | ^Pointer [fn-handle args] 455 | (resource/stack-resource-context 456 | ;;do not GC my stuff when I am marshalling function arguments to julia 457 | (let [jl-args (julia-ffi/with-disabled-julia-gc (jvm-args->julia args))] 458 | (let [retval 459 | (case (count jl-args) 460 | 0 (julia-ffi/jl_call0 fn-handle) 461 | 1 (julia-ffi/jl_call1 fn-handle (first jl-args)) 462 | 2 (julia-ffi/jl_call2 fn-handle (first jl-args) (second jl-args)) 463 | 3 (apply julia-ffi/jl_call3 fn-handle jl-args) 464 | (let [n-args (count args) 465 | ;;This will be cleaned up when the resource stack context unwraps. 466 | ptr-buf (dtype/make-container :native-heap ptr-dtype 467 | {:resource-type :stack} 468 | (mapv #(if % 469 | (ffi-ptr-value/ptr-value %) 470 | 0) 471 | jl-args))] 472 | (julia-ffi/jl_call fn-handle ptr-buf n-args)))] 473 | (check-last-error) 474 | retval)))) 475 | 476 | 477 | (defn call-function 478 | "Call a function. The result will be marshalled back to the jvm and if necessary, 479 | rooted. This method is normally not necessary but useful if you would like to 480 | use keywords in your argument list or specify to avoid rooting the result." 481 | ([fn-handle args options] 482 | (let [retval (raw-call-function fn-handle args)] 483 | (julia-proto/julia->jvm retval options))) 484 | ([fn-handle args] 485 | (call-function fn-handle args nil))) 486 | 487 | (defn apply-tuple-type 488 | "Create a new Julia tuple type from a sequence of types." 489 | ^Pointer [args & [options]] 490 | (resource/stack-resource-context 491 | (let [jl-args (julia-ffi/with-disabled-julia-gc (jvm-args->julia-types args)) 492 | n-args (count args) 493 | ptr-buf (dtype/make-container :native-heap ptr-dtype 494 | {:resource-type :stack} 495 | (mapv #(if % 496 | (ffi-ptr-value/ptr-value %) 497 | 0) 498 | jl-args)) 499 | retval (julia-ffi/jl_apply_tuple_type_v ptr-buf n-args)] 500 | (check-last-error) 501 | (julia-proto/julia->jvm retval options)))) 502 | 503 | 504 | (defn apply-type 505 | "Create a new Julia type from an existing type and a sequence of other 506 | types." 507 | ^Pointer [jl-type args] 508 | (resource/stack-resource-context 509 | (let [args (julia-ffi/with-disabled-julia-gc (jvm-args->julia-types args)) 510 | retval 511 | (case (count args) 512 | 1 (julia-ffi/jl_apply_type1 jl-type (first args)) 513 | 2 (julia-ffi/jl_apply_type2 jl-type (first args) (second args)) 514 | (let [n-args (count args) 515 | ptr-buf (dtype/make-container 516 | :native-heap ptr-dtype 517 | {:resource-type :stack} 518 | (mapv #(if % 519 | (ffi-ptr-value/ptr-value %) 520 | 0) 521 | args))] 522 | (julia-ffi/jl_apply_type jl-type ptr-buf n-args)))] 523 | (check-last-error) 524 | (julia-proto/julia->jvm retval nil)))) 525 | 526 | 527 | (defn struct 528 | "Instantiate a Julia struct type (tuple, NamedTuple, etc). Use this after 529 | apply-tuple-type in order to create an instance of your new type." 530 | ^Pointer [jl-type args] 531 | (resource/stack-resource-context 532 | (let [args (julia-ffi/with-disabled-julia-gc (jvm-args->julia args)) 533 | n-args (count args) 534 | ptr-buf (dtype/make-container :native-heap ptr-dtype 535 | {:resource-type :stack} 536 | (mapv #(if % 537 | (ffi-ptr-value/ptr-value %) 538 | 0) 539 | args)) 540 | retval (julia-ffi/jl_new_structv jl-type ptr-buf n-args)] 541 | (check-last-error) 542 | (julia-proto/julia->jvm retval nil)))) 543 | 544 | 545 | (defn tuple 546 | "Create a julia tuple from a set of arguments. The tuple type will be the 547 | same datatype as the argument types. 548 | 549 | This function converts the arguments to julia, calls `apply-tuple-type` 550 | on the resulting argument types, and then instantiates an instance of 551 | the given newly created tuple type." 552 | [args] 553 | (let [[jl-args tuple-type] 554 | (julia-ffi/with-disabled-julia-gc 555 | (let [jl-args (jvm-args->julia args)] 556 | [jl-args 557 | (apply-tuple-type (map julia-ffi/jl_typeof jl-args))]))] 558 | (struct tuple-type jl-args))) 559 | 560 | 561 | (defn named-tuple 562 | "Create a julia named tuple from a map of values. The keys of the map must be 563 | keywords or symbols. A new named tuple type will be created and instantiated." 564 | ([value-map] 565 | (let [generic-nt-type (julia-ffi/lookup-library-type :jl-namedtuple-type) 566 | [jl-values nt-type] 567 | (julia-ffi/with-disabled-julia-gc 568 | (let [item-keys (tuple (jvm-args->julia-symbols (keys value-map))) 569 | map-vals (vals value-map) 570 | jl-values (jvm-args->julia map-vals) 571 | item-type-tuple (apply-tuple-type (map julia-ffi/jl_typeof jl-values)) 572 | nt-type (apply-type generic-nt-type [item-keys item-type-tuple])] 573 | [jl-values nt-type]))] 574 | (check-last-error) 575 | ;;And now, with gc (potentially) enabled, attempt to create the struct 576 | (struct nt-type jl-values))) 577 | ([] 578 | (named-tuple nil))) 579 | 580 | 581 | (defn kw-args->named-tuple 582 | [kw-args] 583 | (if (instance? Map kw-args) 584 | (named-tuple kw-args) 585 | (julia-proto/->julia kw-args))) 586 | 587 | 588 | (defn call-function-kw 589 | "Call a julia function and pass in keyword arguments. This is useful if you 590 | would like to specify the arglist and kw-argmap. Normally this is done for 591 | you." 592 | ([fn-handle args kw-args options] 593 | (call-function (julia-proto/kw-fn fn-handle) 594 | (concat [(julia-ffi/with-disabled-julia-gc 595 | (kw-args->named-tuple kw-args)) 596 | fn-handle] 597 | args) 598 | options)) 599 | ([fn-handle args kw-args] 600 | (call-function-kw fn-handle args kw-args nil)) 601 | ([fn-handle args] 602 | (call-function-kw fn-handle args nil nil))) 603 | 604 | 605 | (dtype-pp/implement-tostring-print Pointer) 606 | 607 | 608 | (defn module-publics 609 | [module] 610 | (->> (module-symbol-names module) 611 | (map (fn [sym-name] 612 | (let [global-sym (julia-ffi/jl_get_function module sym-name)] 613 | (when global-sym 614 | [(symbol sym-name) 615 | {:symbol-type (julia-ffi/jl-ptr->typename global-sym) 616 | :symbol global-sym}])))) 617 | (remove nil?) 618 | (sort-by first))) 619 | 620 | 621 | (defmacro define-module-publics 622 | [module-name unsafe-name-map] 623 | (if-let [mod (julia-ffi/jl_eval_string module-name)] 624 | (let [publics (module-publics mod) 625 | docs-fn (julia-ffi/jl_eval_string "Base.Docs.doc")] 626 | `(do 627 | (def ~'module (julia-ffi/jl_eval_string ~module-name)) 628 | ~@(->> publics 629 | (map (fn [[sym {jl-symbol :symbol}]] 630 | (when jl-symbol 631 | (try 632 | (let [sym-name (name sym) 633 | sym-rawname sym-name 634 | sym-name (.replace ^String sym-name "@" "AT") 635 | sym-name (get unsafe-name-map sym-name sym-name) 636 | docs (jl-obj->str (raw-call-function docs-fn [jl-symbol]))] 637 | `(def ~(with-meta (symbol sym-name) 638 | {:doc docs}) 639 | (try 640 | (julia-proto/julia->jvm 641 | (julia-ffi/jl_get_function ~'module ~sym-rawname) 642 | {:unrooted? true}) 643 | (catch Exception e# 644 | (log/warnf e# "Julia symbol %s(%s) will be unavailable" 645 | ~sym-name ~sym-rawname))))) 646 | (catch Exception e 647 | (log/warnf e "Julia symbol %s will unavailable" (name sym))))))) 648 | (remove nil?)))) 649 | (errors/throwf "Failed to find module: %s" module-name))) 650 | 651 | 652 | (defn julia-array->nd-descriptor 653 | [ary-ptr] 654 | (let [rank (julia-ffi/jl_array_rank ary-ptr) 655 | shape (vec (reverse 656 | (map #(julia-ffi/jl_array_size ary-ptr %) 657 | (range rank)))) 658 | data (julia-ffi/jl_array_ptr ary-ptr) 659 | dtype (-> (julia-ffi/jl_array_eltype ary-ptr) 660 | (julia-ffi/julia-eltype->datatype)) 661 | byte-width (casting/numeric-byte-width dtype) 662 | ;;TODO - Figure out to handle non-packed data. This is a start, however. 663 | strides (->> (dims-analytics/shape-ary->strides shape) 664 | (mapv #(* byte-width (long %))))] 665 | {:ptr (ffi-ptr-value/ptr-value data) 666 | :elemwise-datatype dtype 667 | :shape shape 668 | :strides strides 669 | :julia-array ary-ptr})) 670 | 671 | 672 | (deftype JuliaArray [^Pointer handle] 673 | julia-proto/PToJulia 674 | (->julia [item] handle) 675 | dt-ffi/PToPointer 676 | (convertible-to-pointer? [item] true) 677 | (->pointer [this] handle) 678 | dtype-proto/PElemwiseDatatype 679 | (elemwise-datatype [this] 680 | (-> (julia-ffi/jl_array_eltype handle) 681 | (julia-ffi/julia-eltype->datatype))) 682 | dtype-proto/PShape 683 | (shape [this] 684 | (let [rank (julia-ffi/jl_array_rank handle)] 685 | (mapv #(julia-ffi/jl_array_size handle %) 686 | (reverse (range rank))))) 687 | dtype-proto/PECount 688 | (ecount [this] 689 | (long (apply * (dtype-proto/shape this)))) 690 | dtype-proto/PToTensor 691 | (as-tensor [this] 692 | (-> (julia-array->nd-descriptor handle) 693 | (dtt/nd-buffer-descriptor->tensor))) 694 | dtype-proto/PToNDBufferDesc 695 | (convertible-to-nd-buffer-desc? [this] true) 696 | (->nd-buffer-descriptor [this] (julia-array->nd-descriptor handle)) 697 | dtype-proto/PToBuffer 698 | (convertible-to-buffer? [this] true) 699 | (->buffer [this] (dtype-proto/->buffer (dtt/as-tensor this))) 700 | dtype-proto/PToNativeBuffer 701 | (convertible-to-native-buffer? [this] true) 702 | (->native-buffer [this] (-> (dtype-proto/as-tensor this) 703 | (dtype-proto/->native-buffer))) 704 | Object 705 | (toString [this] 706 | (jl-obj->str handle))) 707 | 708 | 709 | (dtype-pp/implement-tostring-print JuliaArray) 710 | 711 | 712 | (defn- wrap-array 713 | [jl-ptr options] 714 | (root-ptr! jl-ptr options) 715 | (JuliaArray. jl-ptr)) 716 | 717 | 718 | (defmethod julia-proto/julia->jvm "Array" [jl-ptr options] 719 | (wrap-array jl-ptr options)) 720 | 721 | (defmethod julia-proto/julia->jvm :jl-array-int-64-type [jl-ptr options] 722 | (wrap-array jl-ptr options)) 723 | 724 | (defmethod julia-proto/julia->jvm :jl-array-uint-64-type [jl-ptr options] 725 | (wrap-array jl-ptr options)) 726 | 727 | (defmethod julia-proto/julia->jvm :jl-array-int-32-type [jl-ptr options] 728 | (wrap-array jl-ptr options)) 729 | 730 | (defmethod julia-proto/julia->jvm :jl-array-uint-32-type [jl-ptr options] 731 | (wrap-array jl-ptr options)) 732 | 733 | (defmethod julia-proto/julia->jvm :jl-array-int-16-type [jl-ptr options] 734 | (wrap-array jl-ptr options)) 735 | 736 | (defmethod julia-proto/julia->jvm :jl-array-uint-16-type [jl-ptr options] 737 | (wrap-array jl-ptr options)) 738 | 739 | (defmethod julia-proto/julia->jvm :jl-array-int-8-type [jl-ptr options] 740 | (wrap-array jl-ptr options)) 741 | 742 | (defmethod julia-proto/julia->jvm :jl-array-uint-8-type [jl-ptr options] 743 | (wrap-array jl-ptr options)) 744 | 745 | 746 | (deftype JLIterator [iter 747 | ^:unsynchronized-mutable iter-state 748 | ^:unsynchronized-mutable last-value] 749 | Iterator 750 | (hasNext [this] 751 | (boolean (not (nil? iter-state)))) 752 | (next [this] 753 | (when-not iter-state 754 | (throw (NoSuchElementException.))) 755 | (let [next-tuple (module-fn :iterate iter iter-state) 756 | retval last-value] 757 | (if next-tuple 758 | (do 759 | (set! last-value (module-fn :getindex next-tuple 1)) 760 | (set! iter-state (module-fn :getindex next-tuple 2))) 761 | (do 762 | (set! last-value nil) 763 | (set! iter-state nil))) 764 | retval))) 765 | 766 | 767 | (defn julia-obj->iterable 768 | ^Iterable [jl-obj] 769 | (reify Iterable 770 | (iterator [this] 771 | (let [first-tuple (module-fn :iterate jl-obj)] 772 | (if first-tuple 773 | (JLIterator. jl-obj 774 | (module-fn :getindex first-tuple 2) 775 | (module-fn :getindex first-tuple 1)) 776 | (JLIterator. jl-obj nil nil)))))) 777 | 778 | (def comp-iface-def (dt-ffi/define-foreign-interface :int32 [:pointer :pointer])) 779 | 780 | 781 | (defn fn->jl 782 | "Convert a clojure function to a Julia void-ptr." 783 | ([clj-fn {:keys [no-coerce?] :as options}] 784 | (errors/when-not-errorf (instance? IFn clj-fn) 785 | "Item %s is not an instance of IFn" 786 | clj-fn) 787 | (let [inst 788 | (dt-ffi/instantiate-foreign-interface 789 | comp-iface-def 790 | (if no-coerce? 791 | clj-fn 792 | (fn [jl-args] 793 | (try 794 | (let [retval (apply clj-fn (julia-proto/julia->jvm 795 | jl-args options))] 796 | ;;Explicit nil check so we can return booleans. 797 | (if (nil? retval) 798 | (julia-ffi/jl_nothing) 799 | (julia-proto/->julia retval))) 800 | (catch Throwable e 801 | (log/warnf e "Clojure fn errored during julia call") 802 | (julia-ffi/jl_nothing)))))) 803 | 804 | fn-ptr (dt-ffi/foreign-interface-instance->c comp-iface-def inst)] 805 | (julia-proto/julia->jvm (julia-ffi/jl_box_voidpointer fn-ptr) 806 | {:gc-obj [clj-fn fn-ptr]}))) 807 | ([clj-fn] 808 | (fn->jl clj-fn nil))) 809 | 810 | 811 | (def fn-wrapper* (delay (-> 812 | (julia-ffi/jl_eval_string "function(fn_ptr::Ptr{Nothing}) return function(a...) return ccall(fn_ptr, Any, (Any,), a) end end") 813 | (julia-proto/julia->jvm nil)))) 814 | 815 | ;;Object default protocol implementation 816 | (extend-type Object 817 | julia-proto/PToJulia 818 | (->julia [item] 819 | (if (instance? IFn item) 820 | (let [jl-fn (fn->jl item) 821 | ^CallableJuliaObject wrapped-fn (@fn-wrapper* jl-fn)] 822 | (CallableJuliaObject. (.handle wrapped-fn) (.kw-fn-handle wrapped-fn) 823 | [jl-fn (.gc-obj wrapped-fn)])) 824 | (errors/throwf "Item %s is not convertible to julia" item)))) 825 | -------------------------------------------------------------------------------- /src/libjulia_clj/impl/collections.clj: -------------------------------------------------------------------------------- 1 | (ns libjulia-clj.impl.collections 2 | (:require [libjulia-clj.impl.base :as base] 3 | [libjulia-clj.impl.protocols :as julia-proto] 4 | [libjulia-clj.impl.ffi :as julia-ffi] 5 | [tech.v3.datatype.pprint :as dtype-pp] 6 | [tech.v3.datatype.ffi :as dt-ffi]) 7 | (:import [java.util Map Iterator] 8 | [clojure.lang ILookup ISeq MapEntry] 9 | [com.sun.jna Pointer] 10 | [tech.v3.datatype ObjectReader])) 11 | 12 | 13 | (deftype JuliaDict [^Pointer handle] 14 | julia-proto/PToJulia 15 | (->julia [item] handle) 16 | dt-ffi/PToPointer 17 | (convertible-to-pointer? [this] true) 18 | (->pointer [this] handle) 19 | Map 20 | (size [this] (int (base/module-fn :length handle))) 21 | (isEmpty [this] (boolean (base/module-fn :isempty handle))) 22 | (containsKey [this k] (boolean (base/module-fn :haskey handle k))) 23 | (containsValue [this v] (throw (UnsupportedOperationException. "Unimplemented"))) 24 | (get [this k] (base/module-fn :get handle k)) 25 | (put [this k v] (base/module-fn :setindex! handle v k)) 26 | (remove [this k] (base/module-fn :delete! handle k)) 27 | (putAll [this m] (throw (UnsupportedOperationException.))) 28 | (clear [this] (throw (UnsupportedOperationException.))) 29 | (keySet [this] 30 | (throw (UnsupportedOperationException.))) 31 | (values [this] (throw (UnsupportedOperationException.))) 32 | (entrySet [this] 33 | (throw (UnsupportedOperationException.))) 34 | Iterable 35 | (iterator [this] 36 | (let [pairs (base/module-fn :pairs handle) 37 | base-iterator (.iterator (base/julia-obj->iterable pairs))] 38 | (reify Iterator 39 | (hasNext [iter] (.hasNext base-iterator)) 40 | (next [iter] 41 | (let [next-pair (.next base-iterator)] 42 | (MapEntry. (base/module-fn :getindex next-pair 1) 43 | (base/module-fn :getindex next-pair 2))))))) 44 | Object 45 | (toString [this] 46 | (base/jl-obj->str handle))) 47 | 48 | 49 | (dtype-pp/implement-tostring-print JuliaDict) 50 | 51 | 52 | (defmethod julia-proto/julia->jvm "Dict" 53 | [jl-ptr options] 54 | (base/root-ptr! jl-ptr options) 55 | (JuliaDict. jl-ptr)) 56 | 57 | 58 | (deftype JuliaTuple [^Pointer handle] 59 | julia-proto/PToJulia 60 | (->julia [item] handle) 61 | dt-ffi/PToPointer 62 | (convertible-to-pointer? [this] true) 63 | (->pointer [this] handle) 64 | ObjectReader 65 | (elemwiseDatatype [this] 66 | (-> (base/module-fn :eltype handle) 67 | (julia-ffi/julia-eltype->datatype))) 68 | (lsize [this] (long (base/module-fn :length handle))) 69 | (readObject [this idx] 70 | (base/module-fn :getindex handle (int (inc idx)))) 71 | Object 72 | (toString [this] 73 | (base/jl-obj->str handle))) 74 | 75 | 76 | (dtype-pp/implement-tostring-print JuliaTuple) 77 | 78 | 79 | (defmethod julia-proto/julia->jvm "Tuple" 80 | [jl-ptr options] 81 | (base/root-ptr! jl-ptr options) 82 | (JuliaTuple. jl-ptr)) 83 | -------------------------------------------------------------------------------- /src/libjulia_clj/impl/ffi.clj: -------------------------------------------------------------------------------- 1 | (ns libjulia-clj.impl.ffi 2 | "Low level raw JNA bindings and some basic initialization facilities" 3 | (:require [tech.v3.datatype.pprint :as dtype-pp] 4 | [tech.v3.datatype.ffi :as dt-ffi] 5 | [tech.v3.datatype.ffi.size-t :as ffi-size-t] 6 | [tech.v3.datatype.ffi.ptr-value :as ffi-ptr-value] 7 | [tech.v3.datatype.errors :as errors] 8 | [tech.v3.datatype.native-buffer :as native-buffer] 9 | [tech.v3.datatype.casting :as casting] 10 | [tech.v3.datatype :as dt] 11 | [tech.v3.datatype.jvm-map :as jvm-map] 12 | [tech.v3.resource :as resource] 13 | [camel-snake-kebab.core :as csk] 14 | [clojure.string :as s] 15 | [clojure.set :as set] 16 | [clojure.tools.logging :as log])) 17 | 18 | 19 | (set! *warn-on-reflection* true) 20 | 21 | 22 | (defonce julia-library-path* (atom "julia")) 23 | 24 | 25 | (def julia-fns-def 26 | {:jl_parse_opts {:rettype :void 27 | :argtypes [['argcp :pointer] 28 | ['argvp :pointer]] 29 | :doc "parse argc/argv pair"} 30 | :jl_init__threading {:rettype :void 31 | :doc "Initialize julia"} 32 | :jl_is_initialized {:rettype :int32 33 | :doc "Returns 1 if julia is initialized, 0 otherwise"} 34 | :jl_eval_string {:rettype :pointer 35 | :argtypes [['data :string]] 36 | :doc "Eval a string returning a julia object"} 37 | :jl_atexit_hook {:rettype :void 38 | :argytpes [['data :pointer]] 39 | :doc "Register a julia fn to run at program exit"} 40 | :jl_typename_str {:rettype :string 41 | :argtypes [['v :pointer]] 42 | :doc "Return the object typename as a string"} 43 | :jl_typeof_str {:rettype :string 44 | :argtypes [['v :pointer]] 45 | :doc "Return the type of an object as a string"} 46 | :jl_exception_occurred {:rettype :pointer? 47 | :doc "Return the current in-flight exception or nil"} 48 | :jl_symbol {:rettype :pointer 49 | :argtypes [['symbol-name :string]] 50 | :doc "Create a julia symbol from a string"} 51 | :jl_symbol_lookup {:rettype :pointer 52 | :argtypes [['symbol-name :string]] 53 | :doc "Find a julia symbol in the runtime"} 54 | :jl_symbol_n {:rettype :pointer 55 | :argtypes [['symbol-name :pointer] 56 | ['name-len :int64]] 57 | :doc "Create a julia symbol from a string"} 58 | :jl_gensym {:rettype :pointer 59 | :doc "Generate an opaque julia symbol"} 60 | :jl_typeof {:rettype :pointer 61 | :argtypes [['t :pointer]] 62 | :doc "Return the type of a thing as a julia symbol"} 63 | :jl_get_global {:rettype :pointer 64 | :argtypes [['m :pointer] 65 | ['s :pointer]] 66 | :doc "Get reference to module symbol"} 67 | :jl_module_name {:rettype :pointer 68 | :argytpes [['m :pointer]] 69 | :doc "Return the module name as a symbol"} 70 | 71 | :jl_get_module_binding {:doc "Return the module name as a symbol" 72 | :rettype :pointer 73 | :argtypes [['m :pointer] 74 | ['s :pointer]]} 75 | 76 | :jl_module_build_id {:doc "Return the module name as a symbol" 77 | :rettype :int64 78 | :argtypes [['m :pointer]]} 79 | :jl_get_default_sysimg_path {:doc "Get the default sysimg path" 80 | :rettype :string} 81 | 82 | 83 | :jl_subtype {:doc "Return 1 if this is a subtype of that" 84 | :rettype :int32 85 | :argtypes [['a :pointer] 86 | ['b :pointer]]} 87 | :jl_isa {:doc "Return 1 if 't' isa 'x'" 88 | :rettype :int32 89 | ['x :pointer] 90 | ['t :pointer]} 91 | :jl_call {:rettype :pointer 92 | :argtypes [['f :pointer] 93 | ['args :pointer] 94 | ['nargs :int32]] 95 | :doc "Call a julia function with a variable number of arguments"} 96 | :jl_call0 {:rettype :pointer 97 | :argtypes [['f :pointer]] 98 | :doc "Call a julia function with no arguments"} 99 | :jl_call1 {:rettype :pointer 100 | :argtypes [['f :pointer] 101 | ['arg0 :pointer]] 102 | :doc "Call a julia function with 1 argument"} 103 | :jl_call2 {:doc "Call a julia function with 2 arguments" 104 | :rettype :pointer 105 | :argtypes [['f :pointer] 106 | ['a0 :pointer] 107 | ['a1 :pointer]]} 108 | :jl_call3 {:doc "Call a julia function with 3 arguments" 109 | :rettype :pointer 110 | :argtypes [['f :pointer] 111 | ['a0 :pointer] 112 | ['a1 :pointer] 113 | ['a2 :pointer]]} 114 | :jl_box_bool {:doc "Box a boolean value" 115 | :rettype :pointer 116 | :argtypes [['x :int8]]} 117 | 118 | :jl_box_uint8 {:doc "Box a uint8 value" 119 | :rettype :pointer 120 | :argtypes [['x :int8]]} 121 | :jl_box_int8 {:doc "Box a int8 value" 122 | :rettype :pointer 123 | :argtypes [['x :int8]]} 124 | 125 | :jl_box_uint16 {:doc "Box a uint16 value" 126 | :rettype :pointer 127 | :argtypes [['x :int16]]} 128 | :jl_box_int16 {:doc "Box a int16 value" 129 | :rettype :pointer 130 | :argtypes [['x :int16]]} 131 | 132 | :jl_box_uint32 {:doc "Box a uint32 value" 133 | :rettype :pointer 134 | :argtypes [['x :int32]]} 135 | :jl_box_int32 {:doc "Box a int32 value" 136 | :rettype :pointer 137 | :argtypes [['x :int32]]} 138 | 139 | :jl_box_uint64 {:doc "Box a uint64 value" 140 | :rettype :pointer 141 | :argtypes [['x :int64]]} 142 | :jl_box_int64 {:doc "Box a int64 value" 143 | :rettype :pointer 144 | :argtypes [['x :int64]]} 145 | 146 | :jl_box_float32 {:doc "Box a float32 value" 147 | :rettype :pointer 148 | :argtypes [['x :float32]]} 149 | :jl_box_float64 {:doc "Box a float64 value" 150 | :rettype :pointer 151 | :argtypes [['x :float64]]} 152 | 153 | :jl_box_voidpointer {:doc "Box a pointer value to a julia voidptr value" 154 | :rettype :pointer 155 | :argtypes [['x :pointer]]} 156 | 157 | :jl_unbox_bool {:doc "Unbox a boolean" 158 | :rettype :int32 159 | :argtypes [['x :pointer]]} 160 | 161 | :jl_unbox_uint8 {:doc "Unbox a uint8 value" 162 | :rettype :int8 163 | :argtypes [['x :pointer]]} 164 | :jl_unbox_int8 {:doc "Unbox a int8 value" 165 | :rettype :int8 166 | :argtypes [['x :pointer]]} 167 | 168 | :jl_unbox_uint16 {:doc "Unbox a uint16 value" 169 | :rettype :int16 170 | :argtypes [['x :pointer]]} 171 | :jl_unbox_int16 {:doc "Unbox a int16 value" 172 | :rettype :int16 173 | :argtypes [['x :pointer]]} 174 | 175 | :jl_unbox_uint32 {:doc "Unbox a uint32 value" 176 | :rettype :int32 177 | :argtypes [['x :pointer]]} 178 | :jl_unbox_int32 {:doc "Unbox a int32 value" 179 | :rettype :int32 180 | :argtypes [['x :pointer]]} 181 | 182 | :jl_unbox_uint64 {:doc "Unbox a uint64 value" 183 | :rettype :int64 184 | :argtypes [['x :pointer]]} 185 | :jl_unbox_int64 {:doc "Unbox a int64 value" 186 | :rettype :int64 187 | :argtypes [['x :pointer]]} 188 | 189 | :jl_unbox_float32 {:doc "Unbox a float32 value" 190 | :rettype :float32 191 | :argtypes [['x :pointer]]} 192 | :jl_unbox_float64 {:doc "Unbox a float64 value" 193 | :rettype :float64 194 | :argtypes [['x :pointer]]} 195 | 196 | :jl_cstr_to_string {:doc "Convert a jvm string to a julia string" 197 | :rettype :pointer 198 | :argtypes [['arg :string]]} 199 | 200 | :jl_string_ptr {:doc "Convert a julia string to the jvm" 201 | :rettype :string 202 | :argtypes [['arg :pointer]]} 203 | 204 | :jl_array_size {:doc "Return the size of this dimension of the array" 205 | :rettype :size-t 206 | :argtypes [['ary :pointer] 207 | ['d :int32]]} 208 | 209 | :jl_array_rank {:doc "Return the rank of the array" 210 | :rettype :int32 211 | :argtypes [['ary :pointer]]} 212 | 213 | :jl_array_eltype {:doc "Return elemwise datatype of the array" 214 | :rettype :pointer 215 | :argtypes [['ary :pointer]]} 216 | 217 | :jl_array_ptr {:doc "Return a pointer to the elemwise data of the array" 218 | :rettype :pointer 219 | :argtypes [['ary :pointer]]} 220 | 221 | :jl_arrayref {:doc "Return the rank of the array" 222 | :rettype :pointer 223 | :argtypes [['ary :pointer] 224 | ['d :int32]]} 225 | 226 | :jl_apply_tuple_type_v {:doc "Create a new tuple type." 227 | :rettype :pointer 228 | :argtypes [['ary :pointer?] 229 | ['d :size-t]]} 230 | 231 | :jl_apply_type {:doc "Apply a julia type" 232 | :rettype :pointer 233 | :argtypes [['tc :pointer] 234 | ['params :pointer] 235 | ['n :size-t]]} 236 | 237 | :jl_apply_type1 {:doc "Apply a julia type to 1 argument" 238 | :rettype :pointer 239 | :argtypes [['tc :pointer] 240 | ['p1 :pointer]]} 241 | 242 | :jl_apply_type2 {:doc "Apply julia type to 2 arguments" 243 | :rettype :pointer 244 | :argtypes [['tc :pointer] 245 | ['p1 :pointer] 246 | ['p2 :pointer]]} 247 | 248 | :jl_new_structv {:doc "Create a new julia struct from some values" 249 | :rettype :pointer 250 | :argtypes [['datatype :pointer] 251 | ['args :pointer?] 252 | ['n-args :int32]]} 253 | 254 | 255 | :jl_gc_collect {:doc "Force a GC run" 256 | :rettype :void} 257 | 258 | :jl_gc_enable {:doc "Enable/disable the gc - 1 is enabled, 0 is disabled" 259 | :rettype :int32 260 | :argtypes [['enable :int32]]} 261 | 262 | :jl_gc_is_enabled {:doc "Return 1 if the julia gc is enabled" 263 | :rettype :int32} 264 | 265 | }) 266 | 267 | 268 | ;;The symbols we need from the shared library 269 | (def julia-symbols-def ["jl_core_module" 270 | "jl_base_module" 271 | "jl_bool_type" 272 | "jl_symbol_type" 273 | "jl_string_type" 274 | "jl_bool_type" 275 | "jl_int8_type" 276 | "jl_uint8_type" 277 | "jl_int16_type" 278 | "jl_uint16_type" 279 | "jl_int32_type" 280 | "jl_uint32_type" 281 | "jl_int64_type" 282 | "jl_uint64_type" 283 | "jl_float32_type" 284 | "jl_float64_type"]) 285 | 286 | 287 | (defonce julia (dt-ffi/library-singleton #'julia-fns-def #'julia-symbols-def nil)) 288 | (dt-ffi/library-singleton-reset! julia) 289 | (defn set-library-instance! 290 | [lib-instance] 291 | (dt-ffi/library-singleton-set-instance! julia lib-instance)) 292 | 293 | 294 | (defn- windows-os? 295 | [] 296 | (-> (System/getProperty "os.name") 297 | (.toLowerCase) 298 | (.contains "windows"))) 299 | 300 | 301 | (defn initialize! 302 | "Load the libjulia library. This does no further initialization, so it is necessary 303 | to the call 'init-process-options' and then 'jl_init__threading' at which point 304 | 'jl_is_initialized' should be true." 305 | [& [options]] 306 | (let [jh (or (:julia-home options) (System/getenv "JULIA_HOME")) 307 | _ (errors/when-not-errorf jh "JULIA_HOME is unset") 308 | jdirs ["lib" "bin"] 309 | jpaths (map #(-> (java.nio.file.Paths/get 310 | jh (into-array String [% (System/mapLibraryName 311 | (if (windows-os?) 312 | ;;julia is libXXX nomenclature on 313 | ;;all operating systems. 314 | "libjulia" 315 | "julia"))])) 316 | (.toString)) 317 | jdirs) 318 | found-dir (-> (filter #(.exists (java.io.File. ^String %)) jpaths) 319 | first)] 320 | (errors/when-not-errorf found-dir 321 | "Julia shared library not found at paths\n%s 322 | - is JULIA_HOME set incorrectly?", (interpose jpaths "\n")) 323 | (dt-ffi/library-singleton-set! julia found-dir) 324 | :ok)) 325 | 326 | 327 | (defn find-fn [fn-name] (dt-ffi/library-singleton-find-fn julia fn-name)) 328 | 329 | (dt-ffi/define-library-functions libjulia-clj.impl.ffi/julia-fns-def find-fn nil) 330 | 331 | (defn jl_symbol_name 332 | ^String [ptr] 333 | (dt-ffi/c->string ptr)) 334 | 335 | 336 | (defn jl_get_function 337 | "Find a julia function in a julia module" 338 | [module fn-name] 339 | (jl_get_global module (jl_symbol fn-name))) 340 | 341 | 342 | (defn native-addr 343 | ^long [data] 344 | (-> (dt/as-native-buffer data) 345 | (.address))) 346 | 347 | 348 | (defn init-process-options 349 | "Must be called before jl_init__threading. 350 | 351 | Options are: 352 | * `:enable-signals?` - true to enable julia signal handling. If false you must disable 353 | threads (set n-threads to 1). If using signals you must preload libjsig.so - see 354 | documentation. Defaults to false. 355 | * `:n-threads` - Set the number of threads to use with julia. Defaults to nil which means 356 | unless JULIA_NUM_THREADS is set signals are disabled." 357 | [& [{:keys [enable-signals? n-threads] 358 | :or {enable-signals? false}}]] 359 | (resource/stack-resource-context 360 | (let [n-threads (cond 361 | (= n-threads -1) (.availableProcessors (Runtime/getRuntime)) 362 | (nil? n-threads) (when-let [env-val (System/getenv "JULIA_NUM_THREADS")] 363 | (Integer/parseInt env-val)) 364 | (= n-threads :auto) :auto 365 | :else (int n-threads)) 366 | enable-signals? (if (boolean? enable-signals?) 367 | enable-signals? 368 | (or enable-signals? (not (nil? n-threads)))) 369 | opt-strs [(format "--handle-signals=%s" (if enable-signals? "yes" "no")) 370 | "--threads" (str (or n-threads 1))] 371 | ptr-type (ffi-size-t/lower-ptr-type :pointer) 372 | str-ptrs (mapv (comp native-addr dt-ffi/string->c) opt-strs) 373 | argv (dt/make-container :native-heap ptr-type str-ptrs) 374 | argvp (dt/make-container :native-heap ptr-type [(native-addr argv)]) 375 | argcp (dt/make-container :native-heap ptr-type [(count opt-strs)])] 376 | (log/infof "julia library arguments: %s" opt-strs) 377 | (jl_parse_opts argcp argvp) 378 | (argcp 0)))) 379 | 380 | (defonce julia-symbols (jvm-map/concurrent-hash-map)) 381 | 382 | (defn find-julia-symbol 383 | ^tech.v3.datatype.ffi.Pointer [sym-name] 384 | (jvm-map/compute-if-absent! julia-symbols sym-name 385 | (fn [& args] 386 | (-> (dt-ffi/library-singleton-find-symbol julia sym-name) 387 | (dt-ffi/->pointer))))) 388 | 389 | 390 | (defn find-deref-julia-symbol 391 | ^tech.v3.datatype.ffi.Pointer [sym-name] 392 | (let [ptr-ptr (-> (find-julia-symbol sym-name) 393 | (.address)) 394 | buf-dtype (ffi-size-t/lower-ptr-type :pointer) 395 | nbuf (native-buffer/wrap-address ptr-ptr (casting/numeric-byte-width buf-dtype) 396 | buf-dtype :little-endian nil)] 397 | ;;deref the ptr 398 | (tech.v3.datatype.ffi.Pointer. (nbuf 0)))) 399 | 400 | 401 | (defn jl_main_module 402 | [] 403 | (find-deref-julia-symbol "jl_main_module")) 404 | 405 | (defn jl_core_module 406 | [] 407 | (find-deref-julia-symbol "jl_core_module")) 408 | 409 | (defn jl_base_module 410 | [] 411 | (find-deref-julia-symbol "jl_base_module")) 412 | 413 | (defn jl_top_module 414 | [] 415 | (find-deref-julia-symbol "jl_top_module")) 416 | 417 | (defn jl_nothing 418 | [] 419 | (find-deref-julia-symbol "jl_nothing")) 420 | 421 | 422 | (def jl-type->datatype 423 | {:jl-bool-type :boolean 424 | :jl-int8-type :int8 425 | :jl-uint8-type :uint8 426 | :jl-int16-type :int16 427 | :jl-uint16-type :uint16 428 | :jl-int32-type :int32 429 | :jl-uint32-type :uint32 430 | :jl-int64-type :int64 431 | :jl-uint64-type :uint64 432 | :jl-float32-type :float32 433 | :jl-float64-type :float64 434 | :jl-string-type :string 435 | :jl-symbol-type :symbol}) 436 | 437 | 438 | (def datatype->jl-type 439 | (set/map-invert jl-type->datatype)) 440 | 441 | (defonce type-ptr->kwd (jvm-map/concurrent-hash-map)) 442 | (defonce kwd->type-ptr (jvm-map/concurrent-hash-map)) 443 | 444 | (defn add-library-julia-type! 445 | ([type-symbol-name clj-kwd] 446 | (let [type-ptr (find-deref-julia-symbol type-symbol-name)] 447 | (jvm-map/compute-if-absent! type-ptr->kwd type-ptr (constantly clj-kwd)) 448 | (jvm-map/compute-if-absent! kwd->type-ptr clj-kwd (constantly type-ptr)) 449 | type-ptr)) 450 | ([type-symbol-name] 451 | (add-library-julia-type! type-symbol-name (keyword type-symbol-name)))) 452 | 453 | 454 | (defn initialize-type-map! 455 | [] 456 | (doseq [[typename clj-kwd] jl-type->datatype] 457 | (add-library-julia-type! (.replace (name typename) "-" "_") clj-kwd)) 458 | :ok) 459 | 460 | 461 | (defn lookup-library-type 462 | [type-kwd] 463 | (if-let [retval (get kwd->type-ptr type-kwd)] 464 | retval 465 | (add-library-julia-type! (.replace (name (get datatype->jl-type type-kwd type-kwd)) 466 | "-" "_") type-kwd))) 467 | 468 | 469 | (defn jl-ptr->typename 470 | "If the typename is a known typename, return the keyword typename. 471 | Else return typeof_str." 472 | [item-ptr] 473 | (when (not= 0 (ffi-ptr-value/ptr-value? item-ptr)) 474 | (if-let [retval (get type-ptr->kwd (jl_typeof item-ptr))] 475 | retval 476 | (let [^String type-str (jl_typeof_str item-ptr)] 477 | (if (.startsWith type-str "#") 478 | :jl-function 479 | type-str))))) 480 | 481 | 482 | (defn julia-eltype->datatype 483 | [eltype] 484 | (get type-ptr->kwd eltype :object)) 485 | 486 | 487 | (defmacro with-disabled-julia-gc 488 | "Run a block of code with the julia garbage collector temporarily disabled." 489 | [& body] 490 | `(let [cur-enabled# (jl_gc_is_enabled)] 491 | (jl_gc_enable 0) 492 | (try 493 | ~@body 494 | (finally 495 | (jl_gc_enable cur-enabled#))))) 496 | -------------------------------------------------------------------------------- /src/libjulia_clj/impl/gc.clj: -------------------------------------------------------------------------------- 1 | (ns libjulia-clj.impl.gc 2 | "Binding of various sort of gc semantics optimized specifically for 3 | libpython-clj. For general bindings, see tech.resource" 4 | (:import [java.util.concurrent ConcurrentHashMap ConcurrentLinkedDeque] 5 | [java.lang.ref ReferenceQueue] 6 | [tech.resource GCReference])) 7 | 8 | 9 | (set! *warn-on-reflection* true) 10 | 11 | 12 | (defonce ^:dynamic *stack-gc-context* nil) 13 | (defn stack-context 14 | ^ConcurrentLinkedDeque [] 15 | *stack-gc-context*) 16 | 17 | 18 | (defonce reference-queue-var (ReferenceQueue.)) 19 | (defn reference-queue 20 | ^ReferenceQueue [] 21 | reference-queue-var) 22 | 23 | 24 | (defonce ptr-set-var (ConcurrentHashMap/newKeySet)) 25 | (defn ptr-set 26 | ^java.util.Set [] 27 | ptr-set-var) 28 | 29 | 30 | (defn track 31 | [item dispose-fn] 32 | (let [ptr-val (GCReference. item (reference-queue) (fn [ptr-val] 33 | (.remove (ptr-set) ptr-val) 34 | (dispose-fn))) 35 | ^ConcurrentLinkedDeque stack-context (stack-context)] 36 | ;;We have to keep track of the pointer. If we do not the pointer gets gc'd then 37 | ;;it will not be put on the reference queue when the object itself is gc'd. 38 | ;;Nice little gotcha there. 39 | (if stack-context 40 | (.add stack-context ptr-val) 41 | ;;Ensure we don't lose track of the weak reference. If it gets cleaned up 42 | ;;the gc system will fail. 43 | (.add (ptr-set) ptr-val)) 44 | item)) 45 | 46 | 47 | (defn clear-reference-queue 48 | [] 49 | (when-let [next-ref (.poll (reference-queue))] 50 | (.run ^Runnable next-ref) 51 | (recur))) 52 | 53 | 54 | (defn clear-stack-context 55 | [] 56 | (when-let [next-ref (.pollLast (stack-context))] 57 | (.run ^Runnable next-ref) 58 | (recur))) 59 | 60 | 61 | (defmacro with-stack-context 62 | [& body] 63 | `(with-bindings {#'*stack-gc-context* (ConcurrentLinkedDeque.)} 64 | (try 65 | ~@body 66 | (finally 67 | (clear-stack-context))))) 68 | -------------------------------------------------------------------------------- /src/libjulia_clj/impl/protocols.clj: -------------------------------------------------------------------------------- 1 | (ns libjulia-clj.impl.protocols 2 | "Protocols and multimethod definitions for the base julia binding. 3 | Protocols get completely rebound (thus loosing all older bindings) 4 | when recompiled so it is often a good idea to separate them out into 5 | their own file." 6 | (:require [libjulia-clj.impl.ffi :as julia-ffi])) 7 | 8 | 9 | (defprotocol PToJulia 10 | (->julia [item])) 11 | 12 | 13 | (defprotocol PJuliaKWFn 14 | "Implementation protocol to get the julia kw fn for a given fn." 15 | (kw-fn [item])) 16 | 17 | 18 | (defmulti julia->jvm 19 | "Convert a julia value to the JVM. 20 | 21 | Options: 22 | 23 | * `:unrooted?` - defaults to false. When true, value is not rooted and thus the 24 | julia GC may remove the value any point in your program's execution most 25 | likely resulting in a crash. 26 | * `:log-level` - When anything at all, jvm-gc<->julia-gc bindings will emit messages 27 | when an object is bound or unbound. 28 | * `:gc-obj` - Have the new julia object maintain a reference to this object. Only 29 | used for very special cases." 30 | (fn [julia-val options] 31 | (let [dispatch (julia-ffi/jl-ptr->typename julia-val)] 32 | dispatch))) 33 | -------------------------------------------------------------------------------- /src/libjulia_clj/java_api.clj: -------------------------------------------------------------------------------- 1 | (ns libjulia-clj.java-api 2 | "Java Users - Use the -aot postfixed version of the jar on clojars. Then import 3 | `libjulia_clj.java_api`. Each method in this namespace is exposed as a public static 4 | method on the java_api class so for instance `-initialize` is exposed as: 5 | 6 | ```java 7 | java_api.initialize(options); 8 | ```" 9 | (:import [clojure.java.api Clojure] 10 | [java.util Map Map$Entry] 11 | [java.util.function Supplier]) 12 | (:gen-class 13 | :name libjulia_clj.java_api 14 | :main false 15 | :methods [#^{:static true} [initialize [java.util.Map] Object] 16 | #^{:static true} [runString [String] Object] 17 | #^{:static true} [inJlContext [java.util.function.Supplier] Object] 18 | #^{:static true} [namedTuple [java.util.Map] Object] 19 | #^{:static true} [createArray [String Object Object] Object] 20 | #^{:static true} [arrayToJVM [Object] java.util.Map]])) 21 | 22 | 23 | (set! *warn-on-reflection* true) 24 | 25 | 26 | (defonce requires* (delay 27 | (let [require (Clojure/var "clojure.core" "require")] 28 | (require (Clojure/read "tech.v3.datatype")) 29 | (require (Clojure/read "libjulia-clj.julia")) 30 | (require (Clojure/read "tech.v3.tensor"))))) 31 | 32 | 33 | (defn -initialize 34 | "Initialize the julia interpreter. See documentation for [[libjulia-clj.julia/initialize!]]. 35 | Options may be null or must be a map of string->value for one of the supported initialization 36 | values. JULIA_HOME can be set by the user by setting the key \"julia-home\" in the 37 | options map to the desired value and this will supercede the environment variable 38 | version. 39 | 40 | Example: 41 | 42 | ```clojure 43 | (japi/-initialize (jvm-map/hash-map {\"n-threads\" 8})) 44 | ```" 45 | [options] 46 | @requires* 47 | ((Clojure/var "libjulia-clj.julia" "initialize!") 48 | (->> options 49 | (map (fn [^Map$Entry entry] 50 | [(keyword (.getKey entry)) 51 | (.getValue entry)])) 52 | (into {})))) 53 | 54 | 55 | (defn -runString 56 | "Run a string in Julia returning a jvm object if the return value is simple or 57 | a julia object if not. The returned object will have a property overloaded 58 | toString method for introspection." 59 | [^String data] 60 | ((Clojure/var "libjulia-clj.julia" "jl") data)) 61 | 62 | 63 | (defn -inJlContext 64 | "Execute a function in a context where all julia objects created will be released 65 | just after the function returns. The function must return pure JVM data - it cannot 66 | return a reference to a julia object." 67 | [^Supplier fn] 68 | ((Clojure/var "libjulia-clj.julia" "in-jl-ctx") fn)) 69 | 70 | 71 | (defn -namedTuple 72 | "Create a julia named tuple. This is required for calling keyword functions. The 73 | path for calling keyword functions looks something like: 74 | 75 | * `data` - must be an implementation of java.util.Map with strings as keys. 76 | 77 | ```clojure 78 | (let [add-fn (jl \"function teste(a;c = 1.0, b = 2.0) 79 | a+b+c 80 | end\") 81 | kwfunc (jl \"Core.kwfunc\") 82 | add-kwf (kwfunc add-fn)] 83 | (is (= 38.0 (add-kwf (jl/named-tuple {'b 10 'c 20}) 84 | add-fn 85 | 8.0))) 86 | (is (= 19.0 (add-kwf (jl/named-tuple {'b 10}) 87 | add-fn 88 | 8.0))) 89 | (is (= 11.0 (add-kwf (jl/named-tuple) 90 | add-fn 91 | 8.0))) 92 | 93 | (is (= 38.0 (add-fn 8.0 :b 10 :c 20))) 94 | (is (= 19.0 (add-fn 8 :b 10))) 95 | (is (= 11.0 (add-fn 8)))) 96 | ```" 97 | [^Map data] 98 | ((Clojure/var "libjulia-clj.julia" "named-tuple") data)) 99 | 100 | 101 | (defn -createArray 102 | "Return julia array out of the tuple of datatype, shape, and data. 103 | 104 | * `datatype` - must be one of the strings `[\"int8\" \"uint8\" \"int16\" \"uin16\" 105 | \"int32\" \"uint32\" \"int64\" \"uint64\" \"float32\" \"float64\"]. 106 | * `shape` - an array or implementation of java.util.List that specifies the row-major 107 | shape intended of the data. Note that Julia is column-major so this data will appear 108 | transposed when printed via Julia. 109 | * `data` may be a java array or an implementation of java.util.List. Ideally data is 110 | of the same datatype as data." 111 | [datatype shape data] 112 | (let [datatype (keyword datatype) 113 | reshape (Clojure/var "tech.v3.tensor" "reshape") 114 | ->array (Clojure/var "libjulia-clj.julia" "->array")] 115 | (-> ((Clojure/var "tech.v3.tensor" "->tensor") data :datatype (keyword datatype)) 116 | (reshape shape) 117 | (->array)))) 118 | 119 | 120 | (defn -arrayToJVM 121 | "Returns a map with three keys - shape, datatype, and data. Shape is an integer array 122 | that is the reverse of the julia shape, datatype is a string denoting one of the supported 123 | datatypes, and data is a primitive array of data." 124 | [jlary] 125 | (let [tens-data ((Clojure/var "tech.v3.tensor" "as-tensor") jlary) 126 | tens-shape ((Clojure/var "tech.v3.datatype" "shape") tens-data) 127 | n-dims (count tens-shape)] 128 | {"shape" ((Clojure/var "tech.v3.datatype" "->int-array") tens-shape) 129 | "datatype" (name ((Clojure/var "tech.v3.datatype" "elemwise-datatype") tens-data)) 130 | "data" ((Clojure/var "tech.v3.datatype" "->array") tens-data)})) 131 | -------------------------------------------------------------------------------- /src/libjulia_clj/julia.clj: -------------------------------------------------------------------------------- 1 | (ns libjulia-clj.julia 2 | "Public API for Julia functionality. Initialize! must be called before any other functions 3 | and there are a choice of garbgage collection mechanisms (see topic in docs). 4 | is probably a bad idea to call Julia from multiple threads so access to julia 5 | should be from one thread or protected via a mutex. 6 | 7 | Example: 8 | 9 | ```clojure 10 | user> (require '[libjulia-clj.julia :as julia]) 11 | nil 12 | user> (julia/initialize!) 13 | :ok 14 | user> (def ones-fn (julia/jl \"Base.ones\")) 15 | Nov 27, 2020 12:39:39 PM clojure.tools.logging$eval6611$fn__6614 invoke 16 | INFO: Rooting address 0x00007F3E092D6E40 17 | #'user/ones-fn 18 | user> (def jl-ary (ones-fn 3 4)) 19 | 20 | Nov 27, 2020 12:40:02 PM clojure.tools.logging$eval6611$fn__6614 invoke 21 | INFO: Rooting address 0x00007F3DFF9C92A0 22 | #'user/jl-ary 23 | user> jl-ary 24 | [1.0 1.0 1.0 1.0; 1.0 1.0 1.0 1.0; 1.0 1.0 1.0 1.0] 25 | user> (type jl-ary) 26 | libjulia_clj.impl.base.JuliaArray 27 | user> (require '[tech.v3.tensor :as dtt]) 28 | nil 29 | user> ;;zero-copy 30 | user> (def tens (dtt/as-tensor jl-ary)) 31 | #'user/tens 32 | user> tens 33 | #tech.v3.tensor[3 4] 34 | [[1.000 1.000 1.000 1.000] 35 | [1.000 1.000 1.000 1.000] 36 | [1.000 1.000 1.000 1.000]] 37 | user> (dtt/mset! tens 0 25) 38 | #tech.v3.tensor[3 4] 39 | [[25.00 25.00 25.00 25.00] 40 | [1.000 1.000 1.000 1.000] 41 | [1.000 1.000 1.000 1.000]] 42 | user> jl-ary 43 | [25.0 25.0 25.0 25.0; 1.0 1.0 1.0 1.0; 1.0 1.0 1.0 1.0] 44 | ```" 45 | (:require [libjulia-clj.impl.base :as base] 46 | [libjulia-clj.impl.protocols :as julia-proto] 47 | [libjulia-clj.impl.ffi :as julia-ffi] 48 | [libjulia-clj.impl.gc :as julia-gc] 49 | ;;pure language extensions for now. 50 | [libjulia-clj.impl.collections] 51 | [tech.v3.datatype :as dtype] 52 | [tech.v3.tensor :as dtt] 53 | [tech.v3.datatype.export-symbols :refer [export-symbols]] 54 | [tech.v3.datatype.errors :as errors]) 55 | (:refer-clojure :exclude [struct])) 56 | 57 | 58 | (export-symbols libjulia-clj.impl.base 59 | initialize! 60 | apply-tuple-type 61 | apply-type 62 | struct 63 | tuple 64 | named-tuple 65 | call-function 66 | call-function-kw) 67 | 68 | 69 | (defn jl 70 | "Eval a string in julia returning the result. If the result is callable in Julia, 71 | the result will be callable in Clojure." 72 | ([str-data options] 73 | (let [retval (julia-ffi/jl_eval_string str-data)] 74 | (base/check-last-error) 75 | (julia-proto/julia->jvm retval nil))) 76 | ([str-data] 77 | (jl str-data nil))) 78 | 79 | 80 | (defn typeof 81 | "Get the julia type of an item." 82 | [item] 83 | (when item 84 | (let [retval (julia-ffi/jl_typeof item)] 85 | (base/check-last-error) 86 | (julia-proto/julia->jvm retval nil)))) 87 | 88 | 89 | (defn ^{:doc (:doc (meta #'base/apply-tuple-type))} 90 | apply-tuple-type 91 | [& args] 92 | (base/apply-tuple-type args)) 93 | 94 | 95 | (defn ^{:doc (:doc (meta #'base/apply-type))} apply-type 96 | [jl-type & args] 97 | (base/apply-type jl-type args)) 98 | 99 | 100 | (defn ^{:doc (:doc (meta #'base/struct))} struct 101 | [struct-type & args] 102 | (base/struct struct-type args)) 103 | 104 | 105 | (defn ^{:doc (:doc (meta #'base/tuple))} tuple 106 | [& args] 107 | (base/tuple args)) 108 | 109 | 110 | (defn cycle-gc! 111 | "Call periodically to release rooted Julia objects. We root return values if they 112 | aren't primitives and then hook them to the jvm GC such that they get put in a queue 113 | once they aren't reachable by the program. This call clears that queue and unroots 114 | them thus notifying the Julia GC that they aren't reachable any more. 115 | 116 | In the future point this may be done for you." 117 | [] 118 | (julia-gc/clear-reference-queue)) 119 | 120 | 121 | (defmacro with-stack-context 122 | "Run code in which all objects created within this context will be released once 123 | the stack unwinds where released means unrooted and thus potentially available to 124 | the next julia garbage collection run." 125 | [& body] 126 | `(julia-gc/with-stack-context 127 | ~@body)) 128 | 129 | 130 | (defn ^:no-doc in-jl-ctx 131 | [^java.util.function.Supplier supplier] 132 | (with-stack-context 133 | (-> (.get supplier) 134 | (julia-proto/julia->jvm nil)))) 135 | 136 | 137 | (defn set-julia-gc-root-log-level! 138 | "Set the log level to use when rooting/unrooting julia objects. We automatically 139 | root julia objects in a julia id dictionary that we expose to JVM objects. 140 | This code isn't yet perfect, so sometimes it is worthwhile to log all 141 | root/unroot operations. 142 | 143 | Valid log levels are valid log levels for 144 | [`clojure/tools.logging`](http://clojure.github.io/tools.logging/)." 145 | [log-level] 146 | (reset! base/julia-gc-root-log-level* log-level)) 147 | 148 | 149 | (defonce ^{:doc "Resolves to the base julia array datatype"} 150 | base-ary-type* (delay (jl "Base.Array"))) 151 | 152 | (defonce ^{:doc "Resolves to the julia undef type"} 153 | jl-undef* (delay (jl "Base.undef"))) 154 | 155 | 156 | (defonce zeros* (delay (jl "zeros"))) 157 | 158 | 159 | (defn new-array 160 | "Create a new, uninitialized dense julia array. Because Julia is column major 161 | while tech.v3.datatype is row major, the returned array's size will be the 162 | reverse of dtype/shape as that keeps the same in memory alignment of data." 163 | ([shape datatype] 164 | (let [jl-dtype (julia-ffi/lookup-library-type datatype)] 165 | (apply @zeros* jl-dtype shape))) 166 | ([shape] 167 | (new-array shape :float64))) 168 | 169 | 170 | (defn ->array 171 | "Create a new dense julia array that is the 'transpose' of the input tensor. 172 | Transposing ensures the memory alignment matches as Julia is column-major 173 | while datatype is row-major." 174 | [tens] 175 | (let [dtype (dtype/elemwise-datatype tens) 176 | tens-shape (dtype/shape tens) 177 | retval (new-array (reverse tens-shape) dtype)] 178 | (dtype/copy! tens retval) 179 | retval)) 180 | -------------------------------------------------------------------------------- /src/libjulia_clj/modules/Base.clj: -------------------------------------------------------------------------------- 1 | (ns libjulia-clj.modules.Base 2 | (:require [libjulia-clj.impl.base :as base]) 3 | (:refer-clojure :exclude [* + - < <= == > >= 4 | cat conj conj! count denominator empty first filter float flush gensym hash 5 | keys last map max merge iterate macroexpand methods min mod numerator 6 | peek pop! print println rand range rationalize read reduce rem repeat 7 | replace reverse sort time vec get identity])) 8 | 9 | (base/initialize!) 10 | 11 | 12 | (base/define-module-publics 13 | "Base" 14 | ;;Remap names to avoid compilation errors 15 | {"def" "jl-def" 16 | "'" "quote" 17 | ":" "colon" 18 | "/" "div" 19 | "//" "divdiv" 20 | "div" "divv" 21 | "Enum" "jl-Enum"}) 22 | -------------------------------------------------------------------------------- /src/libjulia_clj/modules/Core.clj: -------------------------------------------------------------------------------- 1 | (ns libjulia-clj.modules.Core 2 | (:require [libjulia-clj.impl.base :as base]) 3 | (:refer-clojure :exclude [eval])) 4 | 5 | 6 | (base/initialize!) 7 | 8 | 9 | (base/define-module-publics 10 | "Core" 11 | {"AssertionError" "jl-AssertionError" 12 | "Exception" "jl-Exception" 13 | "Integer" "jl-Integer" 14 | "Number" "jl-Number" 15 | "OutOfMemoryError" "jl-OutOfMemoryError" 16 | "StackOverflowError" "jl-StackOverflowError" 17 | "String" "jl-String"}) 18 | -------------------------------------------------------------------------------- /src/libjulia_clj/modules/LinearAlgebra.clj: -------------------------------------------------------------------------------- 1 | (ns libjulia-clj.modules.LinearAlgebra 2 | (:require [libjulia-clj.impl.base :as base]) 3 | (:refer-clojure :exclude [/ cond])) 4 | 5 | 6 | (base/initialize!) 7 | 8 | 9 | (base/define-module-publics 10 | "import LinearAlgebra;LinearAlgebra" 11 | ;;Remap names to avoid compilation errors 12 | {}) 13 | -------------------------------------------------------------------------------- /test/libjulia_clj/api_test.clj: -------------------------------------------------------------------------------- 1 | (ns libjulia-clj.api-test 2 | (:require [libjulia-clj.java-api :as japi] 3 | [tech.v3.datatype.jvm-map :as jvm-map] 4 | [clojure.test :refer [deftest is]])) 5 | 6 | 7 | (deftest java-api-test 8 | (japi/-initialize (jvm-map/hash-map {"n-threads" 8})) 9 | (is (= 4 (japi/-runString "2 + 2"))) 10 | (let [tuple (japi/-namedTuple {"a" 1 "b" 2}) 11 | getindex (japi/-runString "getindex")] 12 | (is (instance? clojure.lang.IFn getindex)) 13 | (is (= 1 (getindex tuple 1))) 14 | (is (= 2 (getindex tuple 2)))) 15 | (let [ary (japi/-createArray "int32" [2 3] (range 6))] 16 | (japi/-arrayToJVM ary))) 17 | -------------------------------------------------------------------------------- /test/libjulia_clj/julia_test.clj: -------------------------------------------------------------------------------- 1 | (ns libjulia-clj.julia-test 2 | (:require [libjulia-clj.julia :refer [jl] :as jl] 3 | [libjulia-clj.impl.base :as jl-base] 4 | [tech.v3.datatype :as dtype] 5 | [tech.v3.tensor :as dtt] 6 | [clojure.test :refer [deftest is]])) 7 | 8 | ;;init only once 9 | 10 | (defonce init* (delay (jl/initialize!))) 11 | 12 | @init* 13 | 14 | (jl/set-julia-gc-root-log-level! :info) 15 | 16 | 17 | (deftest julia-test 18 | (let [ones-fn (jl "Base.ones") 19 | jl-ary (ones-fn 3 4) 20 | tens-data (dtt/as-tensor jl-ary)] 21 | (dtt/mset! tens-data 0 25) 22 | ;;Make sure the both gc's are playing nice 23 | ;;with each other. 24 | (System/gc) 25 | (jl/cycle-gc!) 26 | ;;datatype is transpose of julia 27 | (is (= [4 3] (dtype/shape jl-ary))) 28 | (is (= [[25.0 25.0 25.0] 29 | [1.0 1.0 1.0] 30 | [1.0 1.0 1.0] 31 | [1.0 1.0 1.0]] 32 | (mapv vec (dtt/as-tensor jl-ary))))) 33 | (System/gc) 34 | (jl/cycle-gc!)) 35 | 36 | 37 | (deftest kw-manual-args-test 38 | (jl/with-stack-context 39 | (let [add-fn (jl "function teste(a;c = 1.0, b = 2.0) 40 | a+b+c 41 | end") 42 | kwfunc (jl "Core.kwfunc") 43 | add-kwf (kwfunc add-fn)] 44 | (is (= 38.0 (add-kwf (jl/named-tuple {'b 10 'c 20}) 45 | add-fn 46 | 8.0))) 47 | (is (= 19.0 (add-kwf (jl/named-tuple {'b 10}) 48 | add-fn 49 | 8.0))) 50 | (is (= 11.0 (add-kwf (jl/named-tuple) 51 | add-fn 52 | 8.0))) 53 | 54 | (is (= 38.0 (add-fn 8.0 :b 10 :c 20))) 55 | (is (= 19.0 (add-fn 8 :b 10))) 56 | (is (= 11.0 (add-fn 8)))))) 57 | 58 | 59 | (deftest stack-context 60 | (jl/with-stack-context 61 | (let [jl-data (jl/new-array [2 2] :float32) 62 | size (jl "size")] 63 | (= [2 2] (vec (size jl-data))))) 64 | (jl/with-stack-context 65 | (let [tdata (dtt/->tensor (partition 3 (range 9)) :datatype :int32) 66 | jl-ary (jl/->array tdata) 67 | ary-data (dtype/make-container :int32 jl-ary)] 68 | (is (= (vec (range 9)) 69 | (vec ary-data))))) 70 | (jl/with-stack-context 71 | (let [tdata (dtt/->tensor (partition 3 (range 9)) :datatype :int32) 72 | jl-ary (jl/->array tdata) 73 | id-fn (jl "identity") 74 | ;;This one should not get rooted 75 | jl-ary2 (id-fn jl-ary)] 76 | (is (= (vec (range 9)) 77 | (vec (dtype/make-container :int64 jl-ary2))))))) 78 | -------------------------------------------------------------------------------- /topics/gc.md: -------------------------------------------------------------------------------- 1 | # JVM/Julia Garbage Collection Integration 2 | 3 | Both the JVM and julia rely on garbage collection in order to decided 4 | when and how to cleanup objects. This means that we need a mechanism 5 | to ensure that objects visible across language boundaries are not cleaned 6 | up prematurely. 7 | 8 | 9 | ## Julia Objects in the JVM 10 | 11 | 12 | When a new julia object is returned from a function we first check 13 | if this is a primitive or atomic type - numbers, symbols, strings. 14 | If so, we convert the value into the JVM immediately. Else we 15 | return a JVM object and `root` the julia object in a datastructure 16 | we declared to julia. 17 | 18 | 19 | The user has a choice to link the Julia object to the JVM's GC mechanism 20 | such that when the JVM decided the object is no longer reachable we will 21 | then unroot the Julia object. This is default and requires the user 22 | to periodically call 23 | [`cycle-gc!`](https://github.com/cnuernber/libjulia-clj/blob/4bf826aa9651c848985e8e13c5d392db4da26d69/src/libjulia_clj/julia.clj#L112) 24 | in order to unroot objects as the JVM's callback happens in another thread and 25 | thus we can only mark objects that should be unrooted automatically. The 26 | gc based unrooting needs to be cooperative at this point to ensure it happens 27 | in whichever thread is currently using Julia. 28 | 29 | 30 | There is also a stack based mechanism, 31 | [`with-stack-context`](https://github.com/cnuernber/libjulia-clj/blob/4bf826aa9651c848985e8e13c5d392db4da26d69/src/libjulia_clj/julia.clj#L123) 32 | by which we can ensure that Julia objects rooted within a given stack 33 | scope are unrooted when programmatic flow exits that scope either 34 | normally or via an exception. 35 | 36 | 37 | ## JVM Objects in Julia 38 | 39 | 40 | - TODO - not sure if this is very necessary or the best way to handle this. 41 | Currently you can pass functions to Julia but this isn't very fleshed 42 | out and it is most likely not bullet-proof. If they are called from tasks 43 | then there is a decent chance they will be silently ignored. Most likely 44 | you, from the JVM side, will need to ensure they do not leave JVM scope 45 | while you think they are in scope in Julia. 46 | -------------------------------------------------------------------------------- /topics/images/julia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cnuernber/libjulia-clj/42685f6bc13d141522783efbf827d70c4d895336/topics/images/julia.png -------------------------------------------------------------------------------- /topics/signals.md: -------------------------------------------------------------------------------- 1 | # Julia, The JVM, and Signals 2 | 3 | Julia and the JVM both rely on an operating concept called signals 4 | which are a simple method of IPC. If you aren't familiar with them 5 | it probably isn't necessary to get familiar right now but it is necessary 6 | in order to use libjulia-clj for you to understand how the signal mechanism 7 | in these two systems interact and what happens when they interact poorly. 8 | 9 | 10 | ## Two Worlds Collide 11 | 12 | When using both Julia and the JVM in the same process you are likely to run into 13 | instances where, during their normal course of operation their respective usage of 14 | signals conflict. For instance, the JVM uses `SIGSEGV` during it's normal course of 15 | operation and if the Julia handler for `SIGSEGV` is installed then things like a 16 | normal JVM garbage collection run can cause the process to unceremoniously exit: 17 | 18 | ```clojure 19 | user> (require '[libjulia-clj.julia :as julia]) 20 | nil 21 | user> (julia/initialize! {:signals-enabled? true}) 22 | 16:14:40.838 [nRepl-session-926e4a65-853b-40b4-a182-0f4b8a0cdfa3] INFO libjulia-clj.impl.base - Attempting to initialize Julia at /home/chrisn/dev/cnuernber/libjulia-clj/julia-1.5.3/lib/libjulia.so 23 | 16:14:40.875 [nRepl-session-926e4a65-853b-40b4-a182-0f4b8a0cdfa3] INFO tech.v3.jna.base - Library /home/chrisn/dev/cnuernber/libjulia-clj/julia-1.5.3/lib/libjulia.so found at [:system "/home/chrisn/dev/cnuernber/libjulia-clj/julia-1.5.3/lib/libjulia.so"] 24 | 16:14:40.881 [nRepl-session-926e4a65-853b-40b4-a182-0f4b8a0cdfa3] INFO libjulia-clj.impl.jna - Julia startup options: n-threads null, signals? true 25 | :ok 26 | user> (System/gc) 27 | 28 | *** Closed on Mon Dec 14 16:14:45 2020 *** 29 | ``` 30 | 31 | Julia has an option to disable its use of signals but this results in a crash as it 32 | requires the use of at least `SIGINT` in order to manage garbage collection in 33 | multithreaded code. 34 | 35 | If we simply disable Julia's use of signals then single-threaded code works fine. 36 | Multithreaded code, however, will eventually crash without warning: 37 | 38 | ```clojure 39 | user> (require '[libjulia-clj.julia :as julia]) 40 | nil 41 | user> (julia/initialize! {:n-threads 8 :signals-enabled? false}) 42 | 16:28:10.854 [nRepl-session-a6713b61-bf94-4492-bcbb-7cc7e44c2a4f] INFO libjulia-clj.impl.base - Attempting to initialize Julia at /home/chrisn/dev/cnuernber/libjulia-clj/julia-1.5.3/lib/libjulia.so 43 | 16:28:10.908 [nRepl-session-a6713b61-bf94-4492-bcbb-7cc7e44c2a4f] INFO tech.v3.jna.base - Library /home/chrisn/dev/cnuernber/libjulia-clj/julia-1.5.3/lib/libjulia.so found at [:system "/home/chrisn/dev/cnuernber/libjulia-clj/julia-1.5.3/lib/libjulia.so"] 44 | 16:28:10.925 [nRepl-session-a6713b61-bf94-4492-bcbb-7cc7e44c2a4f] INFO libjulia-clj.impl.jna - Julia startup options: n-threads 8, signals? false 45 | :ok 46 | user> (System/gc) 47 | nil 48 | user> (julia/eval-string "Threads.@threads for i in 1:1000; zeros(1024, 1024) .+ zeros(1024, 1024); end") 49 | 50 | *** Closed on Mon Dec 14 16:28:25 2020 *** 51 | ``` 52 | 53 | ## The Work-Around For Now 54 | 55 | The JVM has a facility for [signal chaining](https://docs.oracle.com/javase/10/troubleshoot/handle-signals-and-exceptions.htm#JSTGD356). This allows us to launch the JVM 56 | in a particular way where installs a handler that listens for entities attempting 57 | to install a signal handler and it records these handlers. When a signal occurse, 58 | it can detect whether it came from the JVM or from an outside entity and thus route 59 | the correct signal to the correct handler as required. 60 | 61 | Using this facility is fairly simple, setup an environment variable LD_PRELOAD that 62 | forces the operating system to load a shared library that exports functions that 63 | allow the JVM to chain signals as opposed to simply handling them. 64 | 65 | 66 | If we modify our example from before with this pathway we can successfully run 67 | our example: 68 | 69 | ```console 70 | chrisn@chrisn-lt-01:~/dev/cnuernber/libjulia-clj$ find /usr -name "*jsig*" 71 | /usr/lib/jvm/java-11-openjdk-amd64/lib/server/libjsig.so 72 | /usr/lib/jvm/java-11-openjdk-amd64/lib/libjsig.so 73 | /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/server/libjsig.so 74 | /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/libjsig.so 75 | chrisn@chrisn-lt-01:~/dev/cnuernber/libjulia-clj$ export LD_PRELOAD=/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/libjsig.so 76 | ``` 77 | 78 | ```clojure 79 | user> (require '[libjulia-clj.julia :as julia]) 80 | nil 81 | user> ;;Signals are automatically enabled if n-threads has a value 82 | user> (julia/initialize! {:n-threads 8}) 83 | 16:37:40.695 [nRepl-session-447a06c6-bf23-4338-9618-34e0f841c82b] INFO libjulia-clj.impl.base - Attempting to initialize Julia at /home/chrisn/dev/cnuernber/libjulia-clj/julia-1.5.3/lib/libjulia.so 84 | 16:37:40.741 [nRepl-session-447a06c6-bf23-4338-9618-34e0f841c82b] INFO tech.v3.jna.base - Library /home/chrisn/dev/cnuernber/libjulia-clj/julia-1.5.3/lib/libjulia.so found at [:system "/home/chrisn/dev/cnuernber/libjulia-clj/julia-1.5.3/lib/libjulia.so"] 85 | 16:37:40.747 [nRepl-session-447a06c6-bf23-4338-9618-34e0f841c82b] INFO libjulia-clj.impl.jna - Julia startup options: n-threads 8, signals? true 86 | :ok 87 | user> (julia/eval-string "Threads.@threads for i in 1:1000; zeros(1024, 1024) .+ zeros(1024, 1024); end") 88 | nil 89 | ``` 90 | 91 | So, for now, we have to setup some nonstandard JVM state in order to use Julia to 92 | it's full potential. Still, it is pretty amazing that the chaining facility exists 93 | and that it works as advertised and at least we have a solid pathway forward in 94 | order to using Julia from the JVM or vice versa. 95 | --------------------------------------------------------------------------------