{"id":115614,"date":"2021-02-17T14:10:46","date_gmt":"2021-02-17T13:10:46","guid":{"rendered":"https:\/\/blog.jetbrains.com\/?post_type=kotlin&#038;p=115614"},"modified":"2021-04-28T22:15:23","modified_gmt":"2021-04-28T21:15:23","slug":"multik-multidimensional-arrays-in-kotlin","status":"publish","type":"kotlin","link":"https:\/\/blog.jetbrains.com\/fr\/kotlin\/2021\/02\/multik-multidimensional-arrays-in-kotlin","title":{"rendered":"Multik: Multidimensional Arrays in Kotlin"},"content":{"rendered":"<p>A lot of data-heavy tasks, as well as optimization problems, boil down to performing computations over multidimensional arrays. Today we\u2019d like to share with you the first preview of a library that aims to serve as a foundation for such computations \u2013 <a href=\"https:\/\/github.com\/Kotlin\/multik\" target=\"_blank\" rel=\"noopener\">Multik<\/a>. <\/p>\n<p>Multik offers both multidimensional array data structures and implementations of mathematical operations over them. The library has a simple and straightforward API and offers optimized performance. <\/p>\n<h2>Using Multik<\/h2>\n<p>Without further ado, here are some of the things you can do with Multik. <\/p>\n<h3>Create multidimensional arrays<\/h3>\n<p>Create a vector:<\/p>\n<pre class=\"kotlin-code\" data-highlight-only=\"true\" theme=\"idea\" indent=\"4\" style=\"visibility: hidden; padding: 36px 0;\">\r\nval a = mk.ndarray(mk[1, 2, 3])\r\n\/* [1, 2, 3] *\/\r\n<\/pre>\n<\/p>\n<p>Create a vector from a collection:<\/p>\n<pre class=\"kotlin-code\" data-highlight-only=\"true\" theme=\"idea\" indent=\"4\" style=\"visibility: hidden; padding: 36px 0;\">\r\nval myList = listOf(1, 2, 3)\r\nval a = mk.ndarray(myList)\r\n\/* [1, 2, 3] *\/\r\n<\/pre>\n<\/p>\n<p>Create a matrix (two-dimensional array):<\/p>\n<pre class=\"kotlin-code\" data-highlight-only=\"true\" theme=\"idea\" indent=\"4\" style=\"visibility: hidden; padding: 36px 0;\">\r\nval m = mk.ndarray(mk[myList, myList])\r\n\/*\r\n[[1, 2, 3],\r\n[1, 2, 3]]\r\n*\/\r\n<\/pre>\n<\/p>\n<p>Create a fixed-shape array of zeros:<\/p>\n<pre class=\"kotlin-code\" data-highlight-only=\"true\" theme=\"idea\" indent=\"4\" style=\"visibility: hidden; padding: 36px 0;\">\r\nmk.empty&lt;Double, D2&gt;(3, 4)\r\n\/*\r\n[[0.0, 0.0, 0.0, 0.0],\r\n[0.0, 0.0, 0.0, 0.0],\r\n[0.0, 0.0, 0.0, 0.0]]\r\n*\/\r\n<\/pre>\n<\/p>\n<p>Create an identity matrix (ones on the diagonal, the rest is set to 0)<\/p>\n<pre class=\"kotlin-code\" data-highlight-only=\"true\" theme=\"idea\" indent=\"4\" style=\"visibility: hidden; padding: 36px 0;\">\r\nval e = mk.identity&lt;Double&gt;(3) \/\/ create an identity array of shape (3, 3)\r\n\/*\r\n[[1.0, 0.0, 0.0],\r\n[0.0, 1.0, 0.0],\r\n[0.0, 0.0, 1.0]]\r\n*\/\r\n<\/pre>\n<\/p>\n<p>Create a 3-dimensional array (multik supports up to 4 dimensions):<\/p>\n<pre class=\"kotlin-code\" data-highlight-only=\"true\" theme=\"idea\" indent=\"4\" style=\"visibility: hidden; padding: 36px 0;\">\r\nmk.d3array(2, 2, 3) { it * it } \r\n\/*\r\n[[[0, 1, 4],\r\n[9, 16, 25]],\r\n\r\n[[36, 49, 64],\r\n[81, 100, 121]]]\r\n*\/\r\n<\/pre>\n<\/p>\n<h3>Perform mathematical operations over multidimensional arrays<\/h3>\n<pre class=\"kotlin-code\" data-highlight-only=\"true\" theme=\"idea\" indent=\"4\" style=\"visibility: hidden; padding: 36px 0;\">\r\nval a = mk.ndarray(mk[mk[1.0,2.0], mk[3.0,4.0]])\r\nval b = mk.identity&lt;Double&gt;(2)\r\n\r\na + b\r\n\/*\r\n[[2.0, 2.0],\r\n[3.0, 5.0]]\r\n*\/\r\n<\/pre>\n<pre class=\"kotlin-code\" data-highlight-only=\"true\" theme=\"idea\" indent=\"4\" style=\"visibility: hidden; padding: 36px 0;\">\r\na - b\r\n\/*\r\n[[0.0, 2.0],\r\n[3.0, 3.0]]\r\n*\/\r\n<\/pre>\n<pre class=\"kotlin-code\" data-highlight-only=\"true\" theme=\"idea\" indent=\"4\" style=\"visibility: hidden; padding: 36px 0;\">\r\nb \/ a\r\n\/*\r\n[[1.0, 0.0],\r\n[0.0, 0.25]]\r\n*\/\r\n<\/pre>\n<pre class=\"kotlin-code\" data-highlight-only=\"true\" theme=\"idea\" indent=\"4\" style=\"visibility: hidden; padding: 36px 0;\">\r\nb * a \r\n\/*\r\n[[1.0, 0.0],\r\n[0.0, 4.0]]\r\n*\/\r\n<\/pre>\n<h3>Element-wise mathematical operations<\/h3>\n<pre class=\"kotlin-code\" data-highlight-only=\"true\" theme=\"idea\" indent=\"4\" style=\"visibility: hidden; padding: 36px 0;\">\r\nmk.math.sin(a) \/\/ element-wise sin \r\nmk.math.cos(a) \/\/ element-wise cos\r\nmk.math.log(b) \/\/ element-wise natural logarithm\r\nmk.math.exp(b) \/\/ element-wise exp\r\nmk.linalg.dot(a, b) \/\/ dot product\r\n<\/pre>\n<h3>Aggregate functions<\/h3>\n<pre class=\"kotlin-code\" data-highlight-only=\"true\" theme=\"idea\" indent=\"4\" style=\"visibility: hidden; padding: 36px 0;\">\r\nmk.math.sum(a) \/\/ array-wise sum\r\nmk.math.min(b) \/\/ array-wise minimum elements\r\nmk.math.cumSum(b, axis=1) \/\/ cumulative sum of the elements\r\nmk.stat.mean(a) \/\/ mean\r\nmk.stat.median(b) \/\/ median\r\n<\/pre>\n<h3>Iterable operations<\/h3>\n<pre class=\"kotlin-code\" data-highlight-only=\"true\" theme=\"idea\" indent=\"4\" style=\"visibility: hidden; padding: 36px 0;\">\r\na.filter { it &gt; 3 } \/\/ select all elements that are larger than 3\r\nb.map { (it * it).toInt() } \/\/ return squares\r\na.groupNdarrayBy { it % 2 } \/\/ group elements by condition\r\na.sorted() \/\/ sort elements\r\n<\/pre>\n<h3>Indexing\/Slicing\/Iterating<\/h3>\n<pre class=\"kotlin-code\" data-highlight-only=\"true\" theme=\"idea\" indent=\"4\" style=\"visibility: hidden; padding: 36px 0;\">\r\na[2]  \/\/ select the element at the 2 index for a vector\r\nb[1, 2] \/\/ select the element at row 1 column 2\r\nb[1] \/\/ select row 1 \r\nb[0.r..2, 1] \/\/ select elements at rows 0 and 1 in column 1\r\nb[0..1..1] \/\/ select all elements at row 0\r\n<\/pre>\n<pre class=\"kotlin-code\" data-highlight-only=\"true\" theme=\"idea\" indent=\"4\" style=\"visibility: hidden; padding: 36px 0;\">\r\nfor (el in b) {\r\n    print(&quot;$el, &quot;) \/\/ 1.5, 2.1, 3.0, 4.0, 5.0, 6.0, \r\n}\r\n<\/pre>\n<pre class=\"kotlin-code\" data-highlight-only=\"true\" theme=\"idea\" indent=\"4\" style=\"visibility: hidden; padding: 36px 0;\">\r\n\/\/ for n-dimensional\r\nval q = b.asDNArray()\r\nfor (index in q.multiIndices) {\r\n    print(&quot;${q[index]}, &quot;) \/\/ 1.5, 2.1, 3.0, 4.0, 5.0, 6.0, \r\n}\r\n<\/pre>\n<h2>Multik Architecture<\/h2>\n<p>Initially, we attempted to add Kotlin bindings to existing solutions, such as NumPy. However, this proved cumbersome and introduced unnecessary environmental complexity while providing little benefit to justify the overhead. As a result, we have abandoned that approach and started Multik from scratch.<\/p>\n<p>In Multik, the data structures are separate from the implementation of operations over them, and you need to add them as individual dependencies to your project. This approach gives you a consistent API no matter what implementation you decide to use in your project. So what are these different implementations?<\/p>\n<p>Currently, there are three different ones: <\/p>\n<ul>\n<li><code>multik-jvm<\/code>: a Kotlin\/JVM implementation of the math operations.<\/li>\n<li><code>multik-native<\/code>: a C++ implementation. OpenBLAS is used for linear algebra. <\/li>\n<li><code>multik-default<\/code>: the default implementation, which combines native and JVM implementations for optimal performance. <\/li>\n<\/ul>\n<p>You can also write your own! <\/p>\n<p>Multik is still in the early stages of development, and we are looking forward to your feedback, feature requests, and contributions! Check out the project\u2019s <a href=\"https:\/\/github.com\/Kotlin\/multik\" target=\"_blank\" rel=\"noopener\">GitHub repo<\/a>, try Multik, and let us know what you\u2019d like to see in future versions. Thanks!<\/p>\n","protected":false},"author":70,"featured_media":116968,"comment_status":"closed","ping_status":"closed","template":"","categories":[909],"tags":[],"cross-post-tag":[],"acf":[],"_links":{"self":[{"href":"https:\/\/blog.jetbrains.com\/fr\/wp-json\/wp\/v2\/kotlin\/115614"}],"collection":[{"href":"https:\/\/blog.jetbrains.com\/fr\/wp-json\/wp\/v2\/kotlin"}],"about":[{"href":"https:\/\/blog.jetbrains.com\/fr\/wp-json\/wp\/v2\/types\/kotlin"}],"author":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/fr\/wp-json\/wp\/v2\/users\/70"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/fr\/wp-json\/wp\/v2\/comments?post=115614"}],"version-history":[{"count":2,"href":"https:\/\/blog.jetbrains.com\/fr\/wp-json\/wp\/v2\/kotlin\/115614\/revisions"}],"predecessor-version":[{"id":115641,"href":"https:\/\/blog.jetbrains.com\/fr\/wp-json\/wp\/v2\/kotlin\/115614\/revisions\/115641"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/fr\/wp-json\/wp\/v2\/media\/116968"}],"wp:attachment":[{"href":"https:\/\/blog.jetbrains.com\/fr\/wp-json\/wp\/v2\/media?parent=115614"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/fr\/wp-json\/wp\/v2\/categories?post=115614"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/fr\/wp-json\/wp\/v2\/tags?post=115614"},{"taxonomy":"cross-post-tag","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/fr\/wp-json\/wp\/v2\/cross-post-tag?post=115614"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}