diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 09f6cff..2d1378f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,6 +13,11 @@ jobs: build: runs-on: ubuntu-latest steps: + - name: Configure Java + uses: actions/setup-java@v2 + with: + distribution: 'adopt' + java-version: '11' - name: Checkout repository uses: actions/checkout@v1 - name: Build project diff --git a/.gitignore b/.gitignore index 26a9bfe..16d3539 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ *.iml +.idea target + +settings.xml diff --git a/README.md b/README.md index 5197fdf..dfe4822 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ To import the NdArray library in your project, simply add the following dependen org.tensorflow ndarray - 0.3.1 + 1.0.0 ``` @@ -37,7 +37,7 @@ assertEquals(4095L, bufferB.size()); assertEquals(2, bufferB.getInt(0)); // Resize a buffer to 10 elements -IntDataBuffer bufferC = bufferA.narrow(10); +IntDataBuffer bufferC = bufferB.narrow(10); assertEquals(10L, bufferB.size()); assertEquals(2, bufferB.getInt(0)); ``` @@ -75,7 +75,7 @@ IntNdArray vector = matrix3d.get(0, 1); assertEquals(1, vector.rank()); // Rewriting the values of the vector using a primitive array -vector.write(new int[] { 7, 8 }); +vector.copyFrom(DataBuffers.of(new int[] { 7, 8 })); assertEquals(7, matrix3d.getInt(0, 1, 0)); assertEquals(8, matrix3d.getInt(0, 1, 1)); diff --git a/RELEASE.md b/RELEASE.md new file mode 100644 index 0000000..30de621 --- /dev/null +++ b/RELEASE.md @@ -0,0 +1,177 @@ +# Releasing NdArray Java Library + +The +[NdArray Java Library](https://github.com/tensorflow/java-ndarray) is available on Maven Central and JCenter +through artifacts uploaded to +[OSS Sonatype](https://oss.sonatype.org/content/repositories/releases/org/tensorflow/). This +document describes the process of updating the release artifacts. It does _not_ describe how to use +the artifacts, for which the reader is referred to the +[NdArray Java Library installation instructions](https://github.com/tensorflow/java-ndarray/blob/main/README.md). + +## Release process overview + +NdArray Java Library must be conducted locally in a [Docker](https://www.docker.com) container for a hermetic release process. + +It is important to note that any change pushed to a release branch (i.e. a branch prefixed +by `r`) will start a new release workflow. Therefore, these changes should always increment the +version number. + +### Pre-requisites + +- `docker` +- An account at [oss.sonatype.org](https://oss.sonatype.org/), that has + permissions to update artifacts in the `org.tensorflow` group. If your + account does not have permissions, then you'll need to ask someone who does + to [file a ticket](https://issues.sonatype.org/) to add to the permissions + ([sample ticket](https://issues.sonatype.org/browse/MVNCENTRAL-1637)). +- A GPG signing key, required + [to sign the release artifacts](http://central.sonatype.org/pages/apache-maven.html#gpg-signed-components). + +### Preparing a release + +#### Major or minor release + +1. Get a clean version of the source code by cloning the + [NdArray Java Library GitHub repository](https://github.com/tensorflow/java-ndarray) + ``` + git clone https://github.com/tensorflow/java-ndarray + ``` +2. Create a new branch for the release named `r.` + ``` + git checkout -b r1.0 + ``` +3. Update the version of the Maven artifacts to the full version of the release + ``` + mvn versions:set -DnewVersion=1.0.0 + ``` +4. Update the NdArray Java Library version to reflect the new release at the following locations: + - https://github.com/tensorflow/java-ndarray/blob/main/README.md#introduction + +5. Commit the changes and push the branch to the GitHub repository + ``` + git add . + git commit -m "Releasing 1.0.0" + git push --set-upstream origin r1.0 + ``` + +#### Patch release + +1. Get a clean version of the source code by cloning the + [NdArray Java Library GitHub repository](https://github.com/tensorflow/java-ndarray) + ``` + git clone https://github.com/tensorflow/java-ndarray + ``` +2. Switch to the release branch of the version to patch + ``` + git checkout r1.0 + ``` +3. Patch the code with your changes. For example, changes could be merged from another branch you + were working on or be applied directly to this branch when the required changes are minimal. + +4. Update the version of the Maven artifacts to the full version of the release + ``` + mvn versions:set -DnewVersion=1.0.1 + ``` +5. Update the NdArray Java Library version to reflect the new release at the following locations: + - https://github.com/tensorflow/java-ndarray/blob/main/README.md#introduction + +6. Commit the changes and push the branch to the GitHub repository + ``` + git add . + git commit -m "Releasing 1.0.1" + git push + ``` + +### Performing the release + +1. At the root of your repository copy, create a Maven settings.xml file with your OSSRH credentials and + your GPG key passphrase: + ```sh + SONATYPE_USERNAME="your_sonatype.org_username_here" + SONATYPE_PASSWORD="your_sonatype.org_password_here" + GPG_PASSPHRASE="your_gpg_passphrase_here" + cat > settings.xml < + + + ossrh + ${SONATYPE_USERNAME} + ${SONATYPE_PASSWORD} + + + ossrh-staging + ${SONATYPE_USERNAME} + ${SONATYPE_PASSWORD} + + + + + + true + + + gpg2 + ${GPG_PASSPHRASE} + + + + + EOF + ``` +2. Execute the `release.sh` script. This will sign and deploy artifacts on OSS Sonatype. + + On success, the released artifacts are uploaded to the private staging repository in OSS Sonatype (check at the Maven + build output to know the exact location of the staging repository). After inspecting the artifacts in OSS Sonatype, you + should release or drop them. + + Visit https://oss.sonatype.org/#stagingRepositories, find the `orgtensorflow-*` + of your release and click `Close` and `Release` to finalize the release. You always have the option + to `Drop` it to abort and restart if something went wrong. + +3. Go to GitHub and create a release tag on the release branch with a summary of what the version includes. + +Some things of note: + - For details, look at the [Sonatype guide](http://central.sonatype.org/pages/releasing-the-deployment.html). + - Syncing with [Maven Central](http://repo1.maven.org/maven2/org/tensorflow/) + can take 10 minutes to 2 hours (as per the [OSSRH guide](http://central.sonatype.org/pages/ossrh-guide.html#releasing-to-central)). + +### Finishing a release + +#### Major or minor release + +1. Checkout the main branch and merge back changes from the released branch + ``` + git checkout main + git merge r1.0 + ``` +2. In your local copy, checkout the main branch and increase the next snapshot version. + ``` + mvn versions:set -DnewVersion=1.1.0-SNAPSHOT + ``` + +3. Commit your changes and push the main branch to the GitHub repository + ``` + git add . + git commit -m "Increase version for next iteration" + git push + ``` + +#### Patch release + +1. Checkout the main branch and merge back changes from the released branch, preserving current snapshot version + ``` + git checkout main + git merge r1.0 + ``` +2. Commit the main and push the main branch to the GitHub repository + ``` + git add . + git commit -m "Merge release 1.0.1" + git push + ``` + +## References + +- [Sonatype guide](http://central.sonatype.org/pages/ossrh-guide.html) for + hosting releases. +- [Ticket that created the `org/tensorflow` configuration](https://issues.sonatype.org/browse/OSSRH-28072) on OSSRH. diff --git a/ndarray/pom.xml b/ndarray/pom.xml index 3a8ba71..543cf70 100644 --- a/ndarray/pom.xml +++ b/ndarray/pom.xml @@ -22,7 +22,7 @@ org.tensorflow tensorflow-java-ndarray - 0.4.0-SNAPSHOT + 1.1.0-SNAPSHOT ndarray jar @@ -75,16 +75,31 @@ org.apache.maven.plugins maven-surefire-plugin - 2.22.2 + 3.0.0-M5 1 false - -Xmx2G -XX:MaxPermSize=256m + -Xmx2G **/*Test.java + false + + org.apache.maven.plugins + maven-compiler-plugin + + + default-testCompile + + + --add-modules=java.desktop + + + + + diff --git a/ndarray/src/main/java/module-info.java b/ndarray/src/main/java/module-info.java new file mode 100644 index 0000000..6b33ed6 --- /dev/null +++ b/ndarray/src/main/java/module-info.java @@ -0,0 +1,38 @@ +/* + Copyright 2022 The TensorFlow Authors. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + ======================================================================= + */ +module org.tensorflow.ndarray { + requires jdk.unsupported; // required by raw buffer implementations using Unsafe + + exports org.tensorflow.ndarray; + exports org.tensorflow.ndarray.buffer; + exports org.tensorflow.ndarray.buffer.layout; + exports org.tensorflow.ndarray.index; + + // Expose all implementions of our interfaces, so consumers can write custom + // implementations easily by extending from them + exports org.tensorflow.ndarray.impl.buffer; + exports org.tensorflow.ndarray.impl.buffer.adapter; + exports org.tensorflow.ndarray.impl.buffer.layout; + exports org.tensorflow.ndarray.impl.buffer.misc; + exports org.tensorflow.ndarray.impl.buffer.nio; + exports org.tensorflow.ndarray.impl.buffer.raw; + exports org.tensorflow.ndarray.impl.dense; + exports org.tensorflow.ndarray.impl.dimension; + exports org.tensorflow.ndarray.impl.sequence; + exports org.tensorflow.ndarray.impl.sparse; + exports org.tensorflow.ndarray.impl.sparse.slice; +} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/BooleanNdArray.java b/ndarray/src/main/java/org/tensorflow/ndarray/BooleanNdArray.java index 5b4bedb..a762896 100644 --- a/ndarray/src/main/java/org/tensorflow/ndarray/BooleanNdArray.java +++ b/ndarray/src/main/java/org/tensorflow/ndarray/BooleanNdArray.java @@ -68,6 +68,9 @@ public interface BooleanNdArray extends NdArray { */ BooleanNdArray setBoolean(boolean value, long... coordinates); + @Override + BooleanNdArray withShape(Shape shape); + @Override BooleanNdArray slice(Index... indices); @@ -97,12 +100,12 @@ default BooleanNdArray setObject(Boolean value, long... coordinates) { BooleanNdArray copyTo(NdArray dst); @Override - BooleanNdArray read(DataBuffer dst); + BooleanNdArray copyTo(DataBuffer dst); - BooleanNdArray read(BooleanDataBuffer dst); + BooleanNdArray copyTo(BooleanDataBuffer dst); @Override - BooleanNdArray write(DataBuffer src); + BooleanNdArray copyFrom(DataBuffer src); - BooleanNdArray write(BooleanDataBuffer src); + BooleanNdArray copyFrom(BooleanDataBuffer src); } diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/ByteNdArray.java b/ndarray/src/main/java/org/tensorflow/ndarray/ByteNdArray.java index 0e6f118..7a6f0cd 100644 --- a/ndarray/src/main/java/org/tensorflow/ndarray/ByteNdArray.java +++ b/ndarray/src/main/java/org/tensorflow/ndarray/ByteNdArray.java @@ -68,6 +68,9 @@ public interface ByteNdArray extends NdArray { */ ByteNdArray setByte(byte value, long... coordinates); + @Override + ByteNdArray withShape(Shape shape); + @Override ByteNdArray slice(Index... indices); @@ -97,12 +100,12 @@ default ByteNdArray setObject(Byte value, long... coordinates) { ByteNdArray copyTo(NdArray dst); @Override - ByteNdArray read(DataBuffer dst); + ByteNdArray copyTo(DataBuffer dst); - ByteNdArray read(ByteDataBuffer dst); + ByteNdArray copyTo(ByteDataBuffer dst); @Override - ByteNdArray write(DataBuffer src); + ByteNdArray copyFrom(DataBuffer src); - ByteNdArray write(ByteDataBuffer src); + ByteNdArray copyFrom(ByteDataBuffer src); } diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/DoubleNdArray.java b/ndarray/src/main/java/org/tensorflow/ndarray/DoubleNdArray.java index 80e99b0..d8d2312 100644 --- a/ndarray/src/main/java/org/tensorflow/ndarray/DoubleNdArray.java +++ b/ndarray/src/main/java/org/tensorflow/ndarray/DoubleNdArray.java @@ -1,5 +1,5 @@ /* - Copyright 2019 The TensorFlow Authors. All Rights Reserved. + Copyright 2019-2023 The TensorFlow Authors. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -20,6 +20,9 @@ import org.tensorflow.ndarray.buffer.DoubleDataBuffer; import org.tensorflow.ndarray.index.Index; +import java.util.stream.DoubleStream; +import java.util.stream.StreamSupport; + /** * An {@link NdArray} of doubles. */ @@ -68,6 +71,21 @@ public interface DoubleNdArray extends NdArray { */ DoubleNdArray setDouble(double value, long... coordinates); + /** + * Retrieve all scalar values of this array as a stream of doubles. + * + *

For {@code rank() > 1} arrays, all vectors of the last dimension are collated so that the scalar values are + * returned in sequential order.

+ * + * @return scalar values as a stream + */ + default DoubleStream streamOfDoubles() { + return StreamSupport.stream(scalars().spliterator(), false).mapToDouble(DoubleNdArray::getDouble); + } + + @Override + DoubleNdArray withShape(Shape shape); + @Override DoubleNdArray slice(Index... indices); @@ -97,12 +115,12 @@ default DoubleNdArray setObject(Double value, long... coordinates) { DoubleNdArray copyTo(NdArray dst); @Override - DoubleNdArray read(DataBuffer dst); + DoubleNdArray copyTo(DataBuffer dst); - DoubleNdArray read(DoubleDataBuffer dst); + DoubleNdArray copyTo(DoubleDataBuffer dst); @Override - DoubleNdArray write(DataBuffer src); + DoubleNdArray copyFrom(DataBuffer src); - DoubleNdArray write(DoubleDataBuffer src); + DoubleNdArray copyFrom(DoubleDataBuffer src); } diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/FloatNdArray.java b/ndarray/src/main/java/org/tensorflow/ndarray/FloatNdArray.java index 8d4fbf5..98ec87e 100644 --- a/ndarray/src/main/java/org/tensorflow/ndarray/FloatNdArray.java +++ b/ndarray/src/main/java/org/tensorflow/ndarray/FloatNdArray.java @@ -68,6 +68,9 @@ public interface FloatNdArray extends NdArray { */ FloatNdArray setFloat(float value, long... coordinates); + @Override + FloatNdArray withShape(Shape shape); + @Override FloatNdArray slice(Index... coordinates); @@ -97,12 +100,12 @@ default FloatNdArray setObject(Float value, long... coordinates) { FloatNdArray copyTo(NdArray dst); @Override - FloatNdArray read(DataBuffer dst); + FloatNdArray copyTo(DataBuffer dst); - FloatNdArray read(FloatDataBuffer dst); + FloatNdArray copyTo(FloatDataBuffer dst); @Override - FloatNdArray write(DataBuffer src); + FloatNdArray copyFrom(DataBuffer src); - FloatNdArray write(FloatDataBuffer src); + FloatNdArray copyFrom(FloatDataBuffer src); } diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/IntNdArray.java b/ndarray/src/main/java/org/tensorflow/ndarray/IntNdArray.java index aa2cc65..b165900 100644 --- a/ndarray/src/main/java/org/tensorflow/ndarray/IntNdArray.java +++ b/ndarray/src/main/java/org/tensorflow/ndarray/IntNdArray.java @@ -1,5 +1,5 @@ /* - Copyright 2019 The TensorFlow Authors. All Rights Reserved. + Copyright 2019-2023 The TensorFlow Authors. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -20,6 +20,9 @@ import org.tensorflow.ndarray.buffer.IntDataBuffer; import org.tensorflow.ndarray.index.Index; +import java.util.stream.IntStream; +import java.util.stream.StreamSupport; + /** * An {@link NdArray} of integers. */ @@ -68,6 +71,21 @@ public interface IntNdArray extends NdArray { */ IntNdArray setInt(int value, long... coordinates); + /** + * Retrieve all scalar values of this array as a stream of integers. + * + *

For {@code rank() > 1} arrays, all vectors of the last dimension are collated so that the scalar values are + * returned in sequential order.

+ * + * @return scalar values as a stream + */ + default IntStream streamOfInts() { + return StreamSupport.stream(scalars().spliterator(), false).mapToInt(IntNdArray::getInt); + } + + @Override + IntNdArray withShape(Shape shape); + @Override IntNdArray slice(Index... indices); @@ -97,12 +115,12 @@ default IntNdArray setObject(Integer value, long... coordinates) { IntNdArray copyTo(NdArray dst); @Override - IntNdArray read(DataBuffer dst); + IntNdArray copyTo(DataBuffer dst); - IntNdArray read(IntDataBuffer dst); + IntNdArray copyTo(IntDataBuffer dst); @Override - IntNdArray write(DataBuffer src); + IntNdArray copyFrom(DataBuffer src); - IntNdArray write(IntDataBuffer src); + IntNdArray copyFrom(IntDataBuffer src); } diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/LongNdArray.java b/ndarray/src/main/java/org/tensorflow/ndarray/LongNdArray.java index 3e5be6d..5d5766a 100644 --- a/ndarray/src/main/java/org/tensorflow/ndarray/LongNdArray.java +++ b/ndarray/src/main/java/org/tensorflow/ndarray/LongNdArray.java @@ -1,5 +1,5 @@ /* - Copyright 2019 The TensorFlow Authors. All Rights Reserved. + Copyright 2019-2023 The TensorFlow Authors. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -20,6 +20,9 @@ import org.tensorflow.ndarray.buffer.LongDataBuffer; import org.tensorflow.ndarray.index.Index; +import java.util.stream.LongStream; +import java.util.stream.StreamSupport; + /** * An {@link NdArray} of longs. */ @@ -68,6 +71,21 @@ public interface LongNdArray extends NdArray { */ LongNdArray setLong(long value, long... coordinates); + /** + * Retrieve all scalar values of this array as a stream of longs. + * + *

For {@code rank() > 1} arrays, all vectors of the last dimension are collated so that the scalar values are + * returned in sequential order.

+ * + * @return scalar values as a stream + */ + default LongStream streamOfLongs() { + return StreamSupport.stream(scalars().spliterator(), false).mapToLong(LongNdArray::getLong); + } + + @Override + LongNdArray withShape(Shape shape); + @Override LongNdArray slice(Index... indices); @@ -97,12 +115,12 @@ default LongNdArray setObject(Long value, long... coordinates) { LongNdArray copyTo(NdArray dst); @Override - LongNdArray read(DataBuffer dst); + LongNdArray copyTo(DataBuffer dst); - LongNdArray read(LongDataBuffer dst); + LongNdArray copyTo(LongDataBuffer dst); @Override - LongNdArray write(DataBuffer src); + LongNdArray copyFrom(DataBuffer src); - LongNdArray write(LongDataBuffer src); + LongNdArray copyFrom(LongDataBuffer src); } diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/NdArray.java b/ndarray/src/main/java/org/tensorflow/ndarray/NdArray.java index 6686abd..da34fd3 100644 --- a/ndarray/src/main/java/org/tensorflow/ndarray/NdArray.java +++ b/ndarray/src/main/java/org/tensorflow/ndarray/NdArray.java @@ -1,5 +1,5 @@ /* - Copyright 2019 The TensorFlow Authors. All Rights Reserved. + Copyright 2019-2023 The TensorFlow Authors. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -16,11 +16,14 @@ */ package org.tensorflow.ndarray; -import java.util.function.BiConsumer; -import java.util.function.Consumer; import org.tensorflow.ndarray.buffer.DataBuffer; import org.tensorflow.ndarray.index.Index; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + /** * A data structure of N-dimensions. * @@ -98,6 +101,32 @@ public interface NdArray extends Shaped { */ NdArraySequence> scalars(); + /** + * Returns a new N-dimensional view of this array with the given {@code shape}. + * + *

The provided {@code shape} must comply to the following characteristics: + *

    + *
  • new shape is known (i.e. has no unknown dimension)
  • + *
  • new shape size is equal to the size of the current shape (i.e. same number of elements)
  • + *
+ * For example, + *
{@code
+   *    NdArrays.ofInts(Shape.scalar()).withShape(Shape.of(1, 1));  // ok
+   *    NdArrays.ofInts(Shape.of(2, 3).withShape(Shape.of(3, 2));   // ok
+   *    NdArrays.ofInts(Shape.scalar()).withShape(Shape.of(1, 2));  // not ok, sizes are different (1 != 2)
+   *    NdArrays.ofInts(Shape.of(2, 3)).withShape(Shape.unknown()); // not ok, new shape unknown
+   * }
+ * + *

Any changes applied to the returned view affect the data of this array as well, as there + * is no copy involved. + * + * @param shape the new shape to apply + * @return a new array viewing the data according to the new shape, or this array if shapes are the same + * @throws IllegalArgumentException if the provided {@code shape} is not compliant + * @throws UnsupportedOperationException if this array does not support this operation + */ + NdArray withShape(Shape shape); + /** * Creates a multi-dimensional view (or slice) of this array by mapping one or more dimensions * to the given index selectors. @@ -229,6 +258,18 @@ public interface NdArray extends Shaped { */ NdArray setObject(T value, long... coordinates); + /** + * Retrieve all scalar values of this array as a stream of objects. + * + *

For {@code rank() > 1} arrays, all vectors of the last dimension are collated so that the scalar values are + * returned in sequential order.

+ * + * @return scalar values as a stream + */ + default Stream streamOfObjects() { + return StreamSupport.stream(scalars().spliterator(), false).map(NdArray::getObject); + } + /** * Copy the content of this array to the destination array. * @@ -244,33 +285,39 @@ public interface NdArray extends Shaped { NdArray copyTo(NdArray dst); /** - * Read the content of this N-dimensional array into the destination buffer. + * Copy the content of this N-dimensional array into the destination buffer. * *

The size of the buffer must be equal or greater to the {@link #size()} of this * array, or an exception is thrown. After the copy, content of the buffer and of the array can be * altered independently, without affecting each other. * + *

Note: in version 0.4.0 and earlier, this method was named {@code read(DataBuffer)}. It has been renamed to + * explicitly indicate the direction of the data flow to avoid confusion. + * * @param dst the destination buffer * @return this array * @throws java.nio.BufferOverflowException if the buffer cannot hold the content of this array * @see DataBuffer#size() */ - NdArray read(DataBuffer dst); + NdArray copyTo(DataBuffer dst); /** - * Write the content of this N-dimensional array from the source buffer. + * Copy the content of the source buffer into this N-dimensional array. * *

The size of the buffer must be equal or greater to the {@link #size()} of this * array, or an exception is thrown. After the copy, content of the buffer and of the array can be * altered independently, without affecting each other. * + *

Note: in version 0.4.0 and earlier, this method was named {@code write(DataBuffer)}. It has been renamed to + * explicitly indicate the direction of the data flow to avoid confusion. + * * @param src the source buffer * @return this array * @throws java.nio.BufferUnderflowException if the buffer has not enough remaining data to write * into this array * @see DataBuffer#size() */ - NdArray write(DataBuffer src); + NdArray copyFrom(DataBuffer src); /** * Checks equality between n-dimensional arrays. diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/NdArrays.java b/ndarray/src/main/java/org/tensorflow/ndarray/NdArrays.java index 8ad55ca..d79b781 100644 --- a/ndarray/src/main/java/org/tensorflow/ndarray/NdArrays.java +++ b/ndarray/src/main/java/org/tensorflow/ndarray/NdArrays.java @@ -1,23 +1,21 @@ /* - Copyright 2019 The TensorFlow Authors. All Rights Reserved. +Copyright 2019 The TensorFlow Authors. All Rights Reserved. - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + http://www.apache.org/licenses/LICENSE-2.0 - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - ======================================================================= - */ +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +======================================================================= +*/ package org.tensorflow.ndarray; -import org.tensorflow.ndarray.impl.dense.DenseNdArray; -import org.tensorflow.ndarray.impl.dense.IntDenseNdArray; import org.tensorflow.ndarray.buffer.BooleanDataBuffer; import org.tensorflow.ndarray.buffer.ByteDataBuffer; import org.tensorflow.ndarray.buffer.DataBuffer; @@ -29,14 +27,22 @@ import org.tensorflow.ndarray.buffer.ShortDataBuffer; import org.tensorflow.ndarray.impl.dense.BooleanDenseNdArray; import org.tensorflow.ndarray.impl.dense.ByteDenseNdArray; +import org.tensorflow.ndarray.impl.dense.DenseNdArray; import org.tensorflow.ndarray.impl.dense.DoubleDenseNdArray; import org.tensorflow.ndarray.impl.dense.FloatDenseNdArray; +import org.tensorflow.ndarray.impl.dense.IntDenseNdArray; import org.tensorflow.ndarray.impl.dense.LongDenseNdArray; import org.tensorflow.ndarray.impl.dense.ShortDenseNdArray; - -/** - * Utility class for instantiating {@link NdArray} objects. - */ +import org.tensorflow.ndarray.impl.dimension.DimensionalSpace; +import org.tensorflow.ndarray.impl.sparse.BooleanSparseNdArray; +import org.tensorflow.ndarray.impl.sparse.ByteSparseNdArray; +import org.tensorflow.ndarray.impl.sparse.DoubleSparseNdArray; +import org.tensorflow.ndarray.impl.sparse.FloatSparseNdArray; +import org.tensorflow.ndarray.impl.sparse.IntSparseNdArray; +import org.tensorflow.ndarray.impl.sparse.LongSparseNdArray; +import org.tensorflow.ndarray.impl.sparse.ShortSparseNdArray; + +/** Utility class for instantiating {@link NdArray} objects. */ public final class NdArrays { // BYTE ARRAYS @@ -54,8 +60,8 @@ public static ByteNdArray scalarOf(byte value) { /** * Creates a byte vector (rank 1) initialized with the given values. * - *

Modifying the data of the returned vector will also impact the values in the array - * passed in parameter. + *

Modifying the data of the returned vector will also impact the values in the array passed in + * parameter. * * @param values vector values * @return new byte vector @@ -90,13 +96,51 @@ public static ByteNdArray ofBytes(Shape shape) { * @param shape shape of the array * @param buffer buffer to wrap * @return new byte N-dimensional array - * @throws IllegalArgumentException if shape is null, has unknown dimensions or has size bigger - * in the buffer size + * @throws IllegalArgumentException if shape is null, has unknown dimensions or has size bigger in + * the buffer size */ public static ByteNdArray wrap(Shape shape, ByteDataBuffer buffer) { return ByteDenseNdArray.create(buffer, shape); } + /** + * Creates a Sparse array of byte values with a default value of zero + * + * @param indices A 2-D LongNdArray of shape {@code [N, ndims]}, that specifies the indices of the + * elements in the sparse array that contain non-default values (elements are zero-indexed). + * For example, {@code indices=[[1,3,1], [2,4,0]]} specifies that the elements with indexes of + * {@code [1,3,1]} and {@code [2,4,0]} have non-default values. + * @param values A 1-D ByteNdArray of shape {@code [N]}, which supplies the values for each + * element in indices. For example, given {@code indices=[[1,3,1], [2,4,0]]}, the parameter {@code + * values=[18, 3]} specifies that element {@code [1,3,1]} of the sparse NdArray has a value of + * {@code 18}, and element {@code [2,4,0]} of the NdArray has a value of {@code 3}. + * @param shape the shape of the dense array represented by this sparse array. + * @return the byte sparse array. + */ + public static ByteSparseNdArray sparseOf(LongNdArray indices, ByteNdArray values, Shape shape) { + return ByteSparseNdArray.create(indices, values, DimensionalSpace.create(shape)); + } + + /** + * Creates a Sparse array of byte values + * + * @param indices A 2-D LongNdArray of shape {@code [N, ndims]}, that specifies the indices of the + * elements in the sparse array that contain non-default values (elements are zero-indexed). + * For example, {@code indices=[[1,3,1], [2,4,0]]} specifies that the elements with indexes of + * {@code [1,3,1]} and {@code [2,4,0]} have non default values. + * @param values A 1-D ByteNdArray of shape {@code [N]}, which supplies the values for each + * element in indices. For example, given {@code indices=[[1,3,1], [2,4,0]]}, the parameter {@code + * values=[18, 3]} specifies that element {@code [1,3,1]} of the sparse NdArray has a value of + * {@code 18}, and element {@code [2,4,0]} of the NdArray has a value of {@code 3}. + * @param defaultValue Scalar value to set for indices not specified in 'indices' + * @param shape the shape of the dense array represented by this sparse array. + * @return the byte sparse array. + */ + public static ByteSparseNdArray sparseOf( + LongNdArray indices, ByteNdArray values, byte defaultValue, Shape shape) { + return ByteSparseNdArray.create(indices, values, defaultValue, DimensionalSpace.create(shape)); + } + // LONG ARRAYS /** @@ -112,8 +156,8 @@ public static LongNdArray scalarOf(long value) { /** * Creates a long vector (rank 1) initialized with the given values. * - *

Modifying the data of the returned vector will also impact the values in the array - * passed in parameter. + *

Modifying the data of the returned vector will also impact the values in the array passed in + * parameter. * * @param values vector values * @return new long vector @@ -145,13 +189,51 @@ public static LongNdArray ofLongs(Shape shape) { * @param shape shape of the array * @param buffer buffer to wrap * @return new long N-dimensional array - * @throws IllegalArgumentException if shape is null, has unknown dimensions or has size bigger - * in the buffer size + * @throws IllegalArgumentException if shape is null, has unknown dimensions or has size bigger in + * the buffer size */ public static LongNdArray wrap(Shape shape, LongDataBuffer buffer) { return LongDenseNdArray.create(buffer, shape); } + /** + * Creates a Sparse array of long values with a default value of zero + * + * @param indices A 2-D LongNdArray of shape {@code [N, ndims]}, that specifies the indices of the + * elements in the sparse array that contain non-default values (elements are zero-indexed). + * For example, {@code indices=[[1,3,1], [2,4,0]]} specifies that the elements with indexes of + * {@code [1,3,1]} and {@code [2,4,0]} have non-default values. + * @param values A 1-D LongNdArray of shape {@code [N]}, which supplies the values for each + * element in indices. For example, given {@code indices=[[1,3,1], [2,4,0]]}, the parameter {@code + * values=[18L, 3L]} specifies that element {@code [1,3,1]} of the sparse NdArray has a value of + * {@code 18L}, and element {@code [2,4,0]} of the NdArray has a value of {@code 3L}. + * @param shape the shape of the dense array represented by this sparse array. + * @return the long sparse array. + */ + public static LongSparseNdArray sparseOf(LongNdArray indices, LongNdArray values, Shape shape) { + return LongSparseNdArray.create(indices, values, DimensionalSpace.create(shape)); + } + + /** + * Creates a Sparse array of long values with a default value of zero + * + * @param indices A 2-D LongNdArray of shape {@code [N, ndims]}, that specifies the indices of the + * elements in the sparse array that contain non-default values (elements are zero-indexed). + * For example, {@code indices=[[1,3,1], [2,4,0]]} specifies that the elements with indexes of + * {@code [1,3,1]} and {@code [2,4,0]} have non-default values. + * @param values A 1-D LongNdArray of shape {@code [N]}, which supplies the values for each + * element in indices. For example, given {@code indices=[[1,3,1], [2,4,0]]}, the parameter {@code + * values=[18L, 3L]} specifies that element {@code [1,3,1]} of the sparse NdArray has a value of + * {@code 18L}, and element {@code [2,4,0]} of the NdArray has a value of {@code 3L}. + * @param defaultValue Scalar value to set for indices not specified in 'indices' + * @param shape the shape of the dense array represented by this sparse array. + * @return the long sparse array. + */ + public static LongSparseNdArray sparseOf( + LongNdArray indices, LongNdArray values, long defaultValue, Shape shape) { + return LongSparseNdArray.create(indices, values, defaultValue, DimensionalSpace.create(shape)); + } + // INT ARRAYS /** @@ -167,8 +249,8 @@ public static IntNdArray scalarOf(int value) { /** * Creates a int vector (rank 1) initialized with the given values. * - *

Modifying the data of the returned vector will also impact the values in the array - * passed in parameter. + *

Modifying the data of the returned vector will also impact the values in the array passed in + * parameter. * * @param values vector values * @return new int vector @@ -200,13 +282,51 @@ public static IntNdArray ofInts(Shape shape) { * @param shape shape of the array * @param buffer buffer to wrap * @return new int N-dimensional array - * @throws IllegalArgumentException if shape is null, has unknown dimensions or has size bigger - * in the buffer size + * @throws IllegalArgumentException if shape is null, has unknown dimensions or has size bigger in + * the buffer size */ public static IntNdArray wrap(Shape shape, IntDataBuffer buffer) { return IntDenseNdArray.create(buffer, shape); } + /** + * Creates a Sparse array of int values with a default value of zero. + * + * @param indices A 2-D LongNdArray of shape {@code [N, ndims]}, that specifies the indices of the + * elements in the sparse array that contain non-default values (elements are zero-indexed). + * For example, {@code indices=[[1,3,1], [2,4,0]]} specifies that the elements with indexes of + * {@code [1,3,1]} and {@code [2,4,0]} have non-default values. + * @param values A 1-D IntNdArray of shape {@code [N]}, which supplies the values for each element + * in indices. For example, given {@code indices=[[1,3,1], [2,4,0]]}, the parameter {@code + * values=[18, 3]} specifies that element {@code [1,3,1]} of the sparse NdArray has a value of + * {@code 18}, and element {@code [2,4,0]} of the NdArray has a value of {@code 3}. + * @param shape the shape of the dense array represented by this sparse array. + * @return the int sparse array. + */ + public static IntSparseNdArray sparseOf(LongNdArray indices, IntNdArray values, Shape shape) { + return IntSparseNdArray.create(indices, values, DimensionalSpace.create(shape)); + } + + /** + * Creates a Sparse array of int values + * + * @param indices A 2-D LongNdArray of shape {@code [N, ndims]}, that specifies the indices of the + * elements in the sparse array that contain non-default values (elements are zero-indexed). + * For example, {@code indices=[[1,3,1], [2,4,0]]} specifies that the elements with indexes of + * {@code [1,3,1]} and {@code [2,4,0]} have non-default values. + * @param values A 1-D IntNdArray of shape {@code [N]}, which supplies the values for each element + * in indices. For example, given {@code indices=[[1,3,1], [2,4,0]]}, the parameter {@code + * values=[18, 3]} specifies that element {@code [1,3,1]} of the sparse NdArray has a value of + * {@code 18}, and element {@code [2,4,0]} of the NdArray has a value of {@code 3}. + * @param defaultValue Scalar value to set for indices not specified in 'indices' + * @param shape the shape of the dense array represented by this sparse array. + * @return the int sparse array. + */ + public static IntSparseNdArray sparseOf( + LongNdArray indices, IntNdArray values, int defaultValue, Shape shape) { + return IntSparseNdArray.create(indices, values, defaultValue, DimensionalSpace.create(shape)); + } + // SHORT ARRAYS /** @@ -222,8 +342,8 @@ public static ShortNdArray scalarOf(short value) { /** * Creates a short vector (rank 1) initialized with the given values. * - *

Modifying the data of the returned vector will also impact the values in the array - * passed in parameter. + *

Modifying the data of the returned vector will also impact the values in the array passed in + * parameter. * * @param values vector values * @return new short vector @@ -255,13 +375,51 @@ public static ShortNdArray ofShorts(Shape shape) { * @param shape shape of the array * @param buffer buffer to wrap * @return new short N-dimensional array - * @throws IllegalArgumentException if shape is null, has unknown dimensions or has size bigger - * in the buffer size + * @throws IllegalArgumentException if shape is null, has unknown dimensions or has size bigger in + * the buffer size */ public static ShortNdArray wrap(Shape shape, ShortDataBuffer buffer) { return ShortDenseNdArray.create(buffer, shape); } + /** + * Creates a Sparse array of short values with a default value of zero + * + * @param indices A 2-D LongNdArray of shape {@code [N, ndims]}, that specifies the indices of the + * elements in the sparse array that contain non-default values (elements are zero-indexed). + * For example, {@code indices=[[1,3,1], [2,4,0]]} specifies that the elements with indexes of + * {@code [1,3,1]} and {@code [2,4,0]} have non-default values. + * @param values A 1-D ShortNdArray of shape {@code [N]}, which supplies the values for each + * element in indices. For example, given {@code indices=[[1,3,1], [2,4,0]]}, the parameter {@code + * values=[18, 3]} specifies that element {@code [1,3,1]} of the sparse NdArray has a value of + * {@code 18}, and element {@code [2,4,0]} of the NdArray has a value of {@code 3}. + * @param shape the shape of the dense array represented by this sparse array. + * @return the short sparse array. + */ + public static ShortSparseNdArray sparseOf(LongNdArray indices, ShortNdArray values, Shape shape) { + return ShortSparseNdArray.create(indices, values, DimensionalSpace.create(shape)); + } + + /** + * Creates a Sparse array of short values + * + * @param indices A 2-D LongNdArray of shape {@code [N, ndims]}, that specifies the indices of the + * elements in the sparse array that contain non-default values (elements are zero-indexed). + * For example, {@code indices=[[1,3,1], [2,4,0]]} specifies that the elements with indexes of + * {@code [1,3,1]} and {@code [2,4,0]} have non-default values. + * @param values A 1-D ShortNdArray of shape {@code [N]}, which supplies the values for each + * element in indices. For example, given {@code indices=[[1,3,1], [2,4,0]]}, the parameter {@code + * values=[18, 3]} specifies that element {@code [1,3,1]} of the sparse NdArray has a value of + * {@code 18}, and element {@code [2,4,0]} of the NdArray has a value of {@code 3}. + * @param defaultValue Scalar value to set for indices not specified in 'indices' + * @param shape the shape of the dense array represented by this sparse array. + * @return the short sparse array. + */ + public static ShortSparseNdArray sparseOf( + LongNdArray indices, ShortNdArray values, short defaultValue, Shape shape) { + return ShortSparseNdArray.create(indices, values, defaultValue, DimensionalSpace.create(shape)); + } + // FLOAT ARRAYS /** @@ -277,8 +435,8 @@ public static FloatNdArray scalarOf(float value) { /** * Creates a float vector (rank 1) initialized with the given values. * - *

Modifying the data of the returned vector will also impact the values in the array - * passed in parameter. + *

Modifying the data of the returned vector will also impact the values in the array passed in + * parameter. * * @param values vector values * @return new float vector @@ -310,13 +468,51 @@ public static FloatNdArray ofFloats(Shape shape) { * @param shape shape of the array * @param buffer buffer to wrap * @return new float N-dimensional array - * @throws IllegalArgumentException if shape is null, has unknown dimensions or has size bigger - * in the buffer size + * @throws IllegalArgumentException if shape is null, has unknown dimensions or has size bigger in + * the buffer size */ public static FloatNdArray wrap(Shape shape, FloatDataBuffer buffer) { return FloatDenseNdArray.create(buffer, shape); } + /** + * Creates a Sparse array of float values with a default value of zero + * + * @param indices A 2-D LongNdArray of shape {@code [N, ndims]}, that specifies the indices of the + * elements in the sparse array that contain non-default values (elements are zero-indexed). + * For example, {@code indices=[[1,3,1], [2,4,0]]} specifies that the elements with indexes of + * {@code [1,3,1]} and {@code [2,4,0]} have non-default values. + * @param values A 1-D FloatNdArray of shape {@code [N]}, which supplies the values for each + * element in indices. For example, given {@code indices=[[1,3,1], [2,4,0]]}, the parameter {@code + * values=[18f, 3.8f]} specifies that element {@code [1,3,1]} of the sparse NdArray has a value of + * {@code 18f}, and element {@code [2,4,0]} of the NdArray has a value of {@code 3.8f}. + * @param shape the shape of the dense array represented by this sparse array. + * @return the float sparse array. + */ + public static FloatSparseNdArray sparseOf(LongNdArray indices, FloatNdArray values, Shape shape) { + return FloatSparseNdArray.create(indices, values, DimensionalSpace.create(shape)); + } + + /** + * Creates a Sparse array of float values + * + * @param indices A 2-D LongNdArray of shape {@code [N, ndims]}, that specifies the indices of the + * elements in the sparse array that contain non-default values (elements are zero-indexed). + * For example, {@code indices=[[1,3,1], [2,4,0]]} specifies that the elements with indexes of + * {@code [1,3,1]} and {@code [2,4,0]} have non-default values. + * @param values A 1-D FloatNdArray of shape {@code [N]}, which supplies the values for each + * element in indices. For example, given {@code indices=[[1,3,1], [2,4,0]]}, the parameter {@code + * values=[18f, 3.8f]} specifies that element {@code [1,3,1]} of the sparse NdArray has a value of + * {@code 18f}, and element {@code [2,4,0]} of the NdArray has a value of {@code 3.8f}. + * @param defaultValue Scalar value to set for indices not specified in 'indices' + * @param shape the shape of the dense array represented by this sparse array. + * @return the float sparse array. + */ + public static FloatSparseNdArray sparseOf( + LongNdArray indices, FloatNdArray values, float defaultValue, Shape shape) { + return FloatSparseNdArray.create(indices, values, defaultValue, DimensionalSpace.create(shape)); + } + // DOUBLE ARRAYS /** @@ -332,8 +528,8 @@ public static DoubleNdArray scalarOf(double value) { /** * Creates a double vector (rank 1) initialized with the given values. * - *

Modifying the data of the returned vector will also impact the values in the array - * passed in parameter. + *

Modifying the data of the returned vector will also impact the values in the array passed in + * parameter. * * @param values vector values * @return new double vector @@ -365,13 +561,53 @@ public static DoubleNdArray ofDoubles(Shape shape) { * @param shape shape of the array * @param buffer buffer to wrap * @return new double N-dimensional array - * @throws IllegalArgumentException if shape is null, has unknown dimensions or has size bigger - * in the buffer size + * @throws IllegalArgumentException if shape is null, has unknown dimensions or has size bigger in + * the buffer size */ public static DoubleNdArray wrap(Shape shape, DoubleDataBuffer buffer) { return DoubleDenseNdArray.create(buffer, shape); } + /** + * Creates a Sparse array of double values with a default value of zero + * + * @param indices A 2-D LongNdArray of shape {@code [N, ndims]}, that specifies the indices of the + * elements in the sparse array that contain non-default values (elements are zero-indexed). + * For example, {@code indices=[[1,3,1], [2,4,0]]} specifies that the elements with indexes of + * {@code [1,3,1]} and {@code [2,4,0]} have non-default values. + * @param values A 1-D DoubleNdArray of shape {@code [N]}, which supplies the values for each + * element in indices. For example, given {@code indices=[[1,3,1], [2,4,0]]}, the parameter {@code + * values=[18, 3.8]} specifies that element {@code [1,3,1]} of the sparse NdArray has a value of + * {@code 18}, and element {@code [2,4,0]} of the NdArray has a value of {@code 3.8}. + * @param shape the shape of the dense array represented by this sparse array. + * @return the float sparse array. + */ + public static DoubleSparseNdArray sparseOf( + LongNdArray indices, DoubleNdArray values, Shape shape) { + return DoubleSparseNdArray.create(indices, values, DimensionalSpace.create(shape)); + } + + /** + * Creates a Sparse array of double values + * + * @param indices A 2-D LongNdArray of shape {@code [N, ndims]}, that specifies the indices of the + * elements in the sparse array that contain non-default values (elements are zero-indexed). + * For example, {@code indices=[[1,3,1], [2,4,0]]} specifies that the elements with indexes of + * {@code [1,3,1]} and {@code [2,4,0]} have non-default values. + * @param values A 1-D DoubleNdArray of shape {@code [N]}, which supplies the values for each + * element in indices. For example, given {@code indices=[[1,3,1], [2,4,0]]}, the parameter {@code + * values=[18, 3.8]} specifies that element {@code [1,3,1]} of the sparse NdArray has a value of + * {@code 18}, and element {@code [2,4,0]} of the NdArray has a value of {@code 3.8}. + * @param defaultValue Scalar value to set for indices not specified in 'indices' + * @param shape the shape of the dense array represented by this sparse array. + * @return the float sparse array. + */ + public static DoubleSparseNdArray sparseOf( + LongNdArray indices, DoubleNdArray values, double defaultValue, Shape shape) { + return DoubleSparseNdArray.create( + indices, values, defaultValue, DimensionalSpace.create(shape)); + } + // BOOLEAN ARRAYS /** @@ -387,8 +623,8 @@ public static BooleanNdArray scalarOf(boolean value) { /** * Creates a boolean vector (rank 1) initialized with the given values. * - *

Modifying the data of the returned vector will also impact the values in the array - * passed in parameter. + *

Modifying the data of the returned vector will also impact the values in the array passed in + * parameter. * * @param values vector values * @return new boolean vector @@ -420,13 +656,55 @@ public static BooleanNdArray ofBooleans(Shape shape) { * @param shape shape of the array * @param buffer buffer to wrap * @return new boolean N-dimensional array - * @throws IllegalArgumentException if shape is null, has unknown dimensions or has size bigger - * in the buffer size + * @throws IllegalArgumentException if shape is null, has unknown dimensions or has size bigger in + * the buffer size */ public static BooleanNdArray wrap(Shape shape, BooleanDataBuffer buffer) { return BooleanDenseNdArray.create(buffer, shape); } + /** + * Creates a Sparse array of boolean values with a default value of 'false' + * + * @param indices A 2-D LongNdArray of shape {@code [N, ndims]}, that specifies the indices of the + * elements in the sparse array that contain non-default values (elements are zero-indexed). + * For example, {@code indices=[[1,3,1], [2,4,0]]} specifies that the elements with indexes of + * {@code [1,3,1]} and {@code [2,4,0]} have non-default values. + * @param values A 1-D BooleanNdArray of shape {@code [N]}, which supplies the values for each + * element in indices. For example, given {@code indices=[[1,3,1], [2,4,0]]}, the parameter {@code + * values=[true, true]} specifies that element {@code [1,3,1]} of the sparse NdArray has a value + * of true, and element {@code [2,4,0]} of the NdArray has a value of true. All other values are + * false. + * @param shape the shape of the dense array represented by this sparse array. + * @return the float sparse array. + */ + public static BooleanSparseNdArray sparseOf( + LongNdArray indices, BooleanNdArray values, Shape shape) { + return BooleanSparseNdArray.create(indices, values, DimensionalSpace.create(shape)); + } + + /** + * Creates a Sparse array of boolean values + * + * @param indices A 2-D LongNdArray of shape {@code [N, ndims]}, that specifies the indices of the + * elements in the sparse array that contain non-default values (elements are zero-indexed). + * For example, {@code indices=[[1,3,1], [2,4,0]]} specifies that the elements with indexes of + * {@code [1,3,1]} and {@code [2,4,0]} have non-default values. + * @param values A 1-D BooleanNdArray of shape {@code [N]}, which supplies the values for each + * element in indices. For example, given {@code indices=[[1,3,1], [2,4,0]]}, the parameter {@code + * values=[true, true]} specifies that element {@code [1,3,1]} of the sparse NdArray has a value + * of true, and element {@code [2,4,0]} of the NdArray has a value of true. All other values are + * false. + * @param defaultValue Scalar value to set for indices not specified in 'indices' + * @param shape the shape of the dense array represented by this sparse array. + * @return the float sparse array. + */ + public static BooleanSparseNdArray sparseOf( + LongNdArray indices, BooleanNdArray values, boolean defaultValue, Shape shape) { + return BooleanSparseNdArray.create( + indices, values, defaultValue, DimensionalSpace.create(shape)); + } + // OBJECT ARRAYS /** @@ -441,14 +719,14 @@ public static NdArray scalarOfObject(T value) { if (value == null) { throw new IllegalArgumentException(); } - return ofObjects((Class)value.getClass(), Shape.scalar()).setObject(value); + return ofObjects((Class) value.getClass(), Shape.scalar()).setObject(value); } /** * Creates a vector (rank 1) initialized with the given values. * - *

Modifying the data of the returned vector will also impact the values in the array - * passed in parameter. + *

Modifying the data of the returned vector will also impact the values in the array passed in + * parameter. * * @param values vector values * @param the data type @@ -485,11 +763,55 @@ public static NdArray ofObjects(Class clazz, Shape shape) { * @param buffer buffer to wrap * @param the data type * @return new N-dimensional array - * @throws IllegalArgumentException if shape is null, has unknown dimensions or has size bigger - * in the buffer size + * @throws IllegalArgumentException if shape is null, has unknown dimensions or has size bigger in + * the buffer size */ public static NdArray wrap(Shape shape, DataBuffer buffer) { return DenseNdArray.wrap(buffer, shape); } -} + /** + * Creates a Sparse array of values with a null default value + * + * @param type the class type represented by this sparse array. + * @param indices A 2-D LongNdArray of shape {@code [N, ndims]}, that specifies the indices of the + * elements in the sparse array that contain non-default values (elements are zero-indexed). + * For example, {@code indices=[[1,3,1], [2,4,0]]} specifies that the elements with indexes of + * {@code [1,3,1]} and {@code [2,4,0]} have non-default values. + * @param values A 1-D NdArray of shape {@code [N]}, which supplies the values for each element in + * indices. For example, given {@code indices=[[1,3,1], [2,4,0]]}, the parameter {@code + * values=["one", "two"]} specifies that element {@code [1,3,1]} of the sparse NdArray has a + * value of "one", and element {@code [2,4,0]} of the NdArray has a value of "two"". All other + * values are null. + * @param shape the shape of the dense array represented by this sparse array. + * @return the float sparse array. + */ + public static NdArray sparseOfObjects( + Class type, LongNdArray indices, NdArray values, Shape shape) { + return org.tensorflow.ndarray.impl.sparse.SparseNdArray.create( + type, indices, values, DimensionalSpace.create(shape)); + } + + /** + * Creates a Sparse array of values + * + * @param type the class type represented by this sparse array. + * @param indices A 2-D LongNdArray of shape {@code [N, ndims]}, that specifies the indices of the + * elements in the sparse array that contain non-default values (elements are zero-indexed). + * For example, {@code indices=[[1,3,1], [2,4,0]]} specifies that the elements with indexes of + * {@code [1,3,1]} and {@code [2,4,0]} have non-default values. + * @param values A 1-D NdArray of shape {@code [N]}, which supplies the values for each element in + * indices. For example, given {@code indices=[[1,3,1], [2,4,0]]}, the parameter {@code + * values=["one", "two"]} specifies that element {@code [1,3,1]} of the sparse NdArray has a + * value of "one", and element {@code [2,4,0]} of the NdArray has a value of "two"". All other + * values are null. + * @param defaultValue Scalar value to set for indices not specified in 'indices' + * @param shape the shape of the dense array represented by this sparse array. + * @return the float sparse array. + */ + public static NdArray sparseOfObjects( + Class type, LongNdArray indices, NdArray values, T defaultValue, Shape shape) { + return org.tensorflow.ndarray.impl.sparse.SparseNdArray.create( + type, indices, values, defaultValue, DimensionalSpace.create(shape)); + } +} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/Shape.java b/ndarray/src/main/java/org/tensorflow/ndarray/Shape.java index 85a9054..d8bb2bb 100644 --- a/ndarray/src/main/java/org/tensorflow/ndarray/Shape.java +++ b/ndarray/src/main/java/org/tensorflow/ndarray/Shape.java @@ -17,7 +17,9 @@ package org.tensorflow.ndarray; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; /** * The shape of a Tensor or {@link NdArray}. @@ -74,8 +76,8 @@ public static Shape scalar() { * Shape scalar = Shape.of() * } * - * @param dimensionSizes number of elements in each dimension of this shape, if any, or - * {@link Shape#UNKNOWN_SIZE} if unknown. + * @param dimensionSizes number of elements in each dimension of this shape, if any, or {@link + * Shape#UNKNOWN_SIZE} if unknown. * @return a new shape */ public static Shape of(long... dimensionSizes) { @@ -108,13 +110,34 @@ public long size() { * an unknown size, {@link Shape#UNKNOWN_SIZE} is returned. * * @param i the index of the dimension to get the size for. If this Shape has a known number of - * dimensions, it must be < {@link Shape#numDimensions()}. The index may be negative, in which - * case the position is counted from the end of the shape. E.g.: {@code size(-1)} returns the - * size of the last dimension, {@code size(-2)} the size of the second to last dimension etc. + * dimensions, it must be < {@link Shape#numDimensions()}. The index may be negative, in + * which case the position is counted from the end of the shape. E.g.: {@code size(-1)} + * returns the size of the last dimension, {@code size(-2)} the size of the second to last + * dimension etc. * @return The size of the dimension with the given index if known, {@link Shape#UNKNOWN_SIZE} * otherwise. + * @deprecated Renamed to {@link #get(int)}. */ - public long size(int i) { + @Deprecated + public long size(int i){ + return get(i); + } + + /** + * The size of the dimension with the given index. + * + *

If {@link Shape#isUnknown()} is true or the size of the dimension with the given index has + * an unknown size, {@link Shape#UNKNOWN_SIZE} is returned. + * + * @param i the index of the dimension to get the size for. If this Shape has a known number of + * dimensions, it must be < {@link Shape#numDimensions()}. The index may be negative, in + * which case the position is counted from the end of the shape. E.g.: {@code size(-1)} + * returns the size of the last dimension, {@code size(-2)} the size of the second to last + * dimension etc. + * @return The size of the dimension with the given index if known, {@link Shape#UNKNOWN_SIZE} + * otherwise. + */ + public long get(int i) { if (dimensionSizes == null) { return UNKNOWN_SIZE; } else if (i >= 0) { @@ -177,6 +200,24 @@ public long[] asArray() { } } + /** + * Returns a defensive copy of the this Shape's axes. Changes to the returned list do not change + * this Shape's state. Returns null if {@link Shape#isUnknown()} is true. + */ + public List toListOrNull() { + long[] array = asArray(); + if (array == null) { + return null; + } + + List list = new ArrayList<>(array.length); + for (long l : array) { + list.add(l); + } + + return list; + } + @Override public int hashCode() { return dimensionSizes != null ? Arrays.hashCode(dimensionSizes) : super.hashCode(); @@ -186,6 +227,7 @@ public int hashCode() { * Equals implementation for Shapes. Two Shapes are considered equal iff: * *

+ * *

    *
  • the number of dimensions is defined and equal for both *
  • the size of each dimension is defined and equal for both @@ -236,7 +278,8 @@ public Shape head() { * Returns an n-dimensional Shape with the dimensions matching the first n dimensions of this * shape * - * @param n the number of leading dimensions to get, must be <= than {@link Shape#numDimensions()} + * @param n the number of leading dimensions to get, must be <= than {@link + * Shape#numDimensions()} * @return an n-dimensional Shape with the first n dimensions matching the first n dimensions of * this Shape */ @@ -252,7 +295,9 @@ public Shape take(int n) { /** Returns a new Shape, with this Shape's first dimension removed. */ public Shape tail() { - if (dimensionSizes.length < 2) return Shape.of(); + if (dimensionSizes.length < 2) { + return Shape.of(); + } return Shape.of(Arrays.copyOfRange(dimensionSizes, 1, dimensionSizes.length)); } @@ -276,15 +321,21 @@ public Shape takeLast(int n) { } /** - * Return a {@code end - begin} dimensional shape with dimensions matching this Shape from {@code begin} to {@code end}. + * Return a {@code end - begin} dimensional shape with dimensions matching this Shape from {@code + * begin} to {@code end}. + * * @param begin Where to start the sub-shape. * @param end Where to end the sub-shape, exclusive. * @return the sub-shape bounded by begin and end. */ - public Shape subShape(int begin, int end){ + public Shape subShape(int begin, int end) { if (end > numDimensions()) { throw new ArrayIndexOutOfBoundsException( - "End index " + end + " out of bounds: shape only has " + numDimensions() + " dimensions."); + "End index " + + end + + " out of bounds: shape only has " + + numDimensions() + + " dimensions."); } if (begin < 0) { throw new ArrayIndexOutOfBoundsException( @@ -423,7 +474,7 @@ public boolean isCompatibleWith(Shape shape) { return false; } for (int i = 0; i < numDimensions(); i++) { - if (!isCompatible(size(i), shape.size(i))) { + if (!isCompatible(get(i), shape.get(i))) { return false; } } diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/ShortNdArray.java b/ndarray/src/main/java/org/tensorflow/ndarray/ShortNdArray.java index f9335b4..022ccf7 100644 --- a/ndarray/src/main/java/org/tensorflow/ndarray/ShortNdArray.java +++ b/ndarray/src/main/java/org/tensorflow/ndarray/ShortNdArray.java @@ -68,6 +68,9 @@ public interface ShortNdArray extends NdArray { */ ShortNdArray setShort(short value, long... coordinates); + @Override + ShortNdArray withShape(Shape shape); + @Override ShortNdArray slice(Index... coordinates); @@ -97,12 +100,12 @@ default ShortNdArray setObject(Short value, long... coordinates) { ShortNdArray copyTo(NdArray dst); @Override - ShortNdArray read(DataBuffer dst); + ShortNdArray copyTo(DataBuffer dst); - ShortNdArray read(ShortDataBuffer dst); + ShortNdArray copyTo(ShortDataBuffer dst); @Override - ShortNdArray write(DataBuffer src); + ShortNdArray copyFrom(DataBuffer src); - ShortNdArray write(ShortDataBuffer src); + ShortNdArray copyFrom(ShortDataBuffer src); } diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/SparseNdArray.java b/ndarray/src/main/java/org/tensorflow/ndarray/SparseNdArray.java new file mode 100644 index 0000000..ab91d1c --- /dev/null +++ b/ndarray/src/main/java/org/tensorflow/ndarray/SparseNdArray.java @@ -0,0 +1,50 @@ +/* Copyright 2021 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +=======================================================================*/ +package org.tensorflow.ndarray; + +/** + * Interface for Sparse Arrays + * + * @param the type that the array contains + * @param the type of dense NdArray + */ +public interface SparseNdArray> extends NdArray { + /** + * Gets the Indices + * + *

    Indices are a A 2-D long array of shape {@code [N, ndims]}, that specifies the indices of + * the elements in the sparse array that contain nonzero values (elements are zero-indexed). + * + *

    For example, {@code indices=[[1,3], [2,4]]} specifies that the elements with indexes of + * coordinates {@code [1,3]} and {@code [2,4]} have nonzero values. + * + * @return the Indices + */ + LongNdArray getIndices(); + + /** + * Gets the values. + * + *

    Values are a 1-D array of any type and shape {@code [N]}, that supplies the values for each + * element in indices. + * + *

    For example, given {@code indices=[[1,3], [2,4]]}, and {@code values=[18, 3.6]} specifies + * that element {@code [1,3]} of the sparse array has a value of {@code 18}, and element {@code + * [2,4]} of the sparse array has a value of {@code 3.6}. + * + * @return the values + */ + U getValues(); +} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/StdArrays.java b/ndarray/src/main/java/org/tensorflow/ndarray/StdArrays.java index 7d847bd..7367165 100644 --- a/ndarray/src/main/java/org/tensorflow/ndarray/StdArrays.java +++ b/ndarray/src/main/java/org/tensorflow/ndarray/StdArrays.java @@ -2010,7 +2010,7 @@ public static void copyFrom(IntNdArray src, int[] dst) { if (src.size() > dst.length) { throw new ArrayIndexOutOfBoundsException(String.valueOf(src.size()) + " > " + dst.length); } - src.read(DataBuffers.of(dst, false, false)); + src.copyTo(DataBuffers.of(dst, false, false)); } /** @@ -2113,7 +2113,7 @@ public static void copyFrom(LongNdArray src, long[] dst) { if (src.size() > dst.length) { throw new ArrayIndexOutOfBoundsException(String.valueOf(src.size()) + " > " + dst.length); } - src.read(DataBuffers.of(dst, false, false)); + src.copyTo(DataBuffers.of(dst, false, false)); } /** @@ -2216,7 +2216,7 @@ public static void copyFrom(FloatNdArray src, float[] dst) { if (src.size() > dst.length) { throw new ArrayIndexOutOfBoundsException(String.valueOf(src.size()) + " > " + dst.length); } - src.read(DataBuffers.of(dst, false, false)); + src.copyTo(DataBuffers.of(dst, false, false)); } /** @@ -2319,7 +2319,7 @@ public static void copyFrom(DoubleNdArray src, double[] dst) { if (src.size() > dst.length) { throw new ArrayIndexOutOfBoundsException(String.valueOf(src.size()) + " > " + dst.length); } - src.read(DataBuffers.of(dst, false, false)); + src.copyTo(DataBuffers.of(dst, false, false)); } /** @@ -2422,7 +2422,7 @@ public static void copyFrom(ByteNdArray src, byte[] dst) { if (src.size() > dst.length) { throw new ArrayIndexOutOfBoundsException(String.valueOf(src.size()) + " > " + dst.length); } - src.read(DataBuffers.of(dst, false, false)); + src.copyTo(DataBuffers.of(dst, false, false)); } /** @@ -2525,7 +2525,7 @@ public static void copyFrom(ShortNdArray src, short[] dst) { if (src.size() > dst.length) { throw new ArrayIndexOutOfBoundsException(String.valueOf(src.size()) + " > " + dst.length); } - src.read(DataBuffers.of(dst, false, false)); + src.copyTo(DataBuffers.of(dst, false, false)); } /** @@ -2628,7 +2628,7 @@ public static void copyFrom(BooleanNdArray src, boolean[] dst) { if (src.size() > dst.length) { throw new ArrayIndexOutOfBoundsException(String.valueOf(src.size()) + " > " + dst.length); } - src.read(DataBuffers.of(dst, false, false)); + src.copyTo(DataBuffers.of(dst, false, false)); } /** @@ -2732,7 +2732,7 @@ public static void copyFrom(NdArray src, T[] dst) { if (src.size() > dst.length) { throw new ArrayIndexOutOfBoundsException(String.valueOf(src.size()) + " > " + dst.length); } - src.read(DataBuffers.of(dst, false, false)); + src.copyTo(DataBuffers.of(dst, false, false)); } /** @@ -3798,9 +3798,9 @@ private static int[] computeArrayDims(NdArray ndArray, int expectedRank) { } int[] arrayShape = new int[expectedRank]; for (int i = 0; i < expectedRank; ++i) { - long dimSize = shape.size(i); + long dimSize = shape.get(i); if (dimSize > Integer.MAX_VALUE) { - throw new IllegalArgumentException("Dimension " + i + " is too large to fit in a standard array (" + shape.size(i) + ")"); + throw new IllegalArgumentException("Dimension " + i + " is too large to fit in a standard array (" + shape.get(i) + ")"); } arrayShape[i] = (int)dimSize; } diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/AbstractNdArray.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/AbstractNdArray.java index 690dedc..cf0d9f1 100644 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/AbstractNdArray.java +++ b/ndarray/src/main/java/org/tensorflow/ndarray/impl/AbstractNdArray.java @@ -1,30 +1,38 @@ /* - Copyright 2019 The TensorFlow Authors. All Rights Reserved. +Copyright 2019 The TensorFlow Authors. All Rights Reserved. - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + http://www.apache.org/licenses/LICENSE-2.0 - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - ======================================================================= - */ +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +======================================================================= +*/ package org.tensorflow.ndarray.impl; -import java.util.Iterator; import org.tensorflow.ndarray.NdArray; import org.tensorflow.ndarray.NdArraySequence; import org.tensorflow.ndarray.Shape; import org.tensorflow.ndarray.impl.dimension.DimensionalSpace; +import java.util.Iterator; +import java.util.Objects; + @SuppressWarnings("unchecked") public abstract class AbstractNdArray> implements NdArray { + protected final DimensionalSpace dimensions; + + protected AbstractNdArray(DimensionalSpace dimensions) { + this.dimensions = dimensions; + } + public abstract U slice(long position, DimensionalSpace dimensions); public DimensionalSpace dimensions() { @@ -39,7 +47,7 @@ public Shape shape() { @Override public NdArraySequence scalars() { // negative if this array is a scalar, should be handled in `elements(dimIdx)` - return (NdArraySequence)elements(shape().numDimensions() - 1); + return (NdArraySequence) elements(shape().numDimensions() - 1); } @Override @@ -55,11 +63,7 @@ public boolean equals(Object obj) { if (!(obj instanceof NdArray)) { return false; } - return slowEquals((NdArray)obj); - } - - protected AbstractNdArray(DimensionalSpace dimensions) { - this.dimensions = dimensions; + return slowEquals((NdArray) obj); } protected void slowCopyTo(NdArray array) { @@ -77,16 +81,19 @@ protected int slowHashCode() { } protected boolean slowEquals(NdArray array) { - if (!shape().equals(array.shape())) { // this guarantees also that we have the same number of scalar values + if (!shape() + .equals( + array.shape())) { // this guarantees also that we have the same number of scalar values return false; } - for (Iterator> thisIter = scalars().iterator(), otherIter = array.scalars().iterator(); thisIter.hasNext();) { - if (!thisIter.next().getObject().equals(otherIter.next().getObject())) { + for (Iterator> thisIter = scalars().iterator(), + otherIter = array.scalars().iterator(); + thisIter.hasNext(); ) { + // Use Object.equals to handle nulls. + if (!Objects.equals(thisIter.next().getObject(), otherIter.next().getObject())) { return false; } } return true; } - - protected final DimensionalSpace dimensions; } diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/Validator.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/Validator.java index 285d099..3e0ba20 100644 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/Validator.java +++ b/ndarray/src/main/java/org/tensorflow/ndarray/impl/Validator.java @@ -30,13 +30,13 @@ public static void copyToNdArrayArgs(NdArray ndArray, NdArray otherNdArray } } - public static void readToBufferArgs(NdArray ndArray, DataBuffer dst) { + public static void copyToBufferArgs(NdArray ndArray, DataBuffer dst) { if (dst.size() < ndArray.size()) { throw new BufferOverflowException(); } } - public static void writeFromBufferArgs(NdArray ndArray, DataBuffer src) { + public static void copyFromBufferArgs(NdArray ndArray, DataBuffer src) { if (src.size() < ndArray.size()) { throw new BufferUnderflowException(); } diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/raw/UnsafeReference.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/raw/UnsafeReference.java index 7b95eac..a8cc16d 100644 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/raw/UnsafeReference.java +++ b/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/raw/UnsafeReference.java @@ -36,24 +36,24 @@ static boolean isAvailable() { theUnsafe.setAccessible(true); Object instance = theUnsafe.get(null); if (instance.getClass() == clazz) { - // Validate that this Unsafe instance exposes all methods we need - clazz.getDeclaredMethod("getByte", Object.class, long.class); - clazz.getDeclaredMethod("putByte", Object.class, long.class, byte.class); - clazz.getDeclaredMethod("getShort", Object.class, long.class); - clazz.getDeclaredMethod("putShort", Object.class, long.class, short.class); - clazz.getDeclaredMethod("getInt", Object.class, long.class); - clazz.getDeclaredMethod("putInt", Object.class, long.class, int.class); - clazz.getDeclaredMethod("getLong", Object.class, long.class); - clazz.getDeclaredMethod("putLong", Object.class, long.class, long.class); - clazz.getDeclaredMethod("getFloat", Object.class, long.class); - clazz.getDeclaredMethod("putFloat", Object.class, long.class, float.class); - clazz.getDeclaredMethod("getDouble", Object.class, long.class); - clazz.getDeclaredMethod("putDouble", Object.class, long.class, double.class); - clazz.getDeclaredMethod("getBoolean", Object.class, long.class); - clazz.getDeclaredMethod("putBoolean", Object.class, long.class, boolean.class); - clazz.getDeclaredMethod("copyMemory", Object.class, long.class, Object.class, long.class, long.class); - clazz.getDeclaredMethod("arrayBaseOffset", Class.class); - clazz.getDeclaredMethod("arrayIndexScale", Class.class); + checkMethod(clazz, "getByte", Object.class, long.class); + checkMethod(clazz, "putByte", Object.class, long.class, byte.class); + checkMethod(clazz, "getShort", Object.class, long.class); + checkMethod(clazz, "putShort", Object.class, long.class, short.class); + checkMethod(clazz, "getInt", Object.class, long.class); + checkMethod(clazz, "putInt", Object.class, long.class, int.class); + checkMethod(clazz, "getLong", Object.class, long.class); + checkMethod(clazz, "putLong", Object.class, long.class, long.class); + checkMethod(clazz, "getFloat", Object.class, long.class); + checkMethod(clazz, "putFloat", Object.class, long.class, float.class); + checkMethod(clazz, "getDouble", Object.class, long.class); + checkMethod(clazz, "putDouble", Object.class, long.class, double.class); + checkMethod(clazz, "getBoolean", Object.class, long.class); + checkMethod(clazz, "putBoolean", Object.class, long.class, boolean.class); + checkMethod(clazz, "copyMemory", Object.class, long.class, Object.class, long.class, long.class); + checkMethod(clazz, "arrayBaseOffset", Class.class); + checkMethod(clazz, "arrayIndexScale", Class.class); + unsafe = (Unsafe) instance; } } catch (ClassNotFoundException | NoSuchMethodException | NoSuchFieldException | SecurityException | IllegalAccessException | ClassCastException ex) { @@ -61,4 +61,14 @@ static boolean isAvailable() { } UNSAFE = unsafe; } + + /** + * Validate that this Unsafe instance exposes this method + * + * ErrorProne does not like that we do nothing with the returned method... but there is nothing to do with it, so disable the check + */ + @SuppressWarnings("ReturnValueIgnored") + private static void checkMethod(Class unsafeClass, String methodName, Class... parameterTypes) throws NoSuchMethodException { + unsafeClass.getDeclaredMethod(methodName, parameterTypes); + } } diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/dense/AbstractDenseNdArray.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/dense/AbstractDenseNdArray.java index 0497095..a22518d 100644 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/dense/AbstractDenseNdArray.java +++ b/ndarray/src/main/java/org/tensorflow/ndarray/impl/dense/AbstractDenseNdArray.java @@ -18,6 +18,7 @@ import org.tensorflow.ndarray.NdArray; import org.tensorflow.ndarray.NdArraySequence; +import org.tensorflow.ndarray.Shape; import org.tensorflow.ndarray.impl.AbstractNdArray; import org.tensorflow.ndarray.impl.dimension.RelativeDimensionalSpace; import org.tensorflow.ndarray.impl.sequence.FastElementSequence; @@ -43,7 +44,7 @@ public NdArraySequence elements(int dimensionIdx) { DimensionalSpace elemDims = dimensions().from(dimensionIdx + 1); try { DataBufferWindow> elemWindow = buffer().window(elemDims.physicalSize()); - U element = instantiate(elemWindow.buffer(), elemDims); + U element = instantiateView(elemWindow.buffer(), elemDims); return new FastElementSequence(this, dimensionIdx, element, elemWindow); } catch (UnsupportedOperationException e) { // If buffer windows are not supported, fallback to slicing (and slower) sequence @@ -51,10 +52,21 @@ public NdArraySequence elements(int dimensionIdx) { } } + @Override + public U withShape(Shape shape) { + if (shape == null || shape.isUnknown() || shape.size() != this.shape().size()) { + throw new IllegalArgumentException("Shape " + shape + " cannot be used to reshape ndarray of shape " + this.shape()); + } + if (shape.equals(this.shape())) { + return (U)this; + } + return instantiateView(buffer(), DimensionalSpace.create(shape)); + } + @Override public U slice(long position, DimensionalSpace sliceDimensions) { DataBuffer sliceBuffer = buffer().slice(position, sliceDimensions.physicalSize()); - return instantiate(sliceBuffer, sliceDimensions); + return instantiateView(sliceBuffer, sliceDimensions); } @Override @@ -89,15 +101,15 @@ public U setObject(T value, long... coords) { } @Override - public U read(DataBuffer dst) { - Validator.readToBufferArgs(this, dst); + public U copyTo(DataBuffer dst) { + Validator.copyToBufferArgs(this, dst); DataTransfer.execute(buffer(), dimensions(), dst, DataTransfer::ofValue); return (U)this; } @Override - public U write(DataBuffer src) { - Validator.writeFromBufferArgs(this, src); + public U copyFrom(DataBuffer src) { + Validator.copyFromBufferArgs(this, src); DataTransfer.execute(src, buffer(), dimensions(), DataTransfer::ofValue); return (U)this; } @@ -132,13 +144,22 @@ public boolean equals(Object obj) { return buffer().equals(other.buffer()); } + /** + * A String showing the type and shape of this dense ndarray. + * @return A string containing the type and shape. + */ + @Override + public String toString() { + return this.getClass().getSimpleName() + "(shape=" + this.shape() + ")"; + } + protected AbstractDenseNdArray(DimensionalSpace dimensions) { super(dimensions); } abstract protected DataBuffer buffer(); - abstract U instantiate(DataBuffer buffer, DimensionalSpace dimensions); + abstract U instantiateView(DataBuffer buffer, DimensionalSpace dimensions); long positionOf(long[] coords, boolean isValue) { if (coords == null || coords.length == 0) { diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/dense/BooleanDenseNdArray.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/dense/BooleanDenseNdArray.java index 0764146..c3df6e8 100644 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/dense/BooleanDenseNdArray.java +++ b/ndarray/src/main/java/org/tensorflow/ndarray/impl/dense/BooleanDenseNdArray.java @@ -55,15 +55,15 @@ public BooleanNdArray copyTo(NdArray dst) { } @Override - public BooleanNdArray read(BooleanDataBuffer dst) { - Validator.readToBufferArgs(this, dst); + public BooleanNdArray copyTo(BooleanDataBuffer dst) { + Validator.copyToBufferArgs(this, dst); DataTransfer.execute(buffer, dimensions(), dst, DataTransfer::ofBoolean); return this; } @Override - public BooleanNdArray write(BooleanDataBuffer src) { - Validator.writeFromBufferArgs(this, src); + public BooleanNdArray copyFrom(BooleanDataBuffer src) { + Validator.copyFromBufferArgs(this, src); DataTransfer.execute(src, buffer, dimensions(), DataTransfer::ofBoolean); return this; } @@ -73,7 +73,7 @@ protected BooleanDenseNdArray(BooleanDataBuffer buffer, Shape shape) { } @Override - BooleanDenseNdArray instantiate(DataBuffer buffer, DimensionalSpace dimensions) { + BooleanDenseNdArray instantiateView(DataBuffer buffer, DimensionalSpace dimensions) { return new BooleanDenseNdArray((BooleanDataBuffer)buffer, dimensions); } diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/dense/ByteDenseNdArray.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/dense/ByteDenseNdArray.java index 172432b..1f01ce6 100644 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/dense/ByteDenseNdArray.java +++ b/ndarray/src/main/java/org/tensorflow/ndarray/impl/dense/ByteDenseNdArray.java @@ -55,15 +55,15 @@ public ByteNdArray copyTo(NdArray dst) { } @Override - public ByteNdArray read(ByteDataBuffer dst) { - Validator.readToBufferArgs(this, dst); + public ByteNdArray copyTo(ByteDataBuffer dst) { + Validator.copyToBufferArgs(this, dst); DataTransfer.execute(buffer, dimensions(), dst, DataTransfer::ofByte); return this; } @Override - public ByteNdArray write(ByteDataBuffer src) { - Validator.writeFromBufferArgs(this, src); + public ByteNdArray copyFrom(ByteDataBuffer src) { + Validator.copyFromBufferArgs(this, src); DataTransfer.execute(src, buffer, dimensions(), DataTransfer::ofByte); return this; } @@ -73,7 +73,7 @@ protected ByteDenseNdArray(ByteDataBuffer buffer, Shape shape) { } @Override - ByteDenseNdArray instantiate(DataBuffer buffer, DimensionalSpace dimensions) { + ByteDenseNdArray instantiateView(DataBuffer buffer, DimensionalSpace dimensions) { return new ByteDenseNdArray((ByteDataBuffer)buffer, dimensions); } diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/dense/DenseNdArray.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/dense/DenseNdArray.java index 819d95d..18b3755 100644 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/dense/DenseNdArray.java +++ b/ndarray/src/main/java/org/tensorflow/ndarray/impl/dense/DenseNdArray.java @@ -45,7 +45,7 @@ protected DenseNdArray(DataBuffer buffer, Shape shape) { } @Override - DenseNdArray instantiate(DataBuffer buffer, DimensionalSpace dimensions) { + DenseNdArray instantiateView(DataBuffer buffer, DimensionalSpace dimensions) { return new DenseNdArray<>(buffer, dimensions); } diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/dense/DoubleDenseNdArray.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/dense/DoubleDenseNdArray.java index f54b8d0..d983d16 100644 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/dense/DoubleDenseNdArray.java +++ b/ndarray/src/main/java/org/tensorflow/ndarray/impl/dense/DoubleDenseNdArray.java @@ -55,15 +55,15 @@ public DoubleNdArray copyTo(NdArray dst) { } @Override - public DoubleNdArray read(DoubleDataBuffer dst) { - Validator.readToBufferArgs(this, dst); + public DoubleNdArray copyTo(DoubleDataBuffer dst) { + Validator.copyToBufferArgs(this, dst); DataTransfer.execute(buffer, dimensions(), dst, DataTransfer::ofDouble); return this; } @Override - public DoubleNdArray write(DoubleDataBuffer src) { - Validator.writeFromBufferArgs(this, src); + public DoubleNdArray copyFrom(DoubleDataBuffer src) { + Validator.copyFromBufferArgs(this, src); DataTransfer.execute(src, buffer, dimensions(), DataTransfer::ofDouble); return this; } @@ -73,7 +73,7 @@ protected DoubleDenseNdArray(DoubleDataBuffer buffer, Shape shape) { } @Override - DoubleDenseNdArray instantiate(DataBuffer buffer, DimensionalSpace dimensions) { + DoubleDenseNdArray instantiateView(DataBuffer buffer, DimensionalSpace dimensions) { return new DoubleDenseNdArray((DoubleDataBuffer)buffer, dimensions); } diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/dense/FloatDenseNdArray.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/dense/FloatDenseNdArray.java index 196b5ef..779e047 100644 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/dense/FloatDenseNdArray.java +++ b/ndarray/src/main/java/org/tensorflow/ndarray/impl/dense/FloatDenseNdArray.java @@ -55,15 +55,15 @@ public FloatNdArray copyTo(NdArray dst) { } @Override - public FloatNdArray read(FloatDataBuffer dst) { - Validator.readToBufferArgs(this, dst); + public FloatNdArray copyTo(FloatDataBuffer dst) { + Validator.copyToBufferArgs(this, dst); DataTransfer.execute(buffer, dimensions(), dst, DataTransfer::ofFloat); return this; } @Override - public FloatNdArray write(FloatDataBuffer src) { - Validator.writeFromBufferArgs(this, src); + public FloatNdArray copyFrom(FloatDataBuffer src) { + Validator.copyFromBufferArgs(this, src); DataTransfer.execute(src, buffer, dimensions(), DataTransfer::ofFloat); return this; } @@ -73,7 +73,7 @@ protected FloatDenseNdArray(FloatDataBuffer buffer, Shape shape) { } @Override - FloatDenseNdArray instantiate(DataBuffer buffer, DimensionalSpace dimensions) { + FloatDenseNdArray instantiateView(DataBuffer buffer, DimensionalSpace dimensions) { return new FloatDenseNdArray((FloatDataBuffer) buffer, dimensions); } diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/dense/IntDenseNdArray.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/dense/IntDenseNdArray.java index a7af498..4e2183d 100644 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/dense/IntDenseNdArray.java +++ b/ndarray/src/main/java/org/tensorflow/ndarray/impl/dense/IntDenseNdArray.java @@ -55,15 +55,15 @@ public IntNdArray copyTo(NdArray dst) { } @Override - public IntNdArray read(IntDataBuffer dst) { - Validator.readToBufferArgs(this, dst); + public IntNdArray copyTo(IntDataBuffer dst) { + Validator.copyToBufferArgs(this, dst); DataTransfer.execute(buffer, dimensions(), dst, DataTransfer::ofInt); return this; } @Override - public IntNdArray write(IntDataBuffer src) { - Validator.writeFromBufferArgs(this, src); + public IntNdArray copyFrom(IntDataBuffer src) { + Validator.copyFromBufferArgs(this, src); DataTransfer.execute(src, buffer, dimensions(), DataTransfer::ofInt); return this; } @@ -73,7 +73,7 @@ protected IntDenseNdArray(IntDataBuffer buffer, Shape shape) { } @Override - IntDenseNdArray instantiate(DataBuffer buffer, DimensionalSpace dimensions) { + IntDenseNdArray instantiateView(DataBuffer buffer, DimensionalSpace dimensions) { return new IntDenseNdArray((IntDataBuffer)buffer, dimensions); } diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/dense/LongDenseNdArray.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/dense/LongDenseNdArray.java index cd56dad..cde91a3 100644 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/dense/LongDenseNdArray.java +++ b/ndarray/src/main/java/org/tensorflow/ndarray/impl/dense/LongDenseNdArray.java @@ -55,15 +55,15 @@ public LongNdArray copyTo(NdArray dst) { } @Override - public LongNdArray read(LongDataBuffer dst) { - Validator.readToBufferArgs(this, dst); + public LongNdArray copyTo(LongDataBuffer dst) { + Validator.copyToBufferArgs(this, dst); DataTransfer.execute(buffer, dimensions(), dst, DataTransfer::ofLong); return this; } @Override - public LongNdArray write(LongDataBuffer src) { - Validator.writeFromBufferArgs(this, src); + public LongNdArray copyFrom(LongDataBuffer src) { + Validator.copyFromBufferArgs(this, src); DataTransfer.execute(src, buffer, dimensions(), DataTransfer::ofLong); return this; } @@ -73,7 +73,7 @@ protected LongDenseNdArray(LongDataBuffer buffer, Shape shape) { } @Override - LongDenseNdArray instantiate(DataBuffer buffer, DimensionalSpace dimensions) { + LongDenseNdArray instantiateView(DataBuffer buffer, DimensionalSpace dimensions) { return new LongDenseNdArray((LongDataBuffer)buffer, dimensions); } diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/dense/ShortDenseNdArray.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/dense/ShortDenseNdArray.java index 291c01a..8d0dfc8 100644 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/dense/ShortDenseNdArray.java +++ b/ndarray/src/main/java/org/tensorflow/ndarray/impl/dense/ShortDenseNdArray.java @@ -55,15 +55,15 @@ public ShortNdArray copyTo(NdArray dst) { } @Override - public ShortNdArray read(ShortDataBuffer dst) { - Validator.readToBufferArgs(this, dst); + public ShortNdArray copyTo(ShortDataBuffer dst) { + Validator.copyToBufferArgs(this, dst); DataTransfer.execute(buffer, dimensions(), dst, DataTransfer::ofShort); return this; } @Override - public ShortNdArray write(ShortDataBuffer src) { - Validator.writeFromBufferArgs(this, src); + public ShortNdArray copyFrom(ShortDataBuffer src) { + Validator.copyFromBufferArgs(this, src); DataTransfer.execute(src, buffer, dimensions(), DataTransfer::ofShort); return this; } @@ -73,7 +73,7 @@ protected ShortDenseNdArray(ShortDataBuffer buffer, Shape shape) { } @Override - ShortDenseNdArray instantiate(DataBuffer buffer, DimensionalSpace dimensions) { + ShortDenseNdArray instantiateView(DataBuffer buffer, DimensionalSpace dimensions) { return new ShortDenseNdArray((ShortDataBuffer)buffer, dimensions); } diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/dimension/DimensionalSpace.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/dimension/DimensionalSpace.java index 7d0f022..71d1677 100644 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/dimension/DimensionalSpace.java +++ b/ndarray/src/main/java/org/tensorflow/ndarray/impl/dimension/DimensionalSpace.java @@ -28,7 +28,7 @@ public static DimensionalSpace create(Shape shape) { // Start from the last dimension, where all elements are continuous for (int i = dimensions.length - 1, elementSize = 1; i >= 0; --i) { - dimensions[i] = new Axis(shape.size(i), elementSize); + dimensions[i] = new Axis(shape.get(i), elementSize); elementSize *= dimensions[i].numElements(); } return new DimensionalSpace(dimensions, shape); @@ -144,7 +144,7 @@ public DimensionalSpace from(int dimensionStart) { throw new IndexOutOfBoundsException(); } Dimension[] newDimensions = Arrays.copyOfRange(dimensions, dimensionStart, dimensions.length); - if (segmentationIdx > dimensionStart) { + if (segmentationIdx >= dimensionStart) { return new DimensionalSpace(newDimensions, segmentationIdx - dimensionStart); } return new DimensionalSpace(newDimensions); @@ -189,7 +189,9 @@ public long positionOf(long[] coords) { return position; } - /** Succinct description of the shape meant for debugging. */ + /** + * Succinct description of the shape meant for debugging. + */ @Override public String toString() { return Arrays.toString(dimensions); diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/sparse/AbstractSparseNdArray.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/sparse/AbstractSparseNdArray.java new file mode 100644 index 0000000..e4a2fba --- /dev/null +++ b/ndarray/src/main/java/org/tensorflow/ndarray/impl/sparse/AbstractSparseNdArray.java @@ -0,0 +1,550 @@ +/* Copyright 2021 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +=======================================================================*/ +package org.tensorflow.ndarray.impl.sparse; + +import org.tensorflow.ndarray.IllegalRankException; +import org.tensorflow.ndarray.LongNdArray; +import org.tensorflow.ndarray.NdArray; +import org.tensorflow.ndarray.NdArraySequence; +import org.tensorflow.ndarray.NdArrays; +import org.tensorflow.ndarray.Shape; +import org.tensorflow.ndarray.SparseNdArray; +import org.tensorflow.ndarray.impl.AbstractNdArray; +import org.tensorflow.ndarray.impl.dense.AbstractDenseNdArray; +import org.tensorflow.ndarray.impl.dimension.Dimension; +import org.tensorflow.ndarray.impl.dimension.DimensionalSpace; +import org.tensorflow.ndarray.impl.dimension.RelativeDimensionalSpace; +import org.tensorflow.ndarray.impl.sequence.SingleElementSequence; +import org.tensorflow.ndarray.impl.sequence.SlicingElementSequence; +import org.tensorflow.ndarray.index.Index; + +import java.nio.ReadOnlyBufferException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.LongStream; + +/** + * Abstract base class for sparse array. + * + *

    A sparse array as two separate dense arrays: indices, values, and a shape that represents the + * dense shape. + * + *

    NOTE: all Sparse Arrays are readonly for the {@link #set(NdArray, long...)} and + * {@link #setObject(Object, long...)} methods + * + *

    {@code
    + * FloatSparseNdArray st = new FloatSparseNdArray(
    + *      StdArrays.of(new long[][] {{0, 0}, {1, 2}}),
    + *      NdArrays.vectorOf(1f, 2f),
    + *      Shape.of(3, 4));
    + *
    + * }
    + * + *

    represents the dense array: + * + *

    {@code
    + * [[1, 0, 0, 0]
    + *  [0, 0, 2, 0]
    + *  [0, 0, 0, 0]]
    + *
    + * }
    + * + * @param the type that the array contains + * @param the type of dense NdArray + */ +public abstract class AbstractSparseNdArray> extends AbstractNdArray + implements SparseNdArray { + /** + * A 2-D long array of shape {@code [N, ndims]}, that specifies the indices of the elements in the + * sparse array that contain non-default values (elements are zero-indexed). + * + *

    For example, {@code indices=[[1,3], [2,4]]} specifies that the elements with indexes of + * coordinates {@code [1,3]} and {@code [2,4]} have non-default values. + */ + private LongNdArray indices; + + /** + * A 1-D array of any type and shape {@code [N]}, that supplies the values for each element in + * indices. + * + *

    For example, given {@code indices=[[1,3], [2,4]]}, and {@code values=[18, 3.6]} specifies + * that element {@code [1,3]} of the sparse array has a value of {@code 18}, and element {@code + * [2,4]} of the sparse array has a value of {@code 3.6}. + */ + private U values; + + /** + * Scalar value to set for indices not specified in {@link #getIndices()} This will default to + * zero, false, or the empty string depending on the data type of the values. + */ + private T defaultValue; + + /** + * Scalar NdArray to use for indices not specified in {@link #getIndices()} This will default to + * zero, false, or the empty string depending on the data type of the values, otherwise it will + * contain the defaultValue. + */ + private U defaultArray; + + /** + * Creates an abstract SparseNdArray + * + * @param indices A 2-D LongNdArray of shape {@code [N, ndims]}, that specifies the indices of the + * elements in the sparse array that contain non-default values (elements are zero-indexed). + * For example, {@code indices=[[1,3], [2,4]]} specifies that the elements with indexes of + * {@code [1,3]} and {@code [2,4]} have non-default values. + * @param values A 1-D NdArray of any type and shape {@code [N]}, which supplies the values for + * each element in indices. For example, given {@code indices=[[1,3], [2,4]]}, the parameter + * {@code values=[18, 3.6]} specifies that element {@code [1,3]} of the sparse NdArray has a + * value of {@code 18}, and element {@code [2,4]} of the NdArray has a value of {@code 3.6}. + * @param defaultValue Scalar value to set for indices not specified in {@link #indices} + * @param dimensions the dimensional space for the dense object represented by this sparse array. + */ + protected AbstractSparseNdArray( + LongNdArray indices, U values, T defaultValue, DimensionalSpace dimensions) { + super(dimensions); + this.indices = indices; + this.values = values; + setDefaultValue(defaultValue); + + // sanity checks on shapes, indices (shape = {@code [N, ndims]}, where N is the number of values + // (shape = {@code [N]}}. + if (this.indices.shape().get(0) != this.values.shape().get(0)) { + throw new IllegalArgumentException( + String.format( + "The number of rows in indices (%d) does not match the number of elements in values(%d).", + this.indices.shape().get(0), this.values.shape().get(0))); + } + + // sanity checks on shapes, indices (shape = {@code [N, ndims]}, where ndims = the number of + // dimensions in the dense shape. + if (this.indices.shape().get(1) != shape().numDimensions()) { + throw new IllegalArgumentException( + String.format( + "The number of columns in indices (%d) does not match the number of dimensions in shape (%d).", + this.indices.shape().get(1), shape().get(0))); + } + } + + /** + * Creates an abstract SparseNdArray + * + * @param defaultValue Scalar value to set for indices not specified in {@link #getIndices()} + * @param dimensions the dimensional space for the dense object represented by this sparse array, + */ + protected AbstractSparseNdArray(T defaultValue, DimensionalSpace dimensions) { + super(dimensions); + setDefaultValue(defaultValue); + } + + /** {@inheritDoc} */ + @Override + public NdArraySequence elements(int dimensionIdx) { + if (dimensionIdx >= shape().numDimensions()) { + throw new IllegalArgumentException( + "Cannot iterate elements in dimension '" + + dimensionIdx + + "' of array with shape " + + shape()); + } + if (rank() == 0 && dimensionIdx < 0) { + return new SingleElementSequence<>(this); + } + DimensionalSpace elemDims = dimensions().from(dimensionIdx + 1); + + return new SlicingElementSequence<>(this, dimensionIdx, elemDims); + } + + /** + * Computes the coordinates based on a relative position to the beginning of the dimension space. + * + * @param dimensions the dimension space + * @param position relative position to the beginning of the dimension space. + * @return the coordinates + */ + // TODO should have automatical access to the coordinates from which this position is coming from. + // But that will require some refactoring even at the dense level. + protected long[] toCoordinates(DimensionalSpace dimensions, long position) { + long[] result = new long[dimensions.numDimensions()]; + long p = position; + + for (int dim = 0; dim < dimensions.numDimensions(); dim++) { + Dimension dimension = dimensions.get(dim); + result[dim] = p / dimension.elementSize(); + p = p % dimension.elementSize(); + } + return result; + } + + /** + * Converts the given set of indices coordinates to a long array of coordinates. + * + *

    The shape of the NdArray is {@code [ndims]} + * + * @param l the LongNdArray containing the coordinates + * @return the long array containing the coordinates. + */ + protected long[] getIndicesCoordinates(LongNdArray l) { + long[] results = new long[(int) l.size()]; + for (int i = 0; i < l.size(); i++) { + results[i] = l.getLong(i); + } + return results; + } + + /** + * Converts this sparse array to a dense array. + * + * @return the dense array. + */ + public abstract U toDense(); + + @Override + public U withShape(Shape shape) { + throw new UnsupportedOperationException("Sparse NdArrays cannot be viewed with a different shape"); + } + + /** {@inheritDoc} */ + @Override + public NdArray slice(Index... indices) { + if (indices == null) { + throw new IllegalArgumentException("Slicing requires at least one index"); + } + RelativeDimensionalSpace sliceDimensions = dimensions().mapTo(indices); + return slice(sliceDimensions.position(), sliceDimensions); + } + + /** {@inheritDoc} */ + @Override + public NdArray get(long... coordinates) { + return slice(positionOf(coordinates, false), dimensions().from(coordinates.length)); + } + + /** {@inheritDoc} */ + @Override + public T getObject(long... coordinates) { + if (coordinates.length != shape().numDimensions()) { + throw new IllegalRankException( + String.format( + "Length of coordinates (%s)%s does not match the rank %d", + coordinates.length, Arrays.toString(coordinates), shape().numDimensions())); + } + long index = locateIndex(coordinates); + if (index >= 0) { + return getValues().getObject(index); + } else { + return defaultValue; + } + } + + /** {@inheritDoc} */ + @Override + public NdArray setObject(T value, long... coords) { + throw new ReadOnlyBufferException(); + } + + /** {@inheritDoc} */ + @Override + public NdArray set(NdArray src, long... coordinates) { + throw new ReadOnlyBufferException(); + } + + /** + * Creates a dense array of the type that this sparse array represents. + * + * @param shape the shape of the dense array. + * @return the dense of the type that this sparse array represents. + */ + public abstract U createValues(Shape shape); + + /** {@inheritDoc} */ + @Override + public NdArray copyTo(NdArray dst) { + if (dst instanceof AbstractSparseNdArray) { + AbstractSparseNdArray sparse = (AbstractSparseNdArray) dst; + LongNdArray indicesCopy = NdArrays.ofLongs(indices.shape()); + this.indices.copyTo(indicesCopy); + U valuesCopy = createValues(values.shape()); + this.values.copyTo(valuesCopy); + sparse.setIndices(indicesCopy); + sparse.setValues(valuesCopy); + } else { + U dense = toDense(); + dense.copyTo(dst); + } + return this; + } + + /** + * Computes the position within the dense array given by the coordinates + * + * @param coords the coordinates within the dense array + * @param isValue indicator whether the coordinates represents a value or higher level dimension. + * @return the position within the array + */ + protected long positionOf(long[] coords, boolean isValue) { + if (coords == null || coords.length == 0) { + return 0; + } + Validator.coordinates(dimensions, coords, isValue); + return dimensions.positionOf(coords); + } + + /** {@inheritDoc} */ + @Override + protected void slowCopyTo(NdArray array) { + if (array instanceof AbstractDenseNdArray) { + AbstractDenseNdArray dst = (AbstractDenseNdArray) array; + long offset = 0L; + for (NdArray s : scalars()) { + dst.setObject(s.getObject(), offset++); + } + } else if (array instanceof AbstractSparseNdArray) { + AbstractSparseNdArray dst = (AbstractSparseNdArray) array; + indices.copyTo(dst.getIndices()); + values.copyTo(dst.values); + } else { + super.slowCopyTo(array); + } + } + + /** + * Gets the Indices + * + * @return the Indices + */ + public LongNdArray getIndices() { + return indices; + } + + /** + * Sets the Indices + * + * @param indices the Indices + */ + public void setIndices(LongNdArray indices) { + this.indices = indices; + } + + /** + * Gets the values + * + * @return the values + */ + public U getValues() { + return values; + } + + /** + * Sets the values + * + * @param values the values + */ + public void setValues(U values) { + this.values = values; + } + + /** + * Gets the values index by coordinates + * + * @param coordinates the coordinates to locate + * @return index of the coordinates, if the coordinates are contained in the {@code indices} + * array; otherwise, {@code (-(insertion point) - 1)}. The insertion point is defined as the + * point at which the {@code coordinates} would be inserted into the {@code indices} array: + * the index of the first element greater than the key, or {@code indices.shape().get(0)}; if + * all elements in the array are less than the specified key. Note that this guarantees that + * the return value will be {@code >= 0}, only if the coordinates are found. + */ + protected long locateIndex(long[] coordinates) { + long size = indices.shape().get(0); + LongNdArray coordArray = NdArrays.vectorOf(coordinates); + return binarySearch(size, coordArray); + } + + /** {@inheritDoc} */ + @Override + public int hashCode() { + if (dimensions().isSegmented()) { + return slowHashCode(); + } + final int prime = 31; + int result = 1; + result = prime * result + indices.hashCode(); + result = prime * result + values.hashCode(); + result = prime * result + shape().hashCode(); + return result; + } + + /** {@inheritDoc} */ + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof AbstractSparseNdArray)) { + return super.equals(obj); + } + AbstractSparseNdArray other = (AbstractSparseNdArray) obj; + if (!shape().equals(other.shape())) { + return false; + } + if (!indices.equals(other.indices)) { + return false; + } + return values.equals(other.values); + } + + /** + * A String showing the type, default value, number of elements and + * the dense shape of this sparse ndarray. + * @return A string containing the type, default value, number of elements and shape. + */ + @Override + public String toString() { + long numElements = values == null ? 0 : values.size(); + String strDefault; + if (defaultValue == null) { + strDefault = ""; + } else if (defaultValue instanceof Number) { + strDefault = defaultValue.toString(); + } else { + strDefault = "'" + defaultValue + "'"; + } + return this.getClass().getSimpleName() + "(defaultValue=" + strDefault + + ", numElements=" + numElements + ", shape=" + this.shape() + ")"; + } + + /** + * Performs a binary search on the indices array to locate the index of the specified coordinates. + * The indices array must be sorted by coordinates, row major. + * + * @param toIndex the index of the last element (exclusive) to be searched + * @param coordinates the coordinates to locate + * @return index of the coordinates, if the coordinates are contained in the {@code indices} + * array; otherwise, {@code (-(insertion point) - 1)}. The insertion point is defined as the + * point at which the {@code coordinates} would be inserted into the {@code indices} array: + * the index of the first element greater than the key, or {@code indices.shape().get(0)}; if + * all elements in the array are less than the specified key. Note that this guarantees that + * the return value will be @{code >= 0}, only if the coordinates are found. + */ + private long binarySearch(long toIndex, LongNdArray coordinates) { + + long low = 0; + long high = toIndex - 1; + + while (low <= high) { + long mid = (low + high) >>> 1; + LongNdArray comparable = indices.get(mid); + int rc = compareCoordinates(comparable, coordinates); + if (rc < 0) { // less than + low = mid + 1; + } else if (rc > 0) { // higher than + high = mid - 1; + } else { // match + return mid; + } + } + return -(low + 1); // no match + } + + /** + * Sorts the indices and values in ascending row-major coordinates. + * + * @return this instance + */ + @SuppressWarnings("UnusedReturnValue") + public AbstractSparseNdArray sortIndicesAndValues() { + + // indices will contain the indexes into the indices and values ndArrays, resorted. + List indexes = new ArrayList<>(); + // create a range for the length of values + LongStream.range(0, values.size()).forEach(indexes::add); + + // then sort this range based on ascending row-wise coordinates. + indexes.sort((a, b) -> compareCoordinates(indices.get(a), indices.get(b))); + + LongNdArray newIndices = NdArrays.ofLongs(indices.shape()); + U newValues = createValues(values.shape()); + // used the sorted indexes to set up the sorted Indices and Values + for (long i = 0; i < indexes.size(); i++) { + long moveIndex = indexes.get((int) i); + newIndices.set(indices.get(moveIndex), i); + newValues.setObject(values.getObject(moveIndex), i); + } + indices = newIndices; + values = newValues; + return this; + } + + /** + * Compares its two arguments for row major coordinate order. + * + * @return a negative integer, zero, or a positive integer as the first argument is less than, + * equal to, or greater than the second. + */ + private int compareCoordinates(LongNdArray a, LongNdArray b) { + int rc = (int) (a.size() - b.size()); + if (rc != 0) { + return rc; + } + + for (long i = 0; i < a.size(); i++) { + long l = a.getLong(i); + rc = (int) (l - b.getLong(i)); + if (rc != 0) { + return rc; + } + } + return 0; + } + + /** + * Scalar value to set for indices not specified in {@link #indices}, defaults to zero, false, or + * the empty String depending on the data type. + */ + public T getDefaultValue() { + return defaultValue; + } + + /** + * Sets the defaultValue + * + * @param defaultValue the default value + */ + public void setDefaultValue(T defaultValue) { + this.defaultValue = defaultValue; + defaultArray = null; + } + + /** + * Creates the NdArray with the default value as a scalar + * + * @return the default NdArray of the default value as a scalar + */ + public abstract U createDefaultArray(); + + /** + * Scalar NdArray to use for indices not specified in {@link #getIndices()} This will default to + * zero, false, or the empty string depending on the data type of the values, otherwise it will + * contain the {@link #defaultValue}. + */ + public U getDefaultArray() { + if (defaultArray == null) { + defaultArray = createDefaultArray(); + } + return defaultArray; + } +} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/sparse/BooleanSparseNdArray.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/sparse/BooleanSparseNdArray.java new file mode 100644 index 0000000..d5d8d72 --- /dev/null +++ b/ndarray/src/main/java/org/tensorflow/ndarray/impl/sparse/BooleanSparseNdArray.java @@ -0,0 +1,428 @@ +/* Copyright 2021 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +=======================================================================*/ +package org.tensorflow.ndarray.impl.sparse; + +import org.tensorflow.ndarray.BooleanNdArray; +import org.tensorflow.ndarray.LongNdArray; +import org.tensorflow.ndarray.NdArray; +import org.tensorflow.ndarray.NdArrays; +import org.tensorflow.ndarray.Shape; +import org.tensorflow.ndarray.StdArrays; +import org.tensorflow.ndarray.buffer.BooleanDataBuffer; +import org.tensorflow.ndarray.buffer.DataBuffer; +import org.tensorflow.ndarray.buffer.DataBuffers; +import org.tensorflow.ndarray.impl.dimension.DimensionalSpace; +import org.tensorflow.ndarray.impl.sparse.slice.BooleanSparseSlice; +import org.tensorflow.ndarray.index.Index; + +import java.nio.ReadOnlyBufferException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * sparse array for the boolean data type + * + *

    A sparse array as two separate dense arrays: indices, values, and a shape that represents the + * dense shape. + * + *

    NOTE: all Sparse Arrays are readonly for the {@link #set(NdArray, long...)} and + * {@link #setObject(Boolean, long...)} methods + * + *

    {@code
    + * FloatSparseNdArray st = new BooleanSparseNdArray(
    + *      StdArrays.of(new long[][] {{0, 0}, {1, 2}}),
    + *      NdArrays.vectorOf(true, true),
    + *      Shape.of(3, 4));
    + *
    + * }
    + * + *

    represents the dense array: + * + *

    {@code
    + * [[true, false, false, false]
    + *  [false, false, true, false]
    + *  [false, false, false, false]]
    + *
    + * }
    + */ +public class BooleanSparseNdArray extends AbstractSparseNdArray + implements BooleanNdArray { + + /** + * Creates a BooleanSparseNdArray + * + * @param indices A 2-D LongNdArray of shape {@code [N, ndims]}, that specifies the indices of the + * elements in the sparse array that contain non-default values (elements are zero-indexed). + * For example, {@code indices=[[1,3], [2,4]]} specifies that the elements with indexes of + * {@code [1,3]} and {@code [2,4]} have non-default values. + * @param values A 1-D NdArray of Boolean type and shape {@code [N]}, which supplies the values + * for each element in indices. For example, given {@code indices=[[1,3], [2,4]]}, the + * parameter {@code values=[18, 3.6]} specifies that element {@code [1,3]} of the sparse + * NdArray has a value of {@code 18}, and element {@code [2,4]} of the NdArray has a value of + * {@code 3.6}. + * @param defaultValue Scalar value to set for indices not specified in {@link #getIndices()} + * @param dimensions the dimensional space for the dense object represented by this sparse array, + */ + protected BooleanSparseNdArray( + LongNdArray indices, + BooleanNdArray values, + boolean defaultValue, + DimensionalSpace dimensions) { + super(indices, values, defaultValue, dimensions); + } + + /** + * Creates a BooleanSparseNdArray with a default value of false. + * + * @param indices A 2-D LongNdArray of shape {@code [N, ndims]}, that specifies the indices of the + * elements in the sparse array that contain non-default values (elements are zero-indexed). + * For example, {@code indices=[[1,3], [2,4]]} specifies that the elements with indexes of + * {@code [1,3]} and {@code [2,4]} have non-default values. + * @param values A 1-D NdArray of Boolean type and shape {@code [N]}, which supplies the values + * for each element in indices. For example, given {@code indices=[[1,3], [2,4]]}, the + * parameter {@code values=[18, 3.6]} specifies that element {@code [1,3]} of the sparse + * NdArray has a value of {@code 18}, and element {@code [2,4]} of the NdArray has a value of + * {@code 3.6}. + * @param dimensions the dimensional space for the dense object represented by this sparse array, + */ + BooleanSparseNdArray(LongNdArray indices, BooleanNdArray values, DimensionalSpace dimensions) { + this(indices, values, false, dimensions); + } + + /** + * Creates a BooleanSparseNdArray + * + * @param dataBuffer a dense dataBuffer used to create the spars array + * @param dimensions the dimensional space for the dense object represented by this sparse array, + */ + BooleanSparseNdArray(BooleanDataBuffer dataBuffer, DimensionalSpace dimensions) { + this(dataBuffer, false, dimensions); + } + + /** + * Creates a BooleanSparseNdArray + * + * @param dataBuffer a dense dataBuffer used to create the spars array + * @param defaultValue Scalar value to set for indices not specified in {@link #getIndices()} + * @param dimensions the dimensional space for the dense object represented by this sparse array, + */ + BooleanSparseNdArray( + BooleanDataBuffer dataBuffer, boolean defaultValue, DimensionalSpace dimensions) { + super(defaultValue, dimensions); + // use write to set up the indices and values + copyFrom(dataBuffer); + } + + /** + * Creates a zero-filled BooleanSparseNdArray + * + * @param dimensions the dimensional space for the dense object represented by this sparse array, + */ + BooleanSparseNdArray(DimensionalSpace dimensions) { + this(false, dimensions); + } + + /** + * Creates a zero-filled BooleanSparseNdArray + * + * @param defaultValue Scalar value to set for indices not specified in {@link #getIndices()} + * @param dimensions the dimensional space for the dense object represented by this sparse array, + */ + BooleanSparseNdArray(boolean defaultValue, DimensionalSpace dimensions) { + super(defaultValue, dimensions); + } + + /** + * Creates a new BooleanSparseNdArray + * + * @param indices A 2-D LongNdArray of shape {@code [N, ndims]}, that specifies the indices of the + * elements in the sparse array that contain non-default values (elements are zero-indexed). + * For example, {@code indices=[[1,3], [2,4]]} specifies that the elements with indexes of + * {@code [1,3]} and {@code [2,4]} have non-default values. + * @param values A 1-D NdArray of any type and shape {@code [N]}, which supplies the values for + * each element in indices. For example, given {@code indices=[[1,3], [2,4]]}, the parameter + * {@code values=[18, 3.6]} specifies that element {@code [1,3]} of the sparse NdArray has a + * value of {@code 18}, and element {@code [2,4]} of the NdArray has a value of {@code 3.6}. + * @param dimensions the dimensional space for the dense object represented by this sparse array. + * @return the new Sparse Array + */ + public static BooleanSparseNdArray create( + LongNdArray indices, BooleanNdArray values, DimensionalSpace dimensions) { + return new BooleanSparseNdArray(indices, values, dimensions); + } + + /** + * Creates a new BooleanSparseNdArray + * + * @param indices A 2-D LongNdArray of shape {@code [N, ndims]}, that specifies the indices of the + * elements in the sparse array that contain non-default values (elements are zero-indexed). + * For example, {@code indices=[[1,3], [2,4]]} specifies that the elements with indexes of + * {@code [1,3]} and {@code [2,4]} have non-default values. + * @param values A 1-D NdArray of any type and shape {@code [N]}, which supplies the values for + * each element in indices. For example, given {@code indices=[[1,3], [2,4]]}, the parameter + * {@code values=[18, 3.6]} specifies that element {@code [1,3]} of the sparse NdArray has a + * value of {@code 18}, and element {@code [2,4]} of the NdArray has a value of {@code 3.6}. + * @param defaultValue Scalar value to set for indices not specified in {@link #getIndices()} + * @param dimensions the dimensional space for the dense object represented by this sparse array. + * @return the new Sparse Array + */ + public static BooleanSparseNdArray create( + LongNdArray indices, + BooleanNdArray values, + boolean defaultValue, + DimensionalSpace dimensions) { + return new BooleanSparseNdArray(indices, values, defaultValue, dimensions); + } + + /** + * Creates a new BooleanSparseNdArray from a data buffer + * + * @param dataBuffer the databuffer containing the dense array + * @param dimensions the dimensional space for the sparse array + * @return the new Sparse Array + */ + public static BooleanSparseNdArray create( + BooleanDataBuffer dataBuffer, DimensionalSpace dimensions) { + return new BooleanSparseNdArray(dataBuffer, dimensions); + } + + /** + * Creates a new BooleanSparseNdArray from a data buffer + * + * @param dataBuffer the databuffer containing the dense array + * @param defaultValue Scalar value to set for indices not specified in {@link #getIndices()} + * @param dimensions the dimensional space for the sparse array + * @return the new Sparse Array + */ + public static BooleanSparseNdArray create( + BooleanDataBuffer dataBuffer, boolean defaultValue, DimensionalSpace dimensions) { + return new BooleanSparseNdArray(dataBuffer, defaultValue, dimensions); + } + + /** + * Creates a new empty BooleanSparseNdArray from a data buffer + * + * @param dimensions the dimensions array + * @return the new Sparse Array + */ + public static BooleanSparseNdArray create(DimensionalSpace dimensions) { + return new BooleanSparseNdArray(dimensions); + } + + /** + * Creates a new empty BooleanSparseNdArray from a data buffer + * + * @param dimensions the dimensions array + * @param defaultValue Scalar value to set for indices not specified in {@link #getIndices()} + * @return the new Sparse Array + */ + public static BooleanSparseNdArray create(boolean defaultValue, DimensionalSpace dimensions) { + return new BooleanSparseNdArray(defaultValue, dimensions); + } + + /** + * Creates a new empty BooleanSparseNdArray from a float data buffer + * + * @param buffer the data buffer + * @param shape the shape of the sparse array. + * @return the new Sparse Array + */ + public static BooleanSparseNdArray create(BooleanDataBuffer buffer, Shape shape) { + return new BooleanSparseNdArray(buffer, DimensionalSpace.create(shape)); + } + + /** + * Creates a new empty BooleanSparseNdArray from a float data buffer + * + * @param buffer the data buffer + * @param defaultValue Scalar value to set for indices not specified in {@link #getIndices()} + * @param shape the shape of the sparse array. + * @return the new Sparse Array + */ + public static BooleanSparseNdArray create( + BooleanDataBuffer buffer, boolean defaultValue, Shape shape) { + return new BooleanSparseNdArray(buffer, defaultValue, DimensionalSpace.create(shape)); + } + + /** + * Creates a new BooleanSparseNdArray from a BooleanNdArray + * + * @param src the BooleanNdArray + * @return the new Sparse Array + */ + public static BooleanSparseNdArray create(BooleanNdArray src) { + BooleanDataBuffer buffer = DataBuffers.ofBooleans(src.size()); + src.copyTo(buffer); + return new BooleanSparseNdArray(buffer, DimensionalSpace.create(src.shape())); + } + /** + * Creates a new BooleanSparseNdArray from a BooleanNdArray + * + * @param defaultValue Scalar value to set for indices not specified in {@link #getIndices()} + * @param src the BooleanNdArray + * @return the new Sparse Array + */ + public static BooleanSparseNdArray create(BooleanNdArray src, boolean defaultValue) { + BooleanDataBuffer buffer = DataBuffers.ofBooleans(src.size()); + src.copyTo(buffer); + return new BooleanSparseNdArray(buffer, defaultValue, DimensionalSpace.create(src.shape())); + } + + /** {@inheritDoc} */ + @Override + public BooleanNdArray createDefaultArray() { + return NdArrays.scalarOf(getDefaultValue()); + } + + /** + * Creates a BooleanNdArray of the specified shape + * + * @param shape the shape of the dense array. + * @return a BooleanNdArray of the specified shape + */ + public BooleanNdArray createValues(Shape shape) { + return NdArrays.ofBooleans(shape); + } + + /** {@inheritDoc} */ + @Override + public BooleanNdArray slice(long position, DimensionalSpace sliceDimensions) { + return new BooleanSparseSlice(this, position, sliceDimensions); + } + + /** {@inheritDoc} */ + @Override + public boolean getBoolean(long... coordinates) { + return getObject(coordinates); + } + + /** {@inheritDoc} */ + @Override + public BooleanNdArray setBoolean(boolean value, long... coordinates) { + throw new ReadOnlyBufferException(); + } + + /** {@inheritDoc} */ + @Override + public BooleanNdArray copyTo(DataBuffer dst) { + return copyTo((BooleanDataBuffer) dst); + } + + /** {@inheritDoc} */ + @Override + public BooleanNdArray copyTo(BooleanDataBuffer dst) { + // set the values in buf to the default, then overwrite with indices/values + Boolean[] defaults = new Boolean[(int) shape().size()]; + Arrays.fill(defaults, getDefaultValue()); + dst.write(defaults); + + AtomicInteger i = new AtomicInteger(); + getIndices() + .elements(0) + .forEachIndexed( + (idx, l) -> { + long[] coordinates = getIndicesCoordinates(l); + boolean value = getValues().getBoolean(i.getAndIncrement()); + dst.setObject(value, dimensions.positionOf(coordinates)); + }); + return this; + } + + /** {@inheritDoc} */ + @Override + public BooleanNdArray copyFrom(BooleanDataBuffer src) { + List indices = new ArrayList<>(); + List values = new ArrayList<>(); + + for (long i = 0; i < src.size(); i++) { + if (!src.getObject(i).equals(getDefaultValue())) { + indices.add(toCoordinates(dimensions, i)); + values.add(src.getObject(i)); + } + } + long[][] indicesArray = new long[indices.size()][]; + boolean[] valuesArray = new boolean[values.size()]; + for (int i = 0; i < indices.size(); i++) { + indicesArray[i] = indices.get(i); + valuesArray[i] = values.get(i); + } + + setIndices(StdArrays.ndCopyOf(indicesArray)); + setValues(NdArrays.vectorOf(valuesArray)); + return this; + } + + /** {@inheritDoc} */ + @Override + public BooleanNdArray copyFrom(DataBuffer src) { + return copyFrom((BooleanDataBuffer) src); + } + + /** + * Converts the sparse array to a dense array + * + * @return the dense array + */ + public BooleanNdArray toDense() { + BooleanDataBuffer dataBuffer = DataBuffers.ofBooleans(shape().size()); + copyTo(dataBuffer); + return NdArrays.wrap(shape(), dataBuffer); + } + + /** + * Populates this sparse array from a dense array + * + * @param src the dense array + * @return this sparse array + */ + public BooleanNdArray fromDense(BooleanNdArray src) { + BooleanDataBuffer buffer = DataBuffers.ofBooleans(src.size()); + src.copyTo(buffer); + copyFrom(buffer); + return this; + } + + /** {@inheritDoc} */ + @Override + public BooleanNdArray slice(Index... indices) { + return (BooleanNdArray) super.slice(indices); + } + + /** {@inheritDoc} */ + @Override + public BooleanNdArray get(long... coordinates) { + return (BooleanNdArray) super.get(coordinates); + } + + /** {@inheritDoc} */ + @Override + public BooleanNdArray setObject(Boolean value, long... coordinates) { + throw new ReadOnlyBufferException(); + } + + /** {@inheritDoc} */ + @Override + public BooleanNdArray set(NdArray src, long... coordinates) { + throw new ReadOnlyBufferException(); + } + + /** {@inheritDoc} */ + @Override + public BooleanNdArray copyTo(NdArray dst) { + return (BooleanNdArray) super.copyTo(dst); + } +} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/sparse/ByteSparseNdArray.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/sparse/ByteSparseNdArray.java new file mode 100644 index 0000000..633ca49 --- /dev/null +++ b/ndarray/src/main/java/org/tensorflow/ndarray/impl/sparse/ByteSparseNdArray.java @@ -0,0 +1,418 @@ +/* Copyright 2021 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +=======================================================================*/ +package org.tensorflow.ndarray.impl.sparse; + +import org.tensorflow.ndarray.ByteNdArray; +import org.tensorflow.ndarray.LongNdArray; +import org.tensorflow.ndarray.NdArray; +import org.tensorflow.ndarray.NdArrays; +import org.tensorflow.ndarray.Shape; +import org.tensorflow.ndarray.StdArrays; +import org.tensorflow.ndarray.buffer.ByteDataBuffer; +import org.tensorflow.ndarray.buffer.DataBuffer; +import org.tensorflow.ndarray.buffer.DataBuffers; +import org.tensorflow.ndarray.impl.dimension.DimensionalSpace; +import org.tensorflow.ndarray.impl.sparse.slice.ByteSparseSlice; +import org.tensorflow.ndarray.index.Index; + +import java.nio.ReadOnlyBufferException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * sparse array for the byte data type + * + *

    A sparse array as two separate dense arrays: indices, values, and a shape that represents the + * dense shape. + * + *

    NOTE: all Sparse Arrays are readonly for the {@link #set(NdArray, long...)} and + * {@link #setObject(Byte, long...)} methods + * + *

    {@code
    + * ByteSparseNdArray st = new ByteSparseNdArray(
    + *      StdArrays.of(new long[][] {{0, 0}, {1, 2}}),
    + *      NdArrays.vectorOf((byte)1, (byte)255),
    + *      Shape.of(3, 4));
    + *
    + * }
    + * + *

    represents the dense array: + * + *

    {@code
    + * [[(byte)1, (byte)0, (byte)0, (byte)0]
    + *  [(byte)0, (byte)0, (byte)1, (byte)0]
    + *  [(byte)0, (byte)0, (byte)0, (byte)0]]
    + *
    + * }
    + */ +public class ByteSparseNdArray extends AbstractSparseNdArray + implements ByteNdArray { + + /** + * Creates a ByteSparseNdArray with a default value of zero. + * + * @param indices A 2-D LongNdArray of shape {@code [N, ndims]}, that specifies the indices of the + * elements in the sparse array that contain non-default values (elements are zero-indexed). + * For example, {@code indices=[[1,3], [2,4]]} specifies that the elements with indexes of + * {@code [1,3]} and {@code [2,4]} have non-default values. + * @param values A 1-D NdArray of Byte type and shape {@code [N]}, which supplies the values for + * each element in indices. For example, given {@code indices=[[1,3], [2,4]]}, the parameter + * {@code values=[18, 3.6]} specifies that element {@code [1,3]} of the sparse NdArray has a + * value of {@code 18}, and element {@code [2,4]} of the NdArray has a value of {@code 3.6}. + * @param defaultValue Scalar value to set for indices not specified in {@link #getIndices()} + * @param dimensions the dimensional space for the dense object represented by this sparse array, + */ + protected ByteSparseNdArray( + LongNdArray indices, ByteNdArray values, byte defaultValue, DimensionalSpace dimensions) { + super(indices, values, defaultValue, dimensions); + } + + /** + * Creates a ByteSparseNdArray + * + * @param indices A 2-D LongNdArray of shape {@code [N, ndims]}, that specifies the indices of the + * elements in the sparse array that contain non-default values (elements are zero-indexed). + * For example, {@code indices=[[1,3], [2,4]]} specifies that the elements with indexes of + * {@code [1,3]} and {@code [2,4]} have non-default values. + * @param values A 1-D NdArray of Byte type and shape {@code [N]}, which supplies the values for + * each element in indices. For example, given {@code indices=[[1,3], [2,4]]}, the parameter + * {@code values=[18, 3.6]} specifies that element {@code [1,3]} of the sparse NdArray has a + * value of {@code 18}, and element {@code [2,4]} of the NdArray has a value of {@code 3.6}. + * @param dimensions the dimensional space for the dense object represented by this sparse array, + */ + ByteSparseNdArray(LongNdArray indices, ByteNdArray values, DimensionalSpace dimensions) { + this(indices, values, (byte) 0, dimensions); + } + + /** + * Creates a ByteSparseNdArray + * + * @param dataBuffer a dense dataBuffer used to create the spars array + * @param dimensions the dimensional space for the dense object represented by this sparse array, + */ + ByteSparseNdArray(ByteDataBuffer dataBuffer, DimensionalSpace dimensions) { + this(dataBuffer, (byte) 0, dimensions); + } + + /** + * Creates a ByteSparseNdArray + * + * @param dataBuffer a dense dataBuffer used to create the spars array + * @param defaultValue Scalar value to set for indices not specified in {@link #getIndices()} + * @param dimensions the dimensional space for the dense object represented by this sparse array, + */ + ByteSparseNdArray(ByteDataBuffer dataBuffer, byte defaultValue, DimensionalSpace dimensions) { + super(defaultValue, dimensions); + // use write to set up the indices and values + copyFrom(dataBuffer); + } + + /** + * Creates a zero-filled ByteSparseNdArray + * + * @param dimensions the dimensional space for the dense object represented by this sparse array, + */ + ByteSparseNdArray(DimensionalSpace dimensions) { + this((byte) 0, dimensions); + } + + /** + * Creates a zero-filled ByteSparseNdArray + * + * @param defaultValue Scalar value to set for indices not specified in {@link #getIndices()} + * @param dimensions the dimensional space for the dense object represented by this sparse array, + */ + ByteSparseNdArray(byte defaultValue, DimensionalSpace dimensions) { + super(defaultValue, dimensions); + } + + /** + * Creates a new ByteSparseNdArray + * + * @param indices A 2-D LongNdArray of shape {@code [N, ndims]}, that specifies the indices of the + * elements in the sparse array that contain non-default values (elements are zero-indexed). + * For example, {@code indices=[[1,3], [2,4]]} specifies that the elements with indexes of + * {@code [1,3]} and {@code [2,4]} have non-default values. + * @param values A 1-D NdArray of any type and shape {@code [N]}, which supplies the values for + * each element in indices. For example, given {@code indices=[[1,3], [2,4]]}, the parameter + * {@code values=[18, 3.6]} specifies that element {@code [1,3]} of the sparse NdArray has a + * value of {@code 18}, and element {@code [2,4]} of the NdArray has a value of {@code 3.6}. + * @param dimensions the dimensional space for the dense object represented by this sparse array. + * @return the new Sparse Array + */ + public static ByteSparseNdArray create( + LongNdArray indices, ByteNdArray values, DimensionalSpace dimensions) { + return new ByteSparseNdArray(indices, values, dimensions); + } + + /** + * Creates a new ByteSparseNdArray + * + * @param indices A 2-D LongNdArray of shape {@code [N, ndims]}, that specifies the indices of the + * elements in the sparse array that contain non-default values (elements are zero-indexed). + * For example, {@code indices=[[1,3], [2,4]]} specifies that the elements with indexes of + * {@code [1,3]} and {@code [2,4]} have non-default values. + * @param values A 1-D NdArray of any type and shape {@code [N]}, which supplies the values for + * each element in indices. For example, given {@code indices=[[1,3], [2,4]]}, the parameter + * {@code values=[18, 3.6]} specifies that element {@code [1,3]} of the sparse NdArray has a + * value of {@code 18}, and element {@code [2,4]} of the NdArray has a value of {@code 3.6}. + * @param defaultValue Scalar value to set for indices not specified in {@link #getIndices()} + * @param dimensions the dimensional space for the dense object represented by this sparse array. + * @return the new Sparse Array + */ + public static ByteSparseNdArray create( + LongNdArray indices, ByteNdArray values, byte defaultValue, DimensionalSpace dimensions) { + return new ByteSparseNdArray(indices, values, defaultValue, dimensions); + } + + /** + * Creates a new ByteSparseNdArray from a data buffer + * + * @param dataBuffer the databuffer containing the dense array + * @param dimensions the dimensional space for the sparse array + * @return the new Sparse Array + */ + public static ByteSparseNdArray create(ByteDataBuffer dataBuffer, DimensionalSpace dimensions) { + return new ByteSparseNdArray(dataBuffer, dimensions); + } + + /** + * Creates a new ByteSparseNdArray from a data buffer + * + * @param dataBuffer the databuffer containing the dense array + * @param defaultValue Scalar value to set for indices not specified in {@link #getIndices()} + * @param dimensions the dimensional space for the sparse array + * @return the new Sparse Array + */ + public static ByteSparseNdArray create( + ByteDataBuffer dataBuffer, byte defaultValue, DimensionalSpace dimensions) { + return new ByteSparseNdArray(dataBuffer, defaultValue, dimensions); + } + + /** + * Creates a new empty ByteSparseNdArray from a data buffer + * + * @param dimensions the dimensions array + * @return the new Sparse Array + */ + public static ByteSparseNdArray create(DimensionalSpace dimensions) { + return new ByteSparseNdArray(dimensions); + } + + /** + * Creates a new empty ByteSparseNdArray from a data buffer + * + * @param defaultValue Scalar value to set for indices not specified in {@link #getIndices()} + * @param dimensions the dimensions array + * @return the new Sparse Array + */ + public static ByteSparseNdArray create(byte defaultValue, DimensionalSpace dimensions) { + return new ByteSparseNdArray(defaultValue, dimensions); + } + + /** + * Creates a new empty ByteSparseNdArray from a float data buffer + * + * @param buffer the data buffer + * @param shape the shape of the sparse array. + * @return the new Sparse Array + */ + public static ByteSparseNdArray create(ByteDataBuffer buffer, Shape shape) { + return new ByteSparseNdArray(buffer, DimensionalSpace.create(shape)); + } + + /** + * Creates a new empty ByteSparseNdArray from a float data buffer + * + * @param buffer the data buffer + * @param defaultValue Scalar value to set for indices not specified in {@link #getIndices()} + * @param shape the shape of the sparse array. + * @return the new Sparse Array + */ + public static ByteSparseNdArray create(ByteDataBuffer buffer, byte defaultValue, Shape shape) { + return new ByteSparseNdArray(buffer, defaultValue, DimensionalSpace.create(shape)); + } + + /** + * Creates a new ByteSparseNdArray from a ByteNdArray + * + * @param src the ByteNdArray + * @return the new Sparse Array + */ + public static ByteSparseNdArray create(ByteNdArray src) { + ByteDataBuffer buffer = DataBuffers.ofBytes(src.size()); + src.copyTo(buffer); + return new ByteSparseNdArray(buffer, DimensionalSpace.create(src.shape())); + } + + /** + * Creates a new ByteSparseNdArray from a ByteNdArray + * + * @param src the ByteNdArray + * @param defaultValue Scalar value to set for indices not specified in {@link #getIndices()} + * @return the new Sparse Array + */ + public static ByteSparseNdArray create(ByteNdArray src, byte defaultValue) { + ByteDataBuffer buffer = DataBuffers.ofBytes(src.size()); + src.copyTo(buffer); + return new ByteSparseNdArray(buffer, defaultValue, DimensionalSpace.create(src.shape())); + } + + /** + * Creates a ByteNdArray of the specified shape + * + * @param shape the shape of the dense array. + * @return a ByteNdArray of the specified shape + */ + public ByteNdArray createValues(Shape shape) { + return NdArrays.ofBytes(shape); + } + + /** {@inheritDoc} */ + @Override + public ByteNdArray slice(long position, DimensionalSpace sliceDimensions) { + return new ByteSparseSlice(this, position, sliceDimensions); + } + + /** {@inheritDoc} */ + @Override + public byte getByte(long... coordinates) { + return getObject(coordinates); + } + + /** {@inheritDoc} */ + @Override + public ByteNdArray setByte(byte value, long... coordinates) { + throw new ReadOnlyBufferException(); + } + + /** {@inheritDoc} */ + @Override + public ByteNdArray copyTo(DataBuffer dst) { + return copyTo((ByteDataBuffer) dst); + } + + /** {@inheritDoc} */ + @Override + public ByteNdArray copyTo(ByteDataBuffer dst) { + // set the values in buf to the default, then overwrite with indices/values + Byte[] defaults = new Byte[(int) shape().size()]; + Arrays.fill(defaults, getDefaultValue()); + dst.write(defaults); + + AtomicInteger i = new AtomicInteger(); + getIndices() + .elements(0) + .forEachIndexed( + (idx, l) -> { + long[] coordinates = getIndicesCoordinates(l); + byte value = getValues().getByte(i.getAndIncrement()); + dst.setObject(value, dimensions.positionOf(coordinates)); + }); + return this; + } + + /** {@inheritDoc} */ + @Override + public ByteNdArray copyFrom(ByteDataBuffer src) { + List indices = new ArrayList<>(); + List values = new ArrayList<>(); + + for (long i = 0; i < src.size(); i++) { + if (!src.getObject(i).equals(getDefaultValue())) { + indices.add(toCoordinates(dimensions, i)); + values.add(src.getObject(i)); + } + } + long[][] indicesArray = new long[indices.size()][]; + byte[] valuesArray = new byte[values.size()]; + for (int i = 0; i < indices.size(); i++) { + indicesArray[i] = indices.get(i); + valuesArray[i] = values.get(i); + } + + setIndices(StdArrays.ndCopyOf(indicesArray)); + setValues(NdArrays.vectorOf(valuesArray)); + return this; + } + + /** {@inheritDoc} */ + @Override + public ByteNdArray copyFrom(DataBuffer src) { + return copyFrom((ByteDataBuffer) src); + } + + /** + * Converts the sparse array to a dense array + * + * @return the dense array + */ + public ByteNdArray toDense() { + ByteDataBuffer dataBuffer = DataBuffers.ofBytes(shape().size()); + copyTo(dataBuffer); + return NdArrays.wrap(shape(), dataBuffer); + } + + /** + * Populates this sparse array from a dense array + * + * @param src the dense array + * @return this sparse array + */ + public ByteNdArray fromDense(ByteNdArray src) { + ByteDataBuffer buffer = DataBuffers.ofBytes(src.size()); + src.copyTo(buffer); + copyFrom(buffer); + return this; + } + + /** {@inheritDoc} */ + @Override + public ByteNdArray slice(Index... indices) { + return (ByteNdArray) super.slice(indices); + } + + /** {@inheritDoc} */ + @Override + public ByteNdArray get(long... coordinates) { + return (ByteNdArray) super.get(coordinates); + } + + /** {@inheritDoc} */ + @Override + public ByteNdArray setObject(Byte value, long... coordinates) { + throw new ReadOnlyBufferException(); + } + + /** {@inheritDoc} */ + @Override + public ByteNdArray set(NdArray src, long... coordinates) { + throw new ReadOnlyBufferException(); + } + + /** {@inheritDoc} */ + @Override + public ByteNdArray copyTo(NdArray dst) { + return (ByteNdArray) super.copyTo(dst); + } + + /** {@inheritDoc} */ + @Override + public ByteNdArray createDefaultArray() { + return NdArrays.scalarOf(getDefaultValue()); + } +} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/sparse/DoubleSparseNdArray.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/sparse/DoubleSparseNdArray.java new file mode 100644 index 0000000..35b3541 --- /dev/null +++ b/ndarray/src/main/java/org/tensorflow/ndarray/impl/sparse/DoubleSparseNdArray.java @@ -0,0 +1,419 @@ +/* Copyright 2021 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +=======================================================================*/ +package org.tensorflow.ndarray.impl.sparse; + +import org.tensorflow.ndarray.DoubleNdArray; +import org.tensorflow.ndarray.LongNdArray; +import org.tensorflow.ndarray.NdArray; +import org.tensorflow.ndarray.NdArrays; +import org.tensorflow.ndarray.Shape; +import org.tensorflow.ndarray.StdArrays; +import org.tensorflow.ndarray.buffer.DataBuffer; +import org.tensorflow.ndarray.buffer.DataBuffers; +import org.tensorflow.ndarray.buffer.DoubleDataBuffer; +import org.tensorflow.ndarray.impl.dimension.DimensionalSpace; +import org.tensorflow.ndarray.impl.sparse.slice.DoubleSparseSlice; +import org.tensorflow.ndarray.index.Index; + +import java.nio.ReadOnlyBufferException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * A sparse array for the double data type + * + *

    A sparse array as two separate dense arrays: indices, values, and a shape that represents the + * dense shape. + * + *

    NOTE: all Sparse Arrays are readonly for the {@link #set(NdArray, long...)} and + * {@link #setObject(Double, long...)} methods + * + *

    {@code
    + * DoubleSparseNdArray st = new DoubleSparseNdArray(
    + *      StdArrays.of(new long[][] {{0, 0}, {1, 2}}),
    + *      NdArrays.vectorsOf(new double[] {1, 2}),
    + *      Shape.of(3, 4));
    + *
    + * }
    + * + *

    represents the dense array: + * + *

    {@code
    + * [[1, 0, 0, 0]
    + *  [0, 0, 2, 0]
    + *  [0, 0, 0, 0]]
    + *
    + * }
    + */ +public class DoubleSparseNdArray extends AbstractSparseNdArray + implements DoubleNdArray { + + /** + * Creates a DoubleSparseNdArray + * + * @param indices A 2-D LongNdArray of shape {@code [N, ndims]}, that specifies the indices of the + * elements in the sparse array that contain non-default values (elements are zero-indexed). + * For example, {@code indices=[[1,3], [2,4]]} specifies that the elements with indexes of + * {@code [1,3]} and {@code [2,4]} have non-default values. + * @param values A 1-D DoubleNdArray of shape {@code [N]}, which supplies the values for each + * element in indices. For example, given {@code indices=[[1,3], [2,4]]}, the parameter {@code + * values=[18, 3.6]} specifies that element {@code [1,3]} of the sparse NdArray has a value of + * {@code 18}, and element {@code [2,4]} of the NdArray has a value of {@code 3.6}. + * @param defaultValue Scalar value to set for indices not specified in {@link #getIndices()} + * @param dimensions the dimensional space for the dense object represented by this sparse array, + */ + protected DoubleSparseNdArray( + LongNdArray indices, DoubleNdArray values, double defaultValue, DimensionalSpace dimensions) { + super(indices, values, defaultValue, dimensions); + } + + /** + * Creates a DoubleSparseNdArray with a default value of zero. + * + * @param indices A 2-D LongNdArray of shape {@code [N, ndims]}, that specifies the indices of the + * elements in the sparse array that contain non-default values (elements are zero-indexed). + * For example, {@code indices=[[1,3], [2,4]]} specifies that the elements with indexes of + * {@code [1,3]} and {@code [2,4]} have non-default values. + * @param values A 1-D DoubleNdArray of shape {@code [N]}, which supplies the values for each + * element in indices. For example, given {@code indices=[[1,3], [2,4]]}, the parameter {@code + * values=[18, 3.6]} specifies that element {@code [1,3]} of the sparse NdArray has a value of + * {@code 18}, and element {@code [2,4]} of the NdArray has a value of {@code 3.6}. + * @param dimensions the dimensional space for the dense object represented by this sparse array, + */ + DoubleSparseNdArray(LongNdArray indices, DoubleNdArray values, DimensionalSpace dimensions) { + this(indices, values, 0d, dimensions); + } + + /** + * Creates a DoubleSparseNdArray + * + * @param dataBuffer a dense dataBuffer used to create the spars array + * @param dimensions the dimensional space for the dense object represented by this sparse array, + */ + DoubleSparseNdArray(DoubleDataBuffer dataBuffer, DimensionalSpace dimensions) { + this(dataBuffer, 0d, dimensions); + } + /** + * Creates a DoubleSparseNdArray + * + * @param dataBuffer a dense dataBuffer used to create the spars array + * @param defaultValue Scalar value to set for indices not specified in {@link #getIndices()} + * @param dimensions the dimensional space for the dense object represented by this sparse array, + */ + DoubleSparseNdArray( + DoubleDataBuffer dataBuffer, double defaultValue, DimensionalSpace dimensions) { + super(defaultValue, dimensions); + // use write to set up the indices and values + copyFrom(dataBuffer); + } + + /** + * Creates a zero-filled DoubleSparseNdArray + * + * @param dimensions the dimensional space for the dense object represented by this sparse array, + */ + DoubleSparseNdArray(DimensionalSpace dimensions) { + this(0d, dimensions); + } + + /** + * Creates a zero-filled DoubleSparseNdArray + * + * @param defaultValue Scalar value to set for indices not specified in {@link #getIndices()} + * @param dimensions the dimensional space for the dense object represented by this sparse array, + */ + DoubleSparseNdArray(double defaultValue, DimensionalSpace dimensions) { + super(defaultValue, dimensions); + } + + /** + * Creates a new DoubleSparseNdArray + * + * @param indices A 2-D LongNdArray of shape {@code [N, ndims]}, that specifies the indices of the + * elements in the sparse array that contain non-default values (elements are zero-indexed). + * For example, {@code indices=[[1,3], [2,4]]} specifies that the elements with indexes of + * {@code [1,3]} and {@code [2,4]} have non-default values. + * @param values A 1-D NdArray of any type and shape {@code [N]}, which supplies the values for + * each element in indices. For example, given {@code indices=[[1,3], [2,4]]}, the parameter + * {@code values=[18, 3.6]} specifies that element {@code [1,3]} of the sparse NdArray has a + * value of {@code 18}, and element {@code [2,4]} of the NdArray has a value of {@code 3.6}. + * @param dimensions the dimensional space for the dense object represented by this sparse array. + * @return the new Sparse Array + */ + public static DoubleSparseNdArray create( + LongNdArray indices, DoubleNdArray values, DimensionalSpace dimensions) { + return new DoubleSparseNdArray(indices, values, dimensions); + } + + /** + * Creates a new DoubleSparseNdArray + * + * @param indices A 2-D LongNdArray of shape {@code [N, ndims]}, that specifies the indices of the + * elements in the sparse array that contain non-default values (elements are zero-indexed). + * For example, {@code indices=[[1,3], [2,4]]} specifies that the elements with indexes of + * {@code [1,3]} and {@code [2,4]} have non-default values. + * @param values A 1-D NdArray of any type and shape {@code [N]}, which supplies the values for + * each element in indices. For example, given {@code indices=[[1,3], [2,4]]}, the parameter + * {@code values=[18, 3.6]} specifies that element {@code [1,3]} of the sparse NdArray has a + * value of {@code 18}, and element {@code [2,4]} of the NdArray has a value of {@code 3.6}. + * @param defaultValue Scalar value to set for indices not specified in {@link #getIndices()} + * @param dimensions the dimensional space for the dense object represented by this sparse array. + * @return the new Sparse Array + */ + public static DoubleSparseNdArray create( + LongNdArray indices, DoubleNdArray values, double defaultValue, DimensionalSpace dimensions) { + return new DoubleSparseNdArray(indices, values, defaultValue, dimensions); + } + + /** + * Creates a new DoubleSparseNdArray from a data buffer + * + * @param dataBuffer the databuffer containing the dense array + * @param dimensions the dimensional space for the sparse array + * @return the new Sparse Array + */ + public static DoubleSparseNdArray create( + DoubleDataBuffer dataBuffer, DimensionalSpace dimensions) { + return new DoubleSparseNdArray(dataBuffer, dimensions); + } + + /** + * Creates a new DoubleSparseNdArray from a data buffer + * + * @param dataBuffer the databuffer containing the dense array + * @param defaultValue Scalar value to set for indices not specified in {@link #getIndices()} + * @param dimensions the dimensional space for the sparse array + * @return the new Sparse Array + */ + public static DoubleSparseNdArray create( + DoubleDataBuffer dataBuffer, double defaultValue, DimensionalSpace dimensions) { + return new DoubleSparseNdArray(dataBuffer, defaultValue, dimensions); + } + + /** + * Creates a new empty DoubleSparseNdArray from a data buffer + * + * @param dimensions the dimensions array + * @return the new Sparse Array + */ + public static DoubleSparseNdArray create(DimensionalSpace dimensions) { + return new DoubleSparseNdArray(dimensions); + } + + /** + * Creates a new empty DoubleSparseNdArray from a data buffer + * + * @param defaultValue Scalar value to set for indices not specified in {@link #getIndices()} + * @param dimensions the dimensions array + * @return the new Sparse Array + */ + public static DoubleSparseNdArray create(double defaultValue, DimensionalSpace dimensions) { + return new DoubleSparseNdArray(defaultValue, dimensions); + } + + /** + * Creates a new empty DoubleSparseNdArray from a double data buffer + * + * @param buffer the data buffer + * @param shape the shape of the sparse array. + * @return the new Sparse Array + */ + public static DoubleSparseNdArray create(DoubleDataBuffer buffer, Shape shape) { + return new DoubleSparseNdArray(buffer, DimensionalSpace.create(shape)); + } + + /** + * Creates a new empty DoubleSparseNdArray from a double data buffer + * + * @param buffer the data buffer + * @param defaultValue Scalar value to set for indices not specified in {@link #getIndices()} + * @param shape the shape of the sparse array. + * @return the new Sparse Array + */ + public static DoubleSparseNdArray create( + DoubleDataBuffer buffer, double defaultValue, Shape shape) { + return new DoubleSparseNdArray(buffer, defaultValue, DimensionalSpace.create(shape)); + } + + /** + * Creates a new DoubleSparseNdArray from a DoubleNdArray + * + * @param src the DoubleNdArray + * @return the new Sparse Array + */ + public static DoubleSparseNdArray create(DoubleNdArray src) { + DoubleDataBuffer buffer = DataBuffers.ofDoubles(src.size()); + src.copyTo(buffer); + return new DoubleSparseNdArray(buffer, DimensionalSpace.create(src.shape())); + } + /** + * Creates a new DoubleSparseNdArray from a DoubleNdArray + * + * @param src the DoubleNdArray + * @param defaultValue Scalar value to set for indices not specified in {@link #getIndices()} + * @return the new Sparse Array + */ + public static DoubleSparseNdArray create(DoubleNdArray src, double defaultValue) { + DoubleDataBuffer buffer = DataBuffers.ofDoubles(src.size()); + src.copyTo(buffer); + return new DoubleSparseNdArray(buffer, defaultValue, DimensionalSpace.create(src.shape())); + } + + /** + * Creates a DoubleNdArray of the specified shape + * + * @param shape the shape of the dense array. + * @return a DoubleNdArray of the specified shape + */ + public DoubleNdArray createValues(Shape shape) { + return NdArrays.ofDoubles(shape); + } + + /** {@inheritDoc} */ + @Override + public DoubleNdArray slice(long position, DimensionalSpace sliceDimensions) { + return new DoubleSparseSlice(this, position, sliceDimensions); + } + + /** {@inheritDoc} */ + @Override + public double getDouble(long... coordinates) { + return getObject(coordinates); + } + + /** {@inheritDoc} */ + @Override + public DoubleNdArray setDouble(double value, long... coordinates) { + throw new ReadOnlyBufferException(); + } + + /** {@inheritDoc} */ + @Override + public DoubleNdArray copyTo(DataBuffer dst) { + return copyTo((DoubleDataBuffer) dst); + } + + /** {@inheritDoc} */ + @Override + public DoubleNdArray copyTo(DoubleDataBuffer dst) { + // set buf to the default values, then overwrite with the indices/values. + Double[] defaults = new Double[(int) shape().size()]; + Arrays.fill(defaults, getDefaultValue()); + dst.write(defaults); + + AtomicInteger i = new AtomicInteger(); + getIndices() + .elements(0) + .forEachIndexed( + (idx, l) -> { + long[] coordinates = getIndicesCoordinates(l); + double value = getValues().getDouble(i.getAndIncrement()); + dst.setObject(value, dimensions.positionOf(coordinates)); + }); + return this; + } + + /** {@inheritDoc} */ + @Override + public DoubleNdArray copyFrom(DoubleDataBuffer src) { + List indices = new ArrayList<>(); + List values = new ArrayList<>(); + + for (long i = 0; i < src.size(); i++) { + if (!src.getObject(i).equals(getDefaultValue())) { + indices.add(toCoordinates(dimensions, i)); + values.add(src.getObject(i)); + } + } + long[][] indicesArray = new long[indices.size()][]; + double[] valuesArray = new double[values.size()]; + for (int i = 0; i < indices.size(); i++) { + indicesArray[i] = indices.get(i); + valuesArray[i] = values.get(i); + } + + setIndices(StdArrays.ndCopyOf(indicesArray)); + setValues(NdArrays.vectorOf(valuesArray)); + return this; + } + + /** {@inheritDoc} */ + @Override + public DoubleNdArray copyFrom(DataBuffer src) { + return copyFrom((DoubleDataBuffer) src); + } + + /** + * Converts the sparse array to a dense array + * + * @return the dense array + */ + public DoubleNdArray toDense() { + DoubleDataBuffer dataBuffer = DataBuffers.ofDoubles(shape().size()); + copyTo(dataBuffer); + return NdArrays.wrap(shape(), dataBuffer); + } + + /** + * Populates this sparse array from a dense array + * + * @param src the dense array + * @return this sparse array + */ + public DoubleNdArray fromDense(DoubleNdArray src) { + DoubleDataBuffer buffer = DataBuffers.ofDoubles(src.size()); + src.copyTo(buffer); + copyFrom(buffer); + return this; + } + + /** {@inheritDoc} */ + @Override + public DoubleNdArray slice(Index... indices) { + return (DoubleNdArray) super.slice(indices); + } + + /** {@inheritDoc} */ + @Override + public DoubleNdArray get(long... coordinates) { + return (DoubleNdArray) super.get(coordinates); + } + + /** {@inheritDoc} */ + @Override + public DoubleNdArray setObject(Double value, long... coordinates) { + throw new ReadOnlyBufferException(); + } + + /** {@inheritDoc} */ + @Override + public DoubleNdArray set(NdArray src, long... coordinates) { + throw new ReadOnlyBufferException(); + } + + /** {@inheritDoc} */ + @Override + public DoubleNdArray copyTo(NdArray dst) { + return (DoubleNdArray) super.copyTo(dst); + } + + /** {@inheritDoc} */ + @Override + public DoubleNdArray createDefaultArray() { + return NdArrays.scalarOf(getDefaultValue()); + } +} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/sparse/FloatSparseNdArray.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/sparse/FloatSparseNdArray.java new file mode 100644 index 0000000..88f34b1 --- /dev/null +++ b/ndarray/src/main/java/org/tensorflow/ndarray/impl/sparse/FloatSparseNdArray.java @@ -0,0 +1,417 @@ +/* Copyright 2021 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +=======================================================================*/ +package org.tensorflow.ndarray.impl.sparse; + +import org.tensorflow.ndarray.FloatNdArray; +import org.tensorflow.ndarray.LongNdArray; +import org.tensorflow.ndarray.NdArray; +import org.tensorflow.ndarray.NdArrays; +import org.tensorflow.ndarray.Shape; +import org.tensorflow.ndarray.StdArrays; +import org.tensorflow.ndarray.buffer.DataBuffer; +import org.tensorflow.ndarray.buffer.DataBuffers; +import org.tensorflow.ndarray.buffer.FloatDataBuffer; +import org.tensorflow.ndarray.impl.dimension.DimensionalSpace; +import org.tensorflow.ndarray.impl.sparse.slice.FloatSparseSlice; +import org.tensorflow.ndarray.index.Index; + +import java.nio.ReadOnlyBufferException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * sparse array for the float data type + * + *

    A sparse array as two separate dense arrays: indices, values, and a shape that represents the + * dense shape. + * + *

    NOTE: all Sparse Arrays are readonly for the {@link #set(NdArray, long...)} and + * {@link #setObject(Float, long...)} methods + * + *

    {@code
    + * FloatSparseNdArray st = new FloatSparseNdArray(
    + *      StdArrays.of(new long[][] {{0, 0}, {1, 2}}),
    + *      NdArrays.vectorOf(1f, 3.14f}),
    + *      Shape.of(3, 4));
    + *
    + * }
    + * + *

    represents the dense array: + * + *

    {@code
    + * [[1, 0, 0, 0]
    + *  [0, 0, 3.14, 0]
    + *  [0, 0, 0, 0]]
    + *
    + * }
    + */ +public class FloatSparseNdArray extends AbstractSparseNdArray + implements FloatNdArray { + + /** + * Creates a FloatSparseNdArray + * + * @param indices A 2-D LongNdArray of shape {@code [N, ndims]}, that specifies the indices of the + * elements in the sparse array that contain non-default values (elements are zero-indexed). + * For example, {@code indices=[[1,3], [2,4]]} specifies that the elements with indexes of + * {@code [1,3]} and {@code [2,4]} have non-default values. + * @param values A 1-D FloatNdArray of shape {@code [N]}, which supplies the values for each + * element in indices. For example, given {@code indices=[[1,3], [2,4]]}, the parameter {@code + * values=[18, 3.6]} specifies that element {@code [1,3]} of the sparse NdArray has a value of + * {@code 18}, and element {@code [2,4]} of the NdArray has a value of {@code 3.6}. + * @param defaultValue Scalar value to set for indices not specified in {@link #getIndices()} + * @param dimensions the dimensional space for the dense object represented by this sparse array, + */ + protected FloatSparseNdArray( + LongNdArray indices, FloatNdArray values, float defaultValue, DimensionalSpace dimensions) { + super(indices, values, defaultValue, dimensions); + } + + /** + * Creates a FloatSparseNdArray with a default value of zero. + * + * @param indices A 2-D LongNdArray of shape {@code [N, ndims]}, that specifies the indices of the + * elements in the sparse array that contain non-default values (elements are zero-indexed). + * For example, {@code indices=[[1,3], [2,4]]} specifies that the elements with indexes of + * {@code [1,3]} and {@code [2,4]} have non-default values. + * @param values A 1-D FloatNdArray of shape {@code [N]}, which supplies the values for each + * element in indices. For example, given {@code indices=[[1,3], [2,4]]}, the parameter {@code + * values=[18, 3.6]} specifies that element {@code [1,3]} of the sparse NdArray has a value of + * {@code 18}, and element {@code [2,4]} of the NdArray has a value of {@code 3.6}. + * @param dimensions the dimensional space for the dense object represented by this sparse array, + */ + FloatSparseNdArray(LongNdArray indices, FloatNdArray values, DimensionalSpace dimensions) { + this(indices, values, 0f, dimensions); + } + + /** + * Creates a FloatSparseNdArray + * + * @param dataBuffer a dense dataBuffer used to create the spars array + * @param dimensions the dimensional space for the dense object represented by this sparse array, + */ + FloatSparseNdArray(FloatDataBuffer dataBuffer, DimensionalSpace dimensions) { + this(dataBuffer, 0f, dimensions); + } + + /** + * Creates a FloatSparseNdArray + * + * @param dataBuffer a dense dataBuffer used to create the spars array + * @param defaultValue Scalar value to set for indices not specified in {@link #getIndices()} + * @param dimensions the dimensional space for the dense object represented by this sparse array, + */ + FloatSparseNdArray(FloatDataBuffer dataBuffer, float defaultValue, DimensionalSpace dimensions) { + super(defaultValue, dimensions); + // use write to set up the indices and values + copyFrom(dataBuffer); + } + + /** + * Creates a zero-filled FloatSparseNdArray + * + * @param dimensions the dimensional space for the dense object represented by this sparse array, + */ + FloatSparseNdArray(DimensionalSpace dimensions) { + this(0f, dimensions); + } + + /** + * Creates a zero-filled FloatSparseNdArray + * + * @param defaultValue Scalar value to set for indices not specified in {@link #getIndices()} + * @param dimensions the dimensional space for the dense object represented by this sparse array, + */ + FloatSparseNdArray(float defaultValue, DimensionalSpace dimensions) { + super(defaultValue, dimensions); + } + + /** + * Creates a new FloatSparseNdArray + * + * @param indices A 2-D LongNdArray of shape {@code [N, ndims]}, that specifies the indices of the + * elements in the sparse array that contain non-default values (elements are zero-indexed). + * For example, {@code indices=[[1,3], [2,4]]} specifies that the elements with indexes of + * {@code [1,3]} and {@code [2,4]} have non-default values. + * @param values A 1-D NdArray of any type and shape {@code [N]}, which supplies the values for + * each element in indices. For example, given {@code indices=[[1,3], [2,4]]}, the parameter + * {@code values=[18, 3.6]} specifies that element {@code [1,3]} of the sparse NdArray has a + * value of {@code 18}, and element {@code [2,4]} of the NdArray has a value of {@code 3.6}. + * @param dimensions the dimensional space for the dense object represented by this sparse array. + * @return the new Sparse Array + */ + public static FloatSparseNdArray create( + LongNdArray indices, FloatNdArray values, DimensionalSpace dimensions) { + return new FloatSparseNdArray(indices, values, dimensions); + } + + /** + * Creates a new FloatSparseNdArray + * + * @param indices A 2-D LongNdArray of shape {@code [N, ndims]}, that specifies the indices of the + * elements in the sparse array that contain non-default values (elements are zero-indexed). + * For example, {@code indices=[[1,3], [2,4]]} specifies that the elements with indexes of + * {@code [1,3]} and {@code [2,4]} have non-default values. + * @param values A 1-D NdArray of any type and shape {@code [N]}, which supplies the values for + * each element in indices. For example, given {@code indices=[[1,3], [2,4]]}, the parameter + * {@code values=[18, 3.6]} specifies that element {@code [1,3]} of the sparse NdArray has a + * value of {@code 18}, and element {@code [2,4]} of the NdArray has a value of {@code 3.6}. + * @param defaultValue Scalar value to set for indices not specified in {@link #getIndices()} + * @param dimensions the dimensional space for the dense object represented by this sparse array. + * @return the new Sparse Array + */ + public static FloatSparseNdArray create( + LongNdArray indices, FloatNdArray values, float defaultValue, DimensionalSpace dimensions) { + return new FloatSparseNdArray(indices, values, defaultValue, dimensions); + } + + /** + * Creates a new FloatSparseNdArray from a data buffer + * + * @param dataBuffer the databuffer containing the dense array + * @param dimensions the dimensional space for the sparse array + * @return the new Sparse Array + */ + public static FloatSparseNdArray create(FloatDataBuffer dataBuffer, DimensionalSpace dimensions) { + return new FloatSparseNdArray(dataBuffer, dimensions); + } + + /** + * Creates a new FloatSparseNdArray from a data buffer + * + * @param dataBuffer the databuffer containing the dense array + * @param defaultValue Scalar value to set for indices not specified in {@link #getIndices()} + * @param dimensions the dimensional space for the sparse array + * @return the new Sparse Array + */ + public static FloatSparseNdArray create( + FloatDataBuffer dataBuffer, float defaultValue, DimensionalSpace dimensions) { + return new FloatSparseNdArray(dataBuffer, defaultValue, dimensions); + } + + /** + * Creates a new empty FloatSparseNdArray from a data buffer + * + * @param dimensions the dimensions array + * @return the new Sparse Array + */ + public static FloatSparseNdArray create(DimensionalSpace dimensions) { + return new FloatSparseNdArray(dimensions); + } + + /** + * Creates a new empty FloatSparseNdArray from a data buffer + * + * @param defaultValue Scalar value to set for indices not specified in {@link #getIndices()} + * @param dimensions the dimensions array + * @return the new Sparse Array + */ + public static FloatSparseNdArray create(float defaultValue, DimensionalSpace dimensions) { + return new FloatSparseNdArray(defaultValue, dimensions); + } + + /** + * Creates a new empty FloatSparseNdArray from a float data buffer + * + * @param buffer the data buffer + * @param shape the shape of the sparse array. + * @return the new Sparse Array + */ + public static FloatSparseNdArray create(FloatDataBuffer buffer, Shape shape) { + return new FloatSparseNdArray(buffer, DimensionalSpace.create(shape)); + } + + /** + * Creates a new empty FloatSparseNdArray from a float data buffer + * + * @param buffer the data buffer + * @param defaultValue Scalar value to set for indices not specified in {@link #getIndices()} + * @param shape the shape of the sparse array. + * @return the new Sparse Array + */ + public static FloatSparseNdArray create(FloatDataBuffer buffer, float defaultValue, Shape shape) { + return new FloatSparseNdArray(buffer, defaultValue, DimensionalSpace.create(shape)); + } + + /** + * Creates a new FloatSparseNdArray from a FloatNdArray + * + * @param src the FloatNdArray + * @return the new Sparse Array + */ + public static FloatSparseNdArray create(FloatNdArray src) { + FloatDataBuffer buffer = DataBuffers.ofFloats(src.size()); + src.copyTo(buffer); + return new FloatSparseNdArray(buffer, DimensionalSpace.create(src.shape())); + } + /** + * Creates a new FloatSparseNdArray from a FloatNdArray + * + * @param src the FloatNdArray + * @param defaultValue Scalar value to set for indices not specified in {@link #getIndices()} + * @return the new Sparse Array + */ + public static FloatSparseNdArray create(FloatNdArray src, float defaultValue) { + FloatDataBuffer buffer = DataBuffers.ofFloats(src.size()); + src.copyTo(buffer); + return new FloatSparseNdArray(buffer, defaultValue, DimensionalSpace.create(src.shape())); + } + + /** + * Creates a FloatNdArray of the specified shape + * + * @param shape the shape of the dense array. + * @return a FloatNdArray of the specified shape + */ + public FloatNdArray createValues(Shape shape) { + return NdArrays.ofFloats(shape); + } + + /** {@inheritDoc} */ + @Override + public FloatNdArray slice(long position, DimensionalSpace sliceDimensions) { + return new FloatSparseSlice(this, position, sliceDimensions); + } + + /** {@inheritDoc} */ + @Override + public float getFloat(long... coordinates) { + return getObject(coordinates); + } + + /** {@inheritDoc} */ + @Override + public FloatNdArray setFloat(float value, long... coordinates) { + throw new ReadOnlyBufferException(); + } + + /** {@inheritDoc} */ + @Override + public FloatNdArray copyTo(DataBuffer dst) { + return copyTo((FloatDataBuffer) dst); + } + + /** {@inheritDoc} */ + @Override + public FloatNdArray copyTo(FloatDataBuffer dst) { + // set the values in buf to the default, then overwrite with indices/values + Float[] defaults = new Float[(int) shape().size()]; + Arrays.fill(defaults, getDefaultValue()); + dst.write(defaults); + + AtomicInteger i = new AtomicInteger(); + getIndices() + .elements(0) + .forEachIndexed( + (idx, l) -> { + long[] coordinates = getIndicesCoordinates(l); + float value = getValues().getFloat(i.getAndIncrement()); + dst.setObject(value, dimensions.positionOf(coordinates)); + }); + return this; + } + + /** {@inheritDoc} */ + @Override + public FloatNdArray copyFrom(FloatDataBuffer src) { + List indices = new ArrayList<>(); + List values = new ArrayList<>(); + + for (long i = 0; i < src.size(); i++) { + if (!src.getObject(i).equals(getDefaultValue())) { + indices.add(toCoordinates(dimensions, i)); + values.add(src.getObject(i)); + } + } + long[][] indicesArray = new long[indices.size()][]; + float[] valuesArray = new float[values.size()]; + for (int i = 0; i < indices.size(); i++) { + indicesArray[i] = indices.get(i); + valuesArray[i] = values.get(i); + } + + setIndices(StdArrays.ndCopyOf(indicesArray)); + setValues(NdArrays.vectorOf(valuesArray)); + return this; + } + + /** {@inheritDoc} */ + @Override + public FloatNdArray copyFrom(DataBuffer src) { + return copyFrom((FloatDataBuffer) src); + } + + /** + * Converts the sparse array to a dense array + * + * @return the dense array + */ + public FloatNdArray toDense() { + FloatDataBuffer dataBuffer = DataBuffers.ofFloats(shape().size()); + copyTo(dataBuffer); + return NdArrays.wrap(shape(), dataBuffer); + } + + /** + * Populates this sparse array from a dense array + * + * @param src the dense array + * @return this sparse array + */ + public FloatNdArray fromDense(FloatNdArray src) { + FloatDataBuffer buffer = DataBuffers.ofFloats(src.size()); + src.copyTo(buffer); + copyFrom(buffer); + return this; + } + + /** {@inheritDoc} */ + @Override + public FloatNdArray slice(Index... indices) { + return (FloatNdArray) super.slice(indices); + } + + /** {@inheritDoc} */ + @Override + public FloatNdArray get(long... coordinates) { + return (FloatNdArray) super.get(coordinates); + } + + /** {@inheritDoc} */ + @Override + public FloatNdArray setObject(Float value, long... coordinates) { + throw new ReadOnlyBufferException(); + } + + /** {@inheritDoc} */ + @Override + public FloatNdArray set(NdArray src, long... coordinates) { + throw new ReadOnlyBufferException(); + } + + /** {@inheritDoc} */ + @Override + public FloatNdArray copyTo(NdArray dst) { + return (FloatNdArray) super.copyTo(dst); + } + + /** {@inheritDoc} */ + @Override + public FloatNdArray createDefaultArray() { + return NdArrays.scalarOf(getDefaultValue()); + } +} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/sparse/IntSparseNdArray.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/sparse/IntSparseNdArray.java new file mode 100644 index 0000000..d79c441 --- /dev/null +++ b/ndarray/src/main/java/org/tensorflow/ndarray/impl/sparse/IntSparseNdArray.java @@ -0,0 +1,432 @@ +/* Copyright 2021 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +=======================================================================*/ +package org.tensorflow.ndarray.impl.sparse; + +import org.tensorflow.ndarray.IntNdArray; +import org.tensorflow.ndarray.LongNdArray; +import org.tensorflow.ndarray.NdArray; +import org.tensorflow.ndarray.NdArrays; +import org.tensorflow.ndarray.Shape; +import org.tensorflow.ndarray.StdArrays; +import org.tensorflow.ndarray.buffer.DataBuffer; +import org.tensorflow.ndarray.buffer.DataBuffers; +import org.tensorflow.ndarray.buffer.IntDataBuffer; +import org.tensorflow.ndarray.impl.dimension.DimensionalSpace; +import org.tensorflow.ndarray.impl.sparse.slice.IntSparseSlice; +import org.tensorflow.ndarray.index.Index; + +import java.nio.ReadOnlyBufferException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * sparse array for the int data type + * + *

    A sparse array as two separate dense arrays: indices, values, and a shape that represents the + * dense shape. + * + *

    NOTE: all Sparse Arrays are readonly for the {@link #set(NdArray, long...)} and + * {@link #setObject(Integer, long...)} methods + * + *

    {@code
    + * IntSparseNdArray st = new IntSparseNdArray(
    + *      StdArrays.of(new long[][] {{0, 0}, {1, 2}}),
    + *      NdArrays.vectorOf(1, 256),
    + *      Shape.of(3, 4));
    + *
    + * }
    + * + *

    represents the dense array: + * + *

    {@code
    + * [[1, 0, 0, 0]
    + *  [0, 0, 256, 0]
    + *  [0, 0, 0, 0]]
    + *
    + * }
    + */ +public class IntSparseNdArray extends AbstractSparseNdArray + implements IntNdArray { + + /** + * Creates a IntSparseNdArray + * + * @param indices A 2-D LongNdArray of shape {@code [N, ndims]}, that specifies the indices of the + * elements in the sparse array that contain non-default values (elements are zero-indexed). + * For example, {@code indices=[[1,3], [2,4]]} specifies that the elements with indexes of + * {@code [1,3]} and {@code [2,4]} have non-default values. + * @param values A 1-D IntNdArray of shape {@code [N]}, which supplies the values for each element + * in indices. For example, given {@code indices=[[1,3], [2,4]]}, the parameter {@code + * values=[18, 3.6]} specifies that element {@code [1,3]} of the sparse NdArray has a value of + * {@code 18}, and element {@code [2,4]} of the NdArray has a value of {@code 3.6}. + * @param defaultValue Scalar value to set for indices not specified in {@link #getIndices()} + * @param dimensions the dimensional space for the dense object represented by this sparse array, + */ + protected IntSparseNdArray( + LongNdArray indices, IntNdArray values, int defaultValue, DimensionalSpace dimensions) { + super(indices, values, defaultValue, dimensions); + } + + /** + * Creates a IntSparseNdArray with a default value of zero. + * + * @param indices A 2-D LongNdArray of shape {@code [N, ndims]}, that specifies the indices of the + * elements in the sparse array that contain non-default values (elements are zero-indexed). + * For example, {@code indices=[[1,3], [2,4]]} specifies that the elements with indexes of + * {@code [1,3]} and {@code [2,4]} have non-default values. + * @param values A 1-D IntNdArray of shape {@code [N]}, which supplies the values for each element + * in indices. For example, given {@code indices=[[1,3], [2,4]]}, the parameter {@code + * values=[18, 3.6]} specifies that element {@code [1,3]} of the sparse NdArray has a value of + * {@code 18}, and element {@code [2,4]} of the NdArray has a value of {@code 3.6}. + * @param dimensions the dimensional space for the dense object represented by this sparse array, + */ + IntSparseNdArray(LongNdArray indices, IntNdArray values, DimensionalSpace dimensions) { + this(indices, values, 0, dimensions); + } + + /** + * Creates a IntSparseNdArray + * + * @param dataBuffer a dense dataBuffer used to create the spars array + * @param dimensions the dimensional space for the dense object represented by this sparse array, + */ + IntSparseNdArray(IntDataBuffer dataBuffer, DimensionalSpace dimensions) { + this(dataBuffer, 0, dimensions); + } + /** + * Creates a IntSparseNdArray + * + * @param dataBuffer a dense dataBuffer used to create the spars array + * @param dimensions the dimensional space for the dense object represented by this sparse array, + */ + IntSparseNdArray(IntDataBuffer dataBuffer, int defaultValue, DimensionalSpace dimensions) { + super(defaultValue, dimensions); + // use write to set up the indices and values + copyFrom(dataBuffer); + } + + /** + * Creates a zero-filled IntSparseNdArray + * + * @param dimensions the dimensional space for the dense object represented by this sparse array, + */ + IntSparseNdArray(DimensionalSpace dimensions) { + this(0, dimensions); + } + + /** + * Creates a zero-filled IntSparseNdArray + * + * @param dimensions the dimensional space for the dense object represented by this sparse array, + */ + IntSparseNdArray(int defaultValue, DimensionalSpace dimensions) { + super(defaultValue, dimensions); + } + + /** + * Creates a new IntSparseNdArray + * + * @param indices A 2-D LongNdArray of shape {@code [N, ndims]}, that specifies the indices of the + * elements in the sparse array that contain non-default values (elements are zero-indexed). + * For example, {@code indices=[[1,3], [2,4]]} specifies that the elements with indexes of + * {@code [1,3]} and {@code [2,4]} have non-default values. + * @param values A 1-D NdArray of any type and shape {@code [N]}, which supplies the values for + * each element in indices. For example, given {@code indices=[[1,3], [2,4]]}, the parameter + * {@code values=[18, 3.6]} specifies that element {@code [1,3]} of the sparse NdArray has a + * value of {@code 18}, and element {@code [2,4]} of the NdArray has a value of {@code 3.6}. + * @param dimensions the dimensional space for the dense object represented by this sparse array. + * @return the new Sparse Array + */ + public static IntSparseNdArray create( + LongNdArray indices, IntNdArray values, DimensionalSpace dimensions) { + return new IntSparseNdArray(indices, values, dimensions); + } + + /** + * Creates a new IntSparseNdArray + * + * @param indices A 2-D LongNdArray of shape {@code [N, ndims]}, that specifies the indices of the + * elements in the sparse array that contain non-default values (elements are zero-indexed). + * For example, {@code indices=[[1,3], [2,4]]} specifies that the elements with indexes of + * {@code [1,3]} and {@code [2,4]} have non-default values. + * @param values A 1-D NdArray of any type and shape {@code [N]}, which supplies the values for + * each element in indices. For example, given {@code indices=[[1,3], [2,4]]}, the parameter + * {@code values=[18, 3.6]} specifies that element {@code [1,3]} of the sparse NdArray has a + * value of {@code 18}, and element {@code [2,4]} of the NdArray has a value of {@code 3.6}. + * @param dimensions the dimensional space for the dense object represented by this sparse array. + * @return the new Sparse Array + */ + public static IntSparseNdArray create( + LongNdArray indices, IntNdArray values, int defaultValue, DimensionalSpace dimensions) { + return new IntSparseNdArray(indices, values, defaultValue, dimensions); + } + + /** + * Creates a new IntSparseNdArray from a data buffer + * + * @param dataBuffer the databuffer containing the dense array + * @param dimensions the dimensional space for the sparse array + * @return the new Sparse Array + */ + public static IntSparseNdArray create(IntDataBuffer dataBuffer, DimensionalSpace dimensions) { + return new IntSparseNdArray(dataBuffer, dimensions); + } + + /** + * Creates a new IntSparseNdArray from a data buffer + * + * @param dataBuffer the databuffer containing the dense array + * @param dimensions the dimensional space for the sparse array + * @return the new Sparse Array + */ + public static IntSparseNdArray create( + IntDataBuffer dataBuffer, int defaultValue, DimensionalSpace dimensions) { + return new IntSparseNdArray(dataBuffer, defaultValue, dimensions); + } + + /** + * Creates a new empty IntSparseNdArray from a data buffer + * + * @param dimensions the dimensions array + * @return the new Sparse Array + */ + public static IntSparseNdArray create(DimensionalSpace dimensions) { + return new IntSparseNdArray(dimensions); + } + + /** + * Creates a new empty IntSparseNdArray from a data buffer + * + * @param dimensions the dimensions array + * @return the new Sparse Array + */ + public static IntSparseNdArray create(int defaultValue, DimensionalSpace dimensions) { + return new IntSparseNdArray(defaultValue, dimensions); + } + + /** + * Creates a new empty IntSparseNdArray from a data buffer + * + * @param shape the shape of the debse array that this sparse array represents + * @return the new Sparse Array + */ + public static IntSparseNdArray create(Shape shape) { + return new IntSparseNdArray(DimensionalSpace.create(shape)); + } + /** + * Creates a new empty IntSparseNdArray from a data buffer + * + * @param defaultValue Scalar value to set for indices not specified in {@link #getIndices()} + * @param shape the shape of the debse array that this sparse array represents + * @return the new Sparse Array + */ + public static IntSparseNdArray create(int defaultValue, Shape shape) { + return new IntSparseNdArray(defaultValue, DimensionalSpace.create(shape)); + } + + /** + * Creates a new empty IntSparseNdArray from a int data buffer + * + * @param buffer the data buffer + * @param shape the shape of the sparse array. + * @return the new Sparse Array + */ + public static IntSparseNdArray create(IntDataBuffer buffer, Shape shape) { + return new IntSparseNdArray(buffer, DimensionalSpace.create(shape)); + } + + /** + * Creates a new empty IntSparseNdArray from a int data buffer + * + * @param buffer the data buffer + * @param defaultValue Scalar value to set for indices not specified in {@link #getIndices()} + * @param shape the shape of the sparse array. + * @return the new Sparse Array + */ + public static IntSparseNdArray create(IntDataBuffer buffer, int defaultValue, Shape shape) { + return new IntSparseNdArray(buffer, defaultValue, DimensionalSpace.create(shape)); + } + + /** + * Creates a new IntSparseNdArray from a IntNdArray + * + * @param src the IntNdArray + * @return the new Sparse Array + */ + public static IntSparseNdArray create(IntNdArray src) { + IntDataBuffer buffer = DataBuffers.ofInts(src.size()); + src.copyTo(buffer); + return new IntSparseNdArray(buffer, DimensionalSpace.create(src.shape())); + } + + /** + * Creates a new IntSparseNdArray from a IntNdArray + * + * @param src the IntNdArray + * @param defaultValue Scalar value to set for indices not specified in {@link #getIndices()} + * @return the new Sparse Array + */ + public static IntSparseNdArray create(IntNdArray src, int defaultValue) { + IntDataBuffer buffer = DataBuffers.ofInts(src.size()); + src.copyTo(buffer); + return new IntSparseNdArray(buffer, defaultValue, DimensionalSpace.create(src.shape())); + } + + /** + * Creates a IntNdArray of the specified shape + * + * @param shape the shape of the dense array. + * @return a IntNdArray of the specified shape + */ + public IntNdArray createValues(Shape shape) { + return NdArrays.ofInts(shape); + } + + /** {@inheritDoc} */ + @Override + public IntNdArray slice(long position, DimensionalSpace sliceDimensions) { + return new IntSparseSlice(this, position, sliceDimensions); + } + + /** {@inheritDoc} */ + @Override + public int getInt(long... coordinates) { + return getObject(coordinates); + } + + /** {@inheritDoc} */ + @Override + public IntNdArray setInt(int value, long... coordinates) { + throw new ReadOnlyBufferException(); + } + + /** {@inheritDoc} */ + @Override + public IntNdArray copyTo(DataBuffer dst) { + return copyTo((IntDataBuffer) dst); + } + + /** {@inheritDoc} */ + @Override + public IntNdArray copyTo(IntDataBuffer dst) { + // set the values in buf to the default, then overwrite with indices/values + Integer[] defaults = new Integer[(int) shape().size()]; + Arrays.fill(defaults, getDefaultValue()); + dst.write(defaults); + + AtomicInteger i = new AtomicInteger(); + getIndices() + .elements(0) + .forEachIndexed( + (idx, l) -> { + long[] coordinates = getIndicesCoordinates(l); + int value = getValues().getInt(i.getAndIncrement()); + dst.setObject(value, dimensions.positionOf(coordinates)); + }); + return this; + } + + /** {@inheritDoc} */ + @Override + public IntNdArray copyFrom(IntDataBuffer src) { + List indices = new ArrayList<>(); + List values = new ArrayList<>(); + + for (long i = 0; i < src.size(); i++) { + if (!src.getObject(i).equals(getDefaultValue())) { + indices.add(toCoordinates(dimensions, i)); + values.add(src.getObject(i)); + } + } + long[][] indicesArray = new long[indices.size()][]; + int[] valuesArray = new int[values.size()]; + for (int i = 0; i < indices.size(); i++) { + indicesArray[i] = indices.get(i); + valuesArray[i] = values.get(i); + } + + setIndices(StdArrays.ndCopyOf(indicesArray)); + setValues(NdArrays.vectorOf(valuesArray)); + return this; + } + + /** {@inheritDoc} */ + @Override + public IntNdArray copyFrom(DataBuffer src) { + return copyFrom((IntDataBuffer) src); + } + + /** + * Converts the sparse array to a dense array + * + * @return the dense array + */ + public IntNdArray toDense() { + IntDataBuffer dataBuffer = DataBuffers.ofInts(shape().size()); + copyTo(dataBuffer); + return NdArrays.wrap(shape(), dataBuffer); + } + + /** + * Populates this sparse array from a dense array + * + * @param src the dense array + * @return this sparse array + */ + public IntNdArray fromDense(IntNdArray src) { + IntDataBuffer buffer = DataBuffers.ofInts(src.size()); + src.copyTo(buffer); + copyFrom(buffer); + return this; + } + + /** {@inheritDoc} */ + @Override + public IntNdArray slice(Index... indices) { + return (IntNdArray) super.slice(indices); + } + + /** {@inheritDoc} */ + @Override + public IntNdArray get(long... coordinates) { + return (IntNdArray) super.get(coordinates); + } + + /** {@inheritDoc} */ + @Override + public IntNdArray setObject(Integer value, long... coordinates) { + throw new ReadOnlyBufferException(); + } + + /** {@inheritDoc} */ + @Override + public IntNdArray set(NdArray src, long... coordinates) { + throw new ReadOnlyBufferException(); + } + + /** {@inheritDoc} */ + @Override + public IntNdArray copyTo(NdArray dst) { + return (IntNdArray) super.copyTo(dst); + } + + /** {@inheritDoc} */ + @Override + public IntNdArray createDefaultArray() { + return NdArrays.scalarOf(getDefaultValue()); + } +} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/sparse/LongSparseNdArray.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/sparse/LongSparseNdArray.java new file mode 100644 index 0000000..1c01198 --- /dev/null +++ b/ndarray/src/main/java/org/tensorflow/ndarray/impl/sparse/LongSparseNdArray.java @@ -0,0 +1,417 @@ +/* Copyright 2021 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +=======================================================================*/ +package org.tensorflow.ndarray.impl.sparse; + +import org.tensorflow.ndarray.LongNdArray; +import org.tensorflow.ndarray.NdArray; +import org.tensorflow.ndarray.NdArrays; +import org.tensorflow.ndarray.Shape; +import org.tensorflow.ndarray.StdArrays; +import org.tensorflow.ndarray.buffer.DataBuffer; +import org.tensorflow.ndarray.buffer.DataBuffers; +import org.tensorflow.ndarray.buffer.LongDataBuffer; +import org.tensorflow.ndarray.impl.dimension.DimensionalSpace; +import org.tensorflow.ndarray.impl.sparse.slice.LongSparseSlice; +import org.tensorflow.ndarray.index.Index; + +import java.nio.ReadOnlyBufferException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.atomic.AtomicLong; + +/** + * sparse array for the long data type + * + *

    A sparse array as two separate dense arrays: indices, values, and a shape that represents the + * dense shape. + * + *

    NOTE: all Sparse Arrays are readonly for the {@link #set(NdArray, long...)} and + * {@link #setObject(Long, long...)} methods + * + *

    {@code
    + * LongSparseNdArray st = new LongSparseNdArray(
    + *      StdArrays.of(new long[][] {{0, 0}, {1, 2}}),
    + *      NdArrays.vectorOf(1L, 256L),
    + *      Shape.of(3, 4));
    + *
    + * }
    + * + *

    represents the dense array: + * + *

    {@code
    + * [[1, 0, 0, 0]
    + *  [0, 0, 256, 0]
    + *  [0, 0, 0, 0]]
    + *
    + * }
    + */ +public class LongSparseNdArray extends AbstractSparseNdArray + implements LongNdArray { + + /** + * Creates a LongSparseNdArray + * + * @param indices A 2-D LongNdArray of shape {@code [N, ndims]}, that specifies the indices of the + * elements in the sparse array that contain non-default values (elements are zero-indexed). + * For example, {@code indices=[[1,3], [2,4]]} specifies that the elements with indexes of + * {@code [1,3]} and {@code [2,4]} have non-default values. + * @param values A 1-D LongNdArray of shape {@code [N]}, which supplies the values for each + * element in indices. For example, given {@code indices=[[1,3], [2,4]]}, the parameter {@code + * values=[18, 3.6]} specifies that element {@code [1,3]} of the sparse NdArray has a value of + * {@code 18}, and element {@code [2,4]} of the NdArray has a value of {@code 3.6}. + * @param defaultValue Scalar value to set for indices not specified in {@link #getIndices()} + * @param dimensions the dimensional space for the dense object represented by this sparse array, + */ + protected LongSparseNdArray( + LongNdArray indices, LongNdArray values, long defaultValue, DimensionalSpace dimensions) { + super(indices, values, defaultValue, dimensions); + } + + /** + * Creates a LongSparseNdArray with a default value of zero. + * + * @param indices A 2-D LongNdArray of shape {@code [N, ndims]}, that specifies the indices of the + * elements in the sparse array that contain non-default values (elements are zero-indexed). + * For example, {@code indices=[[1,3], [2,4]]} specifies that the elements with indexes of + * {@code [1,3]} and {@code [2,4]} have non-default values. + * @param values A 1-D LongNdArray of shape {@code [N]}, which supplies the values for each + * element in indices. For example, given {@code indices=[[1,3], [2,4]]}, the parameter {@code + * values=[18, 3.6]} specifies that element {@code [1,3]} of the sparse NdArray has a value of + * {@code 18}, and element {@code [2,4]} of the NdArray has a value of {@code 3.6}. + * @param dimensions the dimensional space for the dense object represented by this sparse array, + */ + LongSparseNdArray(LongNdArray indices, LongNdArray values, DimensionalSpace dimensions) { + this(indices, values, 0L, dimensions); + } + + /** + * Creates a LongSparseNdArray + * + * @param dataBuffer a dense dataBuffer used to create the spars array + * @param dimensions the dimensional space for the dense object represented by this sparse array, + */ + LongSparseNdArray(LongDataBuffer dataBuffer, DimensionalSpace dimensions) { + this(dataBuffer, 0L, dimensions); + } + + /** + * Creates a LongSparseNdArray + * + * @param dataBuffer a dense dataBuffer used to create the spars array + * @param defaultValue Scalar value to set for indices not specified in {@link #getIndices()} + * @param dimensions the dimensional space for the dense object represented by this sparse array, + */ + LongSparseNdArray(LongDataBuffer dataBuffer, long defaultValue, DimensionalSpace dimensions) { + super(defaultValue, dimensions); + // use write to set up the indices and values + copyFrom(dataBuffer); + } + + /** + * Creates a zero-filled LongSparseNdArray + * + * @param dimensions the dimensional space for the dense object represented by this sparse array, + */ + LongSparseNdArray(DimensionalSpace dimensions) { + this(0L, dimensions); + } + + /** + * Creates a zero-filled LongSparseNdArray + * + * @param defaultValue Scalar value to set for indices not specified in {@link #getIndices()} + * @param dimensions the dimensional space for the dense object represented by this sparse array, + */ + LongSparseNdArray(long defaultValue, DimensionalSpace dimensions) { + super(defaultValue, dimensions); + } + + /** + * Creates a new LongSparseNdArray + * + * @param indices A 2-D LongNdArray of shape {@code [N, ndims]}, that specifies the indices of the + * elements in the sparse array that contain non-default values (elements are zero-indexed). + * For example, {@code indices=[[1,3], [2,4]]} specifies that the elements with indexes of + * {@code [1,3]} and {@code [2,4]} have non-default values. + * @param values A 1-D NdArray of any type and shape {@code [N]}, which supplies the values for + * each element in indices. For example, given {@code indices=[[1,3], [2,4]]}, the parameter + * {@code values=[18, 3.6]} specifies that element {@code [1,3]} of the sparse NdArray has a + * value of {@code 18}, and element {@code [2,4]} of the NdArray has a value of {@code 3.6}. + * @param dimensions the dimensional space for the dense object represented by this sparse array. + * @return the new Sparse Array + */ + public static LongSparseNdArray create( + LongNdArray indices, LongNdArray values, DimensionalSpace dimensions) { + return new LongSparseNdArray(indices, values, dimensions); + } + + /** + * Creates a new LongSparseNdArray + * + * @param indices A 2-D LongNdArray of shape {@code [N, ndims]}, that specifies the indices of the + * elements in the sparse array that contain non-default values (elements are zero-indexed). + * For example, {@code indices=[[1,3], [2,4]]} specifies that the elements with indexes of + * {@code [1,3]} and {@code [2,4]} have non-default values. + * @param values A 1-D NdArray of any type and shape {@code [N]}, which supplies the values for + * each element in indices. For example, given {@code indices=[[1,3], [2,4]]}, the parameter + * {@code values=[18, 3.6]} specifies that element {@code [1,3]} of the sparse NdArray has a + * value of {@code 18}, and element {@code [2,4]} of the NdArray has a value of {@code 3.6}. + * @param defaultValue Scalar value to set for indices not specified in {@link #getIndices()} + * @param dimensions the dimensional space for the dense object represented by this sparse array. + * @return the new Sparse Array + */ + public static LongSparseNdArray create( + LongNdArray indices, LongNdArray values, long defaultValue, DimensionalSpace dimensions) { + return new LongSparseNdArray(indices, values, defaultValue, dimensions); + } + + /** + * Creates a new LongSparseNdArray from a data buffer + * + * @param dataBuffer the databuffer containing the dense array + * @param dimensions the dimensional space for the sparse array + * @return the new Sparse Array + */ + public static LongSparseNdArray create(LongDataBuffer dataBuffer, DimensionalSpace dimensions) { + return new LongSparseNdArray(dataBuffer, dimensions); + } + + /** + * Creates a new LongSparseNdArray from a data buffer + * + * @param dataBuffer the databuffer containing the dense array + * @param defaultValue Scalar value to set for indices not specified in {@link #getIndices()} + * @param dimensions the dimensional space for the sparse array + * @return the new Sparse Array + */ + public static LongSparseNdArray create( + LongDataBuffer dataBuffer, long defaultValue, DimensionalSpace dimensions) { + return new LongSparseNdArray(dataBuffer, defaultValue, dimensions); + } + + /** + * Creates a new empty LongSparseNdArray from a data buffer + * + * @param dimensions the dimensions array + * @return the new Sparse Array + */ + public static LongSparseNdArray create(DimensionalSpace dimensions) { + return new LongSparseNdArray(dimensions); + } + + /** + * Creates a new empty LongSparseNdArray from a data buffer + * + * @param defaultValue Scalar value to set for indices not specified in {@link #getIndices()} + * @param dimensions the dimensions array + * @return the new Sparse Array + */ + public static LongSparseNdArray create(long defaultValue, DimensionalSpace dimensions) { + return new LongSparseNdArray(defaultValue, dimensions); + } + + /** + * Creates a new empty LongSparseNdArray from a long data buffer + * + * @param buffer the data buffer + * @param shape the shape of the sparse array. + * @return the new Sparse Array + */ + public static LongSparseNdArray create(LongDataBuffer buffer, Shape shape) { + return new LongSparseNdArray(buffer, DimensionalSpace.create(shape)); + } + + /** + * Creates a new empty LongSparseNdArray from a long data buffer + * + * @param buffer the data buffer + * @param defaultValue Scalar value to set for indices not specified in {@link #getIndices()} + * @param shape the shape of the sparse array. + * @return the new Sparse Array + */ + public static LongSparseNdArray create(LongDataBuffer buffer, long defaultValue, Shape shape) { + return new LongSparseNdArray(buffer, defaultValue, DimensionalSpace.create(shape)); + } + + /** + * Creates a new LongSparseNdArray from a LongNdArray + * + * @param src the LongNdArray + * @return the new Sparse Array + */ + public static LongSparseNdArray create(LongNdArray src) { + LongDataBuffer buffer = DataBuffers.ofLongs(src.size()); + src.copyTo(buffer); + return new LongSparseNdArray(buffer, DimensionalSpace.create(src.shape())); + } + + /** + * Creates a new LongSparseNdArray from a LongNdArray + * + * @param src the LongNdArray + * @param defaultValue Scalar value to set for indices not specified in {@link #getIndices()} + * @return the new Sparse Array + */ + public static LongSparseNdArray create(LongNdArray src, long defaultValue) { + LongDataBuffer buffer = DataBuffers.ofLongs(src.size()); + src.copyTo(buffer); + return new LongSparseNdArray(buffer, defaultValue, DimensionalSpace.create(src.shape())); + } + + /** + * Creates a LongNdArray of the specified shape + * + * @param shape the shape of the dense array. + * @return a LongNdArray of the specified shape + */ + public LongNdArray createValues(Shape shape) { + return NdArrays.ofLongs(shape); + } + + /** {@inheritDoc} */ + @Override + public LongNdArray slice(long position, DimensionalSpace sliceDimensions) { + return new LongSparseSlice(this, position, sliceDimensions); + } + + /** {@inheritDoc} */ + @Override + public long getLong(long... coordinates) { + return getObject(coordinates); + } + + /** {@inheritDoc} */ + @Override + public LongNdArray setLong(long value, long... coordinates) { + throw new ReadOnlyBufferException(); + } + + /** {@inheritDoc} */ + @Override + public LongNdArray copyTo(DataBuffer dst) { + return copyTo((LongDataBuffer) dst); + } + + /** {@inheritDoc} */ + @Override + public LongNdArray copyTo(LongDataBuffer dst) { + // set the values in buf to the default, then overwrite with indices/values + Long[] defaults = new Long[(int) shape().size()]; + Arrays.fill(defaults, getDefaultValue()); + dst.write(defaults); + + AtomicLong i = new AtomicLong(); + getIndices() + .elements(0) + .forEachIndexed( + (idx, l) -> { + long[] coordinates = getIndicesCoordinates(l); + long value = getValues().getLong(i.getAndIncrement()); + dst.setObject(value, dimensions.positionOf(coordinates)); + }); + return this; + } + + /** {@inheritDoc} */ + @Override + public LongNdArray copyFrom(LongDataBuffer src) { + List indices = new ArrayList<>(); + List values = new ArrayList<>(); + + for (long i = 0; i < src.size(); i++) { + if (!src.getObject(i).equals(getDefaultValue())) { + indices.add(toCoordinates(dimensions, i)); + values.add(src.getObject(i)); + } + } + long[][] indicesArray = new long[indices.size()][]; + long[] valuesArray = new long[values.size()]; + for (int i = 0; i < indices.size(); i++) { + indicesArray[i] = indices.get(i); + valuesArray[i] = values.get(i); + } + + setIndices(StdArrays.ndCopyOf(indicesArray)); + setValues(NdArrays.vectorOf(valuesArray)); + return this; + } + + /** {@inheritDoc} */ + @Override + public LongNdArray copyFrom(DataBuffer src) { + return copyFrom((LongDataBuffer) src); + } + + /** + * Converts the sparse array to a dense array + * + * @return the dense array + */ + public LongNdArray toDense() { + LongDataBuffer dataBuffer = DataBuffers.ofLongs(shape().size()); + copyTo(dataBuffer); + return NdArrays.wrap(shape(), dataBuffer); + } + + /** + * Populates this sparse array from a dense array + * + * @param src the dense array + * @return this sparse array + */ + public LongNdArray fromDense(LongNdArray src) { + LongDataBuffer buffer = DataBuffers.ofLongs(src.size()); + src.copyTo(buffer); + copyFrom(buffer); + return this; + } + + /** {@inheritDoc} */ + @Override + public LongNdArray slice(Index... indices) { + return (LongNdArray) super.slice(indices); + } + + /** {@inheritDoc} */ + @Override + public LongNdArray get(long... coordinates) { + return (LongNdArray) super.get(coordinates); + } + + /** {@inheritDoc} */ + @Override + public LongNdArray setObject(Long value, long... coordinates) { + throw new ReadOnlyBufferException(); + } + + /** {@inheritDoc} */ + @Override + public LongNdArray set(NdArray src, long... coordinates) { + throw new ReadOnlyBufferException(); + } + + /** {@inheritDoc} */ + @Override + public LongNdArray copyTo(NdArray dst) { + return (LongNdArray) super.copyTo(dst); + } + + /** {@inheritDoc} */ + @Override + public LongNdArray createDefaultArray() { + return NdArrays.scalarOf(getDefaultValue()); + } +} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/sparse/ShortSparseNdArray.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/sparse/ShortSparseNdArray.java new file mode 100644 index 0000000..051a7cb --- /dev/null +++ b/ndarray/src/main/java/org/tensorflow/ndarray/impl/sparse/ShortSparseNdArray.java @@ -0,0 +1,418 @@ +/* Copyright 2021 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +=======================================================================*/ +package org.tensorflow.ndarray.impl.sparse; + +import org.tensorflow.ndarray.LongNdArray; +import org.tensorflow.ndarray.NdArray; +import org.tensorflow.ndarray.NdArrays; +import org.tensorflow.ndarray.Shape; +import org.tensorflow.ndarray.ShortNdArray; +import org.tensorflow.ndarray.StdArrays; +import org.tensorflow.ndarray.buffer.DataBuffer; +import org.tensorflow.ndarray.buffer.DataBuffers; +import org.tensorflow.ndarray.buffer.ShortDataBuffer; +import org.tensorflow.ndarray.impl.dimension.DimensionalSpace; +import org.tensorflow.ndarray.impl.sparse.slice.ShortSparseSlice; +import org.tensorflow.ndarray.index.Index; + +import java.nio.ReadOnlyBufferException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * sparse array for the short data type + * + *

    A sparse array as two separate dense arrays: indices, values, and a shape that represents the + * dense shape. + * + *

    NOTE: all Sparse Arrays are readonly for the {@link #set(NdArray, long...)} and + * {@link #setObject(Short, long...)} methods + * + *

    {@code
    + * ShortSparseNdArray st = new ShortSparseNdArray(
    + *      StdArrays.of(new long[][] {{0, 0}, {1, 2}}),
    + *      NdArrays.vectorOf((short)1, (short)256}),
    + *      Shape.of(3, 4));
    + *
    + * }
    + * + *

    represents the dense array: + * + *

    {@code
    + * [[1, 0, 0, 0]
    + *  [0, 0, 256, 0]
    + *  [0, 0, 0, 0]]
    + *
    + * }
    + */ +public class ShortSparseNdArray extends AbstractSparseNdArray + implements ShortNdArray { + + /** + * Creates a ShortSparseNdArray + * + * @param indices A 2-D LongNdArray of shape {@code [N, ndims]}, that specifies the indices of the + * elements in the sparse array that contain non-default values (elements are zero-indexed). + * For example, {@code indices=[[1,3], [2,4]]} specifies that the elements with indexes of + * {@code [1,3]} and {@code [2,4]} have non-default values. + * @param values A 1-D ShortNdArray of shape {@code [N]}, which supplies the values for each + * element in indices. For example, given {@code indices=[[1,3], [2,4]]}, the parameter {@code + * values=[18, 3.6]} specifies that element {@code [1,3]} of the sparse NdArray has a value of + * {@code 18}, and element {@code [2,4]} of the NdArray has a value of {@code 3.6}. + * @param defaultValue Scalar value to set for indices not specified in {@link #getIndices()} + * @param dimensions the dimensional space for the dense object represented by this sparse array, + */ + protected ShortSparseNdArray( + LongNdArray indices, ShortNdArray values, short defaultValue, DimensionalSpace dimensions) { + super(indices, values, defaultValue, dimensions); + } + + /** + * Creates a ShortSparseNdArray with a default value of zero. + * + * @param indices A 2-D LongNdArray of shape {@code [N, ndims]}, that specifies the indices of the + * elements in the sparse array that contain non-default values (elements are zero-indexed). + * For example, {@code indices=[[1,3], [2,4]]} specifies that the elements with indexes of + * {@code [1,3]} and {@code [2,4]} have non-default values. + * @param values A 1-D ShortNdArray of shape {@code [N]}, which supplies the values for each + * element in indices. For example, given {@code indices=[[1,3], [2,4]]}, the parameter {@code + * values=[18, 3.6]} specifies that element {@code [1,3]} of the sparse NdArray has a value of + * {@code 18}, and element {@code [2,4]} of the NdArray has a value of {@code 3.6}. + * @param dimensions the dimensional space for the dense object represented by this sparse array, + */ + ShortSparseNdArray(LongNdArray indices, ShortNdArray values, DimensionalSpace dimensions) { + this(indices, values, (short) 0, dimensions); + } + + /** + * Creates a ShortSparseNdArray + * + * @param dataBuffer a dense dataBuffer used to create the spars array + * @param dimensions the dimensional space for the dense object represented by this sparse array, + */ + ShortSparseNdArray(ShortDataBuffer dataBuffer, DimensionalSpace dimensions) { + this(dataBuffer, (short) 0, dimensions); + } + + /** + * Creates a ShortSparseNdArray + * + * @param dataBuffer a dense dataBuffer used to create the spars array + * @param defaultValue Scalar value to set for indices not specified in {@link #getIndices()} + * @param dimensions the dimensional space for the dense object represented by this sparse array, + */ + ShortSparseNdArray(ShortDataBuffer dataBuffer, short defaultValue, DimensionalSpace dimensions) { + super(defaultValue, dimensions); + // use write to set up the indices and values + copyFrom(dataBuffer); + } + + /** + * Creates a zero-filled ShortSparseNdArray + * + * @param dimensions the dimensional space for the dense object represented by this sparse array, + */ + ShortSparseNdArray(DimensionalSpace dimensions) { + this((short) 0, dimensions); + } + + /** + * Creates a zero-filled ShortSparseNdArray + * + * @param defaultValue Scalar value to set for indices not specified in {@link #getIndices()} + * @param dimensions the dimensional space for the dense object represented by this sparse array, + */ + ShortSparseNdArray(short defaultValue, DimensionalSpace dimensions) { + super(defaultValue, dimensions); + } + + /** + * Creates a new ShortSparseNdArray + * + * @param indices A 2-D LongNdArray of shape {@code [N, ndims]}, that specifies the indices of the + * elements in the sparse array that contain non-default values (elements are zero-indexed). + * For example, {@code indices=[[1,3], [2,4]]} specifies that the elements with indexes of + * {@code [1,3]} and {@code [2,4]} have non-default values. + * @param values A 1-D NdArray of any type and shape {@code [N]}, which supplies the values for + * each element in indices. For example, given {@code indices=[[1,3], [2,4]]}, the parameter + * {@code values=[18, 3.6]} specifies that element {@code [1,3]} of the sparse NdArray has a + * value of {@code 18}, and element {@code [2,4]} of the NdArray has a value of {@code 3.6}. + * @param dimensions the dimensional space for the dense object represented by this sparse array. + * @return the new Sparse Array + */ + public static ShortSparseNdArray create( + LongNdArray indices, ShortNdArray values, DimensionalSpace dimensions) { + return new ShortSparseNdArray(indices, values, dimensions); + } + + /** + * Creates a new ShortSparseNdArray + * + * @param indices A 2-D LongNdArray of shape {@code [N, ndims]}, that specifies the indices of the + * elements in the sparse array that contain non-default values (elements are zero-indexed). + * For example, {@code indices=[[1,3], [2,4]]} specifies that the elements with indexes of + * {@code [1,3]} and {@code [2,4]} have non-default values. + * @param values A 1-D NdArray of any type and shape {@code [N]}, which supplies the values for + * each element in indices. For example, given {@code indices=[[1,3], [2,4]]}, the parameter + * {@code values=[18, 3.6]} specifies that element {@code [1,3]} of the sparse NdArray has a + * value of {@code 18}, and element {@code [2,4]} of the NdArray has a value of {@code 3.6}. + * @param defaultValue Scalar value to set for indices not specified in {@link #getIndices()} + * @param dimensions the dimensional space for the dense object represented by this sparse array. + * @return the new Sparse Array + */ + public static ShortSparseNdArray create( + LongNdArray indices, ShortNdArray values, short defaultValue, DimensionalSpace dimensions) { + return new ShortSparseNdArray(indices, values, defaultValue, dimensions); + } + + /** + * Creates a new ShortSparseNdArray from a data buffer + * + * @param dataBuffer the databuffer containing the dense array + * @param dimensions the dimensional space for the sparse array + * @return the new Sparse Array + */ + public static ShortSparseNdArray create(ShortDataBuffer dataBuffer, DimensionalSpace dimensions) { + return new ShortSparseNdArray(dataBuffer, dimensions); + } + + /** + * Creates a new ShortSparseNdArray from a data buffer + * + * @param dataBuffer the databuffer containing the dense array + * @param defaultValue Scalar value to set for indices not specified in {@link #getIndices()} + * @param dimensions the dimensional space for the sparse array + * @return the new Sparse Array + */ + public static ShortSparseNdArray create( + ShortDataBuffer dataBuffer, short defaultValue, DimensionalSpace dimensions) { + return new ShortSparseNdArray(dataBuffer, defaultValue, dimensions); + } + + /** + * Creates a new empty ShortSparseNdArray from a data buffer + * + * @param dimensions the dimensions array + * @return the new Sparse Array + */ + public static ShortSparseNdArray create(DimensionalSpace dimensions) { + return new ShortSparseNdArray(dimensions); + } + + /** + * Creates a new empty ShortSparseNdArray from a data buffer + * + * @param defaultValue Scalar value to set for indices not specified in {@link #getIndices()} + * @param dimensions the dimensions array + * @return the new Sparse Array + */ + public static ShortSparseNdArray create(short defaultValue, DimensionalSpace dimensions) { + return new ShortSparseNdArray(defaultValue, dimensions); + } + + /** + * Creates a new empty ShortSparseNdArray from a short data buffer + * + * @param buffer the data buffer + * @param shape the shape of the sparse array. + * @return the new Sparse Array + */ + public static ShortSparseNdArray create(ShortDataBuffer buffer, Shape shape) { + return new ShortSparseNdArray(buffer, DimensionalSpace.create(shape)); + } + + /** + * Creates a new empty ShortSparseNdArray from a short data buffer + * + * @param buffer the data buffer + * @param defaultValue Scalar value to set for indices not specified in {@link #getIndices()} + * @param shape the shape of the sparse array. + * @return the new Sparse Array + */ + public static ShortSparseNdArray create(ShortDataBuffer buffer, short defaultValue, Shape shape) { + return new ShortSparseNdArray(buffer, defaultValue, DimensionalSpace.create(shape)); + } + + /** + * Creates a new ShortSparseNdArray from a ShortNdArray + * + * @param src the ShortNdArray + * @return the new Sparse Array + */ + public static ShortSparseNdArray create(ShortNdArray src) { + ShortDataBuffer buffer = DataBuffers.ofShorts(src.size()); + src.copyTo(buffer); + return new ShortSparseNdArray(buffer, DimensionalSpace.create(src.shape())); + } + + /** + * Creates a new ShortSparseNdArray from a ShortNdArray + * + * @param src the ShortNdArray + * @param defaultValue Scalar value to set for indices not specified in {@link #getIndices()} + * @return the new Sparse Array + */ + public static ShortSparseNdArray create(ShortNdArray src, short defaultValue) { + ShortDataBuffer buffer = DataBuffers.ofShorts(src.size()); + src.copyTo(buffer); + return new ShortSparseNdArray(buffer, defaultValue, DimensionalSpace.create(src.shape())); + } + + /** + * Creates a ShortNdArray of the specified shape + * + * @param shape the shape of the dense array. + * @return a ShortNdArray of the specified shape + */ + public ShortNdArray createValues(Shape shape) { + return NdArrays.ofShorts(shape); + } + + /** {@inheritDoc} */ + @Override + public ShortNdArray slice(long position, DimensionalSpace sliceDimensions) { + return new ShortSparseSlice(this, position, sliceDimensions); + } + + /** {@inheritDoc} */ + @Override + public short getShort(long... coordinates) { + return getObject(coordinates); + } + + /** {@inheritDoc} */ + @Override + public ShortNdArray setShort(short value, long... coordinates) { + throw new ReadOnlyBufferException(); + } + + /** {@inheritDoc} */ + @Override + public ShortNdArray copyTo(DataBuffer dst) { + return copyTo((ShortDataBuffer) dst); + } + + /** {@inheritDoc} */ + @Override + public ShortNdArray copyTo(ShortDataBuffer dst) { + // set the values in buf to the default, then overwrite with indices/values + Short[] defaults = new Short[(int) shape().size()]; + Arrays.fill(defaults, getDefaultValue()); + dst.write(defaults); + + AtomicInteger i = new AtomicInteger(); + getIndices() + .elements(0) + .forEachIndexed( + (idx, l) -> { + long[] coordinates = getIndicesCoordinates(l); + short value = getValues().getShort(i.getAndIncrement()); + dst.setObject(value, dimensions.positionOf(coordinates)); + }); + return this; + } + + /** {@inheritDoc} */ + @Override + public ShortNdArray copyFrom(ShortDataBuffer src) { + List indices = new ArrayList<>(); + List values = new ArrayList<>(); + + for (short i = 0; i < src.size(); i++) { + if (!src.getObject(i).equals(getDefaultValue())) { + indices.add(toCoordinates(dimensions, i)); + values.add(src.getObject(i)); + } + } + long[][] indicesArray = new long[indices.size()][]; + short[] valuesArray = new short[values.size()]; + for (int i = 0; i < indices.size(); i++) { + indicesArray[i] = indices.get(i); + valuesArray[i] = values.get(i); + } + + setIndices(StdArrays.ndCopyOf(indicesArray)); + setValues(NdArrays.vectorOf(valuesArray)); + return this; + } + + /** {@inheritDoc} */ + @Override + public ShortNdArray copyFrom(DataBuffer src) { + return copyFrom((ShortDataBuffer) src); + } + + /** + * Converts the sparse array to a dense array + * + * @return the dense array + */ + public ShortNdArray toDense() { + ShortDataBuffer dataBuffer = DataBuffers.ofShorts(shape().size()); + copyTo(dataBuffer); + return NdArrays.wrap(shape(), dataBuffer); + } + + /** + * Populates this sparse array from a dense array + * + * @param src the dense array + * @return this sparse array + */ + public ShortNdArray fromDense(ShortNdArray src) { + ShortDataBuffer buffer = DataBuffers.ofShorts(src.size()); + src.copyTo(buffer); + copyFrom(buffer); + return this; + } + + /** {@inheritDoc} */ + @Override + public ShortNdArray slice(Index... indices) { + return (ShortNdArray) super.slice(indices); + } + + /** {@inheritDoc} */ + @Override + public ShortNdArray get(long... coordinates) { + return (ShortNdArray) super.get(coordinates); + } + + /** {@inheritDoc} */ + @Override + public ShortNdArray setObject(Short value, long... coordinates) { + throw new ReadOnlyBufferException(); + } + + /** {@inheritDoc} */ + @Override + public ShortNdArray set(NdArray src, long... coordinates) { + throw new ReadOnlyBufferException(); + } + + /** {@inheritDoc} */ + @Override + public ShortNdArray copyTo(NdArray dst) { + return (ShortNdArray) super.copyTo(dst); + } + + /** {@inheritDoc} */ + @Override + public ShortNdArray createDefaultArray() { + return NdArrays.scalarOf(getDefaultValue()); + } +} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/sparse/SparseNdArray.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/sparse/SparseNdArray.java new file mode 100644 index 0000000..c6d93f2 --- /dev/null +++ b/ndarray/src/main/java/org/tensorflow/ndarray/impl/sparse/SparseNdArray.java @@ -0,0 +1,418 @@ +/* Copyright 2021 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +=======================================================================*/ +package org.tensorflow.ndarray.impl.sparse; + +import org.tensorflow.ndarray.LongNdArray; +import org.tensorflow.ndarray.NdArray; +import org.tensorflow.ndarray.NdArrays; +import org.tensorflow.ndarray.Shape; +import org.tensorflow.ndarray.StdArrays; +import org.tensorflow.ndarray.buffer.DataBuffer; +import org.tensorflow.ndarray.buffer.DataBuffers; +import org.tensorflow.ndarray.impl.dimension.DimensionalSpace; +import org.tensorflow.ndarray.impl.sparse.slice.ObjectSparseSlice; + +import java.lang.reflect.Array; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * sparse array for the any data type + * + *

    A sparse array has two separate dense arrays: indices, values, and a shape that represents the + * dense shape. + * + *

    NOTE: all Sparse Arrays are readonly for the {@link #set(NdArray, long...)} and + * {@link #setObject(Object, long...)} methods + * + *

    {@code
    + * SparseNdArray st = new SparseNdArray<>(
    + *      StdArrays.of(new long[][] {{0, 0}, {1, 2}}),
    + *      NdArrays.vectorOf("first", "second"),
    + *      Shape.of(3, 4));
    + *
    + * }
    + * + *

    represents the dense array: + * + *

    {@code
    + * [[true, false, false, false]
    + *  [false, false, true, false]
    + *  [false, false, false, false]]
    + *
    + * }
    + */ +public class SparseNdArray> extends AbstractSparseNdArray + implements org.tensorflow.ndarray.SparseNdArray { + + private final Class type; + + /** + * Creates a SparseNdArray + * + * @param indices A 2-D LongNdArray of shape {@code [N, ndims]}, that specifies the indices of the + * elements in the sparse array that contain non-default values (elements are zero-indexed). + * For example, {@code indices=[[1,3], [2,4]]} specifies that the elements with indexes of + * {@code [1,3]} and {@code [2,4]} have non-default values. + * @param values A 1-D NdArray of Boolean type and shape {@code [N]}, which supplies the values + * for each element in indices. For example, given {@code indices=[[1,3], [2,4]]}, the + * parameter {@code values=[18, 3.6]} specifies that element {@code [1,3]} of the sparse + * NdArray has a value of {@code 18}, and element {@code [2,4]} of the NdArray has a value of + * {@code 3.6}. + * @param defaultValue Scalar value to set for indices not specified in {@link #getIndices()} + * @param dimensions the dimensional space for the dense object represented by this sparse array, + */ + protected SparseNdArray( + Class type, LongNdArray indices, U values, T defaultValue, DimensionalSpace dimensions) { + super(indices, values, defaultValue, dimensions); + this.type = type; + } + + /** + * Creates a SparseNdArray with a default value of null. + * + * @param indices A 2-D LongNdArray of shape {@code [N, ndims]}, that specifies the indices of the + * elements in the sparse array that contain non-default values (elements are zero-indexed). + * For example, {@code indices=[[1,3], [2,4]]} specifies that the elements with indexes of + * {@code [1,3]} and {@code [2,4]} have non-default values. + * @param values A 1-D NdArray of Boolean type and shape {@code [N]}, which supplies the values + * for each element in indices. For example, given {@code indices=[[1,3], [2,4]]}, the + * parameter {@code values=[18, 3.6]} specifies that element {@code [1,3]} of the sparse + * NdArray has a value of {@code 18}, and element {@code [2,4]} of the NdArray has a value of + * {@code 3.6}. + * @param dimensions the dimensional space for the dense object represented by this sparse array, + */ + SparseNdArray(Class type, LongNdArray indices, U values, DimensionalSpace dimensions) { + this(type, indices, values, null, dimensions); + } + + /** + * Creates a SparseNdArray + * + * @param dataBuffer a dense dataBuffer used to create the spars array + * @param dimensions the dimensional space for the dense object represented by this sparse array, + */ + SparseNdArray(Class type, DataBuffer dataBuffer, DimensionalSpace dimensions) { + this(type, dataBuffer, null, dimensions); + } + + /** + * Creates a SparseNdArray + * + * @param dataBuffer a dense dataBuffer used to create the spars array + * @param defaultValue Scalar value to set for indices not specified in {@link #getIndices()} + * @param dimensions the dimensional space for the dense object represented by this sparse array, + */ + SparseNdArray( + Class type, DataBuffer dataBuffer, T defaultValue, DimensionalSpace dimensions) { + super(defaultValue, dimensions); + this.type = type; + // use write to set up the indices and values + copyFrom(dataBuffer); + } + + /** + * Creates a zero-filled SparseNdArray + * + * @param dimensions the dimensional space for the dense object represented by this sparse array, + */ + SparseNdArray(Class type, DimensionalSpace dimensions) { + this(type, (T) null, dimensions); + } + + /** + * Creates a zero-filled SparseNdArray + * + * @param defaultValue Scalar value to set for indices not specified in {@link #getIndices()} + * @param dimensions the dimensional space for the dense object represented by this sparse array, + */ + SparseNdArray(Class type, T defaultValue, DimensionalSpace dimensions) { + super(defaultValue, dimensions); + this.type = type; + } + + /** + * Creates a new SparseNdArray + * + * @param indices A 2-D LongNdArray of shape {@code [N, ndims]}, that specifies the indices of the + * elements in the sparse array that contain non-default values (elements are zero-indexed). + * For example, {@code indices=[[1,3], [2,4]]} specifies that the elements with indexes of + * {@code [1,3]} and {@code [2,4]} have non-default values. + * @param values A 1-D NdArray of any type and shape {@code [N]}, which supplies the values for + * each element in indices. For example, given {@code indices=[[1,3], [2,4]]}, the parameter + * {@code values=[18, 3.6]} specifies that element {@code [1,3]} of the sparse NdArray has a + * value of {@code 18}, and element {@code [2,4]} of the NdArray has a value of {@code 3.6}. + * @param dimensions the dimensional space for the dense object represented by this sparse array. + * @return the new Sparse Array + */ + public static > SparseNdArray create( + Class type, LongNdArray indices, U values, DimensionalSpace dimensions) { + return new SparseNdArray<>(type, indices, values, dimensions); + } + + /** + * Creates a new SparseNdArray + * + * @param indices A 2-D LongNdArray of shape {@code [N, ndims]}, that specifies the indices of the + * elements in the sparse array that contain non-default values (elements are zero-indexed). + * For example, {@code indices=[[1,3], [2,4]]} specifies that the elements with indexes of + * {@code [1,3]} and {@code [2,4]} have non-default values. + * @param values A 1-D NdArray of any type and shape {@code [N]}, which supplies the values for + * each element in indices. For example, given {@code indices=[[1,3], [2,4]]}, the parameter + * {@code values=[18, 3.6]} specifies that element {@code [1,3]} of the sparse NdArray has a + * value of {@code 18}, and element {@code [2,4]} of the NdArray has a value of {@code 3.6}. + * @param defaultValue Scalar value to set for indices not specified in {@link #getIndices()} + * @param dimensions the dimensional space for the dense object represented by this sparse array. + * @return the new Sparse Array + */ + public static > SparseNdArray create( + Class type, LongNdArray indices, U values, T defaultValue, DimensionalSpace dimensions) { + return new SparseNdArray<>(type, indices, values, defaultValue, dimensions); + } + + /** + * Creates a new SparseNdArray from a data buffer + * + * @param dataBuffer the databuffer containing the dense array + * @param dimensions the dimensional space for the sparse array + * @return the new Sparse Array + */ + public static > SparseNdArray create( + Class type, DataBuffer dataBuffer, DimensionalSpace dimensions) { + return new SparseNdArray<>(type, dataBuffer, dimensions); + } + + /** + * Creates a new SparseNdArray from a data buffer + * + * @param dataBuffer the databuffer containing the dense array + * @param defaultValue Scalar value to set for indices not specified in {@link #getIndices()} + * @param dimensions the dimensional space for the sparse array + * @return the new Sparse Array + */ + public static > SparseNdArray create( + Class type, DataBuffer dataBuffer, T defaultValue, DimensionalSpace dimensions) { + return new SparseNdArray<>(type, dataBuffer, defaultValue, dimensions); + } + + /** + * Creates a new empty SparseNdArray from a data buffer + * + * @param dimensions the dimensions array + * @return the new Sparse Array + */ + public static > SparseNdArray create( + Class type, DimensionalSpace dimensions) { + return new SparseNdArray<>(type, dimensions); + } + + /** + * Creates a new empty SparseNdArray from a data buffer + * + * @param dimensions the dimensions array + * @param defaultValue Scalar value to set for indices not specified in {@link #getIndices()} + * @return the new Sparse Array + */ + public static > SparseNdArray create( + Class type, T defaultValue, DimensionalSpace dimensions) { + return new SparseNdArray<>(type, defaultValue, dimensions); + } + + /** + * Creates a new empty SparseNdArray from a float data buffer + * + * @param buffer the data buffer + * @param shape the shape of the sparse array. + * @return the new Sparse Array + */ + public static > SparseNdArray create( + Class type, DataBuffer buffer, Shape shape) { + return new SparseNdArray<>(type, buffer, DimensionalSpace.create(shape)); + } + + /** + * Creates a new empty SparseNdArray from a float data buffer + * + * @param buffer the data buffer + * @param defaultValue Scalar value to set for indices not specified in {@link #getIndices()} + * @param shape the shape of the sparse array. + * @return the new Sparse Array + */ + public static > SparseNdArray create( + Class type, DataBuffer buffer, T defaultValue, Shape shape) { + return new SparseNdArray<>(type, buffer, defaultValue, DimensionalSpace.create(shape)); + } + + /** + * Creates a new SparseNdArray from a NdArray + * + * @param src the NdArray + * @return the new Sparse Array + */ + public static > SparseNdArray create(Class type, U src) { + DataBuffer buffer = DataBuffers.ofObjects(type, src.size()); + src.copyTo(buffer); + return new SparseNdArray<>(type, buffer, DimensionalSpace.create(src.shape())); + } + /** + * Creates a new SparseNdArray from a NdArray + * + * @param defaultValue Scalar value to set for indices not specified in {@link #getIndices()} + * @param src the NdArray + * @return the new Sparse Array + */ + public static > SparseNdArray create( + Class type, U src, T defaultValue) { + DataBuffer buffer = DataBuffers.ofObjects(type, src.size()); + src.copyTo(buffer); + return new SparseNdArray<>(type, buffer, defaultValue, DimensionalSpace.create(src.shape())); + } + + /** {@inheritDoc} */ + @Override + @SuppressWarnings("unchecked") + public U createDefaultArray() { + return getDefaultValue() == null + ? (U) NdArrays.ofObjects(type, Shape.scalar()) + : (U) NdArrays.scalarOfObject(getDefaultValue()); + } + + /** + * Creates a NdArray of the specified shape + * + * @param shape the shape of the dense array. + * @return a NdArray of the specified shape + */ + @SuppressWarnings("unchecked") + public U createValues(Shape shape) { + return (U) NdArrays.ofObjects(type, shape); + } + + /** {@inheritDoc} */ + @Override + @SuppressWarnings("unchecked") + public U slice(long position, DimensionalSpace sliceDimensions) { + return (U) new ObjectSparseSlice<>(this, position, sliceDimensions); + } + + /** {@inheritDoc} */ + @Override + public NdArray copyTo(DataBuffer dst) { + // set the values in buf to the default, then overwrite with indices/values + @SuppressWarnings("unchecked") + T[] defaults = (T[]) Array.newInstance(type, (int) dst.size()); + Arrays.fill(defaults, getDefaultValue()); + dst.write(defaults); + + AtomicInteger i = new AtomicInteger(); + getIndices() + .elements(0) + .forEachIndexed( + (idx, l) -> { + long[] coordinates = getIndicesCoordinates(l); + T value = getValues().getObject(i.getAndIncrement()); + dst.setObject(value, dimensions.positionOf(coordinates)); + }); + return this; + } + + /** {@inheritDoc} */ + @Override + @SuppressWarnings({ + "unchecked", + }) + public NdArray copyFrom(DataBuffer src) { + List indices = new ArrayList<>(); + List values = new ArrayList<>(); + + for (long i = 0; i < src.size(); i++) { + if (!Objects.equals(src.getObject(i), getDefaultValue())) { + indices.add(toCoordinates(dimensions, i)); + values.add(src.getObject(i)); + } + } + long[][] indicesArray = new long[indices.size()][]; + // unchecked cast, suppressed. + T[] valuesArray = (T[]) Array.newInstance(type, values.size()); + for (int i = 0; i < indices.size(); i++) { + indicesArray[i] = indices.get(i); + valuesArray[i] = values.get(i); + } + + setIndices(StdArrays.ndCopyOf(indicesArray)); + + // unchecked cast, suppressed. + setValues((U) NdArrays.vectorOfObjects(valuesArray)); + return this; + } + /** + * Converts the sparse array to a dense array + * + * @return the dense array + */ + @SuppressWarnings("unchecked") + public U toDense() { + DataBuffer dataBuffer = DataBuffers.ofObjects(type, shape().size()); + copyTo(dataBuffer); + // unchecked cast, suppressed. + return (U) NdArrays.wrap(shape(), dataBuffer); + } + + /** + * Populates this sparse array from a dense array + * + * @param src the dense array + * @return this sparse array + */ + public NdArray fromDense(NdArray src) { + DataBuffer buffer = DataBuffers.ofObjects(type, src.size()); + src.copyTo(buffer); + copyFrom(buffer); + return this; + } + + /** + * Gets the class type for this sparse array + * + * @return the class type for this sparse array. + */ + public Class getType() { + return type; + } + + /** + * A String showing the type, default value, number of elements and + * the dense shape of this sparse ndarray. + * @return A string containing the type, default value, number of elements and shape. + */ + @Override + public String toString() { + long numElements = getValues() == null ? 0 : getValues().size(); + String strDefault; + T defaultVal = getDefaultValue(); + if (defaultVal == null) { + strDefault = ""; + } else if (defaultVal instanceof Number) { + strDefault = defaultVal.toString(); + } else { + strDefault = "'" + defaultVal + "'"; + } + return this.getClass().getSimpleName() + "(type="+type.getSimpleName()+", defaultValue=" + strDefault + + ", numElements=" + numElements + ", shape=" + this.shape() + ")"; + } +} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/sparse/Validator.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/sparse/Validator.java new file mode 100644 index 0000000..2fa7736 --- /dev/null +++ b/ndarray/src/main/java/org/tensorflow/ndarray/impl/sparse/Validator.java @@ -0,0 +1,46 @@ +/* Copyright 2021 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +=======================================================================*/ +package org.tensorflow.ndarray.impl.sparse; + +import org.tensorflow.ndarray.IllegalRankException; +import org.tensorflow.ndarray.Shape; +import org.tensorflow.ndarray.buffer.DataBuffer; +import org.tensorflow.ndarray.impl.dimension.DimensionalSpace; + +final class Validator extends org.tensorflow.ndarray.impl.Validator { + + private Validator() {} + + static void coordinates(DimensionalSpace dimensions, long[] coords, boolean isValue) { + if (coords.length > dimensions.numDimensions()) { + throw new IndexOutOfBoundsException(); + } + if (isValue && coords.length != dimensions.numDimensions()) { + throw new IllegalRankException("Not a scalar value"); + } + } + + static void denseShape(DataBuffer buffer, Shape shape) { + if (shape == null) { + throw new IllegalArgumentException("Shape cannot be null"); + } + if (shape.hasUnknownDimension()) { + throw new IllegalArgumentException("Sparse arrays cannot have unknown dimension(s)"); + } + if (buffer.size() < shape.size()) { + throw new IllegalArgumentException("Buffer size is smaller than the shape size"); + } + } +} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/sparse/slice/BooleanSparseSlice.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/sparse/slice/BooleanSparseSlice.java new file mode 100644 index 0000000..945ccdc --- /dev/null +++ b/ndarray/src/main/java/org/tensorflow/ndarray/impl/sparse/slice/BooleanSparseSlice.java @@ -0,0 +1,139 @@ +/* Copyright 2021 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +=======================================================================*/ +package org.tensorflow.ndarray.impl.sparse.slice; + +import org.tensorflow.ndarray.BooleanNdArray; +import org.tensorflow.ndarray.NdArray; +import org.tensorflow.ndarray.NdArrays; +import org.tensorflow.ndarray.buffer.BooleanDataBuffer; +import org.tensorflow.ndarray.buffer.DataBuffer; +import org.tensorflow.ndarray.buffer.DataBuffers; +import org.tensorflow.ndarray.impl.dimension.DimensionalSpace; +import org.tensorflow.ndarray.impl.dimension.RelativeDimensionalSpace; +import org.tensorflow.ndarray.impl.sparse.AbstractSparseNdArray; +import org.tensorflow.ndarray.index.Index; + +import java.nio.ReadOnlyBufferException; +import java.util.concurrent.atomic.AtomicInteger; + +public class BooleanSparseSlice extends SparseSlice + implements BooleanNdArray { + + /** + * Creates a BooleanSparseSlice + * + * @param source the source Sparse Array that this object slices. + * @param sourcePosition the relative source position into the source + * @param dimensions the dimensional space for the window + */ + public BooleanSparseSlice( + AbstractSparseNdArray source, + long sourcePosition, + DimensionalSpace dimensions) { + super(source, sourcePosition, dimensions); + } + + /** {@inheritDoc} */ + @Override + public BooleanNdArray toDense() { + BooleanDataBuffer dataBuffer = DataBuffers.ofBooleans(shape().size()); + copyTo(dataBuffer); + return NdArrays.wrap(shape(), dataBuffer); + } + + @Override + public boolean getBoolean(long... coordinates) { + return getObject(coordinates); + } + + @Override + public BooleanNdArray setBoolean(boolean value, long... coordinates) { + throw new ReadOnlyBufferException(); + } + + @Override + public BooleanNdArray setObject(Boolean value, long... coordinates) { + throw new ReadOnlyBufferException(); + } + + @Override + public BooleanNdArray set(NdArray src, long... coordinates) { + throw new ReadOnlyBufferException(); + } + + /** {@inheritDoc} */ + @Override + public BooleanNdArray copyTo(DataBuffer dst) { + // zero out buf. + Boolean[] defaults = new Boolean[(int) shape().size()]; + dst.write(defaults); + + AtomicInteger i = new AtomicInteger(); + getIndices() + .elements(0) + .forEachIndexed( + (idx, l) -> { + long[] coordinates = getIndicesCoordinates(l); + boolean value = getValues().getBoolean(i.getAndIncrement()); + dst.setObject(value, dimensions.positionOf(coordinates)); + }); + return this; + } + + @Override + public BooleanNdArray copyTo(BooleanDataBuffer dst) { + return copyTo((DataBuffer) dst); + } + + @Override + public BooleanNdArray copyFrom(DataBuffer src) { + throw new ReadOnlyBufferException(); + } + + @Override + public BooleanNdArray copyFrom(BooleanDataBuffer src) { + throw new ReadOnlyBufferException(); + } + + @Override + public BooleanNdArray slice(Index... indices) { + if (indices == null) { + throw new IllegalArgumentException("Slicing requires at least one index"); + } + RelativeDimensionalSpace sliceDimensions = dimensions().mapTo(indices); + return slice(sliceDimensions.position(), sliceDimensions); + } + + /** {@inheritDoc} */ + @Override + public BooleanNdArray slice(long position, DimensionalSpace sliceDimensions) { + return new BooleanSparseSlice(this.source, position + sourcePosition, sliceDimensions); + } + + @Override + public BooleanNdArray get(long... coordinates) { + return (BooleanNdArray) super.get(coordinates); + } + + @Override + public BooleanNdArray copyTo(NdArray dst) { + return (BooleanNdArray) super.copyTo(dst); + } + + @Override + public BooleanNdArray createDefaultArray() { + return source.getDefaultArray(); + } +} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/sparse/slice/ByteSparseSlice.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/sparse/slice/ByteSparseSlice.java new file mode 100644 index 0000000..35e33d9 --- /dev/null +++ b/ndarray/src/main/java/org/tensorflow/ndarray/impl/sparse/slice/ByteSparseSlice.java @@ -0,0 +1,138 @@ +/* Copyright 2021 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +=======================================================================*/ +package org.tensorflow.ndarray.impl.sparse.slice; + +import org.tensorflow.ndarray.ByteNdArray; +import org.tensorflow.ndarray.NdArray; +import org.tensorflow.ndarray.NdArrays; +import org.tensorflow.ndarray.buffer.ByteDataBuffer; +import org.tensorflow.ndarray.buffer.DataBuffer; +import org.tensorflow.ndarray.buffer.DataBuffers; +import org.tensorflow.ndarray.impl.dimension.DimensionalSpace; +import org.tensorflow.ndarray.impl.dimension.RelativeDimensionalSpace; +import org.tensorflow.ndarray.impl.sparse.AbstractSparseNdArray; +import org.tensorflow.ndarray.index.Index; + +import java.nio.ReadOnlyBufferException; +import java.util.concurrent.atomic.AtomicInteger; + +public class ByteSparseSlice extends SparseSlice implements ByteNdArray { + + /** + * Creates a ByteSparseSlice + * + * @param source the source Sparse Array that this object slices. + * @param sourcePosition the relative source position into the source + * @param dimensions the dimensional space for the window + */ + public ByteSparseSlice( + AbstractSparseNdArray source, + long sourcePosition, + DimensionalSpace dimensions) { + super(source, sourcePosition, dimensions); + } + + /** {@inheritDoc} */ + @Override + public ByteNdArray toDense() { + ByteDataBuffer dataBuffer = DataBuffers.ofBytes(shape().size()); + copyTo(dataBuffer); + return NdArrays.wrap(shape(), dataBuffer); + } + + @Override + public byte getByte(long... coordinates) { + return getObject(coordinates); + } + + @Override + public ByteNdArray setByte(byte value, long... coordinates) { + throw new ReadOnlyBufferException(); + } + + @Override + public ByteNdArray setObject(Byte value, long... coordinates) { + throw new ReadOnlyBufferException(); + } + + @Override + public ByteNdArray set(NdArray src, long... coordinates) { + throw new ReadOnlyBufferException(); + } + + /** {@inheritDoc} */ + @Override + public ByteNdArray copyTo(DataBuffer dst) { + // zero out buf. + Byte[] defaults = new Byte[(int) shape().size()]; + dst.write(defaults); + + AtomicInteger i = new AtomicInteger(); + getIndices() + .elements(0) + .forEachIndexed( + (idx, l) -> { + long[] coordinates = getIndicesCoordinates(l); + byte value = getValues().getByte(i.getAndIncrement()); + dst.setObject(value, dimensions.positionOf(coordinates)); + }); + return this; + } + + @Override + public ByteNdArray copyTo(ByteDataBuffer dst) { + return this.copyTo((DataBuffer) dst); + } + + @Override + public ByteNdArray copyFrom(DataBuffer src) { + throw new ReadOnlyBufferException(); + } + + @Override + public ByteNdArray copyFrom(ByteDataBuffer src) { + throw new ReadOnlyBufferException(); + } + + @Override + public ByteNdArray slice(Index... indices) { + if (indices == null) { + throw new IllegalArgumentException("Slicing requires at least one index"); + } + RelativeDimensionalSpace sliceDimensions = dimensions().mapTo(indices); + return slice(sliceDimensions.position(), sliceDimensions); + } + + /** {@inheritDoc} */ + @Override + public ByteNdArray slice(long position, DimensionalSpace sliceDimensions) { + return new ByteSparseSlice(this.source, position + sourcePosition, sliceDimensions); + } + + @Override + public ByteNdArray get(long... coordinates) { + return (ByteNdArray) super.get(coordinates); + } + + @Override + public ByteNdArray copyTo(NdArray dst) { + return (ByteNdArray) super.copyTo(dst); + } + + @Override + public ByteNdArray createDefaultArray() { + return source.getDefaultArray(); + } +} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/sparse/slice/DoubleSparseSlice.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/sparse/slice/DoubleSparseSlice.java new file mode 100644 index 0000000..3f66308 --- /dev/null +++ b/ndarray/src/main/java/org/tensorflow/ndarray/impl/sparse/slice/DoubleSparseSlice.java @@ -0,0 +1,140 @@ +/* Copyright 2021 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +=======================================================================*/ +package org.tensorflow.ndarray.impl.sparse.slice; + +import org.tensorflow.ndarray.DoubleNdArray; +import org.tensorflow.ndarray.NdArray; +import org.tensorflow.ndarray.NdArrays; +import org.tensorflow.ndarray.buffer.DataBuffer; +import org.tensorflow.ndarray.buffer.DataBuffers; +import org.tensorflow.ndarray.buffer.DoubleDataBuffer; +import org.tensorflow.ndarray.impl.dimension.DimensionalSpace; +import org.tensorflow.ndarray.impl.dimension.RelativeDimensionalSpace; +import org.tensorflow.ndarray.impl.sparse.AbstractSparseNdArray; +import org.tensorflow.ndarray.index.Index; + +import java.nio.ReadOnlyBufferException; +import java.util.Arrays; +import java.util.concurrent.atomic.AtomicInteger; + +public class DoubleSparseSlice extends SparseSlice implements DoubleNdArray { + + /** + * Creates a DoubleSparseSlice + * + * @param source the source Sparse Array that this object slices. + * @param sourcePosition the relative source position into the source + * @param dimensions the dimensional space for the window + */ + public DoubleSparseSlice( + AbstractSparseNdArray source, + long sourcePosition, + DimensionalSpace dimensions) { + super(source, sourcePosition, dimensions); + } + + /** {@inheritDoc} */ + @Override + public DoubleNdArray toDense() { + DoubleDataBuffer dataBuffer = DataBuffers.ofDoubles(shape().size()); + copyTo(dataBuffer); + return NdArrays.wrap(shape(), dataBuffer); + } + + @Override + public double getDouble(long... coordinates) { + return getObject(coordinates); + } + + @Override + public DoubleNdArray setDouble(double value, long... coordinates) { + throw new ReadOnlyBufferException(); + } + + @Override + public DoubleNdArray setObject(Double value, long... coordinates) { + throw new ReadOnlyBufferException(); + } + + @Override + public DoubleNdArray set(NdArray src, long... coordinates) { + throw new ReadOnlyBufferException(); + } + + /** {@inheritDoc} */ + @Override + public DoubleNdArray copyTo(DataBuffer dst) { + // set the values in buf to the default, then overwrite with indices/values + Double[] defaults = new Double[(int) shape().size()]; + Arrays.fill(defaults, getDefaultValue()); + dst.write(defaults); + + AtomicInteger i = new AtomicInteger(); + getIndices() + .elements(0) + .forEachIndexed( + (idx, l) -> { + long[] coordinates = getIndicesCoordinates(l); + double value = getValues().getDouble(i.getAndIncrement()); + dst.setObject(value, dimensions.positionOf(coordinates)); + }); + return this; + } + + @Override + public DoubleNdArray copyTo(DoubleDataBuffer dst) { + return this.copyTo((DataBuffer) dst); + } + + @Override + public DoubleNdArray copyFrom(DataBuffer src) { + throw new ReadOnlyBufferException(); + } + + @Override + public DoubleNdArray copyFrom(DoubleDataBuffer src) { + throw new ReadOnlyBufferException(); + } + + @Override + public DoubleNdArray slice(Index... indices) { + if (indices == null) { + throw new IllegalArgumentException("Slicing requires at least one index"); + } + RelativeDimensionalSpace sliceDimensions = dimensions().mapTo(indices); + return slice(sliceDimensions.position(), sliceDimensions); + } + + /** {@inheritDoc} */ + @Override + public DoubleNdArray slice(long position, DimensionalSpace sliceDimensions) { + return new DoubleSparseSlice(this.source, position + sourcePosition, sliceDimensions); + } + + @Override + public DoubleNdArray get(long... coordinates) { + return (DoubleNdArray) super.get(coordinates); + } + + @Override + public DoubleNdArray copyTo(NdArray dst) { + return (DoubleNdArray) super.copyTo(dst); + } + + @Override + public DoubleNdArray createDefaultArray() { + return source.getDefaultArray(); + } +} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/sparse/slice/FloatSparseSlice.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/sparse/slice/FloatSparseSlice.java new file mode 100644 index 0000000..8e2204d --- /dev/null +++ b/ndarray/src/main/java/org/tensorflow/ndarray/impl/sparse/slice/FloatSparseSlice.java @@ -0,0 +1,140 @@ +/* Copyright 2021 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +=======================================================================*/ +package org.tensorflow.ndarray.impl.sparse.slice; + +import org.tensorflow.ndarray.FloatNdArray; +import org.tensorflow.ndarray.NdArray; +import org.tensorflow.ndarray.NdArrays; +import org.tensorflow.ndarray.buffer.DataBuffer; +import org.tensorflow.ndarray.buffer.DataBuffers; +import org.tensorflow.ndarray.buffer.FloatDataBuffer; +import org.tensorflow.ndarray.impl.dimension.DimensionalSpace; +import org.tensorflow.ndarray.impl.dimension.RelativeDimensionalSpace; +import org.tensorflow.ndarray.impl.sparse.AbstractSparseNdArray; +import org.tensorflow.ndarray.index.Index; + +import java.nio.ReadOnlyBufferException; +import java.util.Arrays; +import java.util.concurrent.atomic.AtomicInteger; + +public class FloatSparseSlice extends SparseSlice implements FloatNdArray { + + /** + * Creates a FloatSparseSlice + * + * @param source the source Sparse Array that this object slices. + * @param sourcePosition the relative source position into the source + * @param dimensions the dimensional space for the window + */ + public FloatSparseSlice( + AbstractSparseNdArray source, + long sourcePosition, + DimensionalSpace dimensions) { + super(source, sourcePosition, dimensions); + } + + /** {@inheritDoc} */ + @Override + public FloatNdArray toDense() { + FloatDataBuffer dataBuffer = DataBuffers.ofFloats(shape().size()); + copyTo(dataBuffer); + return NdArrays.wrap(shape(), dataBuffer); + } + + @Override + public float getFloat(long... coordinates) { + return getObject(coordinates); + } + + @Override + public FloatNdArray setFloat(float value, long... coordinates) { + throw new ReadOnlyBufferException(); + } + + @Override + public FloatNdArray setObject(Float value, long... coordinates) { + throw new ReadOnlyBufferException(); + } + + @Override + public FloatNdArray set(NdArray src, long... coordinates) { + throw new ReadOnlyBufferException(); + } + + /** {@inheritDoc} */ + @Override + public FloatNdArray copyTo(DataBuffer dst) { + // set the values in buf to the default, then overwrite with indices/values + Float[] defaults = new Float[(int) shape().size()]; + Arrays.fill(defaults, getDefaultValue()); + dst.write(defaults); + + AtomicInteger i = new AtomicInteger(); + getIndices() + .elements(0) + .forEachIndexed( + (idx, l) -> { + long[] coordinates = getIndicesCoordinates(l); + float value = getValues().getFloat(i.getAndIncrement()); + dst.setObject(value, dimensions.positionOf(coordinates)); + }); + return this; + } + + @Override + public FloatNdArray copyTo(FloatDataBuffer dst) { + return this.copyTo((DataBuffer) dst); + } + + @Override + public FloatNdArray copyFrom(DataBuffer src) { + throw new ReadOnlyBufferException(); + } + + @Override + public FloatNdArray copyFrom(FloatDataBuffer src) { + throw new ReadOnlyBufferException(); + } + + @Override + public FloatNdArray slice(Index... indices) { + if (indices == null) { + throw new IllegalArgumentException("Slicing requires at least one index"); + } + RelativeDimensionalSpace sliceDimensions = dimensions().mapTo(indices); + return slice(sliceDimensions.position(), sliceDimensions); + } + + /** {@inheritDoc} */ + @Override + public FloatNdArray slice(long position, DimensionalSpace sliceDimensions) { + return new FloatSparseSlice(this.source, position + sourcePosition, sliceDimensions); + } + + @Override + public FloatNdArray get(long... coordinates) { + return (FloatNdArray) super.get(coordinates); + } + + @Override + public FloatNdArray copyTo(NdArray dst) { + return (FloatNdArray) super.copyTo(dst); + } + + @Override + public FloatNdArray createDefaultArray() { + return source.getDefaultArray(); + } +} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/sparse/slice/IntSparseSlice.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/sparse/slice/IntSparseSlice.java new file mode 100644 index 0000000..988551b --- /dev/null +++ b/ndarray/src/main/java/org/tensorflow/ndarray/impl/sparse/slice/IntSparseSlice.java @@ -0,0 +1,140 @@ +/* Copyright 2021 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +=======================================================================*/ +package org.tensorflow.ndarray.impl.sparse.slice; + +import org.tensorflow.ndarray.IntNdArray; +import org.tensorflow.ndarray.NdArray; +import org.tensorflow.ndarray.NdArrays; +import org.tensorflow.ndarray.buffer.DataBuffer; +import org.tensorflow.ndarray.buffer.DataBuffers; +import org.tensorflow.ndarray.buffer.IntDataBuffer; +import org.tensorflow.ndarray.impl.dimension.DimensionalSpace; +import org.tensorflow.ndarray.impl.dimension.RelativeDimensionalSpace; +import org.tensorflow.ndarray.impl.sparse.AbstractSparseNdArray; +import org.tensorflow.ndarray.index.Index; + +import java.nio.ReadOnlyBufferException; +import java.util.Arrays; +import java.util.concurrent.atomic.AtomicInteger; + +public class IntSparseSlice extends SparseSlice implements IntNdArray { + + /** + * Creates a IntSparseSlice + * + * @param source the source Sparse Array that this object slices. + * @param sourcePosition the relative source position into the source + * @param dimensions the dimensional space for the window + */ + public IntSparseSlice( + AbstractSparseNdArray source, + long sourcePosition, + DimensionalSpace dimensions) { + super(source, sourcePosition, dimensions); + } + + /** {@inheritDoc} */ + @Override + public IntNdArray toDense() { + IntDataBuffer dataBuffer = DataBuffers.ofInts(shape().size()); + copyTo(dataBuffer); + return NdArrays.wrap(shape(), dataBuffer); + } + + @Override + public int getInt(long... coordinates) { + return getObject(coordinates); + } + + @Override + public IntNdArray setInt(int value, long... coordinates) { + throw new ReadOnlyBufferException(); + } + + @Override + public IntNdArray setObject(Integer value, long... coordinates) { + throw new ReadOnlyBufferException(); + } + + @Override + public IntNdArray set(NdArray src, long... coordinates) { + throw new ReadOnlyBufferException(); + } + + /** {@inheritDoc} */ + @Override + public IntNdArray copyTo(DataBuffer dst) { + // set the values in buf to the default, then overwrite with indices/values + Integer[] defaults = new Integer[(int) shape().size()]; + Arrays.fill(defaults, getDefaultValue()); + dst.write(defaults); + + AtomicInteger i = new AtomicInteger(); + getIndices() + .elements(0) + .forEachIndexed( + (idx, l) -> { + long[] coordinates = getIndicesCoordinates(l); + int value = getValues().getInt(i.getAndIncrement()); + dst.setObject(value, dimensions.positionOf(coordinates)); + }); + return this; + } + + @Override + public IntNdArray copyTo(IntDataBuffer dst) { + return this.copyTo((DataBuffer) dst); + } + + @Override + public IntNdArray copyFrom(DataBuffer src) { + throw new ReadOnlyBufferException(); + } + + @Override + public IntNdArray copyFrom(IntDataBuffer src) { + throw new ReadOnlyBufferException(); + } + + @Override + public IntNdArray slice(Index... indices) { + if (indices == null) { + throw new IllegalArgumentException("Slicing requires at least one index"); + } + RelativeDimensionalSpace sliceDimensions = dimensions().mapTo(indices); + return slice(sliceDimensions.position(), sliceDimensions); + } + + /** {@inheritDoc} */ + @Override + public IntNdArray slice(long position, DimensionalSpace sliceDimensions) { + return new IntSparseSlice(this.source, position + sourcePosition, sliceDimensions); + } + + @Override + public IntNdArray get(long... coordinates) { + return (IntNdArray) super.get(coordinates); + } + + @Override + public IntNdArray copyTo(NdArray dst) { + return (IntNdArray) super.copyTo(dst); + } + + @Override + public IntNdArray createDefaultArray() { + return source.getDefaultArray(); + } +} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/sparse/slice/LongSparseSlice.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/sparse/slice/LongSparseSlice.java new file mode 100644 index 0000000..0916293 --- /dev/null +++ b/ndarray/src/main/java/org/tensorflow/ndarray/impl/sparse/slice/LongSparseSlice.java @@ -0,0 +1,140 @@ +/* Copyright 2021 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +=======================================================================*/ +package org.tensorflow.ndarray.impl.sparse.slice; + +import org.tensorflow.ndarray.LongNdArray; +import org.tensorflow.ndarray.NdArray; +import org.tensorflow.ndarray.NdArrays; +import org.tensorflow.ndarray.buffer.DataBuffer; +import org.tensorflow.ndarray.buffer.DataBuffers; +import org.tensorflow.ndarray.buffer.LongDataBuffer; +import org.tensorflow.ndarray.impl.dimension.DimensionalSpace; +import org.tensorflow.ndarray.impl.dimension.RelativeDimensionalSpace; +import org.tensorflow.ndarray.impl.sparse.AbstractSparseNdArray; +import org.tensorflow.ndarray.index.Index; + +import java.nio.ReadOnlyBufferException; +import java.util.Arrays; +import java.util.concurrent.atomic.AtomicLong; + +public class LongSparseSlice extends SparseSlice implements LongNdArray { + + /** + * Creates a LongSparseSlice + * + * @param source the source Sparse Array that this object slices. + * @param sourcePosition the relative source position into the source + * @param dimensions the dimensional space for the window + */ + public LongSparseSlice( + AbstractSparseNdArray source, + long sourcePosition, + DimensionalSpace dimensions) { + super(source, sourcePosition, dimensions); + } + + /** {@inheritDoc} */ + @Override + public LongNdArray toDense() { + LongDataBuffer dataBuffer = DataBuffers.ofLongs(shape().size()); + copyTo(dataBuffer); + return NdArrays.wrap(shape(), dataBuffer); + } + + @Override + public long getLong(long... coordinates) { + return getObject(coordinates); + } + + @Override + public LongNdArray setLong(long value, long... coordinates) { + throw new ReadOnlyBufferException(); + } + + @Override + public LongNdArray setObject(Long value, long... coordinates) { + throw new ReadOnlyBufferException(); + } + + @Override + public LongNdArray set(NdArray src, long... coordinates) { + throw new ReadOnlyBufferException(); + } + + /** {@inheritDoc} */ + @Override + public LongNdArray copyTo(DataBuffer dst) { + // set the values in buf to the default, then overwrite with indices/values + Long[] defaults = new Long[(int) shape().size()]; + Arrays.fill(defaults, getDefaultValue()); + dst.write(defaults); + + AtomicLong i = new AtomicLong(); + getIndices() + .elements(0) + .forEachIndexed( + (idx, l) -> { + long[] coordinates = getIndicesCoordinates(l); + long value = getValues().getLong(i.getAndIncrement()); + dst.setObject(value, dimensions.positionOf(coordinates)); + }); + return this; + } + + @Override + public LongNdArray copyTo(LongDataBuffer dst) { + return copyTo((DataBuffer) dst); + } + + @Override + public LongNdArray copyFrom(DataBuffer src) { + throw new ReadOnlyBufferException(); + } + + @Override + public LongNdArray copyFrom(LongDataBuffer src) { + throw new ReadOnlyBufferException(); + } + + @Override + public LongNdArray slice(Index... indices) { + if (indices == null) { + throw new IllegalArgumentException("Slicing requires at least one index"); + } + RelativeDimensionalSpace sliceDimensions = dimensions().mapTo(indices); + return slice(sliceDimensions.position(), sliceDimensions); + } + + /** {@inheritDoc} */ + @Override + public LongNdArray slice(long position, DimensionalSpace sliceDimensions) { + return new LongSparseSlice(this.source, position + sourcePosition, sliceDimensions); + } + + @Override + public LongNdArray get(long... coordinates) { + return (LongNdArray) super.get(coordinates); + } + + @Override + public LongNdArray copyTo(NdArray dst) { + return (LongNdArray) super.copyTo(dst); + } + + @Override + public LongNdArray createDefaultArray() { + return source.getDefaultArray(); + } +} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/sparse/slice/ObjectSparseSlice.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/sparse/slice/ObjectSparseSlice.java new file mode 100644 index 0000000..9f62bf8 --- /dev/null +++ b/ndarray/src/main/java/org/tensorflow/ndarray/impl/sparse/slice/ObjectSparseSlice.java @@ -0,0 +1,115 @@ +/* Copyright 2021 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +=======================================================================*/ +package org.tensorflow.ndarray.impl.sparse.slice; + +import org.tensorflow.ndarray.NdArray; +import org.tensorflow.ndarray.NdArrays; +import org.tensorflow.ndarray.buffer.DataBuffer; +import org.tensorflow.ndarray.buffer.DataBuffers; +import org.tensorflow.ndarray.impl.dimension.DimensionalSpace; +import org.tensorflow.ndarray.impl.dimension.RelativeDimensionalSpace; +import org.tensorflow.ndarray.impl.sparse.SparseNdArray; +import org.tensorflow.ndarray.index.Index; + +import java.lang.reflect.Array; +import java.nio.ReadOnlyBufferException; +import java.util.Arrays; +import java.util.concurrent.atomic.AtomicInteger; + +public class ObjectSparseSlice> extends SparseSlice + implements NdArray { + + /** + * Creates a BooleanSparseSlice + * + * @param source the source Sparse Array that this object slices. + * @param sourcePosition the relative source position into the source + * @param dimensions the dimensional space for the window + */ + public ObjectSparseSlice( + SparseNdArray source, long sourcePosition, DimensionalSpace dimensions) { + super(source, sourcePosition, dimensions); + } + + /** {@inheritDoc} */ + @Override + @SuppressWarnings("unchecked") + public U toDense() { + DataBuffer dataBuffer = DataBuffers.ofObjects(getType(), shape().size()); + copyTo(dataBuffer); + // unchecked NdArray to U + return (U) NdArrays.wrap(shape(), dataBuffer); + } + + @Override + public U setObject(T value, long... coordinates) { + throw new ReadOnlyBufferException(); + } + + @Override + public U set(NdArray src, long... coordinates) { + throw new ReadOnlyBufferException(); + } + + /** {@inheritDoc} */ + @Override + @SuppressWarnings("unchecked") + public U copyTo(DataBuffer dst) { + // unchecked Object to T[] + T[] defaults = (T[]) Array.newInstance(getType(), (int) dst.size()); + Arrays.fill(defaults, getDefaultValue()); + dst.write(defaults); + + AtomicInteger i = new AtomicInteger(); + getIndices() + .elements(0) + .forEachIndexed( + (idx, l) -> { + long[] coordinates = getIndicesCoordinates(l); + T value = getValues().getObject(i.getAndIncrement()); + dst.setObject(value, dimensions.positionOf(coordinates)); + }); + // Unchecked cast ObjectSparseSlice to U + return (U) this; + } + + @Override + public U slice(Index... indices) { + if (indices == null) { + throw new IllegalArgumentException("Slicing requires at least one index"); + } + RelativeDimensionalSpace sliceDimensions = dimensions().mapTo(indices); + return slice(sliceDimensions.position(), sliceDimensions); + } + + /** {@inheritDoc} */ + @Override + @SuppressWarnings("unchecked") + public U slice(long position, DimensionalSpace sliceDimensions) { + // unchecked ObjectSparseSlice to U + return (U) + new ObjectSparseSlice<>( + (SparseNdArray) this.source, position + sourcePosition, sliceDimensions); + } + + @Override + public U createDefaultArray() { + return source.getDefaultArray(); + } + + public Class getType() { + return ((SparseNdArray) source).getType(); + } +} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/sparse/slice/ShortSparseSlice.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/sparse/slice/ShortSparseSlice.java new file mode 100644 index 0000000..2da0f8c --- /dev/null +++ b/ndarray/src/main/java/org/tensorflow/ndarray/impl/sparse/slice/ShortSparseSlice.java @@ -0,0 +1,140 @@ +/* Copyright 2021 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +=======================================================================*/ +package org.tensorflow.ndarray.impl.sparse.slice; + +import org.tensorflow.ndarray.NdArray; +import org.tensorflow.ndarray.NdArrays; +import org.tensorflow.ndarray.ShortNdArray; +import org.tensorflow.ndarray.buffer.DataBuffer; +import org.tensorflow.ndarray.buffer.DataBuffers; +import org.tensorflow.ndarray.buffer.ShortDataBuffer; +import org.tensorflow.ndarray.impl.dimension.DimensionalSpace; +import org.tensorflow.ndarray.impl.dimension.RelativeDimensionalSpace; +import org.tensorflow.ndarray.impl.sparse.AbstractSparseNdArray; +import org.tensorflow.ndarray.index.Index; + +import java.nio.ReadOnlyBufferException; +import java.util.Arrays; +import java.util.concurrent.atomic.AtomicLong; + +public class ShortSparseSlice extends SparseSlice implements ShortNdArray { + + /** + * Creates a LongSparseSlice + * + * @param source the source Sparse Array that this object slices. + * @param sourcePosition the relative source position into the source + * @param dimensions the dimensional space for the window + */ + public ShortSparseSlice( + AbstractSparseNdArray source, + long sourcePosition, + DimensionalSpace dimensions) { + super(source, sourcePosition, dimensions); + } + + /** {@inheritDoc} */ + @Override + public ShortNdArray toDense() { + ShortDataBuffer dataBuffer = DataBuffers.ofShorts(shape().size()); + copyTo(dataBuffer); + return NdArrays.wrap(shape(), dataBuffer); + } + + @Override + public short getShort(long... coordinates) { + return getObject(coordinates); + } + + @Override + public ShortNdArray setShort(short value, long... coordinates) { + throw new ReadOnlyBufferException(); + } + + @Override + public ShortNdArray setObject(Short value, long... coordinates) { + throw new ReadOnlyBufferException(); + } + + @Override + public ShortNdArray set(NdArray src, long... coordinates) { + throw new ReadOnlyBufferException(); + } + + /** {@inheritDoc} */ + @Override + public ShortNdArray copyTo(DataBuffer dst) { + // set the values in buf to the default, then overwrite with indices/values + Short[] defaults = new Short[(int) shape().size()]; + Arrays.fill(defaults, getDefaultValue()); + dst.write(defaults); + + AtomicLong i = new AtomicLong(); + getIndices() + .elements(0) + .forEachIndexed( + (idx, l) -> { + long[] coordinates = getIndicesCoordinates(l); + short value = getValues().getShort(i.getAndIncrement()); + dst.setObject(value, dimensions.positionOf(coordinates)); + }); + return this; + } + + @Override + public ShortNdArray copyTo(ShortDataBuffer dst) { + return this.copyTo((DataBuffer) dst); + } + + @Override + public ShortNdArray copyFrom(DataBuffer src) { + throw new ReadOnlyBufferException(); + } + + @Override + public ShortNdArray copyFrom(ShortDataBuffer src) { + throw new ReadOnlyBufferException(); + } + + @Override + public ShortNdArray slice(Index... indices) { + if (indices == null) { + throw new IllegalArgumentException("Slicing requires at least one index"); + } + RelativeDimensionalSpace sliceDimensions = dimensions().mapTo(indices); + return slice(sliceDimensions.position(), sliceDimensions); + } + + /** {@inheritDoc} */ + @Override + public ShortNdArray slice(long position, DimensionalSpace sliceDimensions) { + return new ShortSparseSlice(this.source, position + sourcePosition, sliceDimensions); + } + + @Override + public ShortNdArray get(long... coordinates) { + return (ShortNdArray) super.get(coordinates); + } + + @Override + public ShortNdArray copyTo(NdArray dst) { + return (ShortNdArray) super.copyTo(dst); + } + + @Override + public ShortNdArray createDefaultArray() { + return source.getDefaultArray(); + } +} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/sparse/slice/SparseSlice.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/sparse/slice/SparseSlice.java new file mode 100644 index 0000000..3e5be6f --- /dev/null +++ b/ndarray/src/main/java/org/tensorflow/ndarray/impl/sparse/slice/SparseSlice.java @@ -0,0 +1,145 @@ +/* Copyright 2021 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +=======================================================================*/ +package org.tensorflow.ndarray.impl.sparse.slice; + +import org.tensorflow.ndarray.NdArray; +import org.tensorflow.ndarray.NdArraySequence; +import org.tensorflow.ndarray.Shape; +import org.tensorflow.ndarray.buffer.DataBuffer; +import org.tensorflow.ndarray.impl.dimension.DimensionalSpace; +import org.tensorflow.ndarray.impl.dimension.RelativeDimensionalSpace; +import org.tensorflow.ndarray.impl.sequence.SingleElementSequence; +import org.tensorflow.ndarray.impl.sequence.SlicingElementSequence; +import org.tensorflow.ndarray.impl.sparse.AbstractSparseNdArray; +import org.tensorflow.ndarray.index.Index; + +import java.nio.ReadOnlyBufferException; + +/** + * A sparse window is a view into an AbstractSparseNdArray. It is used internally by the slice + * methods. + * + * @param the type that the array contains + * @param the type of dense NdArray + */ +public abstract class SparseSlice> extends AbstractSparseNdArray { + protected final AbstractSparseNdArray source; + protected final long sourcePosition; + + /** + * Creates a SparseSlice + * + * @param source the source Sparse Array that this object slices. + * @param sourcePosition the relative position into the source array + * @param dimensions the dimensional space for the window + */ + public SparseSlice( + AbstractSparseNdArray source, long sourcePosition, DimensionalSpace dimensions) { + super(source.getDefaultValue(), dimensions); + this.source = source; + this.sourcePosition = sourcePosition; + } + + /** {@inheritDoc} */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + source.hashCode(); + result = prime * result + (int) sourcePosition; + return result; + } + + /** {@inheritDoc} */ + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof SparseSlice)) { + return super.equals(obj); + } + SparseSlice other = (SparseSlice) obj; + if (!source.equals(other.source)) { + return false; + } + if (!shape().equals(other.shape())) { + return false; + } + return sourcePosition == other.sourcePosition; + } + + /** {@inheritDoc} */ + @Override + public T getObject(long... coordinates) { + long position = dimensions().positionOf(coordinates); + long[] sourceCoordinates = toCoordinates(source.dimensions(), sourcePosition + position); + return source.getObject(sourceCoordinates); + } + + /** {@inheritDoc} */ + @Override + public NdArray get(long... coordinates) { + long position = dimensions().positionOf(coordinates); + long[] sourceCoordinates = toCoordinates(source.dimensions(), sourcePosition + position); + return source.get(sourceCoordinates); + } + + /** {@inheritDoc} */ + @Override + public NdArray slice(Index... indices) { + if (indices == null) { + throw new IllegalArgumentException("Slicing requires at least one index"); + } + RelativeDimensionalSpace sliceDimensions = dimensions().mapTo(indices); + return slice(sliceDimensions.position(), sliceDimensions); + } + + /** {@inheritDoc} */ + @Override + public NdArraySequence elements(int dimensionIdx) { + if (dimensionIdx >= shape().numDimensions()) { + throw new IllegalArgumentException( + "Cannot iterate elements in dimension '" + + dimensionIdx + + "' of array with shape " + + shape()); + } + if (rank() == 0 && dimensionIdx < 0) { + return new SingleElementSequence<>(this); + } + DimensionalSpace elemDims = dimensions().from(dimensionIdx + 1); + return new SlicingElementSequence<>(this, dimensionIdx, elemDims); + } + + /** + * Converts the sparse window to a dense NdArray + * + * @return the NdArray + */ + public abstract U toDense(); + + /** {@inheritDoc} */ + @Override + public NdArray copyFrom(DataBuffer src) { + throw new ReadOnlyBufferException(); + } + + /** {@inheritDoc} */ + @Override + public U createValues(Shape shape) { + return source.createValues(shape); + } +} diff --git a/ndarray/src/test/java/module-info.test b/ndarray/src/test/java/module-info.test new file mode 100644 index 0000000..310e500 --- /dev/null +++ b/ndarray/src/test/java/module-info.test @@ -0,0 +1,22 @@ +/* + Copyright 2022 The TensorFlow Authors. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + ======================================================================= + */ +module org.tensorflow.ndarray { + requires java.desktop; // required for java.awt.* + + requires transitive org.junit.jupiter.engine; + requires transitive org.junit.jupiter.api; +} diff --git a/ndarray/src/test/java/org/tensorflow/ndarray/IndexTest.java b/ndarray/src/test/java/org/tensorflow/ndarray/IndexTest.java index 6f92dab..10c0806 100644 --- a/ndarray/src/test/java/org/tensorflow/ndarray/IndexTest.java +++ b/ndarray/src/test/java/org/tensorflow/ndarray/IndexTest.java @@ -18,6 +18,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; import org.junit.jupiter.api.Test; import org.tensorflow.ndarray.index.Indices; @@ -43,6 +44,371 @@ public void testNullConversions(){ assertTrue(Indices.slice(null, null).endMask(), "Passed null for slice end but didn't set end mask"); } + + @Test + public void testIndices(){ + + String[][] indexData = new String[5][4]; + for (int i=0 ; i < 5; i++){ + for (int j=0 ; j < 4; j++) + indexData[i][j] = "("+j+", "+i+")"; + } + + NdArray matrix2d = StdArrays.ndCopyOf(indexData); + assertEquals(2, matrix2d.rank()); + + /* + |(0, 0), (1, 0), (2, 0), (3, 0)| + |(0, 1), (1, 1), (2, 1), (3, 1)| + |(0, 2), (1, 2), (2, 2), (3, 2)| + |(0, 3), (1, 3), (2, 3), (3, 3)| + |(0, 4), (1, 4), (2, 4), (3, 4)| + */ + assertArrayEquals(new String[]{"(0, 0)", "(1, 0)", "(2, 0)", "(3, 0)"}, indexData[0]); + + NdArray same1 = matrix2d.slice(Indices.all()); + String[][] same1j = StdArrays.array2dCopyOf(same1, String.class); + assertEquals(2, same1.rank()); + assertEquals(same1, matrix2d); + assertEquals(matrix2d, StdArrays.ndCopyOf(same1j)); + + NdArray same2 = matrix2d.slice(Indices.ellipsis()); + String[][] same2j = StdArrays.array2dCopyOf(same2, String.class); + assertEquals(2, same2.rank()); + assertEquals(matrix2d, same2); + assertEquals(matrix2d, StdArrays.ndCopyOf(same2j)); + + // All rows, column 1 + NdArray same3 = matrix2d.slice(Indices.all(), Indices.at(1)); + assertEquals(1, same3.rank()); + String[] same3j = StdArrays.array1dCopyOf(same3, String.class); + assertArrayEquals(new String[] { "(1, 0)", "(1, 1)", "(1, 2)", "(1, 3)", "(1, 4)" }, same3j); + + // row 2, all columns + NdArray same4 = matrix2d.slice(Indices.at(2), Indices.all()); + assertEquals(1, same4.rank()); + String[] same4j = StdArrays.array1dCopyOf(same4, String.class); + assertArrayEquals(new String[] {"(0, 2)", "(1, 2)", "(2, 2)", "(3, 2)"}, same4j); + assertEquals(NdArrays.vectorOfObjects("(0, 2)", "(1, 2)", "(2, 2)", "(3, 2)"), same4); + + // row 2, column 1 + NdArray same5 = matrix2d.slice(Indices.at(2), Indices.at(1)); + assertEquals(0, same5.rank()); + assertTrue(same5.shape().isScalar()); + // Don't use an index + String same5j = same5.getObject(); + assertEquals("(1, 2)", same5j); + + // rows 1 to 2, all columns + NdArray same6 = matrix2d.slice(Indices.slice(1,3)); + assertEquals(2, same6.rank()); + String[][] same6j = StdArrays.array2dCopyOf(same6, String.class); + assertArrayEquals( + new String[][] + { + {"(0, 1)", "(1, 1)", "(2, 1)", "(3, 1)"}, + {"(0, 2)", "(1, 2)", "(2, 2)", "(3, 2)"} + }, + same6j + ); + + // Exception in thread "main" java.nio.BufferOverflowException + // all rows, columns 1 to 2 + NdArray same7 = matrix2d.slice(Indices.all(), Indices.slice(1,3)); + assertEquals(2, same7.rank()); + assertEquals(Shape.of(5,2), same7.shape()); + assertEquals(10, same7.size()); + NdArray r7_0 = same7.get(0); + NdArray r7_1 = same7.get(1); + NdArray r7_2 = same7.get(2); + NdArray r7_3 = same7.get(3); + NdArray r7_4 = same7.get(4); + assertEquals(1, r7_0.rank()); + assertEquals(Shape.of(2), r7_0.shape()); + assertEquals(2, r7_0.size()); + // TODO: I get a (0,0) which is not what I expected + // System.out.println(r7_0.getObject()); + // assertEquals("(1,0)", r7_0.getObject()); + assertEquals( "(1, 0)", r7_0.getObject(0)); + assertEquals( "(2, 0)", r7_0.getObject(1)); + assertEquals( "(1, 1)", r7_1.getObject(0)); + assertEquals( "(2, 1)", r7_1.getObject(1)); + assertEquals( "(1, 2)", r7_2.getObject(0)); + assertEquals( "(2, 2)", r7_2.getObject(1)); + assertEquals( "(1, 3)", r7_3.getObject(0)); + assertEquals( "(2, 3)", r7_3.getObject(1)); + assertEquals( "(1, 4)", r7_4.getObject(0)); + assertEquals( "(2, 4)", r7_4.getObject(1)); + String[][] expectedr7 = new String[][] + { + {"(1, 0)", "(2, 0)"}, + {"(1, 1)", "(2, 1)"}, + {"(1, 2)", "(2, 2)"}, + {"(1, 3)", "(2, 3)"}, + {"(1, 4)", "(2, 4)"} + }; + String[][] lArray = new String[5][2]; + StdArrays.copyFrom(same7, lArray); + assertArrayEquals( expectedr7, lArray); + String[][] same7j = StdArrays.array2dCopyOf(same7, String.class); + assertArrayEquals( expectedr7, same7j); + + + // rows 1 to 2, columns 1 to 2 + NdArray same8 = matrix2d.slice(Indices.slice(1,3), Indices.slice(1,3)); + assertEquals(2, same8.rank()); + assertEquals(Shape.of(2,2), same8.shape()); + assertEquals(4, same8.size()); + String[][] same8j = StdArrays.array2dCopyOf(same8, String.class); + // print2D(same8j) + String[][] expected_r8 = new String[][] + { + {"(1, 1)", "(2, 1)"}, + {"(1, 2)", "(2, 2)"} + }; + assertArrayEquals(expected_r8, same8j); + NdArray r8_0 = same8.get(0); + NdArray r8_1 = same8.get(1); + assertEquals(1, r8_0.rank()); + assertEquals(Shape.of(2), r8_0.shape()); + assertEquals(2, r8_0.size()); + assertEquals("(1, 1)", r8_0.getObject(0)); + assertEquals("(2, 1)", r8_0.getObject(1)); + assertEquals("(1, 2)", r8_1.getObject(0)); + assertEquals("(2, 2)", r8_1.getObject(1)); + + // rows 1 to 2, columns 1 to 2 + NdArray same9 = matrix2d.slice(Indices.range(1,3), Indices.range(1,3)); + assertEquals(2, same9.rank()); + assertEquals(Shape.of(2,2), same9.shape()); + assertEquals(4, same9.size()); + String[][] same9j = StdArrays.array2dCopyOf(same9, String.class); + String[][] expected_r9 = new String[][] + { + {"(1, 1)", "(2, 1)"}, + {"(1, 2)", "(2, 2)"} + }; + assertArrayEquals(expected_r9, same9j); + NdArray r9_0 = same9.get(0); + NdArray r9_1 = same9.get(1); + assertEquals(1, r9_0.rank()); + assertEquals(Shape.of(2), r9_0.shape()); + assertEquals(2, r9_0.size()); + assertEquals("(1, 1)", r9_0.getObject(0)); + assertEquals("(2, 1)", r9_0.getObject(1)); + assertEquals("(1, 2)", r9_1.getObject(0)); + assertEquals("(2, 2)", r9_1.getObject(1)); + + // rows 1, 3 and 4, columns 0 to 2 + NdArray same10 = matrix2d.slice(Indices.odd(), Indices.even()); + String[][] same10j = StdArrays.array2dCopyOf(same10, String.class); + assertEquals(2, same10.rank()); + assertEquals(Shape.of(2,2), same10.shape()); + assertEquals(4, same10.size()); + String[][] expected_r10 = new String[][] + { + {"(0, 1)", "(2, 1)"}, + {"(0, 3)", "(2, 3)"} + }; + assertArrayEquals(expected_r10, same10j); + NdArray r10_0 = same10.get(0); + NdArray r10_1 = same10.get(1); + assertEquals(1, r10_0.rank()); + assertEquals(Shape.of(2), r10_0.shape()); + assertEquals(2, r10_0.size()); + assertEquals("(0, 1)", r10_0.getObject(0)); + assertEquals("(2, 1)", r10_0.getObject(1)); + assertEquals("(0, 3)", r10_1.getObject(0)); + assertEquals("(2, 3)", r10_1.getObject(1)); + + // rows 3 and 4, columns 0 and 1. Second value is stride + NdArray same11 = matrix2d.slice(Indices.sliceFrom(3,1), Indices.sliceFrom(2,1)); + String[][] same11j = StdArrays.array2dCopyOf(same11, String.class); + assertEquals(2, same11.rank()); + assertEquals(Shape.of(2,2), same11.shape()); + assertEquals(4, same11.size()); + String[][] expected_r11 = new String[][] + { + {"(2, 3)", "(3, 3)"}, + {"(2, 4)", "(3, 4)"} + }; + assertArrayEquals(expected_r11, same11j); + NdArray r11_0 = same11.get(0); + NdArray r11_1 = same11.get(1); + assertEquals(1, r11_0.rank()); + assertEquals(Shape.of(2), r11_0.shape()); + assertEquals(2, r11_0.size()); + assertEquals("(2, 3)", r11_0.getObject(0)); + assertEquals("(3, 3)", r11_0.getObject(1)); + assertEquals("(2, 4)", r11_1.getObject(0)); + assertEquals("(3, 4)", r11_1.getObject(1)); + + + // rows 0 and 2, columns 0 and 1. Second value is stride. Index non inclusive + NdArray same12 = matrix2d.slice(Indices.sliceTo(3,2), Indices.sliceTo(2,1)); + String[][] same12j = StdArrays.array2dCopyOf(same12, String.class); + assertEquals(2, same12.rank()); + assertEquals(Shape.of(2,2), same12.shape()); + assertEquals(4, same12.size()); + String[][] expected_r12 = new String[][] + { + {"(0, 0)", "(1, 0)"}, + {"(0, 2)", "(1, 2)"} + }; + assertArrayEquals(expected_r12, same12j); + NdArray r12_0 = same12.get(0); + NdArray r12_1 = same12.get(1); + assertEquals(1, r12_0.rank()); + assertEquals(Shape.of(2), r12_0.shape()); + assertEquals(2, r12_0.size()); + assertEquals("(0, 0)", r12_0.getObject(0)); + assertEquals("(1, 0)", r12_0.getObject(1)); + assertEquals("(0, 2)", r12_1.getObject(0)); + assertEquals("(1, 2)", r12_1.getObject(1)); + + // rows 0 and 2, columns 0 and 1. Second value is stride. Index non inclusive + NdArray same13 = matrix2d.slice(Indices.step(2), Indices.step(2)); + String[][] same13j = StdArrays.array2dCopyOf(same13, String.class); + assertEquals(2, same13.rank()); + assertEquals(Shape.of(3,2), same13.shape()); + assertEquals(6, same13.size()); + String[][] expected_r13 = new String[][] + { + {"(0, 0)", "(2, 0)"}, + {"(0, 2)", "(2, 2)"}, + {"(0, 4)", "(2, 4)"} + }; + assertArrayEquals(expected_r13, same13j); + NdArray r13_0 = same13.get(0); + NdArray r13_1 = same13.get(1); + NdArray r13_2 = same13.get(2); + assertEquals(1, r13_0.rank()); + assertEquals(Shape.of(2), r13_0.shape()); + assertEquals(2, r13_0.size()); + assertEquals("(0, 0)", r13_0.getObject(0)); + assertEquals("(2, 0)", r13_0.getObject(1)); + assertEquals("(0, 2)", r13_1.getObject(0)); + assertEquals("(2, 2)", r13_1.getObject(1)); + assertEquals("(0, 4)", r13_2.getObject(0)); + assertEquals("(2, 4)", r13_2.getObject(1)); + + + NdArray same14 = same13.slice(Indices.flip(), Indices.flip()); + String[][] same14j = StdArrays.array2dCopyOf(same14, String.class); + assertEquals(2, same14.rank()); + assertEquals(Shape.of(3,2), same14.shape()); + assertEquals(6, same14.size()); + String[][] expected_r14 = new String[][] + { + {"(2, 4)", "(0, 4)"}, + {"(2, 2)", "(0, 2)"}, + {"(2, 0)", "(0, 0)"} + }; + assertArrayEquals(same14j, expected_r14); + NdArray r14_0 = same14.get(0); + NdArray r14_1 = same14.get(1); + NdArray r14_2 = same14.get(2); + assertEquals(1, r14_0.rank()); + assertEquals(Shape.of(2), r14_0.shape()); + assertEquals(2, r14_0.size()); + assertEquals("(0, 0)", r14_2.getObject(1)); + assertEquals("(2, 0)", r14_2.getObject(0)); + assertEquals("(0, 2)", r14_1.getObject(1)); + assertEquals("(2, 2)", r14_1.getObject(0)); + assertEquals("(0, 4)", r14_0.getObject(1)); + assertEquals("(2, 4)", r14_0.getObject(0)); + + + NdArray same15 = matrix2d.slice(Indices.slice(4,0,-2), Indices.slice(3L,null,-2)); + String[][] same15j = StdArrays.array2dCopyOf(same15, String.class); + assertEquals(2, same15.rank()); + assertEquals(Shape.of(2,2), same15.shape()); + assertEquals(4,same15.size()); + String[][] expected_r15 = new String[][] + { + {"(3, 4)", "(1, 4)"}, + {"(3, 2)", "(1, 2)"}, + }; + assertArrayEquals(expected_r15, same15j); + NdArray r15_0 = same15.get(0); + NdArray r15_1 = same15.get(1); + assertEquals(1, r15_0.rank()); + assertEquals(Shape.of(2), r15_0.shape()); + assertEquals(2, r15_0.size()); + assertEquals("(3, 4)", r15_0.getObject(0)); + assertEquals("(1, 4)", r15_0.getObject(1)); + assertEquals("(3, 2)", r15_1.getObject(0)); + assertEquals("(1, 2)", r15_1.getObject(1)); + + NdArray same16 = matrix2d.slice(Indices.seq(4,2), Indices.seq(3,1)); + String[][] same16j = StdArrays.array2dCopyOf(same16, String.class); + assertEquals(2, same16.rank()); + assertEquals(Shape.of(2,2), same16.shape()); + assertEquals(4, same16.size()); + String[][] expected_r16 = new String[][] + { + {"(3, 4)", "(1, 4)"}, + {"(3, 2)", "(1, 2)"} + }; + assertArrayEquals(expected_r16, same16j); + NdArray r16_0 = same16.get(0); + NdArray r16_1 = same16.get(1); + assertEquals(1, r16_0.rank()); + assertEquals(Shape.of(2), r16_0.shape()); + assertEquals(2, r16_0.size()); + assertEquals("(3, 4)", r16_0.getObject(0)); + assertEquals("(1, 4)", r16_0.getObject(1)); + assertEquals("(3, 2)", r16_1.getObject(0)); + assertEquals("(1, 2)", r16_1.getObject(1)); + + + // New axis always has size 1 + NdArray same17 = matrix2d.slice(Indices.all(), Indices.all(), Indices.newAxis()); + String[][][] same17j = StdArrays.array3dCopyOf(same17, String.class); + assertEquals(3, same17.rank()); + assertEquals(Shape.of(5,4,1), same17.shape()); + assertEquals(20, same17.size()); + String[][][] expected_r17 = new String[][][] + { + {{"(0, 0)"}, {"(1, 0)"}, {"(2, 0)"}, {"(3, 0)"}}, + {{"(0, 1)"}, {"(1, 1)"}, {"(2, 1)"}, {"(3, 1)"}}, + {{"(0, 2)"}, {"(1, 2)"}, {"(2, 2)"}, {"(3, 2)"}}, + {{"(0, 3)"}, {"(1, 3)"}, {"(2, 3)"}, {"(3, 3)"}}, + {{"(0, 4)"}, {"(1, 4)"}, {"(2, 4)"}, {"(3, 4)"}} + }; + assertArrayEquals(expected_r17, same17j); + NdArray r17_0 = same17.get(0); + NdArray r17_1 = same17.get(1); + NdArray r17_2 = same17.get(2); + NdArray r17_3 = same17.get(3); + NdArray r17_4 = same17.get(4); + assertEquals(2, r17_0.rank()); + assertEquals(Shape.of(4,1), r17_0.shape()); + assertEquals(4, r17_0.size()); + // row 0 + // What use case can we have for a new index of size 1? + // row 1 + assertEquals("(0, 1)", r17_1.getObject(0,0)); + assertEquals("(1, 1)", r17_1.getObject(1,0)); + assertEquals("(2, 1)", r17_1.getObject(2,0)); + assertEquals("(3, 1)", r17_1.getObject(3,0)); + // row 2 + assertEquals("(0, 2)", r17_2.getObject(0,0)); + assertEquals("(1, 2)", r17_2.getObject(1,0)); + assertEquals("(2, 2)", r17_2.getObject(2,0)); + assertEquals("(3, 2)", r17_2.getObject(3,0)); + // row 3 + assertEquals("(0, 3)", r17_3.getObject(0,0)); + assertEquals("(1, 3)", r17_3.getObject(1,0)); + assertEquals("(2, 3)", r17_3.getObject(2,0)); + assertEquals("(3, 3)", r17_3.getObject(3,0)); + // row 4 + assertEquals("(0, 4)", r17_4.getObject(0,0)); + assertEquals("(1, 4)", r17_4.getObject(1,0)); + assertEquals("(2, 4)", r17_4.getObject(2,0)); + assertEquals("(3, 4)", r17_4.getObject(3,0)); + + } @Test public void testNewaxis(){ diff --git a/ndarray/src/test/java/org/tensorflow/ndarray/IntNdArrayTestBase.java b/ndarray/src/test/java/org/tensorflow/ndarray/IntNdArrayTestBase.java index 1a3c7cb..0fba8fb 100644 --- a/ndarray/src/test/java/org/tensorflow/ndarray/IntNdArrayTestBase.java +++ b/ndarray/src/test/java/org/tensorflow/ndarray/IntNdArrayTestBase.java @@ -1,5 +1,5 @@ /* - Copyright 2019 The TensorFlow Authors. All Rights Reserved. + Copyright 2019-2023 The TensorFlow Authors. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -16,10 +16,11 @@ */ package org.tensorflow.ndarray; -import static org.junit.jupiter.api.Assertions.assertEquals; - import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; + public abstract class IntNdArrayTestBase extends NdArrayTestBase { @Override @@ -52,4 +53,29 @@ public void iteratePrimitiveElements() { assertEquals(9, matrix3d.getInt(0, 0, 4)); assertEquals(7, matrix3d.getInt(0, 1, 2)); } + + @Test + public void streamingInts() { + IntNdArray scalar = allocate(Shape.scalar()); + scalar.setInt(1); + var values = scalar.streamOfInts().toArray(); + assertArrayEquals(new int[]{1}, values); + + IntNdArray vector = allocate(Shape.of(5)); + vector.setInt(1, 0); + vector.setInt(2, 1); + vector.setInt(3, 2); + vector.setInt(4, 3); + vector.setInt(5, 4); + values = vector.streamOfInts().toArray(); + assertArrayEquals(new int[]{1, 2, 3, 4, 5}, values); + + IntNdArray matrix = allocate(Shape.of(2, 2)); + matrix.setInt(1, 0, 0); + matrix.setInt(2, 0, 1); + matrix.setInt(3, 1, 0); + matrix.setInt(4, 1, 1); + values = matrix.streamOfInts().toArray(); + assertArrayEquals(new int[]{1, 2, 3, 4}, values); + } } diff --git a/ndarray/src/test/java/org/tensorflow/ndarray/LongNdArrayTestBase.java b/ndarray/src/test/java/org/tensorflow/ndarray/LongNdArrayTestBase.java index b91c19d..520d9bc 100644 --- a/ndarray/src/test/java/org/tensorflow/ndarray/LongNdArrayTestBase.java +++ b/ndarray/src/test/java/org/tensorflow/ndarray/LongNdArrayTestBase.java @@ -1,5 +1,5 @@ /* - Copyright 2019 The TensorFlow Authors. All Rights Reserved. + Copyright 2019-2023 The TensorFlow Authors. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -16,6 +16,7 @@ */ package org.tensorflow.ndarray; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test; @@ -52,4 +53,29 @@ public void iteratePrimitiveElements() { assertEquals(9, matrix3d.getLong(0, 0, 4)); assertEquals(7, matrix3d.getLong(0, 1, 2)); } + + @Test + public void streamingLongs() { + LongNdArray scalar = allocate(Shape.scalar()); + scalar.setLong(1L); + var values = scalar.streamOfLongs().toArray(); + assertArrayEquals(new long[]{1L}, values); + + LongNdArray vector = allocate(Shape.of(5)); + vector.setLong(1L, 0); + vector.setLong(2L, 1); + vector.setLong(3L, 2); + vector.setLong(4L, 3); + vector.setLong(5L, 4); + values = vector.streamOfLongs().toArray(); + assertArrayEquals(new long[]{1L, 2L, 3L, 4L, 5L}, values); + + LongNdArray matrix = allocate(Shape.of(2, 2)); + matrix.setLong(1L, 0, 0); + matrix.setLong(2L, 0, 1); + matrix.setLong(3L, 1, 0); + matrix.setLong(4L, 1, 1); + values = matrix.streamOfLongs().toArray(); + assertArrayEquals(new long[]{1L, 2L, 3L, 4L}, values); + } } diff --git a/ndarray/src/test/java/org/tensorflow/ndarray/NdArrayTestBase.java b/ndarray/src/test/java/org/tensorflow/ndarray/NdArrayTestBase.java index 26ac533..36064d2 100644 --- a/ndarray/src/test/java/org/tensorflow/ndarray/NdArrayTestBase.java +++ b/ndarray/src/test/java/org/tensorflow/ndarray/NdArrayTestBase.java @@ -1,5 +1,5 @@ /* - Copyright 2019 The TensorFlow Authors. All Rights Reserved. + Copyright 2019-2023 The TensorFlow Authors. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -16,24 +16,26 @@ */ package org.tensorflow.ndarray; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotEquals; -import static org.junit.jupiter.api.Assertions.fail; +import static org.junit.jupiter.api.Assertions.*; import static org.tensorflow.ndarray.NdArrays.vectorOfObjects; import static org.tensorflow.ndarray.index.Indices.all; import static org.tensorflow.ndarray.index.Indices.at; import static org.tensorflow.ndarray.index.Indices.even; import static org.tensorflow.ndarray.index.Indices.flip; -import static org.tensorflow.ndarray.index.Indices.sliceFrom; import static org.tensorflow.ndarray.index.Indices.odd; import static org.tensorflow.ndarray.index.Indices.range; import static org.tensorflow.ndarray.index.Indices.seq; +import static org.tensorflow.ndarray.index.Indices.sliceFrom; import static org.tensorflow.ndarray.index.Indices.sliceTo; import java.nio.BufferOverflowException; import java.nio.BufferUnderflowException; +import java.util.List; +import java.util.stream.Collectors; + import org.junit.jupiter.api.Test; import org.tensorflow.ndarray.buffer.DataBuffer; +import org.tensorflow.ndarray.index.Indices; public abstract class NdArrayTestBase { @@ -132,15 +134,15 @@ public void iterateElements() { long value = 0L; for (NdArray matrix : matrix3d.elements(0)) { assertEquals(2L, matrix.shape().numDimensions()); - assertEquals(4L, matrix.shape().size(0)); - assertEquals(5L, matrix.shape().size(1)); + assertEquals(4L, matrix.shape().get(0)); + assertEquals(5L, matrix.shape().get(1)); for (NdArray vector : matrix.elements(0)) { - assertEquals(1L, vector.shape().numDimensions()) ; - assertEquals(5L, vector.shape().size(0)); + assertEquals(1L, vector.shape().numDimensions()); + assertEquals(5L, vector.shape().get(0)); for (NdArray scalar : vector.scalars()) { - assertEquals(0L, scalar.shape().numDimensions()) ; + assertEquals(0L, scalar.shape().numDimensions()); scalar.setObject(valueOf(value++)); try { scalar.elements(0); @@ -162,7 +164,7 @@ public void iterateElements() { @Test public void slices() { NdArray matrix3d = allocate(Shape.of(5, 4, 5)); - + T val100 = valueOf(100L); matrix3d.setObject(val100, 1, 0, 0); T val101 = valueOf(101L); @@ -256,7 +258,7 @@ public void writeAndReadWithBuffers() { buffer.setObject(valueOf(val), val); } NdArray matrix = allocate(Shape.of(3, 5)); - matrix.write(buffer); + matrix.copyFrom(buffer); assertEquals(valueOf(0L), matrix.getObject(0, 0)); assertEquals(valueOf(4L), matrix.getObject(0, 4)); assertEquals(valueOf(5L), matrix.getObject(1, 0)); @@ -264,7 +266,7 @@ public void writeAndReadWithBuffers() { assertEquals(valueOf(14L), matrix.getObject(2, 4)); matrix.setObject(valueOf(100L), 1, 0); - matrix.read(buffer); + matrix.copyTo(buffer); assertEquals(valueOf(0L), buffer.getObject(0)); assertEquals(valueOf(4L), buffer.getObject(4)); assertEquals(valueOf(100L), buffer.getObject(5)); @@ -272,13 +274,13 @@ public void writeAndReadWithBuffers() { assertEquals(valueOf(14L), buffer.getObject(14)); try { - matrix.write(buffer.narrow(10)); + matrix.copyFrom(buffer.narrow(10)); fail(); } catch (BufferUnderflowException e) { // as expected } try { - matrix.read(buffer.narrow(10)); + matrix.copyTo(buffer.narrow(10)); fail(); } catch (BufferOverflowException e) { // as expected @@ -318,8 +320,8 @@ public void equalsAndHashCode() { NdArray array4 = allocate(Shape.of(1, 2, 2)); @SuppressWarnings("unchecked") - T[][][] values = (T[][][])(new Object[][][] { - { { valueOf(0L), valueOf(1L) }, { valueOf(2L), valueOf(0L) } } + T[][][] values = (T[][][]) (new Object[][][]{ + {{valueOf(0L), valueOf(1L)}, {valueOf(2L), valueOf(0L)}} }); StdArrays.copyTo(values[0], array1); @@ -335,4 +337,78 @@ public void equalsAndHashCode() { assertNotEquals(array1, array4); assertNotEquals(array1.hashCode(), array4.hashCode()); } + + @Test + public void iterateScalarsOnSegmentedElements() { + NdArray originalTensor = allocate(Shape.of(2, 3)); + + originalTensor + .setObject(valueOf(0L), 0, 0) + .setObject(valueOf(1L), 0, 1) + .setObject(valueOf(2L), 0, 2) + .setObject(valueOf(3L), 1, 0) + .setObject(valueOf(4L), 1, 1) + .setObject(valueOf(5L), 1, 2); + + NdArray slice = originalTensor.slice(Indices.all(), Indices.sliceFrom(1)); + assertEquals(Shape.of(2, 2), slice.shape()); + + slice.elements(0).forEachIndexed((eCoord, e) -> { + e.scalars().forEachIndexed((sCoord, s) -> { + assertEquals(valueOf((eCoord[0] * originalTensor.shape().get(1)) + sCoord[0] + 1), s.getObject()); + }); + }); + } + + @Test + public void streamingObjects() { + NdArray scalar = allocate(Shape.scalar()); + scalar.setObject(valueOf(1L)); + var values = scalar.streamOfObjects().collect(Collectors.toList()); + assertIterableEquals(List.of(valueOf(1L)), values); + + NdArray vector = allocate(Shape.of(5)); + vector.setObject(valueOf(1L), 0); + vector.setObject(valueOf(2L), 1); + vector.setObject(valueOf(3L), 2); + vector.setObject(valueOf(4L), 3); + vector.setObject(valueOf(5L), 4); + values = vector.streamOfObjects().collect(Collectors.toList()); + assertIterableEquals(List.of(valueOf(1L), valueOf(2L), valueOf(3L), valueOf(4L), valueOf(5L)), values); + + NdArray matrix = allocate(Shape.of(2, 2)); + matrix.setObject(valueOf(1L), 0, 0); + matrix.setObject(valueOf(2L), 0, 1); + matrix.setObject(valueOf(3L), 1, 0); + matrix.setObject(valueOf(4L), 1, 1); + values = matrix.streamOfObjects().collect(Collectors.toList()); + assertIterableEquals(List.of(valueOf(1L), valueOf(2L), valueOf(3L), valueOf(4L)), values); + } + + @Test + public void withShape() { + Shape originalShape = Shape.scalar(); + Shape newShape = originalShape.prepend(1).prepend(1); // [1, 1] + + NdArray originalArray = allocate(originalShape); + originalArray.setObject(valueOf(10L)); + assertEquals(valueOf(10L), originalArray.getObject()); + + NdArray newArray = originalArray.withShape(newShape); + assertNotNull(newArray); + assertEquals(newShape, newArray.shape()); + assertEquals(originalShape, originalArray.shape()); + assertEquals(valueOf(10L), newArray.getObject(0, 0)); + + NdArray sameArray = originalArray.withShape(Shape.scalar()); + assertSame(originalArray, sameArray); + + assertThrows(IllegalArgumentException.class, () -> originalArray.withShape(Shape.of(2))); + assertThrows(IllegalArgumentException.class, () -> originalArray.withShape(Shape.unknown())); + + NdArray originalMatrix = allocate(Shape.of(2, 3)); + assertThrows(IllegalArgumentException.class, () -> originalMatrix.withShape(Shape.scalar())); + NdArray newMatrix = originalMatrix.withShape(Shape.of(3, 2)); + assertEquals(Shape.of(3, 2), newMatrix.shape()); + } } diff --git a/ndarray/src/test/java/org/tensorflow/ndarray/ShapeTest.java b/ndarray/src/test/java/org/tensorflow/ndarray/ShapeTest.java index d2e3e43..c1247ab 100644 --- a/ndarray/src/test/java/org/tensorflow/ndarray/ShapeTest.java +++ b/ndarray/src/test/java/org/tensorflow/ndarray/ShapeTest.java @@ -16,9 +16,15 @@ */ package org.tensorflow.ndarray; -import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; -import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; public class ShapeTest { @@ -26,22 +32,22 @@ public class ShapeTest { public void allKnownDimensions() { Shape shape = Shape.of(5, 4, 5); assertEquals(3, shape.numDimensions()); - assertEquals(5, shape.size(0)); - assertEquals(4, shape.size(1)); - assertEquals(5, shape.size(2)); + assertEquals(5, shape.get(0)); + assertEquals(4, shape.get(1)); + assertEquals(5, shape.get(2)); assertEquals(100, shape.size()); - assertArrayEquals(new long[] {5, 4, 5}, shape.asArray()); + assertArrayEquals(new long[]{5, 4, 5}, shape.asArray()); try { - shape.size(3); + shape.get(3); fail(); } catch (IndexOutOfBoundsException e) { // as expected } - assertEquals(5, shape.size(-1)); - assertEquals(4, shape.size(-2)); - assertEquals(5, shape.size(-3)); + assertEquals(5, shape.get(-1)); + assertEquals(4, shape.get(-2)); + assertEquals(5, shape.get(-3)); try { - shape.size(-4); + shape.get(-4); fail(); } catch (IndexOutOfBoundsException e) { // as expected @@ -133,7 +139,7 @@ public void testShapeModification() { long[] internalShape = one.asArray(); assertNotNull(internalShape); internalShape[0] = 42L; - assertEquals(2L, one.size(0)); + assertEquals(2L, one.get(0)); } @Test diff --git a/ndarray/src/test/java/org/tensorflow/ndarray/SparseNdArrayTest.java b/ndarray/src/test/java/org/tensorflow/ndarray/SparseNdArrayTest.java new file mode 100644 index 0000000..0c5d6b3 --- /dev/null +++ b/ndarray/src/test/java/org/tensorflow/ndarray/SparseNdArrayTest.java @@ -0,0 +1,198 @@ +/* Copyright 2021 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +=======================================================================*/ +package org.tensorflow.ndarray; + +import org.junit.jupiter.api.Test; +import org.tensorflow.ndarray.impl.sparse.BooleanSparseNdArray; +import org.tensorflow.ndarray.impl.sparse.ByteSparseNdArray; +import org.tensorflow.ndarray.impl.sparse.DoubleSparseNdArray; +import org.tensorflow.ndarray.impl.sparse.FloatSparseNdArray; +import org.tensorflow.ndarray.impl.sparse.IntSparseNdArray; +import org.tensorflow.ndarray.impl.sparse.LongSparseNdArray; +import org.tensorflow.ndarray.impl.sparse.ShortSparseNdArray; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class SparseNdArrayTest { + long[][] indicesArray = {{0, 0}, {1, 2}, {2, 3}}; + LongNdArray indices = StdArrays.ndCopyOf(indicesArray); + Shape shape = Shape.of(3, 4); + double epsilon = 0.001; + + @Test + public void testBoolean() { + BooleanSparseNdArray instance = + NdArrays.sparseOf(indices, NdArrays.vectorOf(true, true, true), shape); + assertEquals(6, instance.getIndices().size()); + assertEquals(3, instance.getValues().size()); + assertTrue(instance.getBoolean(0, 0)); + assertFalse(instance.getBoolean(0, 1)); + assertFalse(instance.getBoolean(0, 2)); + assertFalse(instance.getBoolean(0, 3)); + + assertFalse(instance.getBoolean(1, 0)); + assertFalse(instance.getBoolean(1, 1)); + assertTrue(instance.getBoolean(1, 2)); + assertFalse(instance.getBoolean(1, 3)); + + assertFalse(instance.getBoolean(2, 0)); + assertFalse(instance.getBoolean(2, 1)); + assertFalse(instance.getBoolean(2, 2)); + assertTrue(instance.getBoolean(2, 3)); + } + + @Test + public void testByte() { + ByteSparseNdArray instance = + NdArrays.sparseOf( + indices, NdArrays.vectorOf((byte) 1, (byte) 18, (byte) 0xff), shape); + assertEquals(6, instance.getIndices().size()); + assertEquals(3, instance.getValues().size()); + assertEquals((byte) 1, instance.getByte(0, 0)); + assertEquals((byte) 0, instance.getByte(0, 1)); + assertEquals((byte) 0, instance.getByte(0, 2)); + assertEquals((byte) 0, instance.getByte(0, 3)); + + assertEquals((byte) 0, instance.getByte(1, 0)); + assertEquals((byte) 0, instance.getByte(1, 1)); + assertEquals((byte) 18, instance.getByte(1, 2)); + assertEquals((byte) 0, instance.getByte(1, 3)); + + assertEquals((byte) 0, instance.getByte(2, 0)); + assertEquals((byte) 0, instance.getByte(2, 1)); + assertEquals((byte) 0, instance.getByte(2, 2)); + assertEquals((byte) 0xff, instance.getByte(2, 3)); + } + + @Test + public void testDouble() { + DoubleSparseNdArray instance = + NdArrays.sparseOf(indices, NdArrays.vectorOf(1., 1.8, 3.14), shape); + assertEquals(6, instance.getIndices().size()); + assertEquals(3, instance.getValues().size()); + assertEquals(1., instance.getDouble(0, 0), epsilon); + assertEquals(0, instance.getDouble(0, 1), epsilon); + assertEquals(0, instance.getDouble(0, 2), epsilon); + assertEquals(0, instance.getDouble(0, 3), epsilon); + + assertEquals(0, instance.getDouble(1, 0), epsilon); + assertEquals(0, instance.getDouble(1, 1), epsilon); + assertEquals(1.8, instance.getDouble(1, 2), epsilon); + assertEquals(0, instance.getDouble(1, 3), epsilon); + + assertEquals(0, instance.getDouble(2, 0), epsilon); + assertEquals(0, instance.getDouble(2, 1), epsilon); + assertEquals(0, instance.getDouble(2, 2), epsilon); + assertEquals(3.14, instance.getDouble(2, 3), epsilon); + } + + @Test + public void testFloat() { + FloatSparseNdArray instance = + NdArrays.sparseOf(indices, NdArrays.vectorOf(1.f, 1.8f, 3.14f), shape); + assertEquals(6, instance.getIndices().size()); + assertEquals(3, instance.getValues().size()); + assertEquals(1.f, instance.getFloat(0, 0), epsilon); + assertEquals(0f, instance.getFloat(0, 1), epsilon); + assertEquals(0f, instance.getFloat(0, 2), epsilon); + assertEquals(0f, instance.getFloat(0, 3), epsilon); + + assertEquals(0f, instance.getFloat(1, 0), epsilon); + assertEquals(0f, instance.getFloat(1, 1), epsilon); + assertEquals(1.8f, instance.getFloat(1, 2), epsilon); + assertEquals(0f, instance.getFloat(1, 3), epsilon); + + assertEquals(0f, instance.getFloat(2, 0), epsilon); + assertEquals(0f, instance.getFloat(2, 1), epsilon); + assertEquals(0f, instance.getFloat(2, 2), epsilon); + assertEquals(3.14f, instance.getFloat(2, 3), epsilon); + } + + @Test + public void testInt() { + IntSparseNdArray instance = + NdArrays.sparseOf(indices, NdArrays.vectorOf(1, 18, 256), shape); + assertEquals(6, instance.getIndices().size()); + assertEquals(3, instance.getValues().size()); + assertEquals(1, instance.getInt(0, 0)); + assertEquals(0, instance.getInt(0, 1)); + assertEquals(0, instance.getInt(0, 2)); + assertEquals(0, instance.getInt(0, 3)); + + assertEquals(0, instance.getInt(1, 0)); + assertEquals(0, instance.getInt(1, 1)); + assertEquals(18, instance.getInt(1, 2)); + assertEquals(0, instance.getInt(1, 3)); + + assertEquals(0, instance.getInt(2, 0)); + assertEquals(0, instance.getInt(2, 1)); + assertEquals(0, instance.getInt(2, 2)); + assertEquals(256, instance.getInt(2, 3)); + } + + @Test + public void testLong() { + LongSparseNdArray instance = + NdArrays.sparseOf(indices, NdArrays.vectorOf(1L, 18L, 256L), shape); + assertEquals(6, instance.getIndices().size()); + assertEquals(3, instance.getValues().size()); + assertEquals(1L, instance.getLong(0, 0)); + assertEquals(0L, instance.getLong(0, 1)); + assertEquals(0L, instance.getLong(0, 2)); + assertEquals(0L, instance.getLong(0, 3)); + + assertEquals(0L, instance.getLong(1, 0)); + assertEquals(0L, instance.getLong(1, 1)); + assertEquals(18, instance.getLong(1, 2)); + assertEquals(0L, instance.getLong(1, 3)); + + assertEquals(0L, instance.getLong(2, 0)); + assertEquals(0L, instance.getLong(2, 1)); + assertEquals(0L, instance.getLong(2, 2)); + assertEquals(256L, instance.getLong(2, 3)); + } + + @Test + public void testShort() { + ShortSparseNdArray instance = + NdArrays.sparseOf( + indices, NdArrays.vectorOf((short) 1, (short) 18, (short) 0xff00), shape); + assertEquals(6, instance.getIndices().size()); + assertEquals(3, instance.getValues().size()); + assertEquals((short) 1, instance.getShort(0, 0)); + assertEquals((short) 0, instance.getShort(0, 1)); + assertEquals((short) 0, instance.getShort(0, 2)); + assertEquals((short) 0, instance.getShort(0, 3)); + + assertEquals((short) 0, instance.getShort(1, 0)); + assertEquals((short) 0, instance.getShort(1, 1)); + assertEquals((short) 18, instance.getShort(1, 2)); + assertEquals((short) 0, instance.getShort(1, 3)); + + assertEquals((short) 0, instance.getShort(2, 0)); + assertEquals((short) 0, instance.getShort(2, 1)); + assertEquals((short) 0, instance.getShort(2, 2)); + assertEquals((short) 0xff00, instance.getShort(2, 3)); + } + + @Test + public void withShape() { + NdArray sparseArray = NdArrays.sparseOf(indices, NdArrays.vectorOf(1, 2, 3), shape); + assertThrows(UnsupportedOperationException.class, () -> sparseArray.withShape(shape.prepend(1))); + } +} diff --git a/ndarray/src/test/java/org/tensorflow/ndarray/impl/dense/BooleanDenseNdArrayTest.java b/ndarray/src/test/java/org/tensorflow/ndarray/impl/dense/BooleanDenseNdArrayTest.java index 3654010..d477668 100644 --- a/ndarray/src/test/java/org/tensorflow/ndarray/impl/dense/BooleanDenseNdArrayTest.java +++ b/ndarray/src/test/java/org/tensorflow/ndarray/impl/dense/BooleanDenseNdArrayTest.java @@ -16,6 +16,8 @@ */ package org.tensorflow.ndarray.impl.dense; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; import org.tensorflow.ndarray.Shape; import org.tensorflow.ndarray.buffer.DataBuffer; import org.tensorflow.ndarray.buffer.DataBuffers; @@ -32,4 +34,12 @@ public class BooleanDenseNdArrayTest extends BooleanNdArrayTestBase { @Override protected DataBuffer allocateBuffer(long size) { return DataBuffers.ofBooleans(size); } + + @Test + public void testToString() { + BooleanNdArray matrix3d = allocate(Shape.of(5, 4, 5)); + Assertions.assertEquals("BooleanDenseNdArray(shape=[5, 4, 5])",matrix3d.toString()); + BooleanNdArray scalar = allocate(Shape.of()); + Assertions.assertEquals("BooleanDenseNdArray(shape=[])",scalar.toString()); + } } diff --git a/ndarray/src/test/java/org/tensorflow/ndarray/impl/dense/DoubleDenseNdArrayTest.java b/ndarray/src/test/java/org/tensorflow/ndarray/impl/dense/DoubleDenseNdArrayTest.java index 9810a74..279c7b8 100644 --- a/ndarray/src/test/java/org/tensorflow/ndarray/impl/dense/DoubleDenseNdArrayTest.java +++ b/ndarray/src/test/java/org/tensorflow/ndarray/impl/dense/DoubleDenseNdArrayTest.java @@ -16,6 +16,8 @@ */ package org.tensorflow.ndarray.impl.dense; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; import org.tensorflow.ndarray.Shape; import org.tensorflow.ndarray.buffer.DataBuffer; import org.tensorflow.ndarray.buffer.DataBuffers; @@ -32,4 +34,14 @@ public class DoubleDenseNdArrayTest extends DoubleNdArrayTestBase { @Override protected DataBuffer allocateBuffer(long size) { return DataBuffers.ofDoubles(size); } + + @Test + public void testToString() { + DoubleNdArray matrix3d = allocate(Shape.of(5, 4, 5)); + Assertions.assertEquals("DoubleDenseNdArray(shape=[5, 4, 5])",matrix3d.toString()); + DoubleNdArray vector = allocate(Shape.of(5)); + Assertions.assertEquals("DoubleDenseNdArray(shape=[5])",vector.toString()); + DoubleNdArray scalar = allocate(Shape.of()); + Assertions.assertEquals("DoubleDenseNdArray(shape=[])",scalar.toString()); + } } diff --git a/ndarray/src/test/java/org/tensorflow/ndarray/impl/dense/FloatDenseNdArrayTest.java b/ndarray/src/test/java/org/tensorflow/ndarray/impl/dense/FloatDenseNdArrayTest.java index efee2bf..900ba8a 100644 --- a/ndarray/src/test/java/org/tensorflow/ndarray/impl/dense/FloatDenseNdArrayTest.java +++ b/ndarray/src/test/java/org/tensorflow/ndarray/impl/dense/FloatDenseNdArrayTest.java @@ -1,35 +1,77 @@ /* - Copyright 2019 The TensorFlow Authors. All Rights Reserved. +Copyright 2019 The TensorFlow Authors. All Rights Reserved. - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + http://www.apache.org/licenses/LICENSE-2.0 - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - ======================================================================= - */ +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +======================================================================= +*/ package org.tensorflow.ndarray.impl.dense; -import org.tensorflow.ndarray.Shape; -import org.tensorflow.ndarray.buffer.DataBuffer; -import org.tensorflow.ndarray.buffer.DataBuffers; +import org.junit.jupiter.api.Test; import org.tensorflow.ndarray.FloatNdArray; import org.tensorflow.ndarray.FloatNdArrayTestBase; import org.tensorflow.ndarray.NdArrays; +import org.tensorflow.ndarray.Shape; +import org.tensorflow.ndarray.buffer.DataBuffer; +import org.tensorflow.ndarray.buffer.DataBuffers; +import org.tensorflow.ndarray.buffer.FloatDataBuffer; +import org.tensorflow.ndarray.index.Indices; + +import java.util.concurrent.atomic.AtomicInteger; + +import static org.junit.jupiter.api.Assertions.assertEquals; public class FloatDenseNdArrayTest extends FloatNdArrayTestBase { - @Override protected FloatNdArray allocate(Shape shape) { + @Override + protected FloatNdArray allocate(Shape shape) { return NdArrays.ofFloats(shape); } - @Override protected DataBuffer allocateBuffer(long size) { + @Override + protected DataBuffer allocateBuffer(long size) { return DataBuffers.ofFloats(size); } + + @Test + public void testSlice() { + Shape shape = Shape.of(3, 4); + Float[] values = { + 1f, 0f, 0f, 0f, + 0f, 0f, 2f, 0f, + 0f, 0f, 0f, 0f + }; + + float[] expected = {0, 0, 2, 0, 0, 0}; + + FloatDataBuffer buffer = (FloatDataBuffer) allocateBuffer(shape.size()); + buffer.write(values); + FloatNdArray instance = FloatDenseNdArray.create(buffer, shape); + + FloatNdArray sliceInstance = instance.slice(Indices.all(), Indices.sliceFrom(2)); + // check the values of the slice against the original array + AtomicInteger i = new AtomicInteger(); + sliceInstance + .scalars() + .forEachIndexed((idx, f) -> assertEquals(expected[i.getAndIncrement()], f.getFloat())); + + // check values from elements(0) of a slice against the original array + i.set(0); + sliceInstance + .elements(0) + .forEachIndexed( + (idx, l) -> + l.scalars() + .forEachIndexed( + (lidx, f) -> assertEquals(expected[i.getAndIncrement()], f.getFloat()))); + } } diff --git a/ndarray/src/test/java/org/tensorflow/ndarray/impl/sparse/BooleanSparseNdArrayTest.java b/ndarray/src/test/java/org/tensorflow/ndarray/impl/sparse/BooleanSparseNdArrayTest.java new file mode 100644 index 0000000..ecd9e85 --- /dev/null +++ b/ndarray/src/test/java/org/tensorflow/ndarray/impl/sparse/BooleanSparseNdArrayTest.java @@ -0,0 +1,314 @@ +package org.tensorflow.ndarray.impl.sparse; + +import org.junit.jupiter.api.Test; +import org.tensorflow.ndarray.BooleanNdArray; +import org.tensorflow.ndarray.LongNdArray; +import org.tensorflow.ndarray.NdArrays; +import org.tensorflow.ndarray.Shape; +import org.tensorflow.ndarray.StdArrays; +import org.tensorflow.ndarray.buffer.BooleanDataBuffer; +import org.tensorflow.ndarray.buffer.DataBuffers; +import org.tensorflow.ndarray.impl.buffer.raw.RawDataBufferFactory; +import org.tensorflow.ndarray.impl.dimension.DimensionalSpace; +import org.tensorflow.ndarray.index.Indices; + +import java.util.concurrent.atomic.AtomicInteger; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class BooleanSparseNdArrayTest { + long[][] indicesArray = {{0, 0}, {1, 2}}; + boolean[] valuesArray = {true, true}; + boolean[] valuesArrayDefaultValue = {false, false}; + boolean[] denseArray = { + true, false, false, false, + false, false, true, false, + false, false, false, false + }; + boolean[][] dense2DArray = { + {true, false, false, false}, {false, false, true, false}, {false, false, false, false} + }; + + Shape shape = Shape.of(3, 4); + LongNdArray indices = StdArrays.ndCopyOf(indicesArray); + BooleanNdArray values = StdArrays.ndCopyOf(valuesArray); + + @Test + public void testBasic() { + BooleanSparseNdArray instance = + new BooleanSparseNdArray(indices, values, DimensionalSpace.create(shape)); + assertEquals(indices, instance.getIndices()); + assertEquals(values, instance.getValues()); + assertEquals(shape, instance.shape()); + } + + @Test + public void testCopyToBuffer() { + BooleanSparseNdArray instance = + new BooleanSparseNdArray(indices, values, DimensionalSpace.create(shape)); + BooleanDataBuffer dataBuffer = DataBuffers.ofBooleans(instance.shape().size()); + + instance.copyTo(dataBuffer); + + boolean[] array = new boolean[denseArray.length]; + dataBuffer.read(array); + assertArrayEquals(denseArray, array); + } + + @Test + public void testCopyFromBuffer() { + + BooleanDataBuffer dataBuffer = RawDataBufferFactory.create(denseArray, false); + // use a zero buffer + BooleanSparseNdArray instance = BooleanSparseNdArray.create(DimensionalSpace.create(shape)); + instance.copyFrom(dataBuffer); + + assertEquals(indices, instance.getIndices()); + assertEquals(values, instance.getValues()); + } + + @Test + public void testWriteDefaultValue() { + // invert true/false + boolean[] denseArrayDefaultValue = new boolean[denseArray.length]; + for (int i = 0; i < denseArrayDefaultValue.length; i++) { + denseArrayDefaultValue[i] = !denseArray[i]; + } + + BooleanNdArray valuesDefault = StdArrays.ndCopyOf(new boolean[] {false, false}); + BooleanDataBuffer dataBuffer = RawDataBufferFactory.create(denseArrayDefaultValue, false); + // use a zero buffer + BooleanSparseNdArray instance = + BooleanSparseNdArray.create(true, DimensionalSpace.create(shape)); + instance.copyFrom(dataBuffer); + + assertEquals(indices, instance.getIndices()); + assertEquals(valuesDefault, instance.getValues()); + } + + @Test + public void testGetObject() { + BooleanNdArray ndArray = StdArrays.ndCopyOf(dense2DArray); + BooleanSparseNdArray instance = + new BooleanSparseNdArray(indices, values, DimensionalSpace.create(shape)); + + for (int n = 0; n < ndArray.shape().get(0); n++) { + for (int m = 0; m < ndArray.shape().get(1); m++) { + assertEquals(ndArray.getObject(n, m), instance.getObject(n, m)); + } + } + } + + @Test + public void testGetBoolean() { + BooleanNdArray ndArray = StdArrays.ndCopyOf(dense2DArray); + BooleanSparseNdArray instance = + new BooleanSparseNdArray(indices, values, DimensionalSpace.create(shape)); + + for (int n = 0; n < ndArray.shape().get(0); n++) { + for (int m = 0; m < ndArray.shape().get(1); m++) { + assertEquals(ndArray.getBoolean(n, m), instance.getBoolean(n, m)); + } + } + } + + @Test + public void testGetBooleanDefaultValue() { + // flip the truth table + BooleanNdArray ndArray = StdArrays.ndCopyOf(dense2DArray); + BooleanSparseNdArray instance = + new BooleanSparseNdArray( + indices, + NdArrays.vectorOf(valuesArrayDefaultValue), + true, + DimensionalSpace.create(shape)); + + for (int n = 0; n < ndArray.shape().get(0); n++) { + for (int m = 0; m < ndArray.shape().get(1); m++) { + assertNotEquals(ndArray.getBoolean(n, m), instance.getBoolean(n, m)); + } + } + } + + @Test + public void testGet() { + BooleanNdArray ndArray = StdArrays.ndCopyOf(dense2DArray); + BooleanSparseNdArray instance = + new BooleanSparseNdArray(indices, values, DimensionalSpace.create(shape)); + + for (int n = 0; n < ndArray.shape().get(0); n++) { + assertEquals(ndArray.get(n), instance.get(n)); + for (int m = 0; m < ndArray.shape().get(1); m++) { + assertEquals(ndArray.get(n, m), instance.get(n, m)); + } + } + } + + @Test + public void testSetObject() { + BooleanSparseNdArray instance = + new BooleanSparseNdArray(indices, values, DimensionalSpace.create(shape)); + + assertThrows(java.nio.ReadOnlyBufferException.class, () -> instance.setObject(false, 0, 0)); + } + + @Test + public void testSet() { + BooleanSparseNdArray instance = + new BooleanSparseNdArray(indices, values, DimensionalSpace.create(shape)); + + assertThrows( + java.nio.ReadOnlyBufferException.class, + () -> instance.set(instance.getDefaultArray(), 0, 0)); + } + + @Test + public void testSort() { + + long[][] indicesArray = {{0, 0}, {1, 2}, {0, 1}, {2, 3}, {1, 4}}; + long[][] sortedIndicesArray = {{0, 0}, {0, 1}, {1, 2}, {1, 4}, {2, 3}}; + boolean[] valuesArray = {true, true, false, true, false}; + boolean[] sortedValuesArray = {true, false, true, false, true}; + + LongNdArray indices = StdArrays.ndCopyOf(indicesArray); + LongNdArray sortedIndices = StdArrays.ndCopyOf(sortedIndicesArray); + BooleanNdArray values = StdArrays.ndCopyOf(valuesArray); + BooleanNdArray sortedValues = StdArrays.ndCopyOf(sortedValuesArray); + + BooleanSparseNdArray instance = + new BooleanSparseNdArray(indices, values, DimensionalSpace.create(shape)); + + instance.sortIndicesAndValues(); + + // should be sorted in ascending row-wise coordinate order based on test values + assertEquals(sortedIndices, instance.getIndices()); + assertEquals(sortedValues, instance.getValues()); + } + + @Test + public void testElements() { + + BooleanSparseNdArray instance = + new BooleanSparseNdArray(indices, values, DimensionalSpace.create(shape)); + + instance + .elements(0) + .forEachIndexed( + (idx, item) -> { + boolean[] slice = dense2DArray[(int) idx[0]]; + item.scalars() + .forEachIndexed((dx, f) -> assertEquals(slice[(int) dx[0]], f.getObject())); + }); + } + + @Test + public void testDense() { + + BooleanSparseNdArray instance = + new BooleanSparseNdArray(indices, values, DimensionalSpace.create(shape)); + BooleanNdArray denseInstance = instance.toDense(); + BooleanNdArray expectedDense = StdArrays.ndCopyOf(dense2DArray); + assertEquals(expectedDense, denseInstance); + } + + @Test + public void testFromDense() { + BooleanNdArray ndArray = StdArrays.ndCopyOf(dense2DArray); + BooleanSparseNdArray instance = + BooleanSparseNdArray.create(DimensionalSpace.create(ndArray.shape())); + instance.fromDense(ndArray); + assertNotNull(instance.getIndices()); + assertEquals(2, instance.getIndices().shape().get(0)); + assertNotNull(instance.getValues()); + assertEquals(2, instance.getValues().size()); + + assertEquals(ndArray.shape(), instance.shape()); + for (int n = 0; n < ndArray.shape().get(0); n++) { + for (int m = 0; m < ndArray.shape().get(1); m++) { + assertEquals(ndArray.getBoolean(n, m), instance.getBoolean(n, m)); + } + } + } + + @Test + public void testElements1() { + boolean[] expected = {true, false, false}; + + BooleanSparseNdArray instance = + new BooleanSparseNdArray(indices, values, DimensionalSpace.create(shape)); + instance + .elements(0) + .forEachIndexed((idx, l) -> assertEquals(expected[(int) idx[0]], l.getObject())); + instance + .elements(1) + .forEachIndexed( + (idx, l) -> assertEquals(dense2DArray[(int) idx[0]][(int) idx[1]], l.getObject())); + } + + @Test + public void testCopyTo() { + BooleanNdArray dst = NdArrays.ofBooleans(shape); + BooleanSparseNdArray instance = + new BooleanSparseNdArray(indices, values, DimensionalSpace.create(shape)); + instance.copyTo(dst); + for (int n = 0; n < instance.shape().get(0); n++) { + for (int m = 0; m < instance.shape().get(1); m++) { + assertEquals(instance.getBoolean(n, m), dst.getBoolean(n, m)); + } + } + } + + @Test + public void testCreate() { + + BooleanSparseNdArray instance = + new BooleanSparseNdArray(indices, values, DimensionalSpace.create(shape)); + BooleanSparseNdArray instanceA = + BooleanSparseNdArray.create(indices, values, DimensionalSpace.create(shape)); + assertEquals(instance, instanceA); + + BooleanDataBuffer dataBuffer = RawDataBufferFactory.create(denseArray, false); + // use a zero buffer + BooleanSparseNdArray instanceB = BooleanSparseNdArray.create(DimensionalSpace.create(shape)); + instanceB.copyFrom(dataBuffer); + assertEquals(instance, instanceB); + + BooleanSparseNdArray instanceC = + BooleanSparseNdArray.create(dataBuffer, DimensionalSpace.create(shape)); + assertEquals(instanceB, instanceC); + + BooleanSparseNdArray instanceD = BooleanSparseNdArray.create(dataBuffer, shape); + assertEquals(instanceB, instanceD); + + BooleanNdArray ndArray = StdArrays.ndCopyOf(dense2DArray); + BooleanSparseNdArray instanceE = BooleanSparseNdArray.create(ndArray); + assertEquals(instance, instanceE); + } + + @Test + public void testSlice() { + boolean[] expected = {false, false, true, false, false, false}; + BooleanSparseNdArray instance = + new BooleanSparseNdArray(indices, values, DimensionalSpace.create(shape)); + + BooleanNdArray sliceInstance = instance.slice(Indices.all(), Indices.sliceFrom(2)); + // check the values of the slice against the original sparse array + AtomicInteger i = new AtomicInteger(); + sliceInstance + .scalars() + .forEachIndexed((idx, f) -> assertEquals(expected[i.getAndIncrement()], f.getBoolean())); + // check values from elements(0) of a slice against the original sparse array + i.set(0); + sliceInstance + .elements(0) + .forEachIndexed( + (idx, l) -> + l.scalars() + .forEachIndexed( + (lidx, f) -> assertEquals(expected[i.getAndIncrement()], f.getBoolean()))); + } +} diff --git a/ndarray/src/test/java/org/tensorflow/ndarray/impl/sparse/ByteSparseNdArrayTest.java b/ndarray/src/test/java/org/tensorflow/ndarray/impl/sparse/ByteSparseNdArrayTest.java new file mode 100644 index 0000000..efa0572 --- /dev/null +++ b/ndarray/src/test/java/org/tensorflow/ndarray/impl/sparse/ByteSparseNdArrayTest.java @@ -0,0 +1,305 @@ +package org.tensorflow.ndarray.impl.sparse; + +import org.junit.jupiter.api.Test; +import org.tensorflow.ndarray.ByteNdArray; +import org.tensorflow.ndarray.LongNdArray; +import org.tensorflow.ndarray.NdArrays; +import org.tensorflow.ndarray.Shape; +import org.tensorflow.ndarray.StdArrays; +import org.tensorflow.ndarray.buffer.ByteDataBuffer; +import org.tensorflow.ndarray.buffer.DataBuffers; +import org.tensorflow.ndarray.impl.buffer.raw.RawDataBufferFactory; +import org.tensorflow.ndarray.impl.dimension.DimensionalSpace; +import org.tensorflow.ndarray.index.Indices; + +import java.util.concurrent.atomic.AtomicInteger; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class ByteSparseNdArrayTest { + long[][] indicesArray = {{0, 0}, {1, 2}}; + byte[] valuesArray = {1, 16}; + byte[] denseArray = { + 1, 0, 0, 0, + 0, 0, 16, 0, + 0, 0, 0, 0 + }; + byte[][] dense2DArray = {{1, 0, 0, 0}, {0, 0, 16, 0}, {0, 0, 0, 0}}; + + byte[][] dense2DArrayDefaultValue = {{1, -1, -1, -1}, {-1, -1, 16, -1}, {-1, -1, -1, -1}}; + + Shape shape = Shape.of(3, 4); + LongNdArray indices = StdArrays.ndCopyOf(indicesArray); + ByteNdArray values = StdArrays.ndCopyOf(valuesArray); + + @Test + public void testBasic() { + ByteSparseNdArray instance = + new ByteSparseNdArray(indices, values, DimensionalSpace.create(shape)); + assertEquals(indices, instance.getIndices()); + assertEquals(values, instance.getValues()); + assertEquals(shape, instance.shape()); + } + + @Test + public void testCopyToBuffer() { + ByteSparseNdArray instance = + new ByteSparseNdArray(indices, values, DimensionalSpace.create(shape)); + ByteDataBuffer dataBuffer = DataBuffers.ofBytes(instance.shape().size()); + + instance.copyTo(dataBuffer); + + byte[] array = new byte[denseArray.length]; + dataBuffer.read(array); + assertArrayEquals(denseArray, array); + } + + @Test + public void testCopyFromBuffer() { + + ByteDataBuffer dataBuffer = RawDataBufferFactory.create(denseArray, false); + // use a zero buffer + ByteSparseNdArray instance = ByteSparseNdArray.create(DimensionalSpace.create(shape)); + instance.copyFrom(dataBuffer); + + assertEquals(indices, instance.getIndices()); + assertEquals(values, instance.getValues()); + } + + @Test + public void testWriteDefaultValue() { + // change 0 to -1 + byte[] denseArrayDefaultValue = new byte[denseArray.length]; + for (int i = 0; i < denseArrayDefaultValue.length; i++) { + denseArrayDefaultValue[i] = denseArray[i] == 0 ? -1 : denseArray[i]; + } + ByteDataBuffer dataBuffer = RawDataBufferFactory.create(denseArrayDefaultValue, false); + // use a zero buffer + ByteSparseNdArray instance = + ByteSparseNdArray.create((byte) -1, DimensionalSpace.create(shape)); + instance.copyFrom(dataBuffer); + + assertEquals(indices, instance.getIndices()); + assertEquals(values, instance.getValues()); + assertEquals((byte) -1, instance.getByte(2, 0)); + } + + @Test + public void testGetObject() { + ByteNdArray ndArray = StdArrays.ndCopyOf(dense2DArray); + ByteSparseNdArray instance = + new ByteSparseNdArray(indices, values, DimensionalSpace.create(shape)); + + for (int n = 0; n < ndArray.shape().get(0); n++) { + for (int m = 0; m < ndArray.shape().get(1); m++) { + assertEquals(ndArray.getObject(n, m), instance.getObject(n, m)); + } + } + } + + @Test + public void testGetByte() { + ByteNdArray ndArray = StdArrays.ndCopyOf(dense2DArray); + ByteSparseNdArray instance = + new ByteSparseNdArray(indices, values, DimensionalSpace.create(shape)); + + for (int n = 0; n < ndArray.shape().get(0); n++) { + for (int m = 0; m < ndArray.shape().get(1); m++) { + assertEquals(ndArray.getByte(n, m), instance.getByte(n, m)); + } + } + } + + @Test + public void testGetByteDefaultValue() { + ByteNdArray ndArray = StdArrays.ndCopyOf(dense2DArrayDefaultValue); + ByteSparseNdArray instance = + new ByteSparseNdArray(indices, values, (byte) -1, DimensionalSpace.create(shape)); + + for (int n = 0; n < ndArray.shape().get(0); n++) { + for (int m = 0; m < ndArray.shape().get(1); m++) { + assertEquals(ndArray.getByte(n, m), instance.getByte(n, m)); + } + } + } + + @Test + public void testGet() { + ByteNdArray ndArray = StdArrays.ndCopyOf(dense2DArray); + ByteSparseNdArray instance = + new ByteSparseNdArray(indices, values, DimensionalSpace.create(shape)); + + for (int n = 0; n < ndArray.shape().get(0); n++) { + assertEquals(ndArray.get(n), instance.get(n)); + for (int m = 0; m < ndArray.shape().get(1); m++) { + assertEquals(ndArray.get(n, m), instance.get(n, m)); + } + } + } + + @Test + public void testSetObject() { + ByteSparseNdArray instance = + new ByteSparseNdArray(indices, values, DimensionalSpace.create(shape)); + + assertThrows(java.nio.ReadOnlyBufferException.class, () -> instance.setObject((byte) 0, 0, 0)); + } + + @Test + public void testSet() { + ByteSparseNdArray instance = + new ByteSparseNdArray(indices, values, DimensionalSpace.create(shape)); + + assertThrows( + java.nio.ReadOnlyBufferException.class, + () -> instance.set(instance.getDefaultArray(), 0, 0)); + } + + @Test + public void testSort() { + + long[][] indicesArray = {{0, 0}, {1, 2}, {0, 1}, {2, 3}, {1, 4}}; + long[][] sortedIndicesArray = {{0, 0}, {0, 1}, {1, 2}, {1, 4}, {2, 3}}; + byte[] valuesArray = {1, 3, 2, 5, 4}; + byte[] sortedValuesArray = {1, 2, 3, 4, 5}; + + LongNdArray indices = StdArrays.ndCopyOf(indicesArray); + LongNdArray sortedIndices = StdArrays.ndCopyOf(sortedIndicesArray); + ByteNdArray values = StdArrays.ndCopyOf(valuesArray); + ByteNdArray sortedValues = StdArrays.ndCopyOf(sortedValuesArray); + + ByteSparseNdArray instance = + new ByteSparseNdArray(indices, values, DimensionalSpace.create(shape)); + + instance.sortIndicesAndValues(); + + // should be sorted in ascending row-wise coordinate order based on test values + assertEquals(sortedIndices, instance.getIndices()); + assertEquals(sortedValues, instance.getValues()); + } + + @Test + public void testElements() { + + ByteSparseNdArray instance = + new ByteSparseNdArray(indices, values, DimensionalSpace.create(shape)); + + instance + .elements(0) + .forEachIndexed( + (idx, item) -> { + byte[] slice = dense2DArray[(int) idx[0]]; + item.scalars() + .forEachIndexed((dx, f) -> assertEquals(slice[(int) dx[0]], f.getObject())); + }); + } + + @Test + public void testDense() { + + ByteSparseNdArray instance = + new ByteSparseNdArray(indices, values, DimensionalSpace.create(shape)); + ByteNdArray denseInstance = instance.toDense(); + ByteNdArray expectedDense = StdArrays.ndCopyOf(dense2DArray); + assertEquals(expectedDense, denseInstance); + } + + @Test + public void testFromDense() { + ByteNdArray ndArray = StdArrays.ndCopyOf(dense2DArray); + ByteSparseNdArray instance = ByteSparseNdArray.create(DimensionalSpace.create(ndArray.shape())); + instance.fromDense(ndArray); + assertNotNull(instance.getIndices()); + assertEquals(2, instance.getIndices().shape().get(0)); + assertNotNull(instance.getValues()); + assertEquals(2, instance.getValues().size()); + + assertEquals(ndArray.shape(), instance.shape()); + for (int n = 0; n < ndArray.shape().get(0); n++) { + for (int m = 0; m < ndArray.shape().get(1); m++) { + assertEquals(ndArray.getByte(n, m), instance.getByte(n, m)); + } + } + } + + @Test + public void testElements1() { + byte[] expected = {1, 0, 0}; + + ByteSparseNdArray instance = + new ByteSparseNdArray(indices, values, DimensionalSpace.create(shape)); + instance + .elements(0) + .forEachIndexed((idx, l) -> assertEquals(expected[(int) idx[0]], l.getObject())); + instance + .elements(1) + .forEachIndexed( + (idx, l) -> assertEquals(dense2DArray[(int) idx[0]][(int) idx[1]], l.getObject())); + } + + @Test + public void testCopyTo() { + ByteNdArray dst = NdArrays.ofBytes(shape); + ByteSparseNdArray instance = + new ByteSparseNdArray(indices, values, DimensionalSpace.create(shape)); + instance.copyTo(dst); + for (int n = 0; n < instance.shape().get(0); n++) { + for (int m = 0; m < instance.shape().get(1); m++) { + assertEquals(instance.getByte(n, m), dst.getByte(n, m)); + } + } + } + + @Test + public void testCreate() { + + ByteSparseNdArray instance = + new ByteSparseNdArray(indices, values, DimensionalSpace.create(shape)); + ByteSparseNdArray instanceA = + ByteSparseNdArray.create(indices, values, DimensionalSpace.create(shape)); + assertEquals(instance, instanceA); + + ByteDataBuffer dataBuffer = RawDataBufferFactory.create(denseArray, false); + // use a zero buffer + ByteSparseNdArray instanceB = ByteSparseNdArray.create(DimensionalSpace.create(shape)); + instanceB.copyFrom(dataBuffer); + assertEquals(instance, instanceB); + + ByteSparseNdArray instanceC = + ByteSparseNdArray.create(dataBuffer, DimensionalSpace.create(shape)); + assertEquals(instanceB, instanceC); + + ByteSparseNdArray instanceD = ByteSparseNdArray.create(dataBuffer, shape); + assertEquals(instanceB, instanceD); + + ByteNdArray ndArray = StdArrays.ndCopyOf(dense2DArray); + ByteSparseNdArray instanceE = ByteSparseNdArray.create(ndArray); + assertEquals(instance, instanceE); + } + + @Test + public void testSlice() { + byte[] expected = {0, 0, 16, 0, 0, 0}; + ByteSparseNdArray instance = + new ByteSparseNdArray(indices, values, DimensionalSpace.create(shape)); + + ByteNdArray sliceInstance = instance.slice(Indices.all(), Indices.sliceFrom(2)); + // check the values of the slice against the original sparse array + AtomicInteger i = new AtomicInteger(); + sliceInstance + .scalars() + .forEachIndexed((idx, f) -> assertEquals(expected[i.getAndIncrement()], f.getByte())); + // check values from elements(0) of a slice against the original sparse array + i.set(0); + sliceInstance + .elements(0) + .forEachIndexed( + (idx, l) -> + l.scalars() + .forEachIndexed( + (lidx, f) -> assertEquals(expected[i.getAndIncrement()], f.getByte()))); + } +} diff --git a/ndarray/src/test/java/org/tensorflow/ndarray/impl/sparse/DoubleSparseNdArrayTest.java b/ndarray/src/test/java/org/tensorflow/ndarray/impl/sparse/DoubleSparseNdArrayTest.java new file mode 100644 index 0000000..f7ff65f --- /dev/null +++ b/ndarray/src/test/java/org/tensorflow/ndarray/impl/sparse/DoubleSparseNdArrayTest.java @@ -0,0 +1,317 @@ +package org.tensorflow.ndarray.impl.sparse; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.tensorflow.ndarray.DoubleNdArray; +import org.tensorflow.ndarray.LongNdArray; +import org.tensorflow.ndarray.NdArrays; +import org.tensorflow.ndarray.Shape; +import org.tensorflow.ndarray.StdArrays; +import org.tensorflow.ndarray.buffer.DataBuffers; +import org.tensorflow.ndarray.buffer.DoubleDataBuffer; +import org.tensorflow.ndarray.impl.buffer.nio.NioDataBufferFactory; +import org.tensorflow.ndarray.impl.buffer.raw.RawDataBufferFactory; +import org.tensorflow.ndarray.impl.dimension.DimensionalSpace; +import org.tensorflow.ndarray.index.Indices; + +import java.nio.DoubleBuffer; +import java.util.Arrays; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class DoubleSparseNdArrayTest { + long[][] indicesArray = {{0, 0}, {1, 2}}; + double[] valuesArray = {1, 256}; + double[] denseArray = { + 1, 0, 0, 0, + 0, 0, 256, 0, + 0, 0, 0, 0 + }; + double[][] dense2DArray = {{1, 0, 0, 0}, {0, 0, 256, 0}, {0, 0, 0, 0}}; + + double[][] dense2DArrayDefaultValue = {{1, -1, -1, -1}, {-1, -1, 256, -1}, {-1, -1, -1, -1}}; + + Shape shape = Shape.of(3, 4); + LongNdArray indices = StdArrays.ndCopyOf(indicesArray); + DoubleNdArray values = StdArrays.ndCopyOf(valuesArray); + + @Test + public void testBasic() { + DoubleSparseNdArray instance = + new DoubleSparseNdArray(indices, values, DimensionalSpace.create(shape)); + assertEquals(indices, instance.getIndices()); + assertEquals(values, instance.getValues()); + assertEquals(shape, instance.shape()); + } + + @Test + public void testCopyToBuffer() { + DoubleSparseNdArray instance = + new DoubleSparseNdArray(indices, values, DimensionalSpace.create(shape)); + DoubleDataBuffer dataBuffer = DataBuffers.ofDoubles(instance.shape().size()); + + instance.copyTo(dataBuffer); + + double[] array = new double[denseArray.length]; + dataBuffer.read(array); + assertArrayEquals(denseArray, array); + } + + @Test + public void testCopyFromBuffer() { + + DoubleDataBuffer dataBuffer = NioDataBufferFactory.create(DoubleBuffer.wrap(denseArray)); + // use a zero buffer + DoubleSparseNdArray instance = DoubleSparseNdArray.create(DimensionalSpace.create(shape)); + instance.copyFrom(dataBuffer); + + assertEquals(indices, instance.getIndices()); + assertEquals(values, instance.getValues()); + } + + @Test + public void testWriteDefaultValue() { + // change 0 to -1 + double[] denseArrayDefaultValue = Arrays.stream(denseArray).map(x -> x == 0 ? -1 : x).toArray(); + DoubleDataBuffer dataBuffer = RawDataBufferFactory.create(denseArrayDefaultValue, false); + // use a zero buffer + DoubleSparseNdArray instance = DoubleSparseNdArray.create(-1d, DimensionalSpace.create(shape)); + instance.copyFrom(dataBuffer); + + assertEquals(indices, instance.getIndices()); + assertEquals(values, instance.getValues()); + assertEquals(-1d, instance.getDouble(2, 0)); + } + + @Test + public void testGetObject() { + + DoubleNdArray ndArray = StdArrays.ndCopyOf(dense2DArray); + DoubleSparseNdArray instance = + new DoubleSparseNdArray(indices, values, DimensionalSpace.create(shape)); + + for (int n = 0; n < ndArray.shape().get(0); n++) { + for (int m = 0; m < ndArray.shape().get(1); m++) { + assertEquals(ndArray.getObject(n, m), instance.getObject(n, m)); + } + } + } + + @Test + public void testGetDouble() { + DoubleNdArray ndArray = StdArrays.ndCopyOf(dense2DArray); + DoubleSparseNdArray instance = + new DoubleSparseNdArray(indices, values, DimensionalSpace.create(shape)); + + for (int n = 0; n < ndArray.shape().get(0); n++) { + for (int m = 0; m < ndArray.shape().get(1); m++) { + assertEquals(ndArray.getDouble(n, m), instance.getDouble(n, m)); + } + } + } + + @Test + public void testGetDoubleDefaultValue() { + DoubleNdArray ndArray = StdArrays.ndCopyOf(dense2DArrayDefaultValue); + DoubleSparseNdArray instance = + new DoubleSparseNdArray(indices, values, -1d, DimensionalSpace.create(shape)); + + for (int n = 0; n < ndArray.shape().get(0); n++) { + for (int m = 0; m < ndArray.shape().get(1); m++) { + assertEquals(ndArray.getDouble(n, m), instance.getDouble(n, m)); + } + } + } + + @Test + public void testGet() { + DoubleNdArray ndArray = StdArrays.ndCopyOf(dense2DArray); + DoubleSparseNdArray instance = + new DoubleSparseNdArray(indices, values, DimensionalSpace.create(shape)); + + for (int n = 0; n < ndArray.shape().get(0); n++) { + assertEquals(ndArray.get(n), instance.get(n)); + for (int m = 0; m < ndArray.shape().get(1); m++) { + assertEquals(ndArray.get(n, m), instance.get(n, m)); + } + } + } + + @Test + public void testSetObject() { + DoubleSparseNdArray instance = + new DoubleSparseNdArray(indices, values, DimensionalSpace.create(shape)); + + assertThrows(java.nio.ReadOnlyBufferException.class, () -> instance.setObject(2d, 0, 0)); + } + + @Test + public void testSet() { + DoubleSparseNdArray instance = + new DoubleSparseNdArray(indices, values, DimensionalSpace.create(shape)); + + assertThrows( + java.nio.ReadOnlyBufferException.class, + () -> instance.set(instance.getDefaultArray(), 0, 0)); + } + + @Test + public void testSort() { + + long[][] indicesArray = {{0, 0}, {1, 2}, {0, 1}, {2, 3}, {1, 4}}; + long[][] sortedIndicesArray = {{0, 0}, {0, 1}, {1, 2}, {1, 4}, {2, 3}}; + double[] valuesArray = {1, 3, 2, 5, 4}; + double[] sortedValuesArray = {1, 2, 3, 4, 5}; + + LongNdArray indices = StdArrays.ndCopyOf(indicesArray); + LongNdArray sortedIndices = StdArrays.ndCopyOf(sortedIndicesArray); + DoubleNdArray values = StdArrays.ndCopyOf(valuesArray); + DoubleNdArray sortedValues = StdArrays.ndCopyOf(sortedValuesArray); + + DoubleSparseNdArray instance = + new DoubleSparseNdArray(indices, values, DimensionalSpace.create(shape)); + + instance.sortIndicesAndValues(); + + // should be sorted in ascending row-wise coordinate order based on test values + assertEquals(sortedIndices, instance.getIndices()); + assertEquals(sortedValues, instance.getValues()); + } + + @Test + public void testElements() { + + DoubleSparseNdArray instance = + new DoubleSparseNdArray(indices, values, DimensionalSpace.create(shape)); + + instance + .elements(0) + .forEachIndexed( + (idx, item) -> { + double[] slice = dense2DArray[(int) idx[0]]; + item.scalars() + .forEachIndexed((dx, f) -> assertEquals(slice[(int) dx[0]], f.getObject())); + }); + } + + @Test + public void testDense() { + + DoubleSparseNdArray instance = + new DoubleSparseNdArray(indices, values, DimensionalSpace.create(shape)); + DoubleNdArray denseInstance = instance.toDense(); + DoubleNdArray expectedDense = StdArrays.ndCopyOf(dense2DArray); + assertEquals(expectedDense, denseInstance); + } + + @Test + public void testFromDense() { + DoubleNdArray ndArray = StdArrays.ndCopyOf(dense2DArray); + DoubleSparseNdArray instance = + DoubleSparseNdArray.create(DimensionalSpace.create(ndArray.shape())); + instance.fromDense(ndArray); + assertNotNull(instance.getIndices()); + assertEquals(2, instance.getIndices().shape().get(0)); + assertNotNull(instance.getValues()); + assertEquals(2, instance.getValues().size()); + + assertEquals(ndArray.shape(), instance.shape()); + for (int n = 0; n < ndArray.shape().get(0); n++) { + for (int m = 0; m < ndArray.shape().get(1); m++) { + assertEquals(ndArray.getDouble(n, m), instance.getDouble(n, m)); + } + } + } + + @Test + public void testElements1() { + double[] expected = {1, 0, 0}; + + DoubleSparseNdArray instance = + new DoubleSparseNdArray(indices, values, DimensionalSpace.create(shape)); + instance + .elements(0) + .forEachIndexed((idx, l) -> assertEquals(expected[(int) idx[0]], l.getObject())); + instance + .elements(1) + .forEachIndexed( + (idx, l) -> assertEquals(dense2DArray[(int) idx[0]][(int) idx[1]], l.getObject())); + } + + @Test + public void testCopyTo() { + DoubleNdArray dst = NdArrays.ofDoubles(shape); + DoubleSparseNdArray instance = + new DoubleSparseNdArray(indices, values, DimensionalSpace.create(shape)); + instance.copyTo(dst); + for (int n = 0; n < instance.shape().get(0); n++) { + for (int m = 0; m < instance.shape().get(1); m++) { + assertEquals(instance.getDouble(n, m), dst.getDouble(n, m)); + } + } + } + + @Test + public void testCreate() { + DoubleSparseNdArray instance = + new DoubleSparseNdArray(indices, values, DimensionalSpace.create(shape)); + DoubleSparseNdArray instanceA = + DoubleSparseNdArray.create(indices, values, DimensionalSpace.create(shape)); + assertEquals(instance, instanceA); + + DoubleDataBuffer dataBuffer = RawDataBufferFactory.create(denseArray, false); + // use a zero buffer + DoubleSparseNdArray instanceB = DoubleSparseNdArray.create(DimensionalSpace.create(shape)); + instanceB.copyFrom(dataBuffer); + assertEquals(instance, instanceB); + + DoubleSparseNdArray instanceC = + DoubleSparseNdArray.create(dataBuffer, DimensionalSpace.create(shape)); + assertEquals(instanceB, instanceC); + + DoubleSparseNdArray instanceD = DoubleSparseNdArray.create(dataBuffer, shape); + assertEquals(instanceB, instanceD); + + DoubleNdArray ndArray = StdArrays.ndCopyOf(dense2DArray); + DoubleSparseNdArray instanceE = DoubleSparseNdArray.create(ndArray); + assertEquals(instance, instanceE); + } + + @Test + public void testSlice() { + double[] expected = {0, 0, 256, 0, 0, 0}; + DoubleSparseNdArray instance = + new DoubleSparseNdArray(indices, values, DimensionalSpace.create(shape)); + + DoubleNdArray sliceInstance = instance.slice(Indices.all(), Indices.sliceFrom(2)); + // check the values of the slice against the original sparse array + AtomicInteger i = new AtomicInteger(); + sliceInstance + .scalars() + .forEachIndexed((idx, f) -> assertEquals(expected[i.getAndIncrement()], f.getDouble())); + // check values from elements(0) of a slice against the original sparse array + i.set(0); + sliceInstance + .elements(0) + .forEachIndexed( + (idx, l) -> + l.scalars() + .forEachIndexed( + (lidx, f) -> assertEquals(expected[i.getAndIncrement()], f.getDouble()))); + } + + @Test + public void testToString() { + DoubleNdArray ndArray = StdArrays.ndCopyOf(dense2DArray); + DoubleSparseNdArray instance = + DoubleSparseNdArray.create(DimensionalSpace.create(ndArray.shape())); + instance.fromDense(ndArray); + Assertions.assertEquals("DoubleSparseNdArray(defaultValue=0.0, numElements=2, shape=[3, 4])",instance.toString()); + DoubleSparseNdArray empty = DoubleSparseNdArray.create(DimensionalSpace.create(Shape.of(5))); + Assertions.assertEquals("DoubleSparseNdArray(defaultValue=0.0, numElements=0, shape=[5])",empty.toString()); + } +} diff --git a/ndarray/src/test/java/org/tensorflow/ndarray/impl/sparse/FloatSparseNdArrayTest.java b/ndarray/src/test/java/org/tensorflow/ndarray/impl/sparse/FloatSparseNdArrayTest.java new file mode 100644 index 0000000..d948d1e --- /dev/null +++ b/ndarray/src/test/java/org/tensorflow/ndarray/impl/sparse/FloatSparseNdArrayTest.java @@ -0,0 +1,314 @@ +package org.tensorflow.ndarray.impl.sparse; + +import org.junit.jupiter.api.Test; +import org.tensorflow.ndarray.FloatNdArray; +import org.tensorflow.ndarray.LongNdArray; +import org.tensorflow.ndarray.NdArrays; +import org.tensorflow.ndarray.Shape; +import org.tensorflow.ndarray.StdArrays; +import org.tensorflow.ndarray.buffer.DataBuffers; +import org.tensorflow.ndarray.buffer.FloatDataBuffer; +import org.tensorflow.ndarray.impl.buffer.nio.NioDataBufferFactory; +import org.tensorflow.ndarray.impl.buffer.raw.RawDataBufferFactory; +import org.tensorflow.ndarray.impl.dimension.DimensionalSpace; +import org.tensorflow.ndarray.index.Indices; + +import java.nio.FloatBuffer; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class FloatSparseNdArrayTest { + long[][] indicesArray = {{0, 0}, {1, 2}}; + float[] valuesArray = {1, 2}; + float[] denseArray = { + 1, 0, 0, 0, + 0, 0, 2, 0, + 0, 0, 0, 0 + }; + + float[][] dense2DArrayDefaultValue = {{1, -1, -1, -1}, {-1, -1, 2, -1}, {-1, -1, -1, -1}}; + + Shape shape = Shape.of(3, 4); + LongNdArray indices = StdArrays.ndCopyOf(indicesArray); + FloatNdArray values = StdArrays.ndCopyOf(valuesArray); + + @Test + public void testBasic() { + FloatSparseNdArray instance = + new FloatSparseNdArray(indices, values, DimensionalSpace.create(shape)); + assertEquals(indices, instance.getIndices()); + assertEquals(values, instance.getValues()); + assertEquals(shape, instance.shape()); + } + + @Test + public void testCopyToBuffer() { + FloatSparseNdArray instance = + new FloatSparseNdArray(indices, values, DimensionalSpace.create(shape)); + FloatDataBuffer dataBuffer = DataBuffers.ofFloats(instance.shape().size()); + + instance.copyTo(dataBuffer); + + float[] array = new float[denseArray.length]; + dataBuffer.read(array); + assertArrayEquals(denseArray, array); + } + + @Test + public void testCopyFromBuffer() { + + FloatDataBuffer dataBuffer = NioDataBufferFactory.create(FloatBuffer.wrap(denseArray)); + // use a zero buffer + FloatSparseNdArray instance = FloatSparseNdArray.create(DimensionalSpace.create(shape)); + instance.copyFrom(dataBuffer); + + assertEquals(indices, instance.getIndices()); + assertEquals(values, instance.getValues()); + } + + @Test + public void testWriteDefaultValue() { + // change 0 to -1 + float[] denseArrayDefaultValue = new float[denseArray.length]; + for (int i = 0; i < denseArrayDefaultValue.length; i++) { + denseArrayDefaultValue[i] = denseArray[i] == 0f ? -1f : denseArray[i]; + } + FloatDataBuffer dataBuffer = RawDataBufferFactory.create(denseArrayDefaultValue, false); + // use a zero buffer + FloatSparseNdArray instance = FloatSparseNdArray.create(-1f, DimensionalSpace.create(shape)); + instance.copyFrom(dataBuffer); + + assertEquals(indices, instance.getIndices()); + assertEquals(values, instance.getValues()); + assertEquals(-1f, instance.getFloat(2, 0)); + } + + @Test + public void testGetObject() { + float[][] dense2DArray = {{1, 0, 0, 0}, {0, 0, 2, 0}, {0, 0, 0, 0}}; + FloatNdArray ndArray = StdArrays.ndCopyOf(dense2DArray); + FloatSparseNdArray instance = + new FloatSparseNdArray(indices, values, DimensionalSpace.create(shape)); + + for (int n = 0; n < ndArray.shape().get(0); n++) { + for (int m = 0; m < ndArray.shape().get(1); m++) { + assertEquals(ndArray.getObject(n, m), instance.getObject(n, m)); + } + } + } + + @Test + public void testGetFloat() { + float[][] dense2DArray = {{1, 0, 0, 0}, {0, 0, 2, 0}, {0, 0, 0, 0}}; + FloatNdArray ndArray = StdArrays.ndCopyOf(dense2DArray); + FloatSparseNdArray instance = + new FloatSparseNdArray(indices, values, DimensionalSpace.create(shape)); + + for (int n = 0; n < ndArray.shape().get(0); n++) { + for (int m = 0; m < ndArray.shape().get(1); m++) { + assertEquals(ndArray.getFloat(n, m), instance.getFloat(n, m)); + } + } + } + + @Test + public void testGetFloatDefaultValue() { + FloatNdArray ndArray = StdArrays.ndCopyOf(dense2DArrayDefaultValue); + FloatSparseNdArray instance = + new FloatSparseNdArray(indices, values, -1, DimensionalSpace.create(shape)); + + for (int n = 0; n < ndArray.shape().get(0); n++) { + for (int m = 0; m < ndArray.shape().get(1); m++) { + assertEquals(ndArray.getFloat(n, m), instance.getFloat(n, m)); + } + } + } + + @Test + public void testGet() { + float[][] dense2DArray = {{1, 0, 0, 0}, {0, 0, 2, 0}, {0, 0, 0, 0}}; + FloatNdArray ndArray = StdArrays.ndCopyOf(dense2DArray); + FloatSparseNdArray instance = + new FloatSparseNdArray(indices, values, DimensionalSpace.create(shape)); + + for (int n = 0; n < ndArray.shape().get(0); n++) { + assertEquals(ndArray.get(n), instance.get(n)); + for (int m = 0; m < ndArray.shape().get(1); m++) { + assertEquals(ndArray.get(n, m), instance.get(n, m)); + } + } + } + + @Test + public void testSetObject() { + FloatSparseNdArray instance = + new FloatSparseNdArray(indices, values, DimensionalSpace.create(shape)); + + assertThrows(java.nio.ReadOnlyBufferException.class, () -> instance.setObject(2f, 0, 0)); + } + + @Test + public void testSet() { + FloatSparseNdArray instance = + new FloatSparseNdArray(indices, values, DimensionalSpace.create(shape)); + + assertThrows( + java.nio.ReadOnlyBufferException.class, + () -> instance.set(instance.getDefaultArray(), 0, 0)); + } + + @Test + public void testSort() { + + long[][] indicesArray = {{0, 0}, {1, 2}, {0, 1}, {2, 3}, {1, 4}}; + long[][] sortedIndicesArray = {{0, 0}, {0, 1}, {1, 2}, {1, 4}, {2, 3}}; + float[] valuesArray = {1, 3, 2, 5, 4}; + float[] sortedValuesArray = {1, 2, 3, 4, 5}; + + LongNdArray indices = StdArrays.ndCopyOf(indicesArray); + LongNdArray sortedIndices = StdArrays.ndCopyOf(sortedIndicesArray); + FloatNdArray values = StdArrays.ndCopyOf(valuesArray); + FloatNdArray sortedValues = StdArrays.ndCopyOf(sortedValuesArray); + + FloatSparseNdArray instance = + new FloatSparseNdArray(indices, values, DimensionalSpace.create(shape)); + + instance.sortIndicesAndValues(); + + // should be sorted in ascending row-wise coordinate order based on test values + assertEquals(sortedIndices, instance.getIndices()); + assertEquals(sortedValues, instance.getValues()); + } + + @Test + public void testElements() { + + FloatSparseNdArray instance = + new FloatSparseNdArray(indices, values, DimensionalSpace.create(shape)); + + float[][] dense2DArray = {{1, 0, 0, 0}, {0, 0, 2, 0}, {0, 0, 0, 0}}; + instance + .elements(0) + .forEachIndexed( + (idx, item) -> { + float[] slice = dense2DArray[(int) idx[0]]; + item.scalars() + .forEachIndexed((dx, f) -> assertEquals(slice[(int) dx[0]], f.getObject())); + }); + } + + @Test + public void testDense() { + float[][] dense2DArray = {{1, 0, 0, 0}, {0, 0, 2, 0}, {0, 0, 0, 0}}; + + FloatSparseNdArray instance = + new FloatSparseNdArray(indices, values, DimensionalSpace.create(shape)); + FloatNdArray denseInstance = instance.toDense(); + FloatNdArray expectedDense = StdArrays.ndCopyOf(dense2DArray); + assertEquals(expectedDense, denseInstance); + } + + @Test + public void testFromDense() { + float[][] dense2DArray = {{1, 0, 0, 0}, {0, 0, 2, 0}, {0, 0, 0, 0}}; + FloatNdArray ndArray = StdArrays.ndCopyOf(dense2DArray); + FloatSparseNdArray instance = + FloatSparseNdArray.create(DimensionalSpace.create(ndArray.shape())); + instance.fromDense(ndArray); + assertNotNull(instance.getIndices()); + assertEquals(2, instance.getIndices().shape().get(0)); + assertNotNull(instance.getValues()); + assertEquals(2, instance.getValues().size()); + + assertEquals(ndArray.shape(), instance.shape()); + for (int n = 0; n < ndArray.shape().get(0); n++) { + for (int m = 0; m < ndArray.shape().get(1); m++) { + assertEquals(ndArray.getFloat(n, m), instance.getFloat(n, m)); + } + } + } + + @Test + public void testElements1() { + float[] expected = {1, 0, 0}; + float[][] dense2DArray = {{1, 0, 0, 0}, {0, 0, 2, 0}, {0, 0, 0, 0}}; + + FloatSparseNdArray instance = + new FloatSparseNdArray(indices, values, DimensionalSpace.create(shape)); + instance + .elements(0) + .forEachIndexed((idx, l) -> assertEquals(expected[(int) idx[0]], l.getObject())); + instance + .elements(1) + .forEachIndexed( + (idx, l) -> assertEquals(dense2DArray[(int) idx[0]][(int) idx[1]], l.getObject())); + } + + @Test + public void testCopyTo() { + FloatNdArray dst = NdArrays.ofFloats(shape); + FloatSparseNdArray instance = + new FloatSparseNdArray(indices, values, DimensionalSpace.create(shape)); + instance.copyTo(dst); + for (int n = 0; n < instance.shape().get(0); n++) { + for (int m = 0; m < instance.shape().get(1); m++) { + assertEquals(instance.getFloat(n, m), dst.getFloat(n, m)); + } + } + } + + @Test + public void testCreate() { + float[] denseArray = {1, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0}; + float[][] dense2Array = {{1, 0, 0, 0}, {0, 0, 2, 0}, {0, 0, 0, 0}}; + FloatSparseNdArray instance = + new FloatSparseNdArray(indices, values, DimensionalSpace.create(shape)); + FloatSparseNdArray instanceA = + FloatSparseNdArray.create(indices, values, DimensionalSpace.create(shape)); + assertEquals(instance, instanceA); + + FloatDataBuffer dataBuffer = RawDataBufferFactory.create(denseArray, false); + // use a zero buffer + FloatSparseNdArray instanceB = FloatSparseNdArray.create(DimensionalSpace.create(shape)); + instanceB.copyFrom(dataBuffer); + assertEquals(instance, instanceB); + + FloatSparseNdArray instanceC = + FloatSparseNdArray.create(dataBuffer, DimensionalSpace.create(shape)); + assertEquals(instanceB, instanceC); + + FloatSparseNdArray instanceD = FloatSparseNdArray.create(dataBuffer, shape); + assertEquals(instanceB, instanceD); + + FloatNdArray ndArray = StdArrays.ndCopyOf(dense2Array); + FloatSparseNdArray instanceE = FloatSparseNdArray.create(ndArray); + assertEquals(instance, instanceE); + } + + @Test + public void testSlice() { + float[] expected = {0, 0, 2, 0, 0, 0}; + FloatSparseNdArray instance = + new FloatSparseNdArray(indices, values, DimensionalSpace.create(shape)); + + FloatNdArray sliceInstance = instance.slice(Indices.all(), Indices.sliceFrom(2)); + // check the values of the slice against the original sparse array + AtomicInteger i = new AtomicInteger(); + sliceInstance + .scalars() + .forEachIndexed((idx, f) -> assertEquals(expected[i.getAndIncrement()], f.getFloat())); + // check values from elements(0) of a slice against the original sparse array + i.set(0); + sliceInstance + .elements(0) + .forEachIndexed( + (idx, l) -> + l.scalars() + .forEachIndexed( + (lidx, f) -> assertEquals(expected[i.getAndIncrement()], f.getFloat()))); + } +} diff --git a/ndarray/src/test/java/org/tensorflow/ndarray/impl/sparse/IntSparseNdArrayTest.java b/ndarray/src/test/java/org/tensorflow/ndarray/impl/sparse/IntSparseNdArrayTest.java new file mode 100644 index 0000000..b60b7db --- /dev/null +++ b/ndarray/src/test/java/org/tensorflow/ndarray/impl/sparse/IntSparseNdArrayTest.java @@ -0,0 +1,312 @@ +package org.tensorflow.ndarray.impl.sparse; + +import org.junit.jupiter.api.Test; +import org.tensorflow.ndarray.IntNdArray; +import org.tensorflow.ndarray.LongNdArray; +import org.tensorflow.ndarray.NdArrays; +import org.tensorflow.ndarray.Shape; +import org.tensorflow.ndarray.StdArrays; +import org.tensorflow.ndarray.buffer.DataBuffers; +import org.tensorflow.ndarray.buffer.IntDataBuffer; +import org.tensorflow.ndarray.impl.buffer.nio.NioDataBufferFactory; +import org.tensorflow.ndarray.impl.buffer.raw.RawDataBufferFactory; +import org.tensorflow.ndarray.impl.dimension.DimensionalSpace; +import org.tensorflow.ndarray.index.Indices; + +import java.nio.IntBuffer; +import java.util.Arrays; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class IntSparseNdArrayTest { + long[][] indicesArray = {{0, 0}, {1, 2}}; + int[] valuesArray = {1, 2}; + int[] denseArray = { + 1, 0, 0, 0, + 0, 0, 2, 0, + 0, 0, 0, 0 + }; + + int[][] dense2DArrayDefaultValue = {{1, -1, -1, -1}, {-1, -1, 2, -1}, {-1, -1, -1, -1}}; + + Shape shape = Shape.of(3, 4); + LongNdArray indices = StdArrays.ndCopyOf(indicesArray); + IntNdArray values = StdArrays.ndCopyOf(valuesArray); + + @Test + public void testBasic() { + IntSparseNdArray instance = + new IntSparseNdArray(indices, values, DimensionalSpace.create(shape)); + assertEquals(indices, instance.getIndices()); + assertEquals(values, instance.getValues()); + assertEquals(shape, instance.shape()); + } + + @Test + public void testCopyToBuffer() { + IntSparseNdArray instance = + new IntSparseNdArray(indices, values, DimensionalSpace.create(shape)); + IntDataBuffer dataBuffer = DataBuffers.ofInts(instance.shape().size()); + + instance.copyTo(dataBuffer); + + int[] array = new int[denseArray.length]; + dataBuffer.read(array); + assertArrayEquals(denseArray, array); + } + + @Test + public void testCopyFromBufferBuffer() { + + IntDataBuffer dataBuffer = NioDataBufferFactory.create(IntBuffer.wrap(denseArray)); + // use a zero buffer + IntSparseNdArray instance = IntSparseNdArray.create(DimensionalSpace.create(shape)); + instance.copyFrom(dataBuffer); + + assertEquals(indices, instance.getIndices()); + assertEquals(values, instance.getValues()); + } + + @Test + public void testWriteDefaultValue() { + // change 0 to -1 + int[] denseArrayDefaultValue = Arrays.stream(denseArray).map(x -> x == 0 ? -1 : x).toArray(); + + IntDataBuffer dataBuffer = RawDataBufferFactory.create(denseArrayDefaultValue, false); + // use a zero buffer + IntSparseNdArray instance = IntSparseNdArray.create(-1, DimensionalSpace.create(shape)); + instance.copyFrom(dataBuffer); + + assertEquals(indices, instance.getIndices()); + assertEquals(values, instance.getValues()); + assertEquals(-1, instance.getInt(2, 0)); + } + + @Test + public void testGetObject() { + int[][] dense2DArray = {{1, 0, 0, 0}, {0, 0, 2, 0}, {0, 0, 0, 0}}; + IntNdArray ndArray = StdArrays.ndCopyOf(dense2DArray); + IntSparseNdArray instance = + new IntSparseNdArray(indices, values, DimensionalSpace.create(shape)); + + for (int n = 0; n < ndArray.shape().get(0); n++) { + for (int m = 0; m < ndArray.shape().get(1); m++) { + assertEquals(ndArray.getObject(n, m), instance.getObject(n, m)); + } + } + } + + @Test + public void testGetInt() { + int[][] dense2DArray = {{1, 0, 0, 0}, {0, 0, 2, 0}, {0, 0, 0, 0}}; + IntNdArray ndArray = StdArrays.ndCopyOf(dense2DArray); + IntSparseNdArray instance = + new IntSparseNdArray(indices, values, DimensionalSpace.create(shape)); + + for (int n = 0; n < ndArray.shape().get(0); n++) { + for (int m = 0; m < ndArray.shape().get(1); m++) { + assertEquals(ndArray.getInt(n, m), instance.getInt(n, m)); + } + } + } + + @Test + public void testGetIntDefaultValue() { + IntNdArray ndArray = StdArrays.ndCopyOf(dense2DArrayDefaultValue); + IntSparseNdArray instance = + new IntSparseNdArray(indices, values, -1, DimensionalSpace.create(shape)); + + for (int n = 0; n < ndArray.shape().get(0); n++) { + for (int m = 0; m < ndArray.shape().get(1); m++) { + assertEquals(ndArray.getInt(n, m), instance.getInt(n, m)); + } + } + } + + @Test + public void testGet() { + int[][] dense2DArray = {{1, 0, 0, 0}, {0, 0, 2, 0}, {0, 0, 0, 0}}; + IntNdArray ndArray = StdArrays.ndCopyOf(dense2DArray); + IntSparseNdArray instance = + new IntSparseNdArray(indices, values, DimensionalSpace.create(shape)); + + for (int n = 0; n < ndArray.shape().get(0); n++) { + assertEquals(ndArray.get(n), instance.get(n)); + for (int m = 0; m < ndArray.shape().get(1); m++) { + assertEquals(ndArray.get(n, m), instance.get(n, m)); + } + } + } + + @Test + public void testSetObject() { + IntSparseNdArray instance = + new IntSparseNdArray(indices, values, DimensionalSpace.create(shape)); + + assertThrows(java.nio.ReadOnlyBufferException.class, () -> instance.setObject(2, 0, 0)); + } + + @Test + public void testSet() { + IntSparseNdArray instance = + new IntSparseNdArray(indices, values, DimensionalSpace.create(shape)); + + assertThrows( + java.nio.ReadOnlyBufferException.class, + () -> instance.set(instance.getDefaultArray(), 0, 0)); + } + + @Test + public void testSort() { + + long[][] indicesArray = {{0, 0}, {1, 2}, {0, 1}, {2, 3}, {1, 4}}; + long[][] sortedIndicesArray = {{0, 0}, {0, 1}, {1, 2}, {1, 4}, {2, 3}}; + int[] valuesArray = {1, 3, 2, 5, 4}; + int[] sortedValuesArray = {1, 2, 3, 4, 5}; + + LongNdArray indices = StdArrays.ndCopyOf(indicesArray); + LongNdArray sortedIndices = StdArrays.ndCopyOf(sortedIndicesArray); + IntNdArray values = StdArrays.ndCopyOf(valuesArray); + IntNdArray sortedValues = StdArrays.ndCopyOf(sortedValuesArray); + + IntSparseNdArray instance = + new IntSparseNdArray(indices, values, DimensionalSpace.create(shape)); + + instance.sortIndicesAndValues(); + + // should be sorted in ascending row-wise coordinate order based on test values + assertEquals(sortedIndices, instance.getIndices()); + assertEquals(sortedValues, instance.getValues()); + } + + @Test + public void testElements() { + + IntSparseNdArray instance = + new IntSparseNdArray(indices, values, DimensionalSpace.create(shape)); + + int[][] dense2DArray = {{1, 0, 0, 0}, {0, 0, 2, 0}, {0, 0, 0, 0}}; + instance + .elements(0) + .forEachIndexed( + (idx, item) -> { + int[] slice = dense2DArray[(int) idx[0]]; + item.scalars() + .forEachIndexed((dx, f) -> assertEquals(slice[(int) dx[0]], f.getObject())); + }); + } + + @Test + public void testDense() { + int[][] dense2DArray = {{1, 0, 0, 0}, {0, 0, 2, 0}, {0, 0, 0, 0}}; + + IntSparseNdArray instance = + new IntSparseNdArray(indices, values, DimensionalSpace.create(shape)); + IntNdArray denseInstance = instance.toDense(); + IntNdArray expectedDense = StdArrays.ndCopyOf(dense2DArray); + assertEquals(expectedDense, denseInstance); + } + + @Test + public void testFromDense() { + int[][] dense2DArray = {{1, 0, 0, 0}, {0, 0, 2, 0}, {0, 0, 0, 0}}; + IntNdArray ndArray = StdArrays.ndCopyOf(dense2DArray); + IntSparseNdArray instance = IntSparseNdArray.create(DimensionalSpace.create(ndArray.shape())); + instance.fromDense(ndArray); + assertNotNull(instance.getIndices()); + assertEquals(2, instance.getIndices().shape().get(0)); + assertNotNull(instance.getValues()); + assertEquals(2, instance.getValues().size()); + + assertEquals(ndArray.shape(), instance.shape()); + for (int n = 0; n < ndArray.shape().get(0); n++) { + for (int m = 0; m < ndArray.shape().get(1); m++) { + assertEquals(ndArray.getInt(n, m), instance.getInt(n, m)); + } + } + } + + @Test + public void testElements1() { + int[] expected = {1, 0, 0}; + int[][] dense2DArray = {{1, 0, 0, 0}, {0, 0, 2, 0}, {0, 0, 0, 0}}; + + IntSparseNdArray instance = + new IntSparseNdArray(indices, values, DimensionalSpace.create(shape)); + instance + .elements(0) + .forEachIndexed((idx, l) -> assertEquals(expected[(int) idx[0]], l.getObject())); + instance + .elements(1) + .forEachIndexed( + (idx, l) -> assertEquals(dense2DArray[(int) idx[0]][(int) idx[1]], l.getObject())); + } + + @Test + public void testCopyTo() { + IntNdArray dst = NdArrays.ofInts(shape); + IntSparseNdArray instance = + new IntSparseNdArray(indices, values, DimensionalSpace.create(shape)); + instance.copyTo(dst); + for (int n = 0; n < instance.shape().get(0); n++) { + for (int m = 0; m < instance.shape().get(1); m++) { + assertEquals(instance.getInt(n, m), dst.getInt(n, m)); + } + } + } + + @Test + public void testCreate() { + int[] denseArray = {1, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0}; + int[][] dense2Array = {{1, 0, 0, 0}, {0, 0, 2, 0}, {0, 0, 0, 0}}; + IntSparseNdArray instance = + new IntSparseNdArray(indices, values, DimensionalSpace.create(shape)); + IntSparseNdArray instanceA = + IntSparseNdArray.create(indices, values, DimensionalSpace.create(shape)); + assertEquals(instance, instanceA); + + IntDataBuffer dataBuffer = RawDataBufferFactory.create(denseArray, false); + // use a zero buffer + IntSparseNdArray instanceB = IntSparseNdArray.create(DimensionalSpace.create(shape)); + instanceB.copyFrom(dataBuffer); + assertEquals(instance, instanceB); + + IntSparseNdArray instanceC = + IntSparseNdArray.create(dataBuffer, DimensionalSpace.create(shape)); + assertEquals(instanceB, instanceC); + + IntSparseNdArray instanceD = IntSparseNdArray.create(dataBuffer, shape); + assertEquals(instanceB, instanceD); + + IntNdArray ndArray = StdArrays.ndCopyOf(dense2Array); + IntSparseNdArray instanceE = IntSparseNdArray.create(ndArray); + assertEquals(instance, instanceE); + } + + @Test + public void testSlice() { + int[] expected = {0, 0, 2, 0, 0, 0}; + IntSparseNdArray instance = + new IntSparseNdArray(indices, values, DimensionalSpace.create(shape)); + + IntNdArray sliceInstance = instance.slice(Indices.all(), Indices.sliceFrom(2)); + // check the values of the slice against the original sparse array + AtomicInteger i = new AtomicInteger(); + sliceInstance + .scalars() + .forEachIndexed((idx, f) -> assertEquals(expected[i.getAndIncrement()], f.getInt())); + // check values from elements(0) of a slice against the original sparse array + i.set(0); + sliceInstance + .elements(0) + .forEachIndexed( + (idx, l) -> + l.scalars() + .forEachIndexed( + (lidx, f) -> assertEquals(expected[i.getAndIncrement()], f.getInt()))); + } +} diff --git a/ndarray/src/test/java/org/tensorflow/ndarray/impl/sparse/LongSparseNdArrayTest.java b/ndarray/src/test/java/org/tensorflow/ndarray/impl/sparse/LongSparseNdArrayTest.java new file mode 100644 index 0000000..7f9a136 --- /dev/null +++ b/ndarray/src/test/java/org/tensorflow/ndarray/impl/sparse/LongSparseNdArrayTest.java @@ -0,0 +1,311 @@ +package org.tensorflow.ndarray.impl.sparse; + +import org.junit.jupiter.api.Test; +import org.tensorflow.ndarray.LongNdArray; +import org.tensorflow.ndarray.NdArrays; +import org.tensorflow.ndarray.Shape; +import org.tensorflow.ndarray.StdArrays; +import org.tensorflow.ndarray.buffer.DataBuffers; +import org.tensorflow.ndarray.buffer.LongDataBuffer; +import org.tensorflow.ndarray.impl.buffer.nio.NioDataBufferFactory; +import org.tensorflow.ndarray.impl.buffer.raw.RawDataBufferFactory; +import org.tensorflow.ndarray.impl.dimension.DimensionalSpace; +import org.tensorflow.ndarray.index.Indices; + +import java.nio.LongBuffer; +import java.util.Arrays; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class LongSparseNdArrayTest { + long[][] indicesArray = {{0, 0}, {1, 2}}; + long[] valuesArray = {1, 2}; + long[] denseArray = { + 1, 0, 0, 0, + 0, 0, 2, 0, + 0, 0, 0, 0 + }; + + long[][] dense2DArrayDefaultValue = {{1, -1, -1, -1}, {-1, -1, 2, -1}, {-1, -1, -1, -1}}; + + Shape shape = Shape.of(3, 4); + LongNdArray indices = StdArrays.ndCopyOf(indicesArray); + LongNdArray values = StdArrays.ndCopyOf(valuesArray); + + @Test + public void testBasic() { + LongSparseNdArray instance = + new LongSparseNdArray(indices, values, DimensionalSpace.create(shape)); + assertEquals(indices, instance.getIndices()); + assertEquals(values, instance.getValues()); + assertEquals(shape, instance.shape()); + } + + @Test + public void testCopyToBuffer() { + LongSparseNdArray instance = + new LongSparseNdArray(indices, values, DimensionalSpace.create(shape)); + LongDataBuffer dataBuffer = DataBuffers.ofLongs(instance.shape().size()); + + instance.copyTo(dataBuffer); + + long[] array = new long[denseArray.length]; + dataBuffer.read(array); + assertArrayEquals(denseArray, array); + } + + @Test + public void testCopyFromBuffer() { + + LongDataBuffer dataBuffer = NioDataBufferFactory.create(LongBuffer.wrap(denseArray)); + // use a zero buffer + LongSparseNdArray instance = LongSparseNdArray.create(DimensionalSpace.create(shape)); + instance.copyFrom(dataBuffer); + + assertEquals(indices, instance.getIndices()); + assertEquals(values, instance.getValues()); + } + + @Test + public void testWriteDefaultValue() { + // change 0 to -1 + long[] denseArrayDefaultValue = Arrays.stream(denseArray).map(x -> x == 0 ? -1 : x).toArray(); + + LongDataBuffer dataBuffer = RawDataBufferFactory.create(denseArrayDefaultValue, false); + // use a zero buffer + LongSparseNdArray instance = LongSparseNdArray.create(-1L, DimensionalSpace.create(shape)); + instance.copyFrom(dataBuffer); + + assertEquals(indices, instance.getIndices()); + assertEquals(values, instance.getValues()); + assertEquals(-1L, instance.getLong(2, 0)); + } + + @Test + public void testGetObject() { + long[][] dense2DArray = {{1, 0, 0, 0}, {0, 0, 2, 0}, {0, 0, 0, 0}}; + LongNdArray ndArray = StdArrays.ndCopyOf(dense2DArray); + LongSparseNdArray instance = + new LongSparseNdArray(indices, values, DimensionalSpace.create(shape)); + + for (int n = 0; n < ndArray.shape().get(0); n++) { + for (int m = 0; m < ndArray.shape().get(1); m++) { + assertEquals(ndArray.getObject(n, m), instance.getObject(n, m)); + } + } + } + + @Test + public void testGetLong() { + long[][] dense2DArray = {{1, 0, 0, 0}, {0, 0, 2, 0}, {0, 0, 0, 0}}; + LongNdArray ndArray = StdArrays.ndCopyOf(dense2DArray); + LongSparseNdArray instance = + new LongSparseNdArray(indices, values, DimensionalSpace.create(shape)); + + for (int n = 0; n < ndArray.shape().get(0); n++) { + for (int m = 0; m < ndArray.shape().get(1); m++) { + assertEquals(ndArray.getLong(n, m), instance.getLong(n, m)); + } + } + } + + @Test + public void testGetLongDefaultValue() { + LongNdArray ndArray = StdArrays.ndCopyOf(dense2DArrayDefaultValue); + LongSparseNdArray instance = + new LongSparseNdArray(indices, values, -1L, DimensionalSpace.create(shape)); + + for (int n = 0; n < ndArray.shape().get(0); n++) { + for (int m = 0; m < ndArray.shape().get(1); m++) { + assertEquals(ndArray.getLong(n, m), instance.getLong(n, m)); + } + } + } + + @Test + public void testGet() { + long[][] dense2DArray = {{1, 0, 0, 0}, {0, 0, 2, 0}, {0, 0, 0, 0}}; + LongNdArray ndArray = StdArrays.ndCopyOf(dense2DArray); + LongSparseNdArray instance = + new LongSparseNdArray(indices, values, DimensionalSpace.create(shape)); + + for (int n = 0; n < ndArray.shape().get(0); n++) { + assertEquals(ndArray.get(n), instance.get(n)); + for (int m = 0; m < ndArray.shape().get(1); m++) { + assertEquals(ndArray.get(n, m), instance.get(n, m)); + } + } + } + + @Test + public void testSetObject() { + LongSparseNdArray instance = + new LongSparseNdArray(indices, values, DimensionalSpace.create(shape)); + + assertThrows(java.nio.ReadOnlyBufferException.class, () -> instance.setObject(2L, 0, 0)); + } + + @Test + public void testSet() { + LongSparseNdArray instance = + new LongSparseNdArray(indices, values, DimensionalSpace.create(shape)); + + assertThrows( + java.nio.ReadOnlyBufferException.class, + () -> instance.set(instance.getDefaultArray(), 0, 0)); + } + + @Test + public void testSort() { + + long[][] indicesArray = {{0, 0}, {1, 2}, {0, 1}, {2, 3}, {1, 4}}; + long[][] sortedIndicesArray = {{0, 0}, {0, 1}, {1, 2}, {1, 4}, {2, 3}}; + long[] valuesArray = {1, 3, 2, 5, 4}; + long[] sortedValuesArray = {1, 2, 3, 4, 5}; + + LongNdArray indices = StdArrays.ndCopyOf(indicesArray); + LongNdArray sortedIndices = StdArrays.ndCopyOf(sortedIndicesArray); + LongNdArray values = StdArrays.ndCopyOf(valuesArray); + LongNdArray sortedValues = StdArrays.ndCopyOf(sortedValuesArray); + + LongSparseNdArray instance = + new LongSparseNdArray(indices, values, DimensionalSpace.create(shape)); + + instance.sortIndicesAndValues(); + + // should be sorted in ascending row-wise coordinate order based on test values + assertEquals(sortedIndices, instance.getIndices()); + assertEquals(sortedValues, instance.getValues()); + } + + @Test + public void testElements() { + + LongSparseNdArray instance = + new LongSparseNdArray(indices, values, DimensionalSpace.create(shape)); + + long[][] dense2DArray = {{1, 0, 0, 0}, {0, 0, 2, 0}, {0, 0, 0, 0}}; + instance + .elements(0) + .forEachIndexed( + (idx, item) -> { + long[] slice = dense2DArray[(int) idx[0]]; + item.scalars() + .forEachIndexed((dx, f) -> assertEquals(slice[(int) dx[0]], f.getObject())); + }); + } + + @Test + public void testDense() { + long[][] dense2DArray = {{1, 0, 0, 0}, {0, 0, 2, 0}, {0, 0, 0, 0}}; + + LongSparseNdArray instance = + new LongSparseNdArray(indices, values, DimensionalSpace.create(shape)); + LongNdArray denseInstance = instance.toDense(); + LongNdArray expectedDense = StdArrays.ndCopyOf(dense2DArray); + assertEquals(expectedDense, denseInstance); + } + + @Test + public void testFromDense() { + long[][] dense2DArray = {{1, 0, 0, 0}, {0, 0, 2, 0}, {0, 0, 0, 0}}; + LongNdArray ndArray = StdArrays.ndCopyOf(dense2DArray); + LongSparseNdArray instance = LongSparseNdArray.create(DimensionalSpace.create(ndArray.shape())); + instance.fromDense(ndArray); + assertNotNull(instance.getIndices()); + assertEquals(2, instance.getIndices().shape().get(0)); + assertNotNull(instance.getValues()); + assertEquals(2, instance.getValues().size()); + + assertEquals(ndArray.shape(), instance.shape()); + for (int n = 0; n < ndArray.shape().get(0); n++) { + for (int m = 0; m < ndArray.shape().get(1); m++) { + assertEquals(ndArray.getLong(n, m), instance.getLong(n, m)); + } + } + } + + @Test + public void testElements1() { + long[] expected = {1, 0, 0}; + long[][] dense2DArray = {{1, 0, 0, 0}, {0, 0, 2, 0}, {0, 0, 0, 0}}; + + LongSparseNdArray instance = + new LongSparseNdArray(indices, values, DimensionalSpace.create(shape)); + instance + .elements(0) + .forEachIndexed((idx, l) -> assertEquals(expected[(int) idx[0]], l.getObject())); + instance + .elements(1) + .forEachIndexed( + (idx, l) -> assertEquals(dense2DArray[(int) idx[0]][(int) idx[1]], l.getObject())); + } + + @Test + public void testCopyTo() { + LongNdArray dst = NdArrays.ofLongs(shape); + LongSparseNdArray instance = + new LongSparseNdArray(indices, values, DimensionalSpace.create(shape)); + instance.copyTo(dst); + for (int n = 0; n < instance.shape().get(0); n++) { + for (int m = 0; m < instance.shape().get(1); m++) { + assertEquals(instance.getLong(n, m), dst.getLong(n, m)); + } + } + } + + @Test + public void testCreate() { + long[] denseArray = {1, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0}; + long[][] dense2Array = {{1, 0, 0, 0}, {0, 0, 2, 0}, {0, 0, 0, 0}}; + LongSparseNdArray instance = + new LongSparseNdArray(indices, values, DimensionalSpace.create(shape)); + LongSparseNdArray instanceA = + LongSparseNdArray.create(indices, values, DimensionalSpace.create(shape)); + assertEquals(instance, instanceA); + + LongDataBuffer dataBuffer = RawDataBufferFactory.create(denseArray, false); + // use a zero buffer + LongSparseNdArray instanceB = LongSparseNdArray.create(DimensionalSpace.create(shape)); + instanceB.copyFrom(dataBuffer); + assertEquals(instance, instanceB); + + LongSparseNdArray instanceC = + LongSparseNdArray.create(dataBuffer, DimensionalSpace.create(shape)); + assertEquals(instanceB, instanceC); + + LongSparseNdArray instanceD = LongSparseNdArray.create(dataBuffer, shape); + assertEquals(instanceB, instanceD); + + LongNdArray ndArray = StdArrays.ndCopyOf(dense2Array); + LongSparseNdArray instanceE = LongSparseNdArray.create(ndArray); + assertEquals(instance, instanceE); + } + + @Test + public void testSlice() { + long[] expected = {0, 0, 2, 0, 0, 0}; + LongSparseNdArray instance = + new LongSparseNdArray(indices, values, DimensionalSpace.create(shape)); + + LongNdArray sliceInstance = instance.slice(Indices.all(), Indices.sliceFrom(2)); + // check the values of the slice against the original sparse array + AtomicInteger i = new AtomicInteger(); + sliceInstance + .scalars() + .forEachIndexed((idx, f) -> assertEquals(expected[i.getAndIncrement()], f.getLong())); + // check values from elements(0) of a slice against the original sparse array + i.set(0); + sliceInstance + .elements(0) + .forEachIndexed( + (idx, l) -> + l.scalars() + .forEachIndexed( + (lidx, f) -> assertEquals(expected[i.getAndIncrement()], f.getLong()))); + } +} diff --git a/ndarray/src/test/java/org/tensorflow/ndarray/impl/sparse/ShortSparseNdArrayTest.java b/ndarray/src/test/java/org/tensorflow/ndarray/impl/sparse/ShortSparseNdArrayTest.java new file mode 100644 index 0000000..5f85413 --- /dev/null +++ b/ndarray/src/test/java/org/tensorflow/ndarray/impl/sparse/ShortSparseNdArrayTest.java @@ -0,0 +1,315 @@ +package org.tensorflow.ndarray.impl.sparse; + +import org.junit.jupiter.api.Test; +import org.tensorflow.ndarray.LongNdArray; +import org.tensorflow.ndarray.NdArrays; +import org.tensorflow.ndarray.Shape; +import org.tensorflow.ndarray.ShortNdArray; +import org.tensorflow.ndarray.StdArrays; +import org.tensorflow.ndarray.buffer.DataBuffers; +import org.tensorflow.ndarray.buffer.ShortDataBuffer; +import org.tensorflow.ndarray.impl.buffer.nio.NioDataBufferFactory; +import org.tensorflow.ndarray.impl.buffer.raw.RawDataBufferFactory; +import org.tensorflow.ndarray.impl.dimension.DimensionalSpace; +import org.tensorflow.ndarray.index.Indices; + +import java.nio.ShortBuffer; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class ShortSparseNdArrayTest { + long[][] indicesArray = {{0, 0}, {1, 2}}; + short[] valuesArray = {1, 2}; + short[] denseArray = { + 1, 0, 0, 0, + 0, 0, 2, 0, + 0, 0, 0, 0 + }; + short[][] dense2DArrayDefaultValue = {{1, -1, -1, -1}, {-1, -1, 2, -1}, {-1, -1, -1, -1}}; + + Shape shape = Shape.of(3, 4); + LongNdArray indices = StdArrays.ndCopyOf(indicesArray); + ShortNdArray values = StdArrays.ndCopyOf(valuesArray); + + @Test + public void testBasic() { + ShortSparseNdArray instance = + new ShortSparseNdArray(indices, values, DimensionalSpace.create(shape)); + assertEquals(indices, instance.getIndices()); + assertEquals(values, instance.getValues()); + assertEquals(shape, instance.shape()); + } + + @Test + public void testCopyToBuffer() { + ShortSparseNdArray instance = + new ShortSparseNdArray(indices, values, DimensionalSpace.create(shape)); + ShortDataBuffer dataBuffer = DataBuffers.ofShorts(instance.shape().size()); + + instance.copyTo(dataBuffer); + + short[] array = new short[denseArray.length]; + dataBuffer.read(array); + assertArrayEquals(denseArray, array); + } + + @Test + public void testCopyFromBuffer() { + + ShortDataBuffer dataBuffer = NioDataBufferFactory.create(ShortBuffer.wrap(denseArray)); + // use a zero buffer + ShortSparseNdArray instance = ShortSparseNdArray.create(DimensionalSpace.create(shape)); + instance.copyFrom(dataBuffer); + + assertEquals(indices, instance.getIndices()); + assertEquals(values, instance.getValues()); + } + + @Test + public void testWriteDefaultValue() { + // change 0 to -1 + short[] denseArrayDefaultValue = new short[denseArray.length]; + for (int i = 0; i < denseArrayDefaultValue.length; i++) { + denseArrayDefaultValue[i] = denseArray[i] == 0 ? (short) -1 : denseArray[i]; + } + + ShortDataBuffer dataBuffer = RawDataBufferFactory.create(denseArrayDefaultValue, false); + // use a zero buffer + ShortSparseNdArray instance = + ShortSparseNdArray.create((short) -1, DimensionalSpace.create(shape)); + instance.copyFrom(dataBuffer); + + assertEquals(indices, instance.getIndices()); + assertEquals(values, instance.getValues()); + assertEquals((short) -1, instance.getShort(2, 0)); + } + + @Test + public void testGetObject() { + short[][] dense2DArray = {{1, 0, 0, 0}, {0, 0, 2, 0}, {0, 0, 0, 0}}; + ShortNdArray ndArray = StdArrays.ndCopyOf(dense2DArray); + ShortSparseNdArray instance = + new ShortSparseNdArray(indices, values, DimensionalSpace.create(shape)); + + for (int n = 0; n < ndArray.shape().get(0); n++) { + for (int m = 0; m < ndArray.shape().get(1); m++) { + assertEquals(ndArray.getObject(n, m), instance.getObject(n, m)); + } + } + } + + @Test + public void testGetShort() { + short[][] dense2DArray = {{1, 0, 0, 0}, {0, 0, 2, 0}, {0, 0, 0, 0}}; + ShortNdArray ndArray = StdArrays.ndCopyOf(dense2DArray); + ShortSparseNdArray instance = + new ShortSparseNdArray(indices, values, DimensionalSpace.create(shape)); + + for (int n = 0; n < ndArray.shape().get(0); n++) { + for (int m = 0; m < ndArray.shape().get(1); m++) { + assertEquals(ndArray.getShort(n, m), instance.getShort(n, m)); + } + } + } + + @Test + public void testGetShortDefaultValue() { + ShortNdArray ndArray = StdArrays.ndCopyOf(dense2DArrayDefaultValue); + ShortSparseNdArray instance = + new ShortSparseNdArray(indices, values, (short) -1, DimensionalSpace.create(shape)); + + for (int n = 0; n < ndArray.shape().get(0); n++) { + for (int m = 0; m < ndArray.shape().get(1); m++) { + assertEquals(ndArray.getShort(n, m), instance.getShort(n, m)); + } + } + } + + @Test + public void testGet() { + short[][] dense2DArray = {{1, 0, 0, 0}, {0, 0, 2, 0}, {0, 0, 0, 0}}; + ShortNdArray ndArray = StdArrays.ndCopyOf(dense2DArray); + ShortSparseNdArray instance = + new ShortSparseNdArray(indices, values, DimensionalSpace.create(shape)); + + for (int n = 0; n < ndArray.shape().get(0); n++) { + assertEquals(ndArray.get(n), instance.get(n)); + for (int m = 0; m < ndArray.shape().get(1); m++) { + assertEquals(ndArray.get(n, m), instance.get(n, m)); + } + } + } + + @Test + public void testSetObject() { + ShortSparseNdArray instance = + new ShortSparseNdArray(indices, values, DimensionalSpace.create(shape)); + + assertThrows(java.nio.ReadOnlyBufferException.class, () -> instance.setObject((short) 2, 0, 0)); + } + + @Test + public void testSet() { + ShortSparseNdArray instance = + new ShortSparseNdArray(indices, values, DimensionalSpace.create(shape)); + + assertThrows( + java.nio.ReadOnlyBufferException.class, + () -> instance.set(instance.getDefaultArray(), 0, 0)); + } + + @Test + public void testSort() { + + long[][] indicesArray = {{0, 0}, {1, 2}, {0, 1}, {2, 3}, {1, 4}}; + long[][] sortedIndicesArray = {{0, 0}, {0, 1}, {1, 2}, {1, 4}, {2, 3}}; + short[] valuesArray = {1, 3, 2, 5, 4}; + short[] sortedValuesArray = {1, 2, 3, 4, 5}; + + LongNdArray indices = StdArrays.ndCopyOf(indicesArray); + LongNdArray sortedIndices = StdArrays.ndCopyOf(sortedIndicesArray); + ShortNdArray values = StdArrays.ndCopyOf(valuesArray); + ShortNdArray sortedValues = StdArrays.ndCopyOf(sortedValuesArray); + + ShortSparseNdArray instance = + new ShortSparseNdArray(indices, values, DimensionalSpace.create(shape)); + + instance.sortIndicesAndValues(); + + // should be sorted in ascending row-wise coordinate order based on test values + assertEquals(sortedIndices, instance.getIndices()); + assertEquals(sortedValues, instance.getValues()); + } + + @Test + public void testElements() { + + ShortSparseNdArray instance = + new ShortSparseNdArray(indices, values, DimensionalSpace.create(shape)); + + short[][] dense2DArray = {{1, 0, 0, 0}, {0, 0, 2, 0}, {0, 0, 0, 0}}; + instance + .elements(0) + .forEachIndexed( + (idx, item) -> { + short[] slice = dense2DArray[(int) idx[0]]; + item.scalars() + .forEachIndexed((dx, f) -> assertEquals(slice[(int) dx[0]], f.getObject())); + }); + } + + @Test + public void testDense() { + short[][] dense2DArray = {{1, 0, 0, 0}, {0, 0, 2, 0}, {0, 0, 0, 0}}; + + ShortSparseNdArray instance = + new ShortSparseNdArray(indices, values, DimensionalSpace.create(shape)); + ShortNdArray denseInstance = instance.toDense(); + ShortNdArray expectedDense = StdArrays.ndCopyOf(dense2DArray); + assertEquals(expectedDense, denseInstance); + } + + @Test + public void testFromDense() { + short[][] dense2DArray = {{1, 0, 0, 0}, {0, 0, 2, 0}, {0, 0, 0, 0}}; + ShortNdArray ndArray = StdArrays.ndCopyOf(dense2DArray); + ShortSparseNdArray instance = + ShortSparseNdArray.create(DimensionalSpace.create(ndArray.shape())); + instance.fromDense(ndArray); + assertNotNull(instance.getIndices()); + assertEquals(2, instance.getIndices().shape().get(0)); + assertNotNull(instance.getValues()); + assertEquals(2, instance.getValues().size()); + + assertEquals(ndArray.shape(), instance.shape()); + for (int n = 0; n < ndArray.shape().get(0); n++) { + for (int m = 0; m < ndArray.shape().get(1); m++) { + assertEquals(ndArray.getShort(n, m), instance.getShort(n, m)); + } + } + } + + @Test + public void testElements1() { + short[] expected = {1, 0, 0}; + short[][] dense2DArray = {{1, 0, 0, 0}, {0, 0, 2, 0}, {0, 0, 0, 0}}; + + ShortSparseNdArray instance = + new ShortSparseNdArray(indices, values, DimensionalSpace.create(shape)); + instance + .elements(0) + .forEachIndexed((idx, l) -> assertEquals(expected[(int) idx[0]], l.getObject())); + instance + .elements(1) + .forEachIndexed( + (idx, l) -> assertEquals(dense2DArray[(int) idx[0]][(int) idx[1]], l.getObject())); + } + + @Test + public void testCopyTo() { + ShortNdArray dst = NdArrays.ofShorts(shape); + ShortSparseNdArray instance = + new ShortSparseNdArray(indices, values, DimensionalSpace.create(shape)); + instance.copyTo(dst); + for (int n = 0; n < instance.shape().get(0); n++) { + for (int m = 0; m < instance.shape().get(1); m++) { + assertEquals(instance.getShort(n, m), dst.getShort(n, m)); + } + } + } + + @Test + public void testCreate() { + short[] denseArray = {1, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0}; + short[][] dense2Array = {{1, 0, 0, 0}, {0, 0, 2, 0}, {0, 0, 0, 0}}; + ShortSparseNdArray instance = + new ShortSparseNdArray(indices, values, DimensionalSpace.create(shape)); + ShortSparseNdArray instanceA = + ShortSparseNdArray.create(indices, values, DimensionalSpace.create(shape)); + assertEquals(instance, instanceA); + + ShortDataBuffer dataBuffer = RawDataBufferFactory.create(denseArray, false); + // use a zero buffer + ShortSparseNdArray instanceB = ShortSparseNdArray.create(DimensionalSpace.create(shape)); + instanceB.copyFrom(dataBuffer); + assertEquals(instance, instanceB); + + ShortSparseNdArray instanceC = + ShortSparseNdArray.create(dataBuffer, DimensionalSpace.create(shape)); + assertEquals(instanceB, instanceC); + + ShortSparseNdArray instanceD = ShortSparseNdArray.create(dataBuffer, shape); + assertEquals(instanceB, instanceD); + + ShortNdArray ndArray = StdArrays.ndCopyOf(dense2Array); + ShortSparseNdArray instanceE = ShortSparseNdArray.create(ndArray); + assertEquals(instance, instanceE); + } + + @Test + public void testSlice() { + short[] expected = {0, 0, 2, 0, 0, 0}; + ShortSparseNdArray instance = + new ShortSparseNdArray(indices, values, DimensionalSpace.create(shape)); + + ShortNdArray sliceInstance = instance.slice(Indices.all(), Indices.sliceFrom(2)); + // check the values of the slice against the original sparse array + AtomicInteger i = new AtomicInteger(); + sliceInstance + .scalars() + .forEachIndexed((idx, f) -> assertEquals(expected[i.getAndIncrement()], f.getShort())); + // check values from elements(0) of a slice against the original sparse array + i.set(0); + sliceInstance + .elements(0) + .forEachIndexed( + (idx, l) -> + l.scalars() + .forEachIndexed( + (lidx, f) -> assertEquals(expected[i.getAndIncrement()], f.getShort()))); + } +} diff --git a/ndarray/src/test/java/org/tensorflow/ndarray/impl/sparse/StringSparseNdArrayTest.java b/ndarray/src/test/java/org/tensorflow/ndarray/impl/sparse/StringSparseNdArrayTest.java new file mode 100644 index 0000000..a93cd45 --- /dev/null +++ b/ndarray/src/test/java/org/tensorflow/ndarray/impl/sparse/StringSparseNdArrayTest.java @@ -0,0 +1,352 @@ +/* Copyright 2021 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +=======================================================================*/ +package org.tensorflow.ndarray.impl.sparse; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.tensorflow.ndarray.LongNdArray; +import org.tensorflow.ndarray.NdArray; +import org.tensorflow.ndarray.NdArrays; +import org.tensorflow.ndarray.Shape; +import org.tensorflow.ndarray.StdArrays; +import org.tensorflow.ndarray.buffer.DataBuffer; +import org.tensorflow.ndarray.buffer.DataBuffers; +import org.tensorflow.ndarray.impl.dimension.DimensionalSpace; +import org.tensorflow.ndarray.index.Indices; + +import java.util.concurrent.atomic.AtomicInteger; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class StringSparseNdArrayTest { + long[][] indicesArray = {{0, 0}, {1, 2}}; + String[] valuesArray = {"alpha", "omega"}; + String[] denseArray = { + "alpha", null, null, null, null, null, "omega", null, null, null, null, null + }; + String[][] dense2DArray = { + {"alpha", null, null, null}, {null, null, "omega", null}, {null, null, null, null} + }; + + String[][] dense2DArrayDefault = { + {"alpha", "default", "default", "default"}, + {"default", "default", "omega", "default"}, + {"default", "default", "default", "default"} + }; + + Shape shape = Shape.of(3, 4); + LongNdArray indices = StdArrays.ndCopyOf(indicesArray); + NdArray values = StdArrays.ndCopyOf(valuesArray); + + @Test + public void testBasic() { + SparseNdArray> instance = + new SparseNdArray<>(String.class, indices, values, DimensionalSpace.create(shape)); + assertEquals(indices, instance.getIndices()); + assertEquals(values, instance.getValues()); + assertEquals(shape, instance.shape()); + } + + @Test + public void testCopyToBuffer() { + SparseNdArray> instance = + new SparseNdArray<>(String.class, indices, values, DimensionalSpace.create(shape)); + DataBuffer dataBuffer = DataBuffers.ofObjects(String.class, instance.shape().size()); + + instance.copyTo(dataBuffer); + + String[] array = new String[denseArray.length]; + dataBuffer.read(array); + assertArrayEquals(denseArray, array); + } + + @Test + public void testCopyFromBuffer() { + + DataBuffer dataBuffer = DataBuffers.ofObjects(denseArray); + // use a zero buffer + SparseNdArray> instance = + SparseNdArray.create(String.class, DimensionalSpace.create(shape)); + instance.copyFrom(dataBuffer); + + assertEquals(indices, instance.getIndices()); + assertEquals(values, instance.getValues()); + assertEquals(indices.shape().get(0), values.size()); + assertEquals(2, values.size()); + } + + @Test + public void testWriteDefaultValue() { + String defaultValue = "default"; + String[] denseArrayDefaultValue = new String[denseArray.length]; + for (int i = 0; i < denseArrayDefaultValue.length; i++) { + denseArrayDefaultValue[i] = denseArray[i] == null ? defaultValue : denseArray[i]; + } + + DataBuffer dataBuffer = DataBuffers.ofObjects(denseArrayDefaultValue); + // use a zero buffer + SparseNdArray> instance = + SparseNdArray.create(String.class, defaultValue, DimensionalSpace.create(shape)); + instance.copyFrom(dataBuffer); + + assertEquals(indices, instance.getIndices()); + assertEquals(values, instance.getValues()); + assertEquals(2, values.size()); + assertEquals(indices.shape().get(0), values.size()); + } + + @Test + public void testGetObject() { + NdArray ndArray = StdArrays.ndCopyOf(dense2DArray); + SparseNdArray> instance = + new SparseNdArray<>(String.class, indices, values, DimensionalSpace.create(shape)); + + for (int n = 0; n < ndArray.shape().get(0); n++) { + for (int m = 0; m < ndArray.shape().get(1); m++) { + assertEquals(ndArray.getObject(n, m), instance.getObject(n, m)); + } + } + } + + @Test + public void testGetObjectDefaultValue() { + String defaultValue = "default"; + + NdArray ndArray = StdArrays.ndCopyOf(dense2DArrayDefault); + SparseNdArray> instance = + new SparseNdArray<>( + String.class, indices, values, defaultValue, DimensionalSpace.create(shape)); + + for (int n = 0; n < ndArray.shape().get(0); n++) { + for (int m = 0; m < ndArray.shape().get(1); m++) { + assertEquals(ndArray.getObject(n, m), instance.getObject(n, m)); + } + } + } + + @Test + public void testGet() { + NdArray ndArray = StdArrays.ndCopyOf(dense2DArray); + SparseNdArray> instance = + new SparseNdArray<>(String.class, indices, values, DimensionalSpace.create(shape)); + + for (int n = 0; n < ndArray.shape().get(0); n++) { + assertEquals(ndArray.get(n), instance.get(n)); + for (int m = 0; m < ndArray.shape().get(1); m++) { + assertEquals(ndArray.get(n, m), instance.get(n, m)); + } + } + } + + @Test + public void testSetObject() { + SparseNdArray> instance = + new SparseNdArray<>(String.class, indices, values, DimensionalSpace.create(shape)); + + assertThrows(java.nio.ReadOnlyBufferException.class, () -> instance.setObject(null, 0, 0)); + } + + @Test + public void testSet() { + SparseNdArray> instance = + new SparseNdArray<>(String.class, indices, values, DimensionalSpace.create(shape)); + + assertThrows( + java.nio.ReadOnlyBufferException.class, + () -> instance.set(instance.getDefaultArray(), 0, 0)); + } + + @Test + public void testSort() { + + long[][] indicesArray = {{0, 0}, {1, 2}, {0, 1}, {2, 3}, {1, 4}}; + long[][] sortedIndicesArray = {{0, 0}, {0, 1}, {1, 2}, {1, 4}, {2, 3}}; + String[] valuesArray = {"b", "d", "a", null, "c"}; + String[] sortedValuesArray = {"b", "a", "d", "c", null}; + + LongNdArray indices = StdArrays.ndCopyOf(indicesArray); + LongNdArray sortedIndices = StdArrays.ndCopyOf(sortedIndicesArray); + NdArray values = StdArrays.ndCopyOf(valuesArray); + NdArray sortedValues = StdArrays.ndCopyOf(sortedValuesArray); + + SparseNdArray> instance = + new SparseNdArray<>(String.class, indices, values, DimensionalSpace.create(shape)); + + instance.sortIndicesAndValues(); + + // should be sorted in ascending row-wise coordinate order based on test values + assertEquals(sortedIndices, instance.getIndices()); + assertEquals(sortedValues, instance.getValues()); + } + + @Test + public void testElements() { + + SparseNdArray> instance = + new SparseNdArray<>(String.class, indices, values, DimensionalSpace.create(shape)); + + instance + .elements(0) + .forEachIndexed( + (idx, item) -> { + String[] slice = dense2DArray[(int) idx[0]]; + item.scalars() + .forEachIndexed((dx, f) -> assertEquals(slice[(int) dx[0]], f.getObject())); + }); + } + + @Test + public void testDense() { + + SparseNdArray> instance = + new SparseNdArray<>(String.class, indices, values, DimensionalSpace.create(shape)); + NdArray denseInstance = instance.toDense(); + NdArray expectedDense = StdArrays.ndCopyOf(dense2DArray); + assertEquals(expectedDense, denseInstance); + } + + @Test + public void testFromDense() { + NdArray ndArray = StdArrays.ndCopyOf(dense2DArray); + SparseNdArray> instance = + SparseNdArray.create(String.class, DimensionalSpace.create(ndArray.shape())); + instance.fromDense(ndArray); + assertNotNull(instance.getIndices()); + assertEquals(2, instance.getIndices().shape().get(0)); + assertNotNull(instance.getValues()); + assertEquals(2, instance.getValues().size()); + + assertEquals(ndArray.shape(), instance.shape()); + for (int n = 0; n < ndArray.shape().get(0); n++) { + for (int m = 0; m < ndArray.shape().get(1); m++) { + assertEquals(ndArray.getObject(n, m), instance.getObject(n, m)); + } + } + } + + @Test + public void testElements1() { + String[] expected = {"alpha", null, null}; + + SparseNdArray> instance = + new SparseNdArray<>(String.class, indices, values, DimensionalSpace.create(shape)); + instance + .elements(0) + .forEachIndexed((idx, l) -> assertEquals(expected[(int) idx[0]], l.getObject())); + instance + .elements(1) + .forEachIndexed( + (idx, l) -> assertEquals(dense2DArray[(int) idx[0]][(int) idx[1]], l.getObject())); + } + + @Test + public void testCopyTo() { + NdArray dst = NdArrays.ofObjects(String.class, shape); + SparseNdArray> instance = + new SparseNdArray<>(String.class, indices, values, DimensionalSpace.create(shape)); + instance.copyTo(dst); + for (int n = 0; n < instance.shape().get(0); n++) { + for (int m = 0; m < instance.shape().get(1); m++) { + assertEquals(instance.getObject(n, m), dst.getObject(n, m)); + } + } + } + + @Test + public void testCreate() { + + SparseNdArray> instance = + new SparseNdArray<>(String.class, indices, values, DimensionalSpace.create(shape)); + SparseNdArray> instanceA = + SparseNdArray.create(String.class, indices, values, DimensionalSpace.create(shape)); + assertEquals(instance, instanceA); + + DataBuffer dataBuffer = DataBuffers.ofObjects(denseArray); + + // use a zero buffer + SparseNdArray> instanceB = + SparseNdArray.create(String.class, DimensionalSpace.create(shape)); + instanceB.copyFrom(dataBuffer); + assertEquals(instance, instanceB); + + SparseNdArray> instanceC = + SparseNdArray.create(String.class, dataBuffer, DimensionalSpace.create(shape)); + assertEquals(instanceB, instanceC); + + SparseNdArray> instanceD = + SparseNdArray.create(String.class, dataBuffer, shape); + assertEquals(instanceB, instanceD); + + NdArray ndArray = StdArrays.ndCopyOf(dense2DArray); + SparseNdArray> instanceE = SparseNdArray.create(String.class, ndArray); + assertEquals(instance, instanceE); + } + + @Test + public void testSlice() { + String[] expected = {null, null, "omega", null, null, null}; + SparseNdArray> instance = + new SparseNdArray<>(String.class, indices, values, DimensionalSpace.create(shape)); + + NdArray sliceInstance = instance.slice(Indices.all(), Indices.sliceFrom(2)); + // check the values of the slice against the original sparse array + AtomicInteger i = new AtomicInteger(); + sliceInstance + .scalars() + .forEachIndexed((idx, f) -> assertEquals(expected[i.getAndIncrement()], f.getObject())); + // check values from elements(0) of a slice against the original sparse array + i.set(0); + sliceInstance + .elements(0) + .forEachIndexed( + (idx, l) -> + l.scalars() + .forEachIndexed( + (lidx, f) -> assertEquals(expected[i.getAndIncrement()], f.getObject()))); + } + + @Test + public void testNullDefault() { + SparseNdArray> instance = + new SparseNdArray<>(String.class, indices, values, DimensionalSpace.create(shape)); + + NdArray dArray = instance.getDefaultArray(); + assertEquals(1L, dArray.size()); + assertNull(dArray.getObject()); + + instance = + new SparseNdArray<>( + String.class, indices, values, "a default", DimensionalSpace.create(shape)); + + dArray = instance.getDefaultArray(); + assertEquals(1L, dArray.size()); + assertNotNull(dArray.getObject()); + assertEquals("a default", dArray.getObject()); + } + + @Test + public void testToString() { + SparseNdArray> instance = + new SparseNdArray<>(String.class, indices, values, DimensionalSpace.create(shape)); + Assertions.assertEquals("SparseNdArray(type=String, defaultValue=, numElements=2, shape=[3, 4])",instance.toString()); + instance = new SparseNdArray<>( + String.class, indices, values, "a default", DimensionalSpace.create(shape)); + Assertions.assertEquals("SparseNdArray(type=String, defaultValue='a default', numElements=2, shape=[3, 4])",instance.toString()); + } +} diff --git a/pom.xml b/pom.xml index 867038c..104f431 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.tensorflow tensorflow-java-ndarray - 0.4.0-SNAPSHOT + 1.1.0-SNAPSHOT pom NdArray Parent @@ -23,9 +23,9 @@ - https://github.com/tensorflow/java-ndarray.git - git@github.com:tensorflow/java-ndarray.git - scm:git:https://github.com/tensorflow/java-ndarray.git + https://github.com/karllessard/tensorflow-java-ndarray.git + git@github.com:karllessard/tensorflow-java-ndarray.git + scm:git:https://github.com/karllessard/tensorflow-java-ndarray.git @@ -34,12 +34,12 @@ ASCII - 1.8 - 1.8 + 11 + 11 5.6.2 1.21 2.7 - 2.6.0 + 2.10.0 true @@ -56,6 +56,7 @@ + ossrh-snapshots @@ -82,7 +83,7 @@ ossrh - https://oss.sonatype.org/service/local/staging/deployByRepositoryId/${stagingRepositoryId}/ + https://oss.sonatype.org/service/local/staging/deploy/maven2/ @@ -125,27 +126,29 @@ false - - - ossrh-staging - OSSRH Sonatype Staging - https://oss.sonatype.org/service/local/staging/deployByRepositoryId/${stagingRepositoryId}/ - - true - - - false - - - + + + + org.sonatype.plugins + nexus-staging-maven-plugin + 1.6.8 + true + + ossrh + https://oss.sonatype.org/ + false + + + + - jdk11 + jdk17 - 11 - 11 - 11 + 17 + 17 + 17 @@ -156,8 +159,6 @@ lint - - (1.9,) !lint.skip @@ -169,7 +170,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.8.0 + 3.9.0 true true @@ -273,7 +274,15 @@ true + + maven-release-plugin + 3.0.0-M5 + + SemVerVersionPolicy + + + diff --git a/release.sh b/release.sh new file mode 100755 index 0000000..2addcdd --- /dev/null +++ b/release.sh @@ -0,0 +1,52 @@ +#!/usr/bin/env bash +# Copyright 2024 The TensorFlow Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================== +# +# Script to upload release artifacts for the TensorFlow Java library to +# Maven Central. See RELEASE.md for explanation. + +cd $(dirname "$0") +shift +shift +CMD="$*" + +# If release fails, debug with +# ./release.sh bash +# To get a shell to poke around the maven artifacts with. +if [[ -z "${CMD}" ]] +then + CMD="mvn clean deploy -B -e --settings ./settings.xml -Preleasing" +fi + +export GPG_TTY=$(tty) +set -ex + +if [[ ! -f settings.xml ]] +then + cp -f ~/.m2/settings.xml . +fi + +docker run \ + -e GPG_TTY="${GPG_TTY}" \ + -v ${PWD}:/tensorflow-java-ndarray \ + -v ${HOME}/.gnupg:/root/.gnupg \ + -w /tensorflow-java-ndarray \ + -it \ + --platform linux/amd64 \ + maven:3.8.6-jdk-11 \ + ${CMD} + +echo +echo "Release completed"