From 4c90dd815c6117401daf62d8f40ab5fa3e633e6f Mon Sep 17 00:00:00 2001 From: klessard Date: Mon, 26 Apr 2021 09:44:50 -0400 Subject: [PATCH] Move ndarray library to its own repository --- README.md | 8 +- ndarray/README.md | 122 - ndarray/pom.xml | 91 - .../tensorflow/ndarray/BooleanNdArray.java | 108 - .../org/tensorflow/ndarray/ByteNdArray.java | 108 - .../org/tensorflow/ndarray/DoubleNdArray.java | 108 - .../org/tensorflow/ndarray/FloatNdArray.java | 108 - .../ndarray/IllegalRankException.java | 27 - .../org/tensorflow/ndarray/IntNdArray.java | 108 - .../org/tensorflow/ndarray/LongNdArray.java | 108 - .../java/org/tensorflow/ndarray/NdArray.java | 302 -- .../tensorflow/ndarray/NdArraySequence.java | 67 - .../java/org/tensorflow/ndarray/NdArrays.java | 495 --- .../java/org/tensorflow/ndarray/Shape.java | 447 -- .../java/org/tensorflow/ndarray/Shaped.java | 51 - .../org/tensorflow/ndarray/ShortNdArray.java | 108 - .../org/tensorflow/ndarray/StdArrays.java | 3809 ----------------- .../ndarray/buffer/BooleanDataBuffer.java | 165 - .../ndarray/buffer/ByteDataBuffer.java | 232 - .../tensorflow/ndarray/buffer/DataBuffer.java | 324 -- .../ndarray/buffer/DataBufferWindow.java | 93 - .../ndarray/buffer/DataBuffers.java | 457 -- .../ndarray/buffer/DataStorageVisitor.java | 147 - .../ndarray/buffer/DoubleDataBuffer.java | 165 - .../ndarray/buffer/FloatDataBuffer.java | 165 - .../ndarray/buffer/IntDataBuffer.java | 165 - .../ndarray/buffer/LongDataBuffer.java | 165 - .../ndarray/buffer/ShortDataBuffer.java | 165 - .../buffer/layout/BooleanDataLayout.java | 65 - .../ndarray/buffer/layout/ByteDataLayout.java | 65 - .../ndarray/buffer/layout/DataLayout.java | 129 - .../ndarray/buffer/layout/DataLayouts.java | 96 - .../buffer/layout/DoubleDataLayout.java | 65 - .../buffer/layout/FloatDataLayout.java | 65 - .../ndarray/buffer/layout/IntDataLayout.java | 66 - .../ndarray/buffer/layout/LongDataLayout.java | 65 - .../buffer/layout/ShortDataLayout.java | 65 - .../ndarray/impl/AbstractNdArray.java | 92 - .../tensorflow/ndarray/impl/Validator.java | 55 - .../impl/buffer/AbstractDataBuffer.java | 182 - .../impl/buffer/AbstractDataBufferWindow.java | 48 - .../ndarray/impl/buffer/Validator.java | 132 - .../adapter/AbstractDataBufferAdapter.java | 68 - .../adapter/BooleanDataBufferAdapter.java | 116 - .../buffer/adapter/ByteDataBufferAdapter.java | 135 - .../buffer/adapter/DataBufferAdapter.java | 47 - .../adapter/DataBufferAdapterFactory.java | 142 - .../adapter/DoubleDataBufferAdapter.java | 116 - .../adapter/FloatDataBufferAdapter.java | 116 - .../buffer/adapter/IntDataBufferAdapter.java | 116 - .../buffer/adapter/LongDataBufferAdapter.java | 116 - .../adapter/ShortDataBufferAdapter.java | 116 - .../impl/buffer/layout/Bfloat16Layout.java | 54 - .../impl/buffer/layout/BoolLayout.java | 47 - .../impl/buffer/layout/Float16Layout.java | 119 - .../impl/buffer/layout/StringLayout.java | 48 - .../impl/buffer/misc/ArrayDataBuffer.java | 126 - .../impl/buffer/misc/BitSetDataBuffer.java | 183 - .../buffer/misc/BooleanArrayDataBuffer.java | 176 - .../buffer/misc/MiscDataBufferFactory.java | 40 - .../buffer/nio/AbstractNioDataBuffer.java | 41 - .../impl/buffer/nio/ByteNioDataBuffer.java | 185 - .../impl/buffer/nio/DoubleNioDataBuffer.java | 147 - .../impl/buffer/nio/FloatNioDataBuffer.java | 147 - .../impl/buffer/nio/IntNioDataBuffer.java | 147 - .../impl/buffer/nio/LongNioDataBuffer.java | 147 - .../impl/buffer/nio/NioDataBufferFactory.java | 61 - .../impl/buffer/nio/ShortNioDataBuffer.java | 147 - .../buffer/raw/AbstractRawDataBuffer.java | 94 - .../impl/buffer/raw/BooleanRawDataBuffer.java | 146 - .../impl/buffer/raw/ByteRawDataBuffer.java | 185 - .../impl/buffer/raw/DoubleRawDataBuffer.java | 149 - .../impl/buffer/raw/FloatRawDataBuffer.java | 150 - .../impl/buffer/raw/IntRawDataBuffer.java | 149 - .../impl/buffer/raw/LongRawDataBuffer.java | 149 - .../impl/buffer/raw/RawDataBufferFactory.java | 148 - .../impl/buffer/raw/RawDataBufferWindow.java | 19 - .../impl/buffer/raw/ShortRawDataBuffer.java | 149 - .../impl/buffer/raw/UnsafeMemoryHandle.java | 195 - .../impl/buffer/raw/UnsafeReference.java | 64 - .../impl/dense/AbstractDenseNdArray.java | 163 - .../impl/dense/BooleanDenseNdArray.java | 91 - .../ndarray/impl/dense/ByteDenseNdArray.java | 91 - .../ndarray/impl/dense/DataTransfer.java | 136 - .../ndarray/impl/dense/DenseNdArray.java | 63 - .../impl/dense/DoubleDenseNdArray.java | 91 - .../ndarray/impl/dense/FloatDenseNdArray.java | 91 - .../ndarray/impl/dense/IntDenseNdArray.java | 91 - .../ndarray/impl/dense/LongDenseNdArray.java | 91 - .../ndarray/impl/dense/ShortDenseNdArray.java | 91 - .../ndarray/impl/dense/Validator.java | 49 - .../impl/dimension/AbstractDimension.java | 45 - .../ndarray/impl/dimension/Axis.java | 61 - .../ndarray/impl/dimension/Dimension.java | 36 - .../impl/dimension/DimensionalSpace.java | 224 - .../impl/dimension/IndexedDimension.java | 69 - .../impl/dimension/ReducedDimension.java | 62 - .../dimension/RelativeDimensionalSpace.java | 32 - .../impl/sequence/CoordinatesIncrementor.java | 38 - .../impl/sequence/FastElementSequence.java | 81 - .../sequence/IndexedPositionIterator.java | 28 - .../IndexedSequentialPositionIterator.java | 51 - .../impl/sequence/NdPositionIterator.java | 70 - .../impl/sequence/PositionIterator.java | 42 - .../sequence/SequentialPositionIterator.java | 55 - .../impl/sequence/SingleElementSequence.java | 71 - .../impl/sequence/SlicingElementSequence.java | 77 - .../org/tensorflow/ndarray/index/All.java | 57 - .../java/org/tensorflow/ndarray/index/At.java | 74 - .../tensorflow/ndarray/index/Ellipsis.java | 48 - .../tensorflow/ndarray/index/Hyperslab.java | 90 - .../org/tensorflow/ndarray/index/Index.java | 131 - .../org/tensorflow/ndarray/index/Indices.java | 363 -- .../org/tensorflow/ndarray/index/NewAxis.java | 53 - .../tensorflow/ndarray/index/Sequence.java | 52 - .../org/tensorflow/ndarray/index/Slice.java | 89 - .../tensorflow/ndarray/index/SliceFrom.java | 86 - .../org/tensorflow/ndarray/index/SliceTo.java | 86 - .../org/tensorflow/ndarray/index/Step.java | 83 - .../ndarray/BooleanNdArrayTestBase.java | 57 - .../ndarray/ByteNdArrayTestBase.java | 55 - .../ndarray/DoubleNdArrayTestBase.java | 55 - .../ndarray/FloatNdArrayTestBase.java | 55 - .../org/tensorflow/ndarray/IndexTest.java | 205 - .../ndarray/IntNdArrayTestBase.java | 55 - .../ndarray/LongNdArrayTestBase.java | 55 - .../tensorflow/ndarray/NdArrayTestBase.java | 338 -- .../org/tensorflow/ndarray/ShapeTest.java | 170 - .../ndarray/ShortNdArrayTestBase.java | 55 - .../org/tensorflow/ndarray/StdArraysTest.java | 211 - .../ndarray/benchmark/NdArrayBenchmark.java | 163 - .../buffer/BooleanDataBufferTestBase.java | 142 - .../buffer/ByteDataBufferTestBase.java | 145 - .../ndarray/buffer/DataBufferTestBase.java | 293 -- .../buffer/DoubleDataBufferTestBase.java | 135 - .../buffer/FloatDataBufferTestBase.java | 135 - .../ndarray/buffer/IntDataBufferTestBase.java | 135 - .../buffer/LongDataBufferTestBase.java | 135 - .../buffer/ShortDataBufferTestBase.java | 135 - .../BigIntegerDataBufferAdapterTest.java | 67 - .../adapter/BooleanDataBufferAdapterTest.java | 45 - .../adapter/ByteDataBufferAdapterTest.java | 27 - .../adapter/DoubleDataBufferAdapterTest.java | 61 - .../adapter/FloatDataBufferAdapterTest.java | 52 - .../adapter/IntDataBufferAdapterTest.java | 51 - .../adapter/LongDataBufferAdapterTest.java | 60 - .../adapter/ShortDataBufferAdapterTest.java | 45 - .../buffer/layout/Bfloat16LayoutTest.java | 84 - .../impl/buffer/layout/BoolLayoutTest.java | 42 - .../impl/buffer/layout/Float16LayoutTest.java | 90 - .../impl/buffer/misc/ArrayDataBufferTest.java | 265 -- .../buffer/misc/BitSetDataBufferTest.java | 34 - .../misc/StringArrayDataBufferTest.java | 33 - .../buffer/nio/ByteNioDataBufferTest.java | 29 - .../buffer/nio/DoubleNioDataBufferTest.java | 29 - .../buffer/nio/FloatNioDataBufferTest.java | 29 - .../impl/buffer/nio/IntNioDataBufferTest.java | 29 - .../buffer/nio/LongNioDataBufferTest.java | 29 - .../buffer/nio/ShortNioDataBufferTest.java | 29 - .../buffer/raw/BooleanRawDataBufferTest.java | 28 - .../buffer/raw/ByteRawDataBufferTest.java | 28 - .../buffer/raw/DoubleRawDataBufferTest.java | 28 - .../buffer/raw/FloatRawDataBufferTest.java | 28 - .../impl/buffer/raw/IntRawDataBufferTest.java | 28 - .../buffer/raw/LongRawDataBufferTest.java | 28 - .../buffer/raw/ShortRawDataBufferTest.java | 28 - .../impl/dense/BooleanDenseNdArrayTest.java | 35 - .../impl/dense/ByteDenseNdArrayTest.java | 35 - .../ndarray/impl/dense/DenseNdArrayTest.java | 51 - .../impl/dense/DoubleDenseNdArrayTest.java | 35 - .../impl/dense/FloatDenseNdArrayTest.java | 35 - .../impl/dense/IntDenseNdArrayTest.java | 35 - .../impl/dense/LongDenseNdArrayTest.java | 35 - .../impl/dense/ShortDenseNdArrayTest.java | 35 - .../impl/dense/StringDenseNdArrayTest.java | 43 - .../impl/sequence/ElementSequenceTest.java | 145 - ndarray/src/test/resources/COPYRIGHT.txt | 1 - ndarray/src/test/resources/castle.jpg | Bin 436164 -> 0 bytes pom.xml | 3 +- tensorflow-core/tensorflow-core-api/pom.xml | 24 +- 180 files changed, 15 insertions(+), 21962 deletions(-) delete mode 100644 ndarray/README.md delete mode 100644 ndarray/pom.xml delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/BooleanNdArray.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/ByteNdArray.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/DoubleNdArray.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/FloatNdArray.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/IllegalRankException.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/IntNdArray.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/LongNdArray.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/NdArray.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/NdArraySequence.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/NdArrays.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/Shape.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/Shaped.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/ShortNdArray.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/StdArrays.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/buffer/BooleanDataBuffer.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/buffer/ByteDataBuffer.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/buffer/DataBuffer.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/buffer/DataBufferWindow.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/buffer/DataBuffers.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/buffer/DataStorageVisitor.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/buffer/DoubleDataBuffer.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/buffer/FloatDataBuffer.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/buffer/IntDataBuffer.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/buffer/LongDataBuffer.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/buffer/ShortDataBuffer.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/buffer/layout/BooleanDataLayout.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/buffer/layout/ByteDataLayout.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/buffer/layout/DataLayout.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/buffer/layout/DataLayouts.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/buffer/layout/DoubleDataLayout.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/buffer/layout/FloatDataLayout.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/buffer/layout/IntDataLayout.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/buffer/layout/LongDataLayout.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/buffer/layout/ShortDataLayout.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/impl/AbstractNdArray.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/impl/Validator.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/AbstractDataBuffer.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/AbstractDataBufferWindow.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/Validator.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/adapter/AbstractDataBufferAdapter.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/adapter/BooleanDataBufferAdapter.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/adapter/ByteDataBufferAdapter.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/adapter/DataBufferAdapter.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/adapter/DataBufferAdapterFactory.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/adapter/DoubleDataBufferAdapter.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/adapter/FloatDataBufferAdapter.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/adapter/IntDataBufferAdapter.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/adapter/LongDataBufferAdapter.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/adapter/ShortDataBufferAdapter.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/layout/Bfloat16Layout.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/layout/BoolLayout.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/layout/Float16Layout.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/layout/StringLayout.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/misc/ArrayDataBuffer.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/misc/BitSetDataBuffer.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/misc/BooleanArrayDataBuffer.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/misc/MiscDataBufferFactory.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/nio/AbstractNioDataBuffer.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/nio/ByteNioDataBuffer.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/nio/DoubleNioDataBuffer.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/nio/FloatNioDataBuffer.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/nio/IntNioDataBuffer.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/nio/LongNioDataBuffer.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/nio/NioDataBufferFactory.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/nio/ShortNioDataBuffer.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/raw/AbstractRawDataBuffer.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/raw/BooleanRawDataBuffer.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/raw/ByteRawDataBuffer.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/raw/DoubleRawDataBuffer.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/raw/FloatRawDataBuffer.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/raw/IntRawDataBuffer.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/raw/LongRawDataBuffer.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/raw/RawDataBufferFactory.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/raw/RawDataBufferWindow.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/raw/ShortRawDataBuffer.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/raw/UnsafeMemoryHandle.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/raw/UnsafeReference.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/impl/dense/AbstractDenseNdArray.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/impl/dense/BooleanDenseNdArray.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/impl/dense/ByteDenseNdArray.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/impl/dense/DataTransfer.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/impl/dense/DenseNdArray.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/impl/dense/DoubleDenseNdArray.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/impl/dense/FloatDenseNdArray.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/impl/dense/IntDenseNdArray.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/impl/dense/LongDenseNdArray.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/impl/dense/ShortDenseNdArray.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/impl/dense/Validator.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/impl/dimension/AbstractDimension.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/impl/dimension/Axis.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/impl/dimension/Dimension.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/impl/dimension/DimensionalSpace.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/impl/dimension/IndexedDimension.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/impl/dimension/ReducedDimension.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/impl/dimension/RelativeDimensionalSpace.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/impl/sequence/CoordinatesIncrementor.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/impl/sequence/FastElementSequence.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/impl/sequence/IndexedPositionIterator.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/impl/sequence/IndexedSequentialPositionIterator.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/impl/sequence/NdPositionIterator.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/impl/sequence/PositionIterator.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/impl/sequence/SequentialPositionIterator.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/impl/sequence/SingleElementSequence.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/impl/sequence/SlicingElementSequence.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/index/All.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/index/At.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/index/Ellipsis.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/index/Hyperslab.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/index/Index.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/index/Indices.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/index/NewAxis.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/index/Sequence.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/index/Slice.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/index/SliceFrom.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/index/SliceTo.java delete mode 100644 ndarray/src/main/java/org/tensorflow/ndarray/index/Step.java delete mode 100644 ndarray/src/test/java/org/tensorflow/ndarray/BooleanNdArrayTestBase.java delete mode 100644 ndarray/src/test/java/org/tensorflow/ndarray/ByteNdArrayTestBase.java delete mode 100644 ndarray/src/test/java/org/tensorflow/ndarray/DoubleNdArrayTestBase.java delete mode 100644 ndarray/src/test/java/org/tensorflow/ndarray/FloatNdArrayTestBase.java delete mode 100644 ndarray/src/test/java/org/tensorflow/ndarray/IndexTest.java delete mode 100644 ndarray/src/test/java/org/tensorflow/ndarray/IntNdArrayTestBase.java delete mode 100644 ndarray/src/test/java/org/tensorflow/ndarray/LongNdArrayTestBase.java delete mode 100644 ndarray/src/test/java/org/tensorflow/ndarray/NdArrayTestBase.java delete mode 100644 ndarray/src/test/java/org/tensorflow/ndarray/ShapeTest.java delete mode 100644 ndarray/src/test/java/org/tensorflow/ndarray/ShortNdArrayTestBase.java delete mode 100644 ndarray/src/test/java/org/tensorflow/ndarray/StdArraysTest.java delete mode 100644 ndarray/src/test/java/org/tensorflow/ndarray/benchmark/NdArrayBenchmark.java delete mode 100644 ndarray/src/test/java/org/tensorflow/ndarray/buffer/BooleanDataBufferTestBase.java delete mode 100644 ndarray/src/test/java/org/tensorflow/ndarray/buffer/ByteDataBufferTestBase.java delete mode 100644 ndarray/src/test/java/org/tensorflow/ndarray/buffer/DataBufferTestBase.java delete mode 100644 ndarray/src/test/java/org/tensorflow/ndarray/buffer/DoubleDataBufferTestBase.java delete mode 100644 ndarray/src/test/java/org/tensorflow/ndarray/buffer/FloatDataBufferTestBase.java delete mode 100644 ndarray/src/test/java/org/tensorflow/ndarray/buffer/IntDataBufferTestBase.java delete mode 100644 ndarray/src/test/java/org/tensorflow/ndarray/buffer/LongDataBufferTestBase.java delete mode 100644 ndarray/src/test/java/org/tensorflow/ndarray/buffer/ShortDataBufferTestBase.java delete mode 100644 ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/adapter/BigIntegerDataBufferAdapterTest.java delete mode 100644 ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/adapter/BooleanDataBufferAdapterTest.java delete mode 100644 ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/adapter/ByteDataBufferAdapterTest.java delete mode 100644 ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/adapter/DoubleDataBufferAdapterTest.java delete mode 100644 ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/adapter/FloatDataBufferAdapterTest.java delete mode 100644 ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/adapter/IntDataBufferAdapterTest.java delete mode 100644 ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/adapter/LongDataBufferAdapterTest.java delete mode 100644 ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/adapter/ShortDataBufferAdapterTest.java delete mode 100644 ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/layout/Bfloat16LayoutTest.java delete mode 100644 ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/layout/BoolLayoutTest.java delete mode 100644 ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/layout/Float16LayoutTest.java delete mode 100644 ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/misc/ArrayDataBufferTest.java delete mode 100644 ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/misc/BitSetDataBufferTest.java delete mode 100644 ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/misc/StringArrayDataBufferTest.java delete mode 100644 ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/nio/ByteNioDataBufferTest.java delete mode 100644 ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/nio/DoubleNioDataBufferTest.java delete mode 100644 ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/nio/FloatNioDataBufferTest.java delete mode 100644 ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/nio/IntNioDataBufferTest.java delete mode 100644 ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/nio/LongNioDataBufferTest.java delete mode 100644 ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/nio/ShortNioDataBufferTest.java delete mode 100644 ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/raw/BooleanRawDataBufferTest.java delete mode 100644 ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/raw/ByteRawDataBufferTest.java delete mode 100644 ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/raw/DoubleRawDataBufferTest.java delete mode 100644 ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/raw/FloatRawDataBufferTest.java delete mode 100644 ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/raw/IntRawDataBufferTest.java delete mode 100644 ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/raw/LongRawDataBufferTest.java delete mode 100644 ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/raw/ShortRawDataBufferTest.java delete mode 100644 ndarray/src/test/java/org/tensorflow/ndarray/impl/dense/BooleanDenseNdArrayTest.java delete mode 100644 ndarray/src/test/java/org/tensorflow/ndarray/impl/dense/ByteDenseNdArrayTest.java delete mode 100644 ndarray/src/test/java/org/tensorflow/ndarray/impl/dense/DenseNdArrayTest.java delete mode 100644 ndarray/src/test/java/org/tensorflow/ndarray/impl/dense/DoubleDenseNdArrayTest.java delete mode 100644 ndarray/src/test/java/org/tensorflow/ndarray/impl/dense/FloatDenseNdArrayTest.java delete mode 100644 ndarray/src/test/java/org/tensorflow/ndarray/impl/dense/IntDenseNdArrayTest.java delete mode 100644 ndarray/src/test/java/org/tensorflow/ndarray/impl/dense/LongDenseNdArrayTest.java delete mode 100644 ndarray/src/test/java/org/tensorflow/ndarray/impl/dense/ShortDenseNdArrayTest.java delete mode 100644 ndarray/src/test/java/org/tensorflow/ndarray/impl/dense/StringDenseNdArrayTest.java delete mode 100644 ndarray/src/test/java/org/tensorflow/ndarray/impl/sequence/ElementSequenceTest.java delete mode 100644 ndarray/src/test/resources/COPYRIGHT.txt delete mode 100644 ndarray/src/test/resources/castle.jpg diff --git a/README.md b/README.md index 53fe11a2036..39ca44a74e6 100644 --- a/README.md +++ b/README.md @@ -27,13 +27,7 @@ The following describes the layout of the repository and its different artifacts * Primary API for building and training neural networks with TensorFlow * Intended audience: neural network developers * For more information: [tensorflow-framework/README.md](tensorflow-framework/README.md) - -* `ndarray` - * Generic utility library for n-dimensional data I/O operations - * Used by TensorFlow but does not depend on TensorFlow - * Intended audience: any developer who needs a Java n-dimensional array implementation, whether or not they - use it with TensorFlow - + ## Communication diff --git a/ndarray/README.md b/ndarray/README.md deleted file mode 100644 index 0c76eecd08f..00000000000 --- a/ndarray/README.md +++ /dev/null @@ -1,122 +0,0 @@ -# NdArray Java Library - -## Introduction - -NdArray is a library exposing utilities for manipulating data in a n-dimensional space in Java. -Unlike other Java artifacts distributed by TensorFlow, this library does not depend on the TensorFlow -runtime, therefore is very lightweight and can be used by any kind of Java project. - -To import the NdArray library in your project, simply add the following dependency: -```xml - - org.tensorflow - ndarray - 0.3.1 - -``` - -### Data Buffers - -Instances of `DataBuffer` map contiguous segments of memory with 64-bits indexing and supports -generic parametrization while still allowing direct access to primitive types. Such segments -could be standard Java arrays, JDK NIO buffers or native memory. In addition, it can serialize and -deserialize data of any type (and not only primitive types, as with `java.util.nio`). - -```java -// Allocate a buffer of 4K int values -IntDataBuffer bufferA = DataBuffers.ofInts(4096L); -assertEquals(4096L, bufferA.size()); - -// Write an int array at the beginning of the buffer -bufferA.write(new int[] { 1, 2, 3 }); -assertEquals(3, bufferA.getInt(2)); - -// Slice buffer after its first value -IntDataBuffer bufferB = bufferA.offset(1); -assertEquals(4095L, bufferB.size()); -assertEquals(2, bufferB.getInt(0)); - -// Resize a buffer to 10 elements -IntDataBuffer bufferC = bufferA.narrow(10); -assertEquals(10L, bufferB.size()); -assertEquals(2, bufferB.getInt(0)); -``` - -### ND Arrays - -Instances of `NdArray` are used to view memory segments stored in a `DataBuffer` as a -multidimensional arrays and to provide an API for traversing, reading, writing and slicing -their data. The goal of these tools is to replace the usage of standard multidimensional Java arrays -(e.g. `new int[][][]`) since those results in slow performances, from the non-contiguous -storage of their data and the multiple dereferences required to access their values. - -```java -// Allocating a 3D matrix of 2x3x2 -IntNdArray matrix3d = NdArrays.ofInts(Shape.of(2, 3, 2)); -assertEquals(3, matrix3d.rank()); - -// Initializing 3D matrix data with vectors from the first dimension (index 0) -matrix3d.elements(0).forEach(matrix -> { - assertEquals(2, matrix.rank()); - assertEquals(Shape.of(3, 2), matrix.shape()); - matrix - .set(NdArrays.vectorOf(1, 2), 0) - .set(NdArrays.vectorOf(3, 4), 1) - .set(NdArrays.vectorOf(5, 6), 2); -}); - -// Visit all scalars of 3D matrix, printing their coordinates and value -matrix3d.scalars().forEachIdx((coords, scalar) -> - System.out.println("Scalar at " + Arrays.toString(coords) + " has value " + scalar.getInt()) -); - -// Retrieving the second vector of the first matrix -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 }); -assertEquals(7, matrix3d.getInt(0, 1, 0)); -assertEquals(8, matrix3d.getInt(0, 1, 1)); - -// Slicing the 3D matrix so we only keep the second element of the second dimension -IntNdArray slice = matrix3d.slice(all(), at(1)); -assertEquals(2, slice.rank()); -assertEquals(Shape.of(2, 2), slice.shape()); -assertEquals(7, slice.getInt(0, 0)); // (0, 1, 0) in the original matrix -assertEquals(3, slice.getInt(1, 0)); // (1, 1, 0) in the original matrix -``` - -## Integration with TensorFlow - -The NdArray library is independent of the TensorFlow runtime library, making it a good choice for -manipulating multi-dimensional data structures from anywhere. But as an example, here -is how it is actually being used by the [TensorFlow Core API](https://github.com/tensorflow/java/tree/master/tensorflow-core/tensorflow-core-api): - -```java -// Allocate a tensor of 32-bits integer of the shape (2, 3, 2) -Tensor tensor = TInt32.ofShape(2, 3, 2); - -// Access tensor memory directly -IntNdArray tensorData = tensor.data(); -assertEquals(3, tensorData.rank()); -assertEquals(12, tensorData.size()); - -try (EagerSession session = EagerSession.create()) { - Ops tf = Ops.create(session); - - // Initialize tensor memory with zeros and take a snapshot - tensorData.scalars().forEach(scalar -> scalar.setInt(0)); - Constant x = tf.constant(tensor); - - // Initialize the same tensor memory with ones and take a snapshot - tensorData.scalars().forEach(scalar -> scalar.setInt(1)); - Constant y = tf.constant(tensor); - - // Subtract y from x and validate the result - Sub sub = tf.math.sub(x, y); - sub.data().scalars().forEach(scalar -> - assertEquals(-1, scalar.getInt()) - ); -} -``` diff --git a/ndarray/pom.xml b/ndarray/pom.xml deleted file mode 100644 index 36acfa5705b..00000000000 --- a/ndarray/pom.xml +++ /dev/null @@ -1,91 +0,0 @@ - - - 4.0.0 - - - org.tensorflow - tensorflow-java - 0.4.0-SNAPSHOT - - ndarray - jar - - TensorFlow NdArray Library - - Utility library for N-dimensional data I/O operations. - - - - org.tensorflow.ndarray - - - - - org.junit.jupiter - junit-jupiter-api - test - - - org.junit.jupiter - junit-jupiter-engine - test - - - org.openjdk.jmh - jmh-core - test - - - org.openjdk.jmh - jmh-generator-annprocess - test - - - - - - - maven-jar-plugin - 3.2.0 - - - - ${java.module.name} - - - - - - org.apache.maven.plugins - maven-surefire-plugin - 2.22.2 - - 1 - false - -Xmx2G -XX:MaxPermSize=256m - - **/*Test.java - - - - - - - diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/BooleanNdArray.java b/ndarray/src/main/java/org/tensorflow/ndarray/BooleanNdArray.java deleted file mode 100644 index 5b4bedb1c84..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/BooleanNdArray.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - 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 - - 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.tensorflow.ndarray.buffer.BooleanDataBuffer; -import org.tensorflow.ndarray.buffer.DataBuffer; -import org.tensorflow.ndarray.index.Index; - -/** - * An {@link NdArray} of booleans. - */ -public interface BooleanNdArray extends NdArray { - - /** - * Returns the boolean value of the scalar found at the given coordinates. - * - *

To access the scalar element, the number of coordinates provided must be equal to the number - * of dimensions of this array (i.e. its rank). For example: - *

{@code
-   *  BooleanNdArray matrix = NdArrays.ofBooleans(shape(2, 2));  // matrix rank = 2
-   *  matrix.getBoolean(0, 1);  // succeeds, returns false
-   *  matrix.getBoolean(0);  // throws IllegalRankException
-   *
-   *  BooleanNdArray scalar = matrix.get(0, 1);  // scalar rank = 0
-   *  scalar.getBoolean();  // succeeds, returns false
-   * }
- * - * @param coordinates coordinates of the scalar to resolve - * @return value of that scalar - * @throws IndexOutOfBoundsException if some coordinates are outside the limits of their respective dimension - * @throws IllegalRankException if number of coordinates is not sufficient to access a scalar element - */ - boolean getBoolean(long... coordinates); - - /** - * Assigns the boolean value of the scalar found at the given coordinates. - * - *

To access the scalar element, the number of coordinates provided must be equal to the number - * of dimensions of this array (i.e. its rank). For example: - *

{@code
-   *  BooleanNdArray matrix = NdArrays.ofBooleans(shape(2, 2));  // matrix rank = 2
-   *  matrix.setBoolean(true, 0, 1);  // succeeds
-   *  matrix.setBoolean(true, 0);  // throws IllegalRankException
-   *
-   *  BooleanNdArray scalar = matrix.get(0, 1);  // scalar rank = 0
-   *  scalar.setBoolean(true);  // succeeds
-   * }
- * - * @param value the value to assign - * @param coordinates coordinates of the scalar to assign - * @return this array - * @throws IndexOutOfBoundsException if some coordinates are outside the limits of their respective dimension - * @throws IllegalRankException if number of coordinates is not sufficient to access a scalar element - */ - BooleanNdArray setBoolean(boolean value, long... coordinates); - - @Override - BooleanNdArray slice(Index... indices); - - @Override - BooleanNdArray get(long... coordinates); - - @Override - BooleanNdArray set(NdArray src, long... coordinates); - - @Override - default Boolean getObject(long... coordinates) { - return getBoolean(coordinates); - } - - @Override - default BooleanNdArray setObject(Boolean value, long... coordinates) { - return setBoolean(value, coordinates); - } - - @Override - NdArraySequence elements(int dimensionIdx); - - @Override - NdArraySequence scalars(); - - @Override - BooleanNdArray copyTo(NdArray dst); - - @Override - BooleanNdArray read(DataBuffer dst); - - BooleanNdArray read(BooleanDataBuffer dst); - - @Override - BooleanNdArray write(DataBuffer src); - - BooleanNdArray write(BooleanDataBuffer src); -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/ByteNdArray.java b/ndarray/src/main/java/org/tensorflow/ndarray/ByteNdArray.java deleted file mode 100644 index 0e6f118f5ef..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/ByteNdArray.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - 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 - - 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.tensorflow.ndarray.buffer.ByteDataBuffer; -import org.tensorflow.ndarray.buffer.DataBuffer; -import org.tensorflow.ndarray.index.Index; - -/** - * An {@link NdArray} of bytes. - */ -public interface ByteNdArray extends NdArray { - - /** - * Returns the byte value of the scalar found at the given coordinates. - * - *

To access the scalar element, the number of coordinates provided must be equal to the number - * of dimensions of this array (i.e. its rank). For example: - *

{@code
-   *  ByteNdArray matrix = NdArrays.ofBytes(shape(2, 2));  // matrix rank = 2
-   *  matrix.getByte(0, 1);  // succeeds, returns 0
-   *  matrix.getByte(0);  // throws IllegalRankException
-   *
-   *  ByteNdArray scalar = matrix.get(0, 1);  // scalar rank = 0
-   *  scalar.getByte();  // succeeds, returns 0
-   * }
- * - * @param coordinates coordinates of the scalar to resolve - * @return value of that scalar - * @throws IndexOutOfBoundsException if some coordinates are outside the limits of their respective dimension - * @throws IllegalRankException if number of coordinates is not sufficient to access a scalar element - */ - byte getByte(long... coordinates); - - /** - * Assigns the byte value of the scalar found at the given coordinates. - * - *

To access the scalar element, the number of coordinates provided must be equal to the number - * of dimensions of this array (i.e. its rank). For example: - *

{@code
-   *  ByteNdArray matrix = NdArrays.ofBytes(shape(2, 2));  // matrix rank = 2
-   *  matrix.setByte(10, 0, 1);  // succeeds
-   *  matrix.setByte(10, 0);  // throws IllegalRankException
-   *
-   *  ByteNdArray scalar = matrix.get(0, 1);  // scalar rank = 0
-   *  scalar.setByte(10);  // succeeds
-   * }
- * - * @param value the value to assign - * @param coordinates coordinates of the scalar to assign - * @return this array - * @throws IndexOutOfBoundsException if some coordinates are outside the limits of their respective dimension - * @throws IllegalRankException if number of coordinates is not sufficient to access a scalar element - */ - ByteNdArray setByte(byte value, long... coordinates); - - @Override - ByteNdArray slice(Index... indices); - - @Override - ByteNdArray get(long... coordinates); - - @Override - ByteNdArray set(NdArray src, long... coordinates); - - @Override - default Byte getObject(long... coordinates) { - return getByte(coordinates); - } - - @Override - default ByteNdArray setObject(Byte value, long... coordinates) { - return setByte(value, coordinates); - } - - @Override - NdArraySequence elements(int dimensionIdx); - - @Override - NdArraySequence scalars(); - - @Override - ByteNdArray copyTo(NdArray dst); - - @Override - ByteNdArray read(DataBuffer dst); - - ByteNdArray read(ByteDataBuffer dst); - - @Override - ByteNdArray write(DataBuffer src); - - ByteNdArray write(ByteDataBuffer src); -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/DoubleNdArray.java b/ndarray/src/main/java/org/tensorflow/ndarray/DoubleNdArray.java deleted file mode 100644 index 80e99b01877..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/DoubleNdArray.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - 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 - - 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.tensorflow.ndarray.buffer.DataBuffer; -import org.tensorflow.ndarray.buffer.DoubleDataBuffer; -import org.tensorflow.ndarray.index.Index; - -/** - * An {@link NdArray} of doubles. - */ -public interface DoubleNdArray extends NdArray { - - /** - * Returns the double value of the scalar found at the given coordinates. - * - *

To access the scalar element, the number of coordinates provided must be equal to the number - * of dimensions of this array (i.e. its rank). For example: - *

{@code
-   *  DoubleNdArray matrix = NdArrays.ofDoubles(shape(2, 2));  // matrix rank = 2
-   *  matrix.getDouble(0, 1);  // succeeds, returns 0.0
-   *  matrix.getDouble(0);  // throws IllegalRankException
-   *
-   *  DoubleNdArray scalar = matrix.get(0, 1);  // scalar rank = 0
-   *  scalar.getDouble();  // succeeds, returns 0.0
-   * }
- * - * @param coordinates coordinates of the scalar to resolve - * @return value of that scalar - * @throws IndexOutOfBoundsException if some coordinates are outside the limits of their respective dimension - * @throws IllegalRankException if number of coordinates is not sufficient to access a scalar element - */ - double getDouble(long... coordinates); - - /** - * Assigns the double value of the scalar found at the given coordinates. - * - *

To access the scalar element, the number of coordinates provided must be equal to the number - * of dimensions of this array (i.e. its rank). For example: - *

{@code
-   *  DoubleNdArray matrix = NdArrays.ofDoubles(shape(2, 2));  // matrix rank = 2
-   *  matrix.setDouble(10.0, 0, 1);  // succeeds
-   *  matrix.setDouble(10.0, 0);  // throws IllegalRankException
-   *
-   *  DoubleNdArray scalar = matrix.get(0, 1);  // scalar rank = 0
-   *  scalar.setDouble(10.0);  // succeeds
-   * }
- * - * @param value value to assign - * @param coordinates coordinates of the scalar to assign - * @return this array - * @throws IndexOutOfBoundsException if some coordinates are outside the limits of their respective dimension - * @throws IllegalRankException if number of coordinates is not sufficient to access a scalar element - */ - DoubleNdArray setDouble(double value, long... coordinates); - - @Override - DoubleNdArray slice(Index... indices); - - @Override - DoubleNdArray get(long... coordinates); - - @Override - DoubleNdArray set(NdArray src, long... coordinates); - - @Override - default Double getObject(long... coordinates) { - return getDouble(coordinates); - } - - @Override - default DoubleNdArray setObject(Double value, long... coordinates) { - return setDouble(value, coordinates); - } - - @Override - NdArraySequence elements(int dimensionIdx); - - @Override - NdArraySequence scalars(); - - @Override - DoubleNdArray copyTo(NdArray dst); - - @Override - DoubleNdArray read(DataBuffer dst); - - DoubleNdArray read(DoubleDataBuffer dst); - - @Override - DoubleNdArray write(DataBuffer src); - - DoubleNdArray write(DoubleDataBuffer src); -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/FloatNdArray.java b/ndarray/src/main/java/org/tensorflow/ndarray/FloatNdArray.java deleted file mode 100644 index 8d4fbf5c1ed..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/FloatNdArray.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - 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 - - 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.tensorflow.ndarray.buffer.DataBuffer; -import org.tensorflow.ndarray.buffer.FloatDataBuffer; -import org.tensorflow.ndarray.index.Index; - -/** - * An {@link NdArray} of floats. - */ -public interface FloatNdArray extends NdArray { - - /** - * Returns the float value of the scalar found at the given coordinates. - * - *

To access the scalar element, the number of coordinates provided must be equal to the number - * of dimensions of this array (i.e. its rank). For example: - *

{@code
-   *  FloatNdArray matrix = NdArrays.ofFloats(shape(2, 2));  // matrix rank = 2
-   *  matrix.getFloat(0, 1);  // succeeds, returns 0.0f
-   *  matrix.getFloat(0);  // throws IllegalRankException
-   *
-   *  FloatNdArray scalar = matrix.get(0, 1);  // scalar rank = 0
-   *  scalar.getFloat();  // succeeds, returns 0.0f
-   * }
- * - * @param coordinates coordinates of the scalar to resolve - * @return value of that scalar - * @throws IndexOutOfBoundsException if some coordinates are outside the limits of their respective dimension - * @throws IllegalRankException if number of coordinates is not sufficient to access a scalar element - */ - float getFloat(long... coordinates); - - /** - * Assigns the float value of the scalar found at the given coordinates. - * - *

To access the scalar element, the number of coordinates provided must be equal to the number - * of dimensions of this array (i.e. its rank). For example: - *

{@code
-   *  FloatNdArray matrix = NdArrays.ofFloats(shape(2, 2));  // matrix rank = 2
-   *  matrix.setFloat(10.0f, 0, 1);  // succeeds
-   *  matrix.setFloat(10.0f, 0);  // throws IllegalRankException
-   *
-   *  FloatNdArray scalar = matrix.get(0, 1);  // scalar rank = 0
-   *  scalar.setFloat(10.0f);  // succeeds
-   * }
- * - * @param value value to assign - * @param coordinates coordinates of the scalar to assign - * @return this array - * @throws IndexOutOfBoundsException if some coordinates are outside the limits of their respective dimension - * @throws IllegalRankException if number of coordinates is not sufficient to access a scalar element - */ - FloatNdArray setFloat(float value, long... coordinates); - - @Override - FloatNdArray slice(Index... coordinates); - - @Override - FloatNdArray get(long... coordinates); - - @Override - FloatNdArray set(NdArray src, long... coordinates); - - @Override - default Float getObject(long... coordinates) { - return getFloat(coordinates); - } - - @Override - default FloatNdArray setObject(Float value, long... coordinates) { - return setFloat(value, coordinates); - } - - @Override - NdArraySequence elements(int dimensionIdx); - - @Override - NdArraySequence scalars(); - - @Override - FloatNdArray copyTo(NdArray dst); - - @Override - FloatNdArray read(DataBuffer dst); - - FloatNdArray read(FloatDataBuffer dst); - - @Override - FloatNdArray write(DataBuffer src); - - FloatNdArray write(FloatDataBuffer src); -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/IllegalRankException.java b/ndarray/src/main/java/org/tensorflow/ndarray/IllegalRankException.java deleted file mode 100644 index b816d338d7e..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/IllegalRankException.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - 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 - - 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; - -/** - * Exception thrown when an operation cannot be completed because of the rank of the targeted array. - */ -public class IllegalRankException extends IllegalArgumentException { - - public IllegalRankException(String message) { - super(message); - } -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/IntNdArray.java b/ndarray/src/main/java/org/tensorflow/ndarray/IntNdArray.java deleted file mode 100644 index aa2cc652d69..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/IntNdArray.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - 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 - - 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.tensorflow.ndarray.buffer.DataBuffer; -import org.tensorflow.ndarray.buffer.IntDataBuffer; -import org.tensorflow.ndarray.index.Index; - -/** - * An {@link NdArray} of integers. - */ -public interface IntNdArray extends NdArray { - - /** - * Returns the integer value of the scalar found at the given coordinates. - * - *

To access the scalar element, the number of coordinates provided must be equal to the number - * of dimensions of this array (i.e. its rank). For example: - *

{@code
-   *  IntNdArray matrix = NdArrays.ofInts(shape(2, 2));  // matrix rank = 2
-   *  matrix.getInt(0, 1);  // succeeds, returns 0
-   *  matrix.getInt(0);  // throws IllegalRankException
-   *
-   *  IntNdArray scalar = matrix.get(0, 1);  // scalar rank = 0
-   *  scalar.getInt();  // succeeds, returns 0
-   * }
- * - * @param coordinates coordinates of the scalar to resolve - * @return value of that scalar - * @throws IndexOutOfBoundsException if some coordinates are outside the limits of their respective dimension - * @throws IllegalRankException if number of coordinates is not sufficient to access a scalar element - */ - int getInt(long... coordinates); - - /** - * Assigns the integer value of the scalar found at the given coordinates. - * - *

To access the scalar element, the number of coordinates provided must be equal to the number - * of dimensions of this array (i.e. its rank). For example: - *

{@code
-   *  IntNdArray matrix = NdArrays.ofInts(shape(2, 2));  // matrix rank = 2
-   *  matrix.setInt(10, 0, 1);  // succeeds
-   *  matrix.setInt(10, 0);  // throws IllegalRankException
-   *
-   *  IntNdArray scalar = matrix.get(0, 1);  // scalar rank = 0
-   *  scalar.setInt(10);  // succeeds
-   * }
- * - * @param value value to assign - * @param coordinates coordinates of the scalar to assign - * @return this array - * @throws IndexOutOfBoundsException if some coordinates are outside the limits of their respective dimension - * @throws IllegalRankException if number of coordinates is not sufficient to access a scalar element - */ - IntNdArray setInt(int value, long... coordinates); - - @Override - IntNdArray slice(Index... indices); - - @Override - IntNdArray get(long... coordinates); - - @Override - IntNdArray set(NdArray src, long... coordinates); - - @Override - default Integer getObject(long... coordinates) { - return getInt(coordinates); - } - - @Override - default IntNdArray setObject(Integer value, long... coordinates) { - return setInt(value, coordinates); - } - - @Override - NdArraySequence elements(int dimensionIdx); - - @Override - NdArraySequence scalars(); - - @Override - IntNdArray copyTo(NdArray dst); - - @Override - IntNdArray read(DataBuffer dst); - - IntNdArray read(IntDataBuffer dst); - - @Override - IntNdArray write(DataBuffer src); - - IntNdArray write(IntDataBuffer src); -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/LongNdArray.java b/ndarray/src/main/java/org/tensorflow/ndarray/LongNdArray.java deleted file mode 100644 index 3e5be6dc7ec..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/LongNdArray.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - 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 - - 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.tensorflow.ndarray.buffer.DataBuffer; -import org.tensorflow.ndarray.buffer.LongDataBuffer; -import org.tensorflow.ndarray.index.Index; - -/** - * An {@link NdArray} of longs. - */ -public interface LongNdArray extends NdArray { - - /** - * Returns the long value of the scalar found at the given coordinates. - * - *

To access the scalar element, the number of coordinates provided must be equal to the number - * of dimensions of this array (i.e. its rank). For example: - *

{@code
-   *  LongNdArray matrix = NdArrays.ofLongs(shape(2, 2));  // matrix rank = 2
-   *  matrix.getLong(0, 1);  // succeeds, returns 0L
-   *  matrix.getLong(0);  // throws IllegalRankException
-   *
-   *  LongNdArray scalar = matrix.get(0, 1);  // scalar rank = 0
-   *  scalar.getLong();  // succeeds, returns 0L
-   * }
- * - * @param coordinates coordinates of the scalar to resolve - * @return value of that scalar - * @throws IndexOutOfBoundsException if some coordinates are outside the limits of their respective dimension - * @throws IllegalRankException if number of coordinates is not sufficient to access a scalar element - */ - long getLong(long... coordinates); - - /** - * Assigns the long value of the scalar found at the given coordinates. - * - *

To access the scalar element, the number of coordinates provided must be equal to the number - * of dimensions of this array (i.e. its rank). For example: - *

{@code
-   *  LongNdArray matrix = NdArrays.ofLongs(shape(2, 2));  // matrix rank = 2
-   *  matrix.setLong(10L, 0, 1);  // succeeds
-   *  matrix.setLong(10L, 0);  // throws IllegalRankException
-   *
-   *  LongNdArray scalar = matrix.get(0, 1);  // scalar rank = 0
-   *  scalar.setLong(10L);  // succeeds
-   * }
- * - * @param value value to assign - * @param coordinates coordinates of the scalar to assign - * @return this array - * @throws IndexOutOfBoundsException if some coordinates are outside the limits of their respective dimension - * @throws IllegalRankException if number of coordinates is not sufficient to access a scalar element - */ - LongNdArray setLong(long value, long... coordinates); - - @Override - LongNdArray slice(Index... indices); - - @Override - LongNdArray get(long... coordinates); - - @Override - LongNdArray set(NdArray src, long... coordinates); - - @Override - default Long getObject(long... coordinates) { - return getLong(coordinates); - } - - @Override - default LongNdArray setObject(Long value, long... coordinates) { - return setLong(value, coordinates); - } - - @Override - NdArraySequence elements(int dimensionIdx); - - @Override - NdArraySequence scalars(); - - @Override - LongNdArray copyTo(NdArray dst); - - @Override - LongNdArray read(DataBuffer dst); - - LongNdArray read(LongDataBuffer dst); - - @Override - LongNdArray write(DataBuffer src); - - LongNdArray write(LongDataBuffer src); -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/NdArray.java b/ndarray/src/main/java/org/tensorflow/ndarray/NdArray.java deleted file mode 100644 index 6686abd9148..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/NdArray.java +++ /dev/null @@ -1,302 +0,0 @@ -/* - 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 - - 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 java.util.function.BiConsumer; -import java.util.function.Consumer; -import org.tensorflow.ndarray.buffer.DataBuffer; -import org.tensorflow.ndarray.index.Index; - -/** - * A data structure of N-dimensions. - * - *

The `NdArray` interface creates an abstraction between the physical storage of a data record, - * which can be linear or segmented, and its logical representation. In general, they achieve - * better performances than standard multi-dimensional arrays in Java by mapping directly linear - * data segments in memory. - * - *

Like {@link DataBuffer}, {@code NdArray} instances support 64-bits indexing so they can be - * used to map very large data records. They also support special coordinates that allow traversing - * their values in any direction or to select only a subset of them. - * - *

Example of usage: - *

{@code
- *    // Creates a 2x3x2 matrix (of rank 3)
- *    FloatNdArray matrix3d = NdArrays.ofFloats(shape(2, 3, 2));
- *
- *    // Initialize sub-matrices data with vectors
- *    matrix.set(NdArrays.vectorOf(1.0f, 2.0f), 0, 0)
- *          .set(NdArrays.vectorOf(3.0f, 4.0f), 0, 1)
- *          .set(NdArrays.vectorOf(5.0f, 6.0f), 0, 2)
- *          .set(NdArrays.vectorOf(7.0f, 8.0f), 1, 0)
- *          .set(NdArrays.vectorOf(9.0f, 10.0f), 1, 1)
- *          .set(NdArrays.vectorOf(11.0f, 12.0f), 1, 2);
- *
- *    // Access the second 3x2 matrix (of rank 2)
- *    FloatNdArray matrix = matrix3d.get(1);
- *
- *    // Access directly the float value at (1, 0) from the second matrix
- *    assertEquals(9.0f, matrix.getFloat(1, 0));
- * }
- * - * @param the type of values to be mapped - */ -public interface NdArray extends Shaped { - - /** - * Returns a sequence of all elements at a given dimension. - * - *

Logically, the N-dimensional array can be flatten in a single vector, where the scalars of - * the {@code (n - 1)}th element precedes those of the {@code (n)}th element, for a total of - * {@link #size()} values. - * - *

For example, given a {@code n x m} matrix on the {@code [x, y]} axes, elements are iterated in - * the following order: - *

x0y0, x0y1, ..., x0ym-1, x1y0, x1y1, ..., xn-1ym-1 - * - *

The returned sequence can then be iterated to visit each elements, either by calling - * {@link NdArraySequence#forEach(Consumer)} or {@link NdArraySequence#forEachIndexed(BiConsumer)}. - *

{@code
-   *    // Iterate matrix for initializing each of its vectors
-   *    matrixOfFloats.elements(0).forEach(v -> {
-   *      v.set(vector(1.0f, 2.0f, 3.0f));
-   *    });
-   *
-   *    // Iterate a vector for reading each of its scalar
-   *    vectorOfFloats.scalars().forEachIdx((coords, s) -> {
-   *      System.out.println("Value " + s.getFloat() + " found at " + coords);
-   *    });
-   * }
- * - * @param dimensionIdx index of the dimension - * @return an {@code NdArray} sequence - * @throws IllegalArgumentException if {@code dimensionIdx} is greater or equal to the total - * number of dimensions of this array - */ - NdArraySequence> elements(int dimensionIdx); - - /** - * Returns a sequence of all scalars in this array. - * - *

This is equivalent to call {@code elements(shape().numDimensions() - 1)} - * - * @return an {@code NdArray} sequence - */ - NdArraySequence> scalars(); - - /** - * Creates a multi-dimensional view (or slice) of this array by mapping one or more dimensions - * to the given index selectors. - * - *

Slices allow to traverse an N-dimensional array in any of its axis and/or to filter only - * elements of interest. For example, for a given matrix on the {@code [x, y]} axes, it is - * possible to iterate elements at {@code y=0} for all {@code x}. - * - *

Any changes applied to the returned slice affect the data of this array as well, as there - * is no copy involved. - * - *

Example of usage: - *

{@code
-   *    FloatNdArray matrix3d = NdArrays.ofFloats(shape(3, 2, 4));  // with [x, y, z] axes
-   *
-   *    // Iterates elements on the x axis by preserving only the 3rd value on the z axis,
-   *    // (i.e. [x, y, 2])
-   *    matrix3d.slice(all(), all(), at(2)).elements(0).forEach(m -> {
-   *      assertEquals(shape(2), m); // y=2, z=0 (scalar)
-   *    });
-   *
-   *    // Creates a slice that contains only the last element of the y axis and elements with an
-   *    // odd `z` coordinate.
-   *    FloatNdArray slice = matrix3d.slice(all(), at(1), odd());
-   *    assertEquals(shape(3, 2), slice.shape());  // x=3, y=0 (scalar), z=2 (odd coordinates)
-   *
-   *    // Iterates backward the elements on the x axis
-   *    matrix3d.slice(flip()).elements(0).forEach(m -> {
-   *      assertEquals(shape(2, 4), m);  // y=2, z=4
-   *    });
-   * }
- * - * @param indices index selectors per dimensions, starting from dimension 0 of this array. - * @return the element resulting of the index selection - * @throws IndexOutOfBoundsException if some coordinates are outside the limits of their - * respective dimension - */ - NdArray slice(Index... indices); - - /** - * Returns the N-dimensional element of this array at the given coordinates. - * - *

Elements of any of the dimensions of this array can be retrieved. For example, if the number - * of coordinates is equal to the number of dimensions of this array, then a rank-0 (scalar) array - * is returned, which value can then be obtained by calling `array.getObject()`. - * - *

Any changes applied to the returned elements affect the data of this array as well, as there - * is no copy involved. - * - *

Note that invoking this method is an equivalent and more efficient way to slice this array - * on single scalar, i.e. {@code array.get(x, y, z)} is equal to - * {@code array.slice(at(x), at(y), at(z))} - * - * @param coordinates coordinates of the element to access, none will return this array - * @return the element at this index - * @throws IndexOutOfBoundsException if some coordinates are outside the limits of their - * respective dimension - */ - NdArray get(long... coordinates); - - /** - * Assigns the value of the N-dimensional element found at the given coordinates. - * - *

The number of coordinates provided can be anywhere between 0 and rank - 1. For example: - *

{@code
-   *  FloatNdArray matrix = NdArrays.ofFloats(shape(2, 2));  // matrix rank = 2
-   *  matrix.set(vector(10.0f, 20.0f), 0);  // success
-   *  matrix.set(scalar(10.0f), 1, 0); // success
-   * }
- * - * @param src an array of the values to assign - * @param coordinates coordinates of the element to assign - * @return this array - * @throws IndexOutOfBoundsException if some coordinates are outside the limits of their - * respective dimension - */ - NdArray set(NdArray src, long... coordinates); - - /** - * Returns the value of the scalar found at the given coordinates. - * - *

To access the scalar element, the number of coordinates provided must be equal to the number - * of dimensions of this array (i.e. its rank). For example: - *

{@code
-   *  FloatNdArray matrix = NdArrays.ofFloats(shape(2, 2));  // matrix rank = 2
-   *  matrix.getObject(0, 1);  // succeeds, returns 0.0f
-   *  matrix.getObject(0);  // throws IllegalRankException
-   *
-   *  FloatNdArray scalar = matrix.get(0, 1);  // scalar rank = 0
-   *  scalar.getObject();  // succeeds, returns 0.0f
-   * }
- * - * Note: if this array stores values of a primitive type, prefer the usage of the specialized - * method in the subclass for that type. For example, {@code floatArray.getFloat(0); }. - * - * @param coordinates coordinates of the scalar to resolve - * @return value of that scalar - * @throws IndexOutOfBoundsException if some coordinates are outside the limits of their - * respective dimension - * @throws IllegalRankException if number of coordinates is not sufficient to access a scalar - * element - */ - T getObject(long... coordinates); - - /** - * Assigns the value of the scalar found at the given coordinates. - * - *

To access the scalar element, the number of coordinates provided must be equal to the number - * of dimensions of this array (i.e. its rank). For example: - *

{@code
-   *  FloatNdArray matrix = NdArrays.ofFloats(shape(2, 2));  // matrix rank = 2
-   *  matrix.setObject(10.0f, 0, 1);  // succeeds
-   *  matrix.setObject(10.0f, 0);  // throws IllegalRankException
-   *
-   *  FloatNdArray scalar = matrix.get(0, 1);  // scalar rank = 0
-   *  scalar.setObject(10.0f);  // succeeds
-   * }
- * - * Note: if this array stores values of a primitive type, prefer the usage of the specialized - * method in the subclass for that type. For example, {@code floatArray.setFloat(10.0f, 0); } - * - * @param value the value to assign - * @param coordinates coordinates of the scalar to assign - * @return this array - * @throws IndexOutOfBoundsException if some coordinates are outside the limits of their - * respective dimension - * @throws IllegalRankException if number of coordinates is not sufficient to access a scalar - * element - */ - NdArray setObject(T value, long... coordinates); - - /** - * Copy the content of this array to the destination array. - * - *

The {@link #shape()} of the destination array must be equal to the shape of this array, or - * an exception is thrown. After the copy, the content of both arrays can be altered - * independently, without affecting each other. - * - * @param dst array to receive a copy of the content of this array - * @return this array - * @throws IllegalArgumentException if the shape of {@code dst} is not equal to the shape of this - * array - */ - NdArray copyTo(NdArray dst); - - /** - * Read 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. - * - * @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); - - /** - * Write the content of this N-dimensional array from the source 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. - * - * @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); - - /** - * Checks equality between n-dimensional arrays. - * - *

An array is equal to another object if this object is another {@link NdArray} of the - * same shape, type and the elements are equal and in the same order. For example: - * - *

{@code
-   * IntNdArray array = NdArrays.ofInts(Shape.of(2, 2))
-   *    .set(NdArrays.vectorOf(1, 2), 0)
-   *    .set(NdArrays.vectorOf(3, 4), 1);
-   *
-   * assertEquals(array, StdArrays.ndCopyOf(new int[][] {{1, 2}, {3, 4}}));  // true
-   * assertEquals(array, StdArrays.ndCopyOf(new Integer[][] {{1, 2}, {3, 4}}));  // true, as Integers are equal to ints
-   * assertNotEquals(array, NdArrays.vectorOf(1, 2, 3, 4));  // false, different shapes
-   * assertNotEquals(array, StdArrays.ndCopyOf(new int[][] {{3, 4}, {1, 2}}));  // false, different order
-   * assertNotEquals(array, StdArrays.ndCopyOf(new long[][] {{1L, 2L}, {3L, 4L}}));  // false, different types
-   * }
- * - *

Note that the computation required to verify equality between two arrays can be expensive - * in some cases and therefore, it is recommended to not use this method in a critical path - * where performances matter. - * - * @param obj object to compare this array with - * @return true if this array is equal to the provided object - */ - @Override - boolean equals(Object obj); -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/NdArraySequence.java b/ndarray/src/main/java/org/tensorflow/ndarray/NdArraySequence.java deleted file mode 100644 index afb930e278b..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/NdArraySequence.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * 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 - * - * 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 java.util.function.BiConsumer; -import org.tensorflow.ndarray.buffer.DataBufferWindow; - -/** - * A sequence of elements of an N-dimensional array. - * - *

An {@code NdArraySequence} is used to traverse an {@code NdArray} in a given dimension - * and visit each of its elements. For example, given a {@code n x m} matrix on the {@code [x, y]} axes, - * elements are iterated in the following order: - *

x0y0, x0y1, ..., x0ym-1, x1y0, x1y1, ..., xn-1ym-1 - * - * @param data type of the array being iterated - */ -public interface NdArraySequence> extends Iterable { - - /** - * Visit each elements of this iteration and their respective coordinates. - * - *

Important: the consumer method should not keep a reference to the coordinates - * as they might be mutable and reused during the iteration to improve performance. - * - * @param consumer method to invoke for each elements - */ - void forEachIndexed(BiConsumer consumer); - - /** - * Returns each element as a new slice. - * - *

Unlike conventional Java collections, elements of a {@code NdArraySequence} are transient, i.e. new {@code NdArray} - * instances are allocated for each iteration. To improve performance, the same instance can be recycled to view - * all elements of this sequence, using a {@link DataBufferWindow}. - * - *

In some cases though, it might be preferable to disable such optimizations to ensure that each element returned is a - * new slice of the original array. For example, if one or more elements visited must live beyond the scope of the sequence - * iteration, {@code asSlices()} makes sure that all elements returned by the sequence are unique instances. - * - *

{@code
-   *     final List vectors = new ArrayList<>();
-   *     IntNdArray matrix = NdArrays.ofInts(Shape.of(6, 6));
-   *     ndArray.elements(0).forEach(e -> vectors::add);  // Not safe, as `e` might always be the same recycled instance
-   *     ndArray.elements(0).asSlices().forEach(e -> vectors::add);  // Safe, each `e` is a distinct NdArray instance
-   * }
- * - * @return a sequence that returns each elements iterated as a new slice - * @see DataBufferWindow - */ - NdArraySequence asSlices(); -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/NdArrays.java b/ndarray/src/main/java/org/tensorflow/ndarray/NdArrays.java deleted file mode 100644 index 8ad55cae7ed..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/NdArrays.java +++ /dev/null @@ -1,495 +0,0 @@ -/* - 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 - - 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.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; -import org.tensorflow.ndarray.buffer.DataBuffers; -import org.tensorflow.ndarray.buffer.DoubleDataBuffer; -import org.tensorflow.ndarray.buffer.FloatDataBuffer; -import org.tensorflow.ndarray.buffer.IntDataBuffer; -import org.tensorflow.ndarray.buffer.LongDataBuffer; -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.DoubleDenseNdArray; -import org.tensorflow.ndarray.impl.dense.FloatDenseNdArray; -import org.tensorflow.ndarray.impl.dense.LongDenseNdArray; -import org.tensorflow.ndarray.impl.dense.ShortDenseNdArray; - -/** - * Utility class for instantiating {@link NdArray} objects. - */ -public final class NdArrays { - - // BYTE ARRAYS - - /** - * Creates byte scalar (rank 0) initialized with the given value. - * - * @param value scalar value - * @return new byte scalar - */ - public static ByteNdArray scalarOf(byte value) { - return ofBytes(Shape.scalar()).setByte(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. - * - * @param values vector values - * @return new byte vector - * @throws IllegalArgumentException if values is null - */ - public static ByteNdArray vectorOf(byte... values) { - if (values == null) { - throw new IllegalArgumentException("Values cannot be null"); - } - return wrap(Shape.of(values.length), DataBuffers.of(values, false, false)); - } - - /** - * Creates an N-dimensional array of bytes of the given shape. - * - *

All values are initialized to zeros. - * - * @param shape shape of the array - * @return new byte N-dimensional array - * @throws IllegalArgumentException if shape is null or has unknown dimensions - */ - public static ByteNdArray ofBytes(Shape shape) { - if (shape == null) { - throw new IllegalArgumentException("Shape cannot be null"); - } - return wrap(shape, DataBuffers.ofBytes(shape.size())); - } - - /** - * Wraps a buffer in a byte N-dimensional array of a given 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 - */ - public static ByteNdArray wrap(Shape shape, ByteDataBuffer buffer) { - return ByteDenseNdArray.create(buffer, shape); - } - - // LONG ARRAYS - - /** - * Creates long scalar (rank 0) initialized with the given value. - * - * @param value scalar value - * @return new long scalar - */ - public static LongNdArray scalarOf(long value) { - return ofLongs(Shape.scalar()).setLong(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. - * - * @param values vector values - * @return new long vector - * @throws IllegalArgumentException if values is null - */ - public static LongNdArray vectorOf(long... values) { - if (values == null) { - throw new IllegalArgumentException(); - } - return wrap(Shape.of(values.length), DataBuffers.of(values, false, false)); - } - - /** - * Creates an N-dimensional array of longs of the given shape. - * - *

All values are initialized to zeros. - * - * @param shape shape of the array - * @return new long N-dimensional array - * @throws IllegalArgumentException if shape is null or has unknown dimensions - */ - public static LongNdArray ofLongs(Shape shape) { - return wrap(shape, DataBuffers.ofLongs(shape.size())); - } - - /** - * Wraps a buffer in a long N-dimensional array of a given 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 - */ - public static LongNdArray wrap(Shape shape, LongDataBuffer buffer) { - return LongDenseNdArray.create(buffer, shape); - } - - // INT ARRAYS - - /** - * Creates long scalar (rank 0) initialized with the given value. - * - * @param value scalar value - * @return new long scalar - */ - public static IntNdArray scalarOf(int value) { - return ofInts(Shape.scalar()).setInt(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. - * - * @param values vector values - * @return new int vector - * @throws IllegalArgumentException if values is null - */ - public static IntNdArray vectorOf(int... values) { - if (values == null) { - throw new IllegalArgumentException(); - } - return wrap(Shape.of(values.length), DataBuffers.of(values, false, false)); - } - - /** - * Creates an N-dimensional array of ints of the given shape. - * - *

All values are initialized to zeros. - * - * @param shape shape of the array - * @return new int N-dimensional array - * @throws IllegalArgumentException if shape is null or has unknown dimensions - */ - public static IntNdArray ofInts(Shape shape) { - return wrap(shape, DataBuffers.ofInts(shape.size())); - } - - /** - * Wraps a buffer in an int N-dimensional array of a given 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 - */ - public static IntNdArray wrap(Shape shape, IntDataBuffer buffer) { - return IntDenseNdArray.create(buffer, shape); - } - - // SHORT ARRAYS - - /** - * Creates short scalar (rank 0) initialized with the given value. - * - * @param value scalar value - * @return new short scalar - */ - public static ShortNdArray scalarOf(short value) { - return ofShorts(Shape.scalar()).setShort(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. - * - * @param values vector values - * @return new short vector - * @throws IllegalArgumentException if values is null - */ - public static ShortNdArray vectorOf(short... values) { - if (values == null) { - throw new IllegalArgumentException(); - } - return wrap(Shape.of(values.length), DataBuffers.of(values, false, false)); - } - - /** - * Creates an N-dimensional array of shorts of the given shape. - * - *

All values are initialized to zeros. - * - * @param shape shape of the array - * @return new short N-dimensional array - * @throws IllegalArgumentException if shape is null or has unknown dimensions - */ - public static ShortNdArray ofShorts(Shape shape) { - return wrap(shape, DataBuffers.ofShorts(shape.size())); - } - - /** - * Wraps a buffer in a short N-dimensional array of a given 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 - */ - public static ShortNdArray wrap(Shape shape, ShortDataBuffer buffer) { - return ShortDenseNdArray.create(buffer, shape); - } - - // FLOAT ARRAYS - - /** - * Creates float scalar (rank 0) initialized with the given value. - * - * @param value scalar value - * @return new float scalar - */ - public static FloatNdArray scalarOf(float value) { - return ofFloats(Shape.scalar()).setFloat(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. - * - * @param values vector values - * @return new float vector - * @throws IllegalArgumentException if values is null - */ - public static FloatNdArray vectorOf(float... values) { - if (values == null) { - throw new IllegalArgumentException(); - } - return wrap(Shape.of(values.length), DataBuffers.of(values, false, false)); - } - - /** - * Creates an N-dimensional array of floats of the given shape. - * - *

All values are initialized to zeros. - * - * @param shape shape of the array - * @return new float N-dimensional array - * @throws IllegalArgumentException if shape is null or has unknown dimensions - */ - public static FloatNdArray ofFloats(Shape shape) { - return wrap(shape, DataBuffers.ofFloats(shape.size())); - } - - /** - * Wraps a buffer in a float N-dimensional array of a given 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 - */ - public static FloatNdArray wrap(Shape shape, FloatDataBuffer buffer) { - return FloatDenseNdArray.create(buffer, shape); - } - - // DOUBLE ARRAYS - - /** - * Creates double scalar (rank 0) initialized with the given value. - * - * @param value scalar value - * @return new double scalar - */ - public static DoubleNdArray scalarOf(double value) { - return ofDoubles(Shape.scalar()).setDouble(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. - * - * @param values vector values - * @return new double vector - * @throws IllegalArgumentException if values is null - */ - public static DoubleNdArray vectorOf(double... values) { - if (values == null) { - throw new IllegalArgumentException(); - } - return wrap(Shape.of(values.length), DataBuffers.of(values, false, false)); - } - - /** - * Creates an N-dimensional array of doubles of the given shape. - * - *

All values are initialized to zeros. - * - * @param shape shape of the array - * @return new double N-dimensional array - * @throws IllegalArgumentException if shape is null or has unknown dimensions - */ - public static DoubleNdArray ofDoubles(Shape shape) { - return wrap(shape, DataBuffers.ofDoubles(shape.size())); - } - - /** - * Wraps a buffer in a double N-dimensional array of a given 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 - */ - public static DoubleNdArray wrap(Shape shape, DoubleDataBuffer buffer) { - return DoubleDenseNdArray.create(buffer, shape); - } - - // BOOLEAN ARRAYS - - /** - * Creates boolean scalar (rank 0) initialized with the given value. - * - * @param value scalar value - * @return new boolean scalar - */ - public static BooleanNdArray scalarOf(boolean value) { - return ofBooleans(Shape.scalar()).setBoolean(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. - * - * @param values vector values - * @return new boolean vector - * @throws IllegalArgumentException if values is null - */ - public static BooleanNdArray vectorOf(boolean... values) { - if (values == null) { - throw new IllegalArgumentException(); - } - return wrap(Shape.of(values.length), DataBuffers.of(values, false, false)); - } - - /** - * Creates an N-dimensional array of booleans of the given shape. - * - *

All values are initialized to zeros. - * - * @param shape shape of the array - * @return new boolean N-dimensional array - * @throws IllegalArgumentException if shape is null or has unknown dimensions - */ - public static BooleanNdArray ofBooleans(Shape shape) { - return wrap(shape, DataBuffers.ofBooleans(shape.size())); - } - - /** - * Wraps a buffer in a boolean N-dimensional array of a given 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 - */ - public static BooleanNdArray wrap(Shape shape, BooleanDataBuffer buffer) { - return BooleanDenseNdArray.create(buffer, shape); - } - - // OBJECT ARRAYS - - /** - * Creates scalar (rank 0) initialized with the given value. - * - * @param value scalar value - * @param the data type - * @return new scalar - */ - @SuppressWarnings("unchecked") - public static NdArray scalarOfObject(T value) { - if (value == null) { - throw new IllegalArgumentException(); - } - 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. - * - * @param values vector values - * @param the data type - * @return new vector - * @throws IllegalArgumentException if values is null - */ - @SafeVarargs - public static NdArray vectorOfObjects(T... values) { - if (values == null || values.length == 0) { - throw new IllegalArgumentException("Null or zero length input supplied to vectorOfObjects."); - } - return wrap(Shape.of(values.length), DataBuffers.of(values, false, false)); - } - - /** - * Creates an N-dimensional array of the given shape. - * - *

All values are initialized to zeros. - * - * @param clazz class of the data to be stored in this array - * @param shape shape of the array - * @param the data type - * @return new N-dimensional array - * @throws IllegalArgumentException if shape is null or has unknown dimensions - */ - public static NdArray ofObjects(Class clazz, Shape shape) { - return wrap(shape, DataBuffers.ofObjects(clazz, shape.size())); - } - - /** - * Wraps a buffer in an N-dimensional array of a given shape. - * - * @param shape shape of the array - * @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 - */ - public static NdArray wrap(Shape shape, DataBuffer buffer) { - return DenseNdArray.wrap(buffer, shape); - } -} - diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/Shape.java b/ndarray/src/main/java/org/tensorflow/ndarray/Shape.java deleted file mode 100644 index 85a905408c7..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/Shape.java +++ /dev/null @@ -1,447 +0,0 @@ -/* -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 - - 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 java.util.Arrays; - -/** - * The shape of a Tensor or {@link NdArray}. - * - *

A {@code Shape} defines sizes along its axes. It may contain an unknown size for one of the - * axes or may be totally unknown, in which case not even the number of axes is known. If the size - * of an axis is unknown, {@link Shape#UNKNOWN_SIZE} should be used as its size. - */ -public final class Shape { - - /** The size of an unknown axis or the total unknown size for an unknown Shape. */ - public static long UNKNOWN_SIZE = -1L; - - /** - * Creates a Shape representing an unknown number of dimensions. - * - * @return A Shape for which {@link Shape#isUnknown()} is true, never null. - */ - public static Shape unknown() { - return new Shape(null); - } - - /** - * Creates a Shape representing a scalar value. - * - * @return A Shape without dimensions for which {@link Shape#isScalar()} is true, never null. - */ - public static Shape scalar() { - return new Shape(new long[0]); - } - - /** - * Create a Shape representing a scalar or an N-dimensional value. - * - *

Creates a Shape representing a scalar or an N-dimensional value (N being at least 1), with - * the provided size for each dimension. A -1 indicates that the size of the corresponding - * dimension is unknown. If no sizes are provided, a Shape representing a scalar is created. For - * example: - * - *

{@code
-   * // A 2-element vector.
-   * Shape vector = Shape.of(2);
-   *
-   * // A 2x3 matrix.
-   * Shape matrix = Shape.of(2, 3);
-   *
-   * // A matrix with 4 columns but an unknown number of rows.
-   * // This is typically used to indicate the shape of tensors that represent
-   * // a variable-sized batch of values. The Shape below might represent a
-   * // variable-sized batch of 4-element vectors.
-   * Shape batch = Shape.of(-1, 4);
-   *
-   * // A scalar. For readability, you should prefer calling 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. - * @return a new shape - */ - public static Shape of(long... dimensionSizes) { - if (dimensionSizes == null || dimensionSizes.length == 0) { - return scalar(); - } - return new Shape(dimensionSizes); - } - - /** - * Returns the total number of elements a Tensor with this Shape would have. - * - *

If {@link Shape#isUnknown()} is true or {@link Shape#hasUnknownDimension()} is true, {@link - * Shape#UNKNOWN_SIZE} is returned. - * - * @return The total number of elements a Tensor with this shape would have if it can be - * calculated, else {@link Shape#UNKNOWN_SIZE}. - */ - public long size() { - if (size == null) { - size = computeSize(dimensionSizes); - } - return size; - } - - /** - * 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 size(int i) { - if (dimensionSizes == null) { - return UNKNOWN_SIZE; - } else if (i >= 0) { - return dimensionSizes[i]; - } else { - return dimensionSizes[dimensionSizes.length + i]; - } - } - - /** - * Returns the number of dimensions of this Shape. -1 if unknown, 0 for a scalar, 1 for a vector, - * 2 for a matrix etc. - */ - public int numDimensions() { - return dimensionSizes != null ? dimensionSizes.length : -1; - } - - /** Returns whether one or more dimensions of this Shape have an unknown size. */ - public boolean hasUnknownDimension() { - if (dimensionSizes == null) { - return true; - } - for (long dimSize : dimensionSizes) { - if (dimSize == UNKNOWN_SIZE) { - return true; - } - } - return false; - } - - /** Returns whether this Shape represents a scalar. */ - public boolean isScalar() { - return dimensionSizes != null && dimensionSizes.length == 0; - } - - /** Returns whether this Shape is the shape of a vector. */ - public boolean isVector() { - return dimensionSizes != null && dimensionSizes.length == 1; - } - - /** Returns whether this Shape is the shape of a matrix */ - public boolean isMatrix() { - return dimensionSizes != null && dimensionSizes.length == 2; - } - - /** Returns whether the number of dimensions of this Shape is unknown. */ - public boolean isUnknown() { - return dimensionSizes == null; - } - - /** - * Returns a defensive copy of the this Shape's axes. Changes to the returned array to not change - * this Shape's state. Returns null if {@link Shape#isUnknown()} is true. - */ - public long[] asArray() { - if (this.dimensionSizes == null) { - return null; - } else { - return Arrays.copyOf(dimensionSizes, dimensionSizes.length); - } - } - - @Override - public int hashCode() { - return dimensionSizes != null ? Arrays.hashCode(dimensionSizes) : super.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 - *
- * - *

If either Shape has unknown dimensions (even if they are the same in both) or if either - * shape has an unknown number of dimensions (even if both return {@code true} for {@link - * Shape#isUnknown()}), they are not considered equal! However, a shape will always equal itself, - * even if it is unknown or contains unknown dimensions. - */ - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - // Shapes are equivalent if all of their dimensions are equals - if (obj instanceof Shape) { - Shape otherShape = (Shape) obj; - if (otherShape.hasUnknownDimension()) { - return false; - } - return Arrays.equals(dimensionSizes, otherShape.dimensionSizes); - } - return false; - } - - /** Succinct description of the Shape meant for debugging. */ - @Override - public String toString() { - return Arrays.toString(dimensionSizes); - } - - private Shape(long[] dimensionSizes) { - this.dimensionSizes = dimensionSizes; - } - - private final long[] dimensionSizes; - private Long size; - - /** - * Returns a 1-dimensional Shape with first dimension matching the first dimension of this Shape. - */ - public Shape head() { - return take(1); - } - - /** - * 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()} - * @return an n-dimensional Shape with the first n dimensions matching the first n dimensions of - * this Shape - */ - public Shape take(int n) { - if (n > numDimensions()) { - throw new ArrayIndexOutOfBoundsException( - "Cannot take " + n + " dimensions, shape has only " + numDimensions() + "."); - } - long[] newDimensions = new long[n]; - System.arraycopy(dimensionSizes, 0, newDimensions, 0, n); - return Shape.of(newDimensions); - } - - /** Returns a new Shape, with this Shape's first dimension removed. */ - public Shape tail() { - if (dimensionSizes.length < 2) return Shape.of(); - return Shape.of(Arrays.copyOfRange(dimensionSizes, 1, dimensionSizes.length)); - } - - /** - * Returns an n-dimensional Shape with the dimensions matching the last n dimensions of this - * Shape. - * - * @param n the number of trailing dimensions to get, must be <= than {@link - * Shape#numDimensions()} - * @return an n-dimensional shape with the dimensions matching the last n dimensions of this - * Shape, never null - */ - public Shape takeLast(int n) { - if (n > numDimensions()) { - throw new ArrayIndexOutOfBoundsException( - "Cannot take last " + n + " dimensions, shape has only " + numDimensions() + "."); - } - long[] newDimensions = new long[n]; - System.arraycopy(dimensionSizes, numDimensions() - n, newDimensions, 0, n); - return Shape.of(newDimensions); - } - - /** - * 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){ - if (end > numDimensions()) { - throw new ArrayIndexOutOfBoundsException( - "End index " + end + " out of bounds: shape only has " + numDimensions() + " dimensions."); - } - if (begin < 0) { - throw new ArrayIndexOutOfBoundsException( - "Begin index " + begin + " out of bounds: cannot be less than 0."); - } - - long[] newDimensions = new long[end - begin]; - System.arraycopy(dimensionSizes, begin, newDimensions, 0, end - begin); - return Shape.of(newDimensions); - } - - /** - * Returns a new Shape, with a new first dimension added. In order for this call to succeed, - * {@link Shape#isUnknown()} must be {@code false}. - * - * @param firstDimension the dimension to prepend - * @return a new shape with the given dimension first, followed by this Shape's dimensions, never - * null - */ - public Shape prepend(long firstDimension) { - long[] newDimensions = new long[dimensionSizes.length + 1]; - newDimensions[0] = firstDimension; - System.arraycopy(dimensionSizes, 0, newDimensions, 1, dimensionSizes.length); - - return Shape.of(newDimensions); - } - - /** - * Returns a new Shape, with a new last dimension added. In order for this call to succeed, {@link - * Shape#isUnknown()} must be {@code false}. - * - * @param lastDimension the dimension to append - * @return a new Shape with this Shape's dimensions followed by the given dimension, never null - */ - public Shape append(long lastDimension) { - long[] newDimensions = new long[dimensionSizes.length + 1]; - newDimensions[newDimensions.length - 1] = lastDimension; - System.arraycopy(dimensionSizes, 0, newDimensions, 0, dimensionSizes.length); - - return Shape.of(newDimensions); - } - - /** - * Returns a new Shape, with another Shape's dimensions prepended. For both this Shape and the - * other Shape, {@link Shape#isUnknown()} must return false. E.g. {@code - * Shape.of(3,4).prepend(Shape.of(1,2)) => Shape.of(1,2,3,4) } - * - * @param other another Shape, must not be {@code null}, must not be unknown - * @return A new Shape consisting of the given Shape's dimensions followed by this Shape's - * dimensions, never null - */ - public Shape prepend(Shape other) { - long[] newDimensions = new long[other.dimensionSizes.length + dimensionSizes.length]; - System.arraycopy(other.dimensionSizes, 0, newDimensions, 0, other.dimensionSizes.length); - System.arraycopy( - dimensionSizes, 0, newDimensions, other.dimensionSizes.length, dimensionSizes.length); - return Shape.of(newDimensions); - } - - /** - * Returns a new Shape, with another Shapes' dimensions appended. For both this Shape and the - * other Shape, {@link Shape#isUnknown()} must return false. E.g. @code - * Shape.of(3,4).append(Shape.of(1,2)) => Shape.of(3,4,1,2) } - * - * @param other another Shape, must not be {@code null}, must not be unknown - * @return A new Shape consisting of this Shape's dimensions followed by the given Shape's - * dimensions - */ - public Shape append(Shape other) { - long[] newDimensions = new long[dimensionSizes.length + other.dimensionSizes.length]; - System.arraycopy(dimensionSizes, 0, newDimensions, 0, dimensionSizes.length); - System.arraycopy( - other.dimensionSizes, 0, newDimensions, dimensionSizes.length, other.dimensionSizes.length); - return Shape.of(newDimensions); - } - - private static long computeSize(long[] dimensionSizes) { - if (dimensionSizes == null) { - return UNKNOWN_SIZE; - } - long computedSize = 1L; - for (long dimensionSize : dimensionSizes) { - if (dimensionSize == UNKNOWN_SIZE) { - return UNKNOWN_SIZE; - } - computedSize *= dimensionSize; - } - return computedSize; - } - - /** - * Determines whether another shape is compatible with this one. - * - *

- * - *

Two possibly-partially-defined shapes are compatible if there exists a fully-defined shape - * that both shapes can represent. Thus, compatibility allows the shape inference code to reason - * about partially-defined shapes. For example: - * - *

    - *
  • Shape.unknown() is compatible with all shapes. - *
  • Shape(UNKNOWN_SIZE, UNKNOWN_SIZE) is compatible with all two-dimensional - * shapes, such as Shape(32, 784), and also Shape.unknown(). It is - * not compatible with, for example, Shape(UNKNOWN_SIZE) or - * Shape(UNKNOWN_SIZE, UNKNOWN_SIZE, UNKNOWN_SIZE). - *
  • Shape(32, UNKNOWN_SIZE) is compatible with all two-dimensional shapes with - * size 32 in the 0th dimension, and also Shape(UNKNOWN_SIZE, UNKNOWN_SIZE) and - * Shape.unknown(). It is not compatible with, for example, Shape(32) - * , Shape(32, UNKNOWN_SIZE, 1) or Shape(64, UNKNOWN_SIZE). - *
  • Shape(32, 784) is compatible with itself, and also - * Shape(32, UNKNOWN_SIZE), Shape(UNKNOWN_SIZE, 784), - * Shape(UNKNOWN_SIZE, UNKNOWN_SIZE) and Shape.unknown(). It is not - * compatible with, for example, Shape(32, 1, 784) or Shape(UNKNOWN_SIZE) - * . - *
- * - *

The compatibility relation is reflexive and symmetric, but not transitive. For example, - * Shape(32, 784) is compatible with Shape.unknown(), and - * Shape.unknown() is compatible with Shape(4, 4), but Shape(32, 784) - * is not compatible with Shape(4, 4). - * - *

Compatibility is not the same as broadcasting. Compatible shapes must have the same number - * of dimensions and for each dimension pair, one dimension has to equal the other dimensions or - * at least one of the dimensions in the pair has to be UNKNOWN_SIZE. - * - *

Broadcasting allows different dimensions, but paired dimensions have to either be equal, or - * one dimension must be 1. If one shape has less dimensions than another shape, the smaller shape - * is "stretched" with dimensions of 1. - * - * @param shape The other shape - * @return true, if the two shapes are compatible. - */ - public boolean isCompatibleWith(Shape shape) { - if (!this.isUnknown() && !shape.isUnknown()) { - if (numDimensions() != shape.numDimensions()) { - return false; - } - for (int i = 0; i < numDimensions(); i++) { - if (!isCompatible(size(i), shape.size(i))) { - return false; - } - } - } - return true; - } - - /** - * Test to see if two shape dimensions are compatible. - * - *

The dimensions are compatible if either dimension is Shape.UNKNOWN_SIZE or both - * dimensions are equal - * - * @param dim the first dimension - * @param otherDim the second dimension - * @return true, if both dimensions are compatible - */ - public static boolean isCompatible(long dim, long otherDim) { - return dim == Shape.UNKNOWN_SIZE || otherDim == Shape.UNKNOWN_SIZE || dim == otherDim; - } -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/Shaped.java b/ndarray/src/main/java/org/tensorflow/ndarray/Shaped.java deleted file mode 100644 index fbe19d75623..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/Shaped.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - Copyright 2020 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 java.util.function.BiConsumer; -import java.util.function.Consumer; -import org.tensorflow.ndarray.buffer.DataBuffer; -import org.tensorflow.ndarray.index.Index; - -/** - * Any data container with a given {@link Shape}. - */ -public interface Shaped { - - /** - * @return the shape of this container - */ - Shape shape(); - - /** - * @return the rank of this container - */ - default int rank() { - return shape().numDimensions(); - } - - /** - * Computes and returns the total size of this container, in number of values. - * - *

For example, given a 3x3x2 matrix, the return value will be 18. - * - * @return number of values in this element - */ - default long size() { - return shape().size(); - } -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/ShortNdArray.java b/ndarray/src/main/java/org/tensorflow/ndarray/ShortNdArray.java deleted file mode 100644 index f9335b4d5d2..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/ShortNdArray.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - 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 - - 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.tensorflow.ndarray.buffer.DataBuffer; -import org.tensorflow.ndarray.buffer.ShortDataBuffer; -import org.tensorflow.ndarray.index.Index; - -/** - * An {@link NdArray} of shorts. - */ -public interface ShortNdArray extends NdArray { - - /** - * Returns the short value of the scalar found at the given coordinates. - * - *

To access the scalar element, the number of coordinates provided must be equal to the number - * of dimensions of this array (i.e. its rank). For example: - *

{@code
-   *  ShortNdArray matrix = NdArrays.ofShorts(shape(2, 2));  // matrix rank = 2
-   *  matrix.getShort(0, 1);  // succeeds, returns 0.0f
-   *  matrix.getShort(0);  // throws IllegalRankException
-   *
-   *  ShortNdArray scalar = matrix.get(0, 1);  // scalar rank = 0
-   *  scalar.getShort();  // succeeds, returns 0.0f
-   * }
- * - * @param coordinates coordinates of the scalar to resolve - * @return value of that scalar - * @throws IndexOutOfBoundsException if some coordinates are outside the limits of their respective dimension - * @throws IllegalRankException if number of coordinates is not sufficient to access a scalar element - */ - short getShort(long... coordinates); - - /** - * Assigns the short value of the scalar found at the given coordinates. - * - *

To access the scalar element, the number of coordinates provided must be equal to the number - * of dimensions of this array (i.e. its rank). For example: - *

{@code
-   *  ShortNdArray matrix = NdArrays.ofShorts(shape(2, 2));  // matrix rank = 2
-   *  matrix.setShort(10.0f, 0, 1);  // succeeds
-   *  matrix.setShort(10.0f, 0);  // throws IllegalRankException
-   *
-   *  ShortNdArray scalar = matrix.get(0, 1);  // scalar rank = 0
-   *  scalar.setShort(10.0f);  // succeeds
-   * }
- * - * @param value value to assign - * @param coordinates coordinates of the scalar to assign - * @return this array - * @throws IndexOutOfBoundsException if some coordinates are outside the limits of their respective dimension - * @throws IllegalRankException if number of coordinates is not sufficient to access a scalar element - */ - ShortNdArray setShort(short value, long... coordinates); - - @Override - ShortNdArray slice(Index... coordinates); - - @Override - ShortNdArray get(long... coordinates); - - @Override - ShortNdArray set(NdArray src, long... coordinates); - - @Override - default Short getObject(long... coordinates) { - return getShort(coordinates); - } - - @Override - default ShortNdArray setObject(Short value, long... coordinates) { - return setShort(value, coordinates); - } - - @Override - NdArraySequence elements(int dimensionIdx); - - @Override - NdArraySequence scalars(); - - @Override - ShortNdArray copyTo(NdArray dst); - - @Override - ShortNdArray read(DataBuffer dst); - - ShortNdArray read(ShortDataBuffer dst); - - @Override - ShortNdArray write(DataBuffer src); - - ShortNdArray write(ShortDataBuffer src); -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/StdArrays.java b/ndarray/src/main/java/org/tensorflow/ndarray/StdArrays.java deleted file mode 100644 index 7d847bd1a9c..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/StdArrays.java +++ /dev/null @@ -1,3809 +0,0 @@ -package org.tensorflow.ndarray; - -import static org.tensorflow.ndarray.NdArrays.vectorOf; - -import java.lang.reflect.Array; -import org.tensorflow.ndarray.buffer.DataBuffers; - -/** - * Utility class for working with {@link NdArray} instances mixed with standard Java arrays. - */ -public final class StdArrays { - - /** - * Copy an array of ints in a new {@link IntNdArray} - * - * @param array source array - * @return the {@code IntNdArray} copy - */ - public static IntNdArray ndCopyOf(int[] array) { - IntNdArray ndArray = NdArrays.ofInts(shapeOf(array)); - copyTo(array, ndArray); - return ndArray; - } - - /** - * Copy a 2-dimensional array of ints in a new {@link IntNdArray} - * - * @param array source array - * @return the {@code IntNdArray} copy - */ - public static IntNdArray ndCopyOf(int[][] array) { - IntNdArray ndArray = NdArrays.ofInts(shapeOf(array)); - copyTo(array, ndArray); - return ndArray; - } - - /** - * Copy a 3-dimensional array of ints in a new {@link IntNdArray} - * - * @param array source array - * @return the {@code IntNdArray} copy - */ - public static IntNdArray ndCopyOf(int[][][] array) { - IntNdArray ndArray = NdArrays.ofInts(shapeOf(array)); - copyTo(array, ndArray); - return ndArray; - } - - /** - * Copy a 4-dimensional array of ints in a new {@link IntNdArray} - * - * @param array source array - * @return the {@code IntNdArray} copy - */ - public static IntNdArray ndCopyOf(int[][][][] array) { - IntNdArray ndArray = NdArrays.ofInts(shapeOf(array)); - copyTo(array, ndArray); - return ndArray; - } - - /** - * Copy a 5-dimensional array of ints in a new {@link IntNdArray} - * - * @param array source array - * @return the {@code IntNdArray} copy - */ - public static IntNdArray ndCopyOf(int[][][][][] array) { - IntNdArray ndArray = NdArrays.ofInts(shapeOf(array)); - copyTo(array, ndArray); - return ndArray; - } - - /** - * Copy a 6-dimensional array of ints in a new {@link IntNdArray} - * - * @param array source array - * @return the {@code IntNdArray} copy - */ - public static IntNdArray ndCopyOf(int[][][][][][] array) { - IntNdArray ndArray = NdArrays.ofInts(shapeOf(array)); - copyTo(array, ndArray); - return ndArray; - } - - /** - * Copy an array of longs in a new {@link LongNdArray} - * - * @param array source array - * @return the {@code LongNdArray} copy - */ - public static LongNdArray ndCopyOf(long[] array) { - LongNdArray ndArray = NdArrays.ofLongs(shapeOf(array)); - copyTo(array, ndArray); - return ndArray; - } - - /** - * Copy a 2-dimensional array of longs in a new {@link LongNdArray} - * - * @param array source array - * @return the {@code LongNdArray} copy - */ - public static LongNdArray ndCopyOf(long[][] array) { - LongNdArray ndArray = NdArrays.ofLongs(shapeOf(array)); - copyTo(array, ndArray); - return ndArray; - } - - /** - * Copy a 3-dimensional array of longs in a new {@link LongNdArray} - * - * @param array source array - * @return the {@code LongNdArray} copy - */ - public static LongNdArray ndCopyOf(long[][][] array) { - LongNdArray ndArray = NdArrays.ofLongs(shapeOf(array)); - copyTo(array, ndArray); - return ndArray; - } - - /** - * Copy a 4-dimensional array of longs in a new {@link LongNdArray} - * - * @param array source array - * @return the {@code LongNdArray} copy - */ - public static LongNdArray ndCopyOf(long[][][][] array) { - LongNdArray ndArray = NdArrays.ofLongs(shapeOf(array)); - copyTo(array, ndArray); - return ndArray; - } - - /** - * Copy a 5-dimensional array of longs in a new {@link LongNdArray} - * - * @param array source array - * @return the {@code LongNdArray} copy - */ - public static LongNdArray ndCopyOf(long[][][][][] array) { - LongNdArray ndArray = NdArrays.ofLongs(shapeOf(array)); - copyTo(array, ndArray); - return ndArray; - } - - /** - * Copy a 6-dimensional array of longs in a new {@link LongNdArray} - * - * @param array source array - * @return the {@code LongNdArray} copy - */ - public static LongNdArray ndCopyOf(long[][][][][][] array) { - LongNdArray ndArray = NdArrays.ofLongs(shapeOf(array)); - copyTo(array, ndArray); - return ndArray; - } - - /** - * Copy an array of floats in a new {@link FloatNdArray} - * - * @param array source array - * @return the {@code FloatNdArray} copy - */ - public static FloatNdArray ndCopyOf(float[] array) { - FloatNdArray ndArray = NdArrays.ofFloats(shapeOf(array)); - copyTo(array, ndArray); - return ndArray; - } - - /** - * Copy a 2-dimensional array of floats in a new {@link FloatNdArray} - * - * @param array source array - * @return the {@code FloatNdArray} copy - */ - public static FloatNdArray ndCopyOf(float[][] array) { - FloatNdArray ndArray = NdArrays.ofFloats(shapeOf(array)); - copyTo(array, ndArray); - return ndArray; - } - - /** - * Copy a 3-dimensional array of floats in a new {@link FloatNdArray} - * - * @param array source array - * @return the {@code FloatNdArray} copy - */ - public static FloatNdArray ndCopyOf(float[][][] array) { - FloatNdArray ndArray = NdArrays.ofFloats(shapeOf(array)); - copyTo(array, ndArray); - return ndArray; - } - - /** - * Copy a 4-dimensional array of floats in a new {@link FloatNdArray} - * - * @param array source array - * @return the {@code FloatNdArray} copy - */ - public static FloatNdArray ndCopyOf(float[][][][] array) { - FloatNdArray ndArray = NdArrays.ofFloats(shapeOf(array)); - copyTo(array, ndArray); - return ndArray; - } - - /** - * Copy a 5-dimensional array of floats in a new {@link FloatNdArray} - * - * @param array source array - * @return the {@code FloatNdArray} copy - */ - public static FloatNdArray ndCopyOf(float[][][][][] array) { - FloatNdArray ndArray = NdArrays.ofFloats(shapeOf(array)); - copyTo(array, ndArray); - return ndArray; - } - - /** - * Copy a 6-dimensional array of floats in a new {@link FloatNdArray} - * - * @param array source array - * @return the {@code FloatNdArray} copy - */ - public static FloatNdArray ndCopyOf(float[][][][][][] array) { - FloatNdArray ndArray = NdArrays.ofFloats(shapeOf(array)); - copyTo(array, ndArray); - return ndArray; - } - - /** - * Copy an array of doubles in a new {@link DoubleNdArray} - * - * @param array source array - * @return the {@code DoubleNdArray} copy - */ - public static DoubleNdArray ndCopyOf(double[] array) { - DoubleNdArray ndArray = NdArrays.ofDoubles(shapeOf(array)); - copyTo(array, ndArray); - return ndArray; - } - - /** - * Copy a 2-dimensional array of doubles in a new {@link DoubleNdArray} - * - * @param array source array - * @return the {@code DoubleNdArray} copy - */ - public static DoubleNdArray ndCopyOf(double[][] array) { - DoubleNdArray ndArray = NdArrays.ofDoubles(shapeOf(array)); - copyTo(array, ndArray); - return ndArray; - } - - /** - * Copy a 3-dimensional array of doubles in a new {@link DoubleNdArray} - * - * @param array source array - * @return the {@code DoubleNdArray} copy - */ - public static DoubleNdArray ndCopyOf(double[][][] array) { - DoubleNdArray ndArray = NdArrays.ofDoubles(shapeOf(array)); - copyTo(array, ndArray); - return ndArray; - } - - /** - * Copy a 4-dimensional array of doubles in a new {@link DoubleNdArray} - * - * @param array source array - * @return the {@code DoubleNdArray} copy - */ - public static DoubleNdArray ndCopyOf(double[][][][] array) { - DoubleNdArray ndArray = NdArrays.ofDoubles(shapeOf(array)); - copyTo(array, ndArray); - return ndArray; - } - - /** - * Copy a 5-dimensional array of doubles in a new {@link DoubleNdArray} - * - * @param array source array - * @return the {@code DoubleNdArray} copy - */ - public static DoubleNdArray ndCopyOf(double[][][][][] array) { - DoubleNdArray ndArray = NdArrays.ofDoubles(shapeOf(array)); - copyTo(array, ndArray); - return ndArray; - } - - /** - * Copy a 6-dimensional array of doubles in a new {@link DoubleNdArray} - * - * @param array source array - * @return the {@code DoubleNdArray} copy - */ - public static DoubleNdArray ndCopyOf(double[][][][][][] array) { - DoubleNdArray ndArray = NdArrays.ofDoubles(shapeOf(array)); - copyTo(array, ndArray); - return ndArray; - } - - /** - * Copy an array of bytes in a new {@link ByteNdArray} - * - * @param array source array - * @return the {@code ByteNdArray} copy - */ - public static ByteNdArray ndCopyOf(byte[] array) { - ByteNdArray ndArray = NdArrays.ofBytes(shapeOf(array)); - copyTo(array, ndArray); - return ndArray; - } - - /** - * Copy a 2-dimensional array of bytes in a new {@link ByteNdArray} - * - * @param array source array - * @return the {@code ByteNdArray} copy - */ - public static ByteNdArray ndCopyOf(byte[][] array) { - ByteNdArray ndArray = NdArrays.ofBytes(shapeOf(array)); - copyTo(array, ndArray); - return ndArray; - } - - /** - * Copy a 3-dimensional array of bytes in a new {@link ByteNdArray} - * - * @param array source array - * @return the {@code ByteNdArray} copy - */ - public static ByteNdArray ndCopyOf(byte[][][] array) { - ByteNdArray ndArray = NdArrays.ofBytes(shapeOf(array)); - copyTo(array, ndArray); - return ndArray; - } - - /** - * Copy a 4-dimensional array of bytes in a new {@link ByteNdArray} - * - * @param array source array - * @return the {@code ByteNdArray} copy - */ - public static ByteNdArray ndCopyOf(byte[][][][] array) { - ByteNdArray ndArray = NdArrays.ofBytes(shapeOf(array)); - copyTo(array, ndArray); - return ndArray; - } - - /** - * Copy a 5-dimensional array of bytes in a new {@link ByteNdArray} - * - * @param array source array - * @return the {@code ByteNdArray} copy - */ - public static ByteNdArray ndCopyOf(byte[][][][][] array) { - ByteNdArray ndArray = NdArrays.ofBytes(shapeOf(array)); - copyTo(array, ndArray); - return ndArray; - } - - /** - * Copy a 6-dimensional array of bytes in a new {@link ByteNdArray} - * - * @param array source array - * @return the {@code ByteNdArray} copy - */ - public static ByteNdArray ndCopyOf(byte[][][][][][] array) { - ByteNdArray ndArray = NdArrays.ofBytes(shapeOf(array)); - copyTo(array, ndArray); - return ndArray; - } - - /** - * Copy an array of shorts in a new {@link ShortNdArray} - * - * @param array source array - * @return the {@code ShortNdArray} copy - */ - public static ShortNdArray ndCopyOf(short[] array) { - ShortNdArray ndArray = NdArrays.ofShorts(shapeOf(array)); - copyTo(array, ndArray); - return ndArray; - } - - /** - * Copy a 2-dimensional array of shorts in a new {@link ShortNdArray} - * - * @param array source array - * @return the {@code ShortNdArray} copy - */ - public static ShortNdArray ndCopyOf(short[][] array) { - ShortNdArray ndArray = NdArrays.ofShorts(shapeOf(array)); - copyTo(array, ndArray); - return ndArray; - } - - /** - * Copy a 3-dimensional array of shorts in a new {@link ShortNdArray} - * - * @param array source array - * @return the {@code ShortNdArray} copy - */ - public static ShortNdArray ndCopyOf(short[][][] array) { - ShortNdArray ndArray = NdArrays.ofShorts(shapeOf(array)); - copyTo(array, ndArray); - return ndArray; - } - - /** - * Copy a 4-dimensional array of shorts in a new {@link ShortNdArray} - * - * @param array source array - * @return the {@code ShortNdArray} copy - */ - public static ShortNdArray ndCopyOf(short[][][][] array) { - ShortNdArray ndArray = NdArrays.ofShorts(shapeOf(array)); - copyTo(array, ndArray); - return ndArray; - } - - /** - * Copy a 5-dimensional array of shorts in a new {@link ShortNdArray} - * - * @param array source array - * @return the {@code ShortNdArray} copy - */ - public static ShortNdArray ndCopyOf(short[][][][][] array) { - ShortNdArray ndArray = NdArrays.ofShorts(shapeOf(array)); - copyTo(array, ndArray); - return ndArray; - } - - /** - * Copy a 6-dimensional array of shorts in a new {@link ShortNdArray} - * - * @param array source array - * @return the {@code ShortNdArray} copy - */ - public static ShortNdArray ndCopyOf(short[][][][][][] array) { - ShortNdArray ndArray = NdArrays.ofShorts(shapeOf(array)); - copyTo(array, ndArray); - return ndArray; - } - - /** - * Copy an array of booleans in a new {@link BooleanNdArray} - * - * @param array source array - * @return the {@code BooleanNdArray} copy - */ - public static BooleanNdArray ndCopyOf(boolean[] array) { - BooleanNdArray ndArray = NdArrays.ofBooleans(shapeOf(array)); - copyTo(array, ndArray); - return ndArray; - } - - /** - * Copy a 2-dimensional array of booleans in a new {@link BooleanNdArray} - * - * @param array source array - * @return the {@code BooleanNdArray} copy - */ - public static BooleanNdArray ndCopyOf(boolean[][] array) { - BooleanNdArray ndArray = NdArrays.ofBooleans(shapeOf(array)); - copyTo(array, ndArray); - return ndArray; - } - - /** - * Copy a 3-dimensional array of booleans in a new {@link BooleanNdArray} - * - * @param array source array - * @return the {@code BooleanNdArray} copy - */ - public static BooleanNdArray ndCopyOf(boolean[][][] array) { - BooleanNdArray ndArray = NdArrays.ofBooleans(shapeOf(array)); - copyTo(array, ndArray); - return ndArray; - } - - /** - * Copy a 4-dimensional array of booleans in a new {@link BooleanNdArray} - * - * @param array source array - * @return the {@code BooleanNdArray} copy - */ - public static BooleanNdArray ndCopyOf(boolean[][][][] array) { - BooleanNdArray ndArray = NdArrays.ofBooleans(shapeOf(array)); - copyTo(array, ndArray); - return ndArray; - } - - /** - * Copy a 5-dimensional array of booleans in a new {@link BooleanNdArray} - * - * @param array source array - * @return the {@code BooleanNdArray} copy - */ - public static BooleanNdArray ndCopyOf(boolean[][][][][] array) { - BooleanNdArray ndArray = NdArrays.ofBooleans(shapeOf(array)); - copyTo(array, ndArray); - return ndArray; - } - - /** - * Copy a 6-dimensional array of booleans in a new {@link BooleanNdArray} - * - * @param array source array - * @return the {@code BooleanNdArray} copy - */ - public static BooleanNdArray ndCopyOf(boolean[][][][][][] array) { - BooleanNdArray ndArray = NdArrays.ofBooleans(shapeOf(array)); - copyTo(array, ndArray); - return ndArray; - } - - /** - * Copy an array of objects in a new {@link NdArray} - * - * @param array source array - * @param data type - * @return the {@code NdArray} copy - */ - public static NdArray ndCopyOf(T[] array) { - @SuppressWarnings("unchecked") - NdArray ndArray = NdArrays.ofObjects(componentTypeOf(array), shapeOf(array)); - copyTo(array, ndArray); - return ndArray; - } - - /** - * Copy a 2-dimensional array of objects in a new {@link NdArray} - * - * @param array source array - * @param data type - * @return the {@code NdArray} copy - */ - public static NdArray ndCopyOf(T[][] array) { - @SuppressWarnings("unchecked") - NdArrayndArray = NdArrays.ofObjects(componentTypeOf(array), shapeOf(array)); - copyTo(array, ndArray); - return ndArray; - } - - /** - * Copy a 3-dimensional array of objects in a new {@link NdArray} - * - * @param array source array - * @param data type - * @return the {@code NdArray} copy - */ - public static NdArray ndCopyOf(T[][][] array) { - @SuppressWarnings("unchecked") - NdArrayndArray = NdArrays.ofObjects(componentTypeOf(array), shapeOf(array)); - copyTo(array, ndArray); - return ndArray; - } - - /** - * Copy a 4-dimensional array of objects in a new {@link NdArray} - * - * @param array source array - * @param data type - * @return the {@code NdArray} copy - */ - public static NdArray ndCopyOf(T[][][][] array) { - @SuppressWarnings("unchecked") - NdArrayndArray = NdArrays.ofObjects(componentTypeOf(array), shapeOf(array)); - copyTo(array, ndArray); - return ndArray; - } - - /** - * Copy a 5-dimensional array of objects in a new {@link NdArray} - * - * @param array source array - * @param data type - * @return the {@code NdArray} copy - */ - public static NdArray ndCopyOf(T[][][][][] array) { - @SuppressWarnings("unchecked") - NdArrayndArray = NdArrays.ofObjects(componentTypeOf(array), shapeOf(array)); - copyTo(array, ndArray); - return ndArray; - } - - /** - * Copy a 6-dimensional array of objects in a new {@link NdArray} - * - * @param array source array - * @param data type - * @return the {@code NdArray} copy - */ - public static NdArray ndCopyOf(T[][][][][][] array) { - @SuppressWarnings("unchecked") - NdArrayndArray = NdArrays.ofObjects(componentTypeOf(array), shapeOf(array)); - copyTo(array, ndArray); - return ndArray; - } - - /** - * Copy a {@link IntNdArray} in a new 1-dimension standard array of ints - * - * @param ndArray source array - * @return the array copy - * @throws IllegalArgumentException if {@code ndArray} is not of rank-1 or has a shape that - * exceeds standard arrays limits - */ - public static int[] array1dCopyOf(IntNdArray ndArray) { - int[] dims = computeArrayDims(ndArray, 1); - int[] array = new int[dims[0]]; - copyFrom(ndArray, array); - return array; - } - - /** - * Copy a {@link IntNdArray} in a new 2-dimension standard array of ints - * - * @param ndArray source array - * @return the array copy - * @throws IllegalArgumentException if {@code ndArray} is not of rank-2 or has a shape that - * exceeds standard arrays limits - */ - public static int[][] array2dCopyOf(IntNdArray ndArray) { - int[] dims = computeArrayDims(ndArray, 2); - int[][] array = new int[dims[0]][dims[1]]; - copyFrom(ndArray, array); - return array; - } - - /** - * Copy a {@link IntNdArray} in a new 3-dimension standard array of ints - * - * @param ndArray source array - * @return the array copy - * @throws IllegalArgumentException if {@code ndArray} is not of rank-3 or has a shape that - * exceeds standard arrays limits - */ - public static int[][][] array3dCopyOf(IntNdArray ndArray) { - int[] dims = computeArrayDims(ndArray, 3); - int[][][] array = new int[dims[0]][dims[1]][dims[2]]; - copyFrom(ndArray, array); - return array; - } - - /** - * Copy a {@link IntNdArray} in a new 4-dimension standard array of ints - * - * @param ndArray source array - * @return the array copy - * @throws IllegalArgumentException if {@code ndArray} is not of rank-4 or has a shape that - * exceeds standard arrays limits - */ - public static int[][][][] array4dCopyOf(IntNdArray ndArray) { - int[] dims = computeArrayDims(ndArray, 4); - int[][][][] array = new int[dims[0]][dims[1]][dims[2]][dims[3]]; - copyFrom(ndArray, array); - return array; - } - - /** - * Copy a {@link IntNdArray} in a new 5-dimension standard array of ints - * - * @param ndArray source array - * @return the array copy - * @throws IllegalArgumentException if {@code ndArray} is not of rank-5 or has a shape that - * exceeds standard arrays limits - */ - public static int[][][][][] array5dCopyOf(IntNdArray ndArray) { - int[] dims = computeArrayDims(ndArray, 5); - int[][][][][] array = new int[dims[0]][dims[1]][dims[2]][dims[3]][dims[4]]; - copyFrom(ndArray, array); - return array; - } - - /** - * Copy a {@link IntNdArray} in a new 6-dimension standard array of ints - * - * @param ndArray source array - * @return the array copy - * @throws IllegalArgumentException if {@code ndArray} is not of rank-6 or has a shape that - * exceeds standard arrays limits - */ - public static int[][][][][][] array6dCopyOf(IntNdArray ndArray) { - int[] dims = computeArrayDims(ndArray, 6); - int[][][][][][] array = new int[dims[0]][dims[1]][dims[2]][dims[3]][dims[4]][dims[5]]; - copyFrom(ndArray, array); - return array; - } - - /** - * Copy a {@link LongNdArray} in a new 1-dimension standard array of longs - * - * @param ndArray source array - * @return the array copy - * @throws IllegalArgumentException if {@code ndArray} is not of rank-1 or has a shape that - * exceeds standard arrays limits - */ - public static long[] array1dCopyOf(LongNdArray ndArray) { - int[] dims = computeArrayDims(ndArray, 1); - long[] array = new long[dims[0]]; - copyFrom(ndArray, array); - return array; - } - - /** - * Copy a {@link LongNdArray} in a new 2-dimension standard array of longs - * - * @param ndArray source array - * @return the array copy - * @throws IllegalArgumentException if {@code ndArray} is not of rank-2 or has a shape that - * exceeds standard arrays limits - */ - public static long[][] array2dCopyOf(LongNdArray ndArray) { - int[] dims = computeArrayDims(ndArray, 2); - long[][] array = new long[dims[0]][dims[1]]; - copyFrom(ndArray, array); - return array; - } - - /** - * Copy a {@link LongNdArray} in a new 3-dimension standard array of longs - * - * @param ndArray source array - * @return the array copy - * @throws IllegalArgumentException if {@code ndArray} is not of rank-3 or has a shape that - * exceeds standard arrays limits - */ - public static long[][][] array3dCopyOf(LongNdArray ndArray) { - int[] dims = computeArrayDims(ndArray, 3); - long[][][] array = new long[dims[0]][dims[1]][dims[2]]; - copyFrom(ndArray, array); - return array; - } - - /** - * Copy a {@link LongNdArray} in a new 4-dimension standard array of longs - * - * @param ndArray source array - * @return the array copy - * @throws IllegalArgumentException if {@code ndArray} is not of rank-4 or has a shape that - * exceeds standard arrays limits - */ - public static long[][][][] array4dCopyOf(LongNdArray ndArray) { - int[] dims = computeArrayDims(ndArray, 4); - long[][][][] array = new long[dims[0]][dims[1]][dims[2]][dims[3]]; - copyFrom(ndArray, array); - return array; - } - - /** - * Copy a {@link LongNdArray} in a new 5-dimension standard array of longs - * - * @param ndArray source array - * @return the array copy - * @throws IllegalArgumentException if {@code ndArray} is not of rank-5 or has a shape that - * exceeds standard arrays limits - */ - public static long[][][][][] array5dCopyOf(LongNdArray ndArray) { - int[] dims = computeArrayDims(ndArray, 5); - long[][][][][] array = new long[dims[0]][dims[1]][dims[2]][dims[3]][dims[4]]; - copyFrom(ndArray, array); - return array; - } - - /** - * Copy a {@link LongNdArray} in a new 6-dimension standard array of longs - * - * @param ndArray source array - * @return the array copy - * @throws IllegalArgumentException if {@code ndArray} is not of rank-6 or has a shape that - * exceeds standard arrays limits - */ - public static long[][][][][][] array6dCopyOf(LongNdArray ndArray) { - int[] dims = computeArrayDims(ndArray, 6); - long[][][][][][] array = new long[dims[0]][dims[1]][dims[2]][dims[3]][dims[4]][dims[5]]; - copyFrom(ndArray, array); - return array; - } - - /** - * Copy a {@link FloatNdArray} in a new 1-dimension standard array of floats - * - * @param ndArray source array - * @return the array copy - * @throws IllegalArgumentException if {@code ndArray} is not of rank-1 or has a shape that - * exceeds standard arrays limits - */ - public static float[] array1dCopyOf(FloatNdArray ndArray) { - int[] dims = computeArrayDims(ndArray, 1); - float[] array = new float[dims[0]]; - copyFrom(ndArray, array); - return array; - } - - /** - * Copy a {@link FloatNdArray} in a new 2-dimension standard array of floats - * - * @param ndArray source array - * @return the array copy - * @throws IllegalArgumentException if {@code ndArray} is not of rank-2 or has a shape that - * exceeds standard arrays limits - */ - public static float[][] array2dCopyOf(FloatNdArray ndArray) { - int[] dims = computeArrayDims(ndArray, 2); - float[][] array = new float[dims[0]][dims[1]]; - copyFrom(ndArray, array); - return array; - } - - /** - * Copy a {@link FloatNdArray} in a new 3-dimension standard array of floats - * - * @param ndArray source array - * @return the array copy - * @throws IllegalArgumentException if {@code ndArray} is not of rank-3 or has a shape that - * exceeds standard arrays limits - */ - public static float[][][] array3dCopyOf(FloatNdArray ndArray) { - int[] dims = computeArrayDims(ndArray, 3); - float[][][] array = new float[dims[0]][dims[1]][dims[2]]; - copyFrom(ndArray, array); - return array; - } - - /** - * Copy a {@link FloatNdArray} in a new 4-dimension standard array of floats - * - * @param ndArray source array - * @return the array copy - * @throws IllegalArgumentException if {@code ndArray} is not of rank-4 or has a shape that - * exceeds standard arrays limits - */ - public static float[][][][] array4dCopyOf(FloatNdArray ndArray) { - int[] dims = computeArrayDims(ndArray, 4); - float[][][][] array = new float[dims[0]][dims[1]][dims[2]][dims[3]]; - copyFrom(ndArray, array); - return array; - } - - /** - * Copy a {@link FloatNdArray} in a new 5-dimension standard array of floats - * - * @param ndArray source array - * @return the array copy - * @throws IllegalArgumentException if {@code ndArray} is not of rank-5 or has a shape that - * exceeds standard arrays limits - */ - public static float[][][][][] array5dCopyOf(FloatNdArray ndArray) { - int[] dims = computeArrayDims(ndArray, 5); - float[][][][][] array = new float[dims[0]][dims[1]][dims[2]][dims[3]][dims[4]]; - copyFrom(ndArray, array); - return array; - } - - /** - * Copy a {@link FloatNdArray} in a new 6-dimension standard array of floats - * - * @param ndArray source array - * @return the array copy - * @throws IllegalArgumentException if {@code ndArray} is not of rank-6 or has a shape that - * exceeds standard arrays limits - */ - public static float[][][][][][] array6dCopyOf(FloatNdArray ndArray) { - int[] dims = computeArrayDims(ndArray, 6); - float[][][][][][] array = new float[dims[0]][dims[1]][dims[2]][dims[3]][dims[4]][dims[5]]; - copyFrom(ndArray, array); - return array; - } - - /** - * Copy a {@link DoubleNdArray} in a new 1-dimension standard array of doubles - * - * @param ndArray source array - * @return the array copy - * @throws IllegalArgumentException if {@code ndArray} is not of rank-1 or has a shape that - * exceeds standard arrays limits - */ - public static double[] array1dCopyOf(DoubleNdArray ndArray) { - int[] dims = computeArrayDims(ndArray, 1); - double[] array = new double[dims[0]]; - copyFrom(ndArray, array); - return array; - } - - /** - * Copy a {@link DoubleNdArray} in a new 2-dimension standard array of doubles - * - * @param ndArray source array - * @return the array copy - * @throws IllegalArgumentException if {@code ndArray} is not of rank-2 or has a shape that - * exceeds standard arrays limits - */ - public static double[][] array2dCopyOf(DoubleNdArray ndArray) { - int[] dims = computeArrayDims(ndArray, 2); - double[][] array = new double[dims[0]][dims[1]]; - copyFrom(ndArray, array); - return array; - } - - /** - * Copy a {@link DoubleNdArray} in a new 3-dimension standard array of doubles - * - * @param ndArray source array - * @return the array copy - * @throws IllegalArgumentException if {@code ndArray} is not of rank-3 or has a shape that - * exceeds standard arrays limits - */ - public static double[][][] array3dCopyOf(DoubleNdArray ndArray) { - int[] dims = computeArrayDims(ndArray, 3); - double[][][] array = new double[dims[0]][dims[1]][dims[2]]; - copyFrom(ndArray, array); - return array; - } - - /** - * Copy a {@link DoubleNdArray} in a new 4-dimension standard array of doubles - * - * @param ndArray source array - * @return the array copy - * @throws IllegalArgumentException if {@code ndArray} is not of rank-4 or has a shape that - * exceeds standard arrays limits - */ - public static double[][][][] array4dCopyOf(DoubleNdArray ndArray) { - int[] dims = computeArrayDims(ndArray, 4); - double[][][][] array = new double[dims[0]][dims[1]][dims[2]][dims[3]]; - copyFrom(ndArray, array); - return array; - } - - /** - * Copy a {@link DoubleNdArray} in a new 5-dimension standard array of doubles - * - * @param ndArray source array - * @return the array copy - * @throws IllegalArgumentException if {@code ndArray} is not of rank-5 or has a shape that - * exceeds standard arrays limits - */ - public static double[][][][][] array5dCopyOf(DoubleNdArray ndArray) { - int[] dims = computeArrayDims(ndArray, 5); - double[][][][][] array = new double[dims[0]][dims[1]][dims[2]][dims[3]][dims[4]]; - copyFrom(ndArray, array); - return array; - } - - /** - * Copy a {@link DoubleNdArray} in a new 6-dimension standard array of doubles - * - * @param ndArray source array - * @return the array copy - * @throws IllegalArgumentException if {@code ndArray} is not of rank-6 or has a shape that - * exceeds standard arrays limits - */ - public static double[][][][][][] array6dCopyOf(DoubleNdArray ndArray) { - int[] dims = computeArrayDims(ndArray, 6); - double[][][][][][] array = new double[dims[0]][dims[1]][dims[2]][dims[3]][dims[4]][dims[5]]; - copyFrom(ndArray, array); - return array; - } - - /** - * Copy a {@link ByteNdArray} in a new 1-dimension standard array of bytes - * - * @param ndArray source array - * @return the array copy - * @throws IllegalArgumentException if {@code ndArray} is not of rank-1 or has a shape that - * exceeds standard arrays limits - */ - public static byte[] array1dCopyOf(ByteNdArray ndArray) { - int[] dims = computeArrayDims(ndArray, 1); - byte[] array = new byte[dims[0]]; - copyFrom(ndArray, array); - return array; - } - - /** - * Copy a {@link ByteNdArray} in a new 2-dimension standard array of bytes - * - * @param ndArray source array - * @return the array copy - * @throws IllegalArgumentException if {@code ndArray} is not of rank-2 or has a shape that - * exceeds standard arrays limits - */ - public static byte[][] array2dCopyOf(ByteNdArray ndArray) { - int[] dims = computeArrayDims(ndArray, 2); - byte[][] array = new byte[dims[0]][dims[1]]; - copyFrom(ndArray, array); - return array; - } - - /** - * Copy a {@link ByteNdArray} in a new 3-dimension standard array of bytes - * - * @param ndArray source array - * @return the array copy - * @throws IllegalArgumentException if {@code ndArray} is not of rank-3 or has a shape that - * exceeds standard arrays limits - */ - public static byte[][][] array3dCopyOf(ByteNdArray ndArray) { - int[] dims = computeArrayDims(ndArray, 3); - byte[][][] array = new byte[dims[0]][dims[1]][dims[2]]; - copyFrom(ndArray, array); - return array; - } - - /** - * Copy a {@link ByteNdArray} in a new 4-dimension standard array of bytes - * - * @param ndArray source array - * @return the array copy - * @throws IllegalArgumentException if {@code ndArray} is not of rank-4 or has a shape that - * exceeds standard arrays limits - */ - public static byte[][][][] array4dCopyOf(ByteNdArray ndArray) { - int[] dims = computeArrayDims(ndArray, 4); - byte[][][][] array = new byte[dims[0]][dims[1]][dims[2]][dims[3]]; - copyFrom(ndArray, array); - return array; - } - - /** - * Copy a {@link ByteNdArray} in a new 5-dimension standard array of bytes - * - * @param ndArray source array - * @return the array copy - * @throws IllegalArgumentException if {@code ndArray} is not of rank-5 or has a shape that - * exceeds standard arrays limits - */ - public static byte[][][][][] array5dCopyOf(ByteNdArray ndArray) { - int[] dims = computeArrayDims(ndArray, 5); - byte[][][][][] array = new byte[dims[0]][dims[1]][dims[2]][dims[3]][dims[4]]; - copyFrom(ndArray, array); - return array; - } - - /** - * Copy a {@link ByteNdArray} in a new 6-dimension standard array of bytes - * - * @param ndArray source array - * @return the array copy - * @throws IllegalArgumentException if {@code ndArray} is not of rank-6 or has a shape that - * exceeds standard arrays limits - */ - public static byte[][][][][][] array6dCopyOf(ByteNdArray ndArray) { - int[] dims = computeArrayDims(ndArray, 6); - byte[][][][][][] array = new byte[dims[0]][dims[1]][dims[2]][dims[3]][dims[4]][dims[5]]; - copyFrom(ndArray, array); - return array; - } - - /** - * Copy a {@link ShortNdArray} in a new 1-dimension standard array of shorts - * - * @param ndArray source array - * @return the array copy - * @throws IllegalArgumentException if {@code ndArray} is not of rank-1 or has a shape that - * exceeds standard arrays limits - */ - public static short[] array1dCopyOf(ShortNdArray ndArray) { - int[] dims = computeArrayDims(ndArray, 1); - short[] array = new short[dims[0]]; - copyFrom(ndArray, array); - return array; - } - - /** - * Copy a {@link ShortNdArray} in a new 2-dimension standard array of shorts - * - * @param ndArray source array - * @return the array copy - * @throws IllegalArgumentException if {@code ndArray} is not of rank-2 or has a shape that - * exceeds standard arrays limits - */ - public static short[][] array2dCopyOf(ShortNdArray ndArray) { - int[] dims = computeArrayDims(ndArray, 2); - short[][] array = new short[dims[0]][dims[1]]; - copyFrom(ndArray, array); - return array; - } - - /** - * Copy a {@link ShortNdArray} in a new 3-dimension standard array of shorts - * - * @param ndArray source array - * @return the array copy - * @throws IllegalArgumentException if {@code ndArray} is not of rank-3 or has a shape that - * exceeds standard arrays limits - */ - public static short[][][] array3dCopyOf(ShortNdArray ndArray) { - int[] dims = computeArrayDims(ndArray, 3); - short[][][] array = new short[dims[0]][dims[1]][dims[2]]; - copyFrom(ndArray, array); - return array; - } - - /** - * Copy a {@link ShortNdArray} in a new 4-dimension standard array of shorts - * - * @param ndArray source array - * @return the array copy - * @throws IllegalArgumentException if {@code ndArray} is not of rank-4 or has a shape that - * exceeds standard arrays limits - */ - public static short[][][][] array4dCopyOf(ShortNdArray ndArray) { - int[] dims = computeArrayDims(ndArray, 4); - short[][][][] array = new short[dims[0]][dims[1]][dims[2]][dims[3]]; - copyFrom(ndArray, array); - return array; - } - - /** - * Copy a {@link ShortNdArray} in a new 5-dimension standard array of shorts - * - * @param ndArray source array - * @return the array copy - * @throws IllegalArgumentException if {@code ndArray} is not of rank-5 or has a shape that - * exceeds standard arrays limits - */ - public static short[][][][][] array5dCopyOf(ShortNdArray ndArray) { - int[] dims = computeArrayDims(ndArray, 5); - short[][][][][] array = new short[dims[0]][dims[1]][dims[2]][dims[3]][dims[4]]; - copyFrom(ndArray, array); - return array; - } - - /** - * Copy a {@link ShortNdArray} in a new 6-dimension standard array of shorts - * - * @param ndArray source array - * @return the array copy - * @throws IllegalArgumentException if {@code ndArray} is not of rank-6 or has a shape that - * exceeds standard arrays limits - */ - public static short[][][][][][] array6dCopyOf(ShortNdArray ndArray) { - int[] dims = computeArrayDims(ndArray, 6); - short[][][][][][] array = new short[dims[0]][dims[1]][dims[2]][dims[3]][dims[4]][dims[5]]; - copyFrom(ndArray, array); - return array; - } - - /** - * Copy a {@link BooleanNdArray} in a new 1-dimension standard array of booleans - * - * @param ndArray source array - * @return the array copy - * @throws IllegalArgumentException if {@code ndArray} is not of rank-1 or has a shape that - * exceeds standard arrays limits - */ - public static boolean[] array1dCopyOf(BooleanNdArray ndArray) { - int[] dims = computeArrayDims(ndArray, 1); - boolean[] array = new boolean[dims[0]]; - copyFrom(ndArray, array); - return array; - } - - /** - * Copy a {@link BooleanNdArray} in a new 2-dimension standard array of booleans - * - * @param ndArray source array - * @return the array copy - * @throws IllegalArgumentException if {@code ndArray} is not of rank-2 or has a shape that - * exceeds standard arrays limits - */ - public static boolean[][] array2dCopyOf(BooleanNdArray ndArray) { - int[] dims = computeArrayDims(ndArray, 2); - boolean[][] array = new boolean[dims[0]][dims[1]]; - copyFrom(ndArray, array); - return array; - } - - /** - * Copy a {@link BooleanNdArray} in a new 3-dimension standard array of booleans - * - * @param ndArray source array - * @return the array copy - * @throws IllegalArgumentException if {@code ndArray} is not of rank-3 or has a shape that - * exceeds standard arrays limits - */ - public static boolean[][][] array3dCopyOf(BooleanNdArray ndArray) { - int[] dims = computeArrayDims(ndArray, 3); - boolean[][][] array = new boolean[dims[0]][dims[1]][dims[2]]; - copyFrom(ndArray, array); - return array; - } - - /** - * Copy a {@link BooleanNdArray} in a new 4-dimension standard array of booleans - * - * @param ndArray source array - * @return the array copy - * @throws IllegalArgumentException if {@code ndArray} is not of rank-4 or has a shape that - * exceeds standard arrays limits - */ - public static boolean[][][][] array4dCopyOf(BooleanNdArray ndArray) { - int[] dims = computeArrayDims(ndArray, 4); - boolean[][][][] array = new boolean[dims[0]][dims[1]][dims[2]][dims[3]]; - copyFrom(ndArray, array); - return array; - } - - /** - * Copy a {@link BooleanNdArray} in a new 5-dimension standard array of booleans - * - * @param ndArray source array - * @return the array copy - * @throws IllegalArgumentException if {@code ndArray} is not of rank-5 or has a shape that - * exceeds standard arrays limits - */ - public static boolean[][][][][] array5dCopyOf(BooleanNdArray ndArray) { - int[] dims = computeArrayDims(ndArray, 5); - boolean[][][][][] array = new boolean[dims[0]][dims[1]][dims[2]][dims[3]][dims[4]]; - copyFrom(ndArray, array); - return array; - } - - /** - * Copy a {@link BooleanNdArray} in a new 6-dimension standard array of booleans - * - * @param ndArray source array - * @return the array copy - * @throws IllegalArgumentException if {@code ndArray} is not of rank-6 or has a shape that - * exceeds standard arrays limits - */ - public static boolean[][][][][][] array6dCopyOf(BooleanNdArray ndArray) { - int[] dims = computeArrayDims(ndArray, 6); - boolean[][][][][][] array = new boolean[dims[0]][dims[1]][dims[2]][dims[3]][dims[4]][dims[5]]; - copyFrom(ndArray, array); - return array; - } - - /** - * Copy a {@link NdArray NdArray<T>} in a new 1-dimension standard array of objects - * - * @param ndArray source array - * @param objectType type of object - * @param data type - * @return the array copy - * @throws IllegalArgumentException if {@code ndArray} is not of rank-1 or has a shape that - * exceeds standard arrays limits - */ - public static T[] array1dCopyOf(NdArray ndArray, Class objectType) { - int[] dims = computeArrayDims(ndArray, 1); - T[] array = (T[])Array.newInstance(objectType, dims[0]); - copyFrom(ndArray, array); - return array; - } - - /** - * Copy a {@link NdArray NdArray<T>} in a new 2-dimension standard array of objects - * - * @param ndArray source array - * @param objectType type of object - * @param data type - * @return the array copy - * @throws IllegalArgumentException if {@code ndArray} is not of rank-2 or has a shape that - * exceeds standard arrays limits - */ - public static T[][] array2dCopyOf(NdArray ndArray, Class objectType) { - int[] dims = computeArrayDims(ndArray, 2); - T[][] array = (T[][])Array.newInstance(objectType, dims[0], dims[1]); - copyFrom(ndArray, array); - return array; - } - - /** - * Copy a {@link NdArray NdArray<T>} in a new 3-dimension standard array of objects - * - * @param ndArray source array - * @param objectType type of object - * @param data type - * @return the array copy - * @throws IllegalArgumentException if {@code ndArray} is not of rank-3 or has a shape that - * exceeds standard arrays limits - */ - public static T[][][] array3dCopyOf(NdArray ndArray, Class objectType) { - int[] dims = computeArrayDims(ndArray, 3); - T[][][] array = (T[][][])Array.newInstance(objectType, dims[0], dims[1], dims[2]); - copyFrom(ndArray, array); - return array; - } - - /** - * Copy a {@link NdArray NdArray<T>} in a new 4-dimension standard array of objects - * - * @param ndArray source array - * @param objectType type of object - * @param data type - * @return the array copy - * @throws IllegalArgumentException if {@code ndArray} is not of rank-4 or has a shape that - * exceeds standard arrays limits - */ - public static T[][][][] array4dCopyOf(NdArray ndArray, Class objectType) { - int[] dims = computeArrayDims(ndArray, 4); - T[][][][] array = (T[][][][])Array.newInstance(objectType, dims[0], dims[1], dims[2], dims[3]); - copyFrom(ndArray, array); - return array; - } - - /** - * Copy a {@link NdArray NdArray<T>} in a new 5-dimension standard array of objects - * - * @param ndArray source array - * @param objectType type of object - * @param data type - * @return the array copy - * @throws IllegalArgumentException if {@code ndArray} is not of rank-5 or has a shape that - * exceeds standard arrays limits - */ - public static T[][][][][] array5dCopyOf(NdArray ndArray, Class objectType) { - int[] dims = computeArrayDims(ndArray, 5); - T[][][][][] array = - (T[][][][][])Array.newInstance(objectType, dims[0], dims[1], dims[2], dims[3], dims[4]); - copyFrom(ndArray, array); - return array; - } - - /** - * Copy a {@link NdArray NdArray<T>} in a new 6-dimension standard array of objects - * - * @param ndArray source array - * @param objectType type of object - * @param data type - * @return the array copy - * @throws IllegalArgumentException if {@code ndArray} is not of rank-6 or has a shape that - * exceeds standard arrays limits - */ - public static T[][][][][][] array6dCopyOf(NdArray ndArray, Class objectType) { - int[] dims = computeArrayDims(ndArray, 6); - T[][][][][][] array = - (T[][][][][][])Array.newInstance(objectType, dims[0], dims[1], dims[2], dims[3], dims[4], dims[5]); - copyFrom(ndArray, array); - return array; - } - - /** - * Copy an array of ints into the {@code dst} {@link NdArray} - * - * @param src source array - * @param dst destination rank-1 array - * @throws IllegalArgumentException if {@code dst} is not of rank-1 or has an incompatible shape - * with the source array - */ - public static void copyTo(int[] src, IntNdArray dst) { - NdArrays.vectorOf(src).copyTo(dst); - } - - /** - * Copy a 2-dimensional array of ints into the {@code dst} {@link NdArray} - * - * @param src source array - * @param dst destination rank-2 array - * @throws IllegalArgumentException if {@code dst} is not of rank-2 or has an incompatible shape - * with the source array - */ - public static void copyTo(int[][] src, IntNdArray dst) { - dst.elements(0).forEachIndexed((idx, e) -> - NdArrays.vectorOf(src[(int)idx[0]]).copyTo(e) - ); - } - - /** - * Copy a 3-dimensional array of ints into the {@code dst} {@link NdArray} - * - * @param src source array - * @param dst destination rank-3 array - * @throws IllegalArgumentException if {@code dst} is not of rank-3 or has an incompatible shape - * with the source array - */ - public static void copyTo(int[][][] src, IntNdArray dst) { - dst.elements(1).forEachIndexed((idx, e) -> - NdArrays.vectorOf(src[(int)idx[0]][(int)idx[1]]).copyTo(e) - ); - } - - /** - * Copy a 4-dimensional array of ints into the {@code dst} {@link NdArray} - * - * @param src source array - * @param dst destination rank-4 array - * @throws IllegalArgumentException if {@code dst} is not of rank-4 or has an incompatible shape - * with the source array - */ - public static void copyTo(int[][][][] src, IntNdArray dst) { - dst.elements(2).forEachIndexed((idx, e) -> - NdArrays.vectorOf(src[(int)idx[0]][(int)idx[1]][(int)idx[2]]).copyTo(e) - ); - } - - /** - * Copy a 5-dimensional array of ints into the {@code dst} {@link NdArray} - * - * @param src source array - * @param dst destination rank-5 array - * @throws IllegalArgumentException if {@code dst} is not of rank-5 or has an incompatible shape - * with the source array - */ - public static void copyTo(int[][][][][] src, IntNdArray dst) { - dst.elements(3).forEachIndexed((idx, e) -> - NdArrays.vectorOf(src[(int)idx[0]][(int)idx[1]][(int)idx[2]][(int)idx[3]]).copyTo(e) - ); - } - - /** - * Copy a 6-dimensional array of ints into the {@code dst} {@link NdArray} - * - * @param src source array - * @param dst destination rank-6 array - * @throws IllegalArgumentException if {@code dst} is not of rank-6 or has an incompatible shape - * with the source array - */ - public static void copyTo(int[][][][][][] src, IntNdArray dst) { - dst.elements(4).forEachIndexed((idx, e) -> - NdArrays.vectorOf(src[(int)idx[0]][(int)idx[1]][(int)idx[2]][(int)idx[3]][(int)idx[4]]).copyTo(e) - ); - } - - /** - * Copy an array of longs into the {@code dst} {@link NdArray} - * - * @param src source array - * @param dst destination rank-1 array - * @throws IllegalArgumentException if {@code dst} is not of rank-1 or has an incompatible shape - * with the source array - */ - public static void copyTo(long[] src, LongNdArray dst) { - NdArrays.vectorOf(src).copyTo(dst); - } - - /** - * Copy a 2-dimensional array of longs into the {@code dst} {@link NdArray} - * - * @param src source array - * @param dst destination rank-2 array - * @throws IllegalArgumentException if {@code dst} is not of rank-2 or has an incompatible shape - * with the source array - */ - public static void copyTo(long[][] src, LongNdArray dst) { - dst.elements(0).forEachIndexed((idx, e) -> - NdArrays.vectorOf(src[(int)idx[0]]).copyTo(e) - ); - } - - /** - * Copy a 3-dimensional array of longs into the {@code dst} {@link NdArray} - * - * @param src source array - * @param dst destination rank-3 array - * @throws IllegalArgumentException if {@code dst} is not of rank-3 or has an incompatible shape - * with the source array - */ - public static void copyTo(long[][][] src, LongNdArray dst) { - dst.elements(1).forEachIndexed((idx, e) -> - NdArrays.vectorOf(src[(int)idx[0]][(int)idx[1]]).copyTo(e) - ); - } - - /** - * Copy a 4-dimensional array of longs into the {@code dst} {@link NdArray} - * - * @param src source array - * @param dst destination rank-4 array - * @throws IllegalArgumentException if {@code dst} is not of rank-4 or has an incompatible shape - * with the source array - */ - public static void copyTo(long[][][][] src, LongNdArray dst) { - dst.elements(2).forEachIndexed((idx, e) -> - NdArrays.vectorOf(src[(int)idx[0]][(int)idx[1]][(int)idx[2]]).copyTo(e) - ); - } - - /** - * Copy a 5-dimensional array of longs into the {@code dst} {@link NdArray} - * - * @param src source array - * @param dst destination rank-5 array - * @throws IllegalArgumentException if {@code dst} is not of rank-5 or has an incompatible shape - * with the source array - */ - public static void copyTo(long[][][][][] src, LongNdArray dst) { - dst.elements(3).forEachIndexed((idx, e) -> - NdArrays.vectorOf(src[(int)idx[0]][(int)idx[1]][(int)idx[2]][(int)idx[3]]).copyTo(e) - ); - } - - /** - * Copy a 6-dimensional array of longs into the {@code dst} {@link NdArray} - * - * @param src source array - * @param dst destination rank-6 array - * @throws IllegalArgumentException if {@code dst} is not of rank-6 or has an incompatible shape - * with the source array - */ - public static void copyTo(long[][][][][][] src, LongNdArray dst) { - dst.elements(4).forEachIndexed((idx, e) -> - NdArrays.vectorOf(src[(int)idx[0]][(int)idx[1]][(int)idx[2]][(int)idx[3]][(int)idx[4]]).copyTo(e) - ); - } - - /** - * Copy an array of floats into the {@code dst} {@link NdArray} - * - * @param src source array - * @param dst destination rank-1 array - * @throws IllegalArgumentException if {@code dst} is not of rank-1 or has an incompatible shape - * with the source array - */ - public static void copyTo(float[] src, FloatNdArray dst) { - NdArrays.vectorOf(src).copyTo(dst); - } - - /** - * Copy a 2-dimensional array of floats into the {@code dst} {@link NdArray} - * - * @param src source array - * @param dst destination rank-2 array - * @throws IllegalArgumentException if {@code dst} is not of rank-2 or has an incompatible shape - * with the source array - */ - public static void copyTo(float[][] src, FloatNdArray dst) { - dst.elements(0).forEachIndexed((idx, e) -> - NdArrays.vectorOf(src[(int)idx[0]]).copyTo(e) - ); - } - - /** - * Copy a 3-dimensional array of floats into the {@code dst} {@link NdArray} - * - * @param src source array - * @param dst destination rank-3 array - * @throws IllegalArgumentException if {@code dst} is not of rank-3 or has an incompatible shape - * with the source array - */ - public static void copyTo(float[][][] src, FloatNdArray dst) { - dst.elements(1).forEachIndexed((idx, e) -> - NdArrays.vectorOf(src[(int)idx[0]][(int)idx[1]]).copyTo(e) - ); - } - - /** - * Copy a 4-dimensional array of floats into the {@code dst} {@link NdArray} - * - * @param src source array - * @param dst destination rank-4 array - * @throws IllegalArgumentException if {@code dst} is not of rank-4 or has an incompatible shape - * with the source array - */ - public static void copyTo(float[][][][] src, FloatNdArray dst) { - dst.elements(2).forEachIndexed((idx, e) -> - NdArrays.vectorOf(src[(int)idx[0]][(int)idx[1]][(int)idx[2]]).copyTo(e) - ); - } - - /** - * Copy a 5-dimensional array of floats into the {@code dst} {@link NdArray} - * - * @param src source array - * @param dst destination rank-5 array - * @throws IllegalArgumentException if {@code dst} is not of rank-5 or has an incompatible shape - * with the source array - */ - public static void copyTo(float[][][][][] src, FloatNdArray dst) { - dst.elements(3).forEachIndexed((idx, e) -> - NdArrays.vectorOf(src[(int)idx[0]][(int)idx[1]][(int)idx[2]][(int)idx[3]]).copyTo(e) - ); - } - - /** - * Copy a 6-dimensional array of floats into the {@code dst} {@link NdArray} - * - * @param src source array - * @param dst destination rank-6 array - * @throws IllegalArgumentException if {@code dst} is not of rank-6 or has an incompatible shape - * with the source array - */ - public static void copyTo(float[][][][][][] src, FloatNdArray dst) { - dst.elements(4).forEachIndexed((idx, e) -> - NdArrays.vectorOf(src[(int)idx[0]][(int)idx[1]][(int)idx[2]][(int)idx[3]][(int)idx[4]]).copyTo(e) - ); - } - - /** - * Copy an array of doubles into the {@code dst} {@link NdArray} - * - * @param src source array - * @param dst destination rank-1 array - * @throws IllegalArgumentException if {@code dst} is not of rank-1 or has an incompatible shape - * with the source array - */ - public static void copyTo(double[] src, DoubleNdArray dst) { - NdArrays.vectorOf(src).copyTo(dst); - } - - /** - * Copy a 2-dimensional array of doubles into the {@code dst} {@link NdArray} - * - * @param src source array - * @param dst destination rank-2 array - * @throws IllegalArgumentException if {@code dst} is not of rank-2 or has an incompatible shape - * with the source array - */ - public static void copyTo(double[][] src, DoubleNdArray dst) { - dst.elements(0).forEachIndexed((idx, e) -> - NdArrays.vectorOf(src[(int)idx[0]]).copyTo(e) - ); - } - - /** - * Copy a 3-dimensional array of doubles into the {@code dst} {@link NdArray} - * - * @param src source array - * @param dst destination rank-3 array - * @throws IllegalArgumentException if {@code dst} is not of rank-3 or has an incompatible shape - * with the source array - */ - public static void copyTo(double[][][] src, DoubleNdArray dst) { - dst.elements(1).forEachIndexed((idx, e) -> - NdArrays.vectorOf(src[(int)idx[0]][(int)idx[1]]).copyTo(e) - ); - } - - /** - * Copy a 4-dimensional array of doubles into the {@code dst} {@link NdArray} - * - * @param src source array - * @param dst destination rank-4 array - * @throws IllegalArgumentException if {@code dst} is not of rank-4 or has an incompatible shape - * with the source array - */ - public static void copyTo(double[][][][] src, DoubleNdArray dst) { - dst.elements(2).forEachIndexed((idx, e) -> - NdArrays.vectorOf(src[(int)idx[0]][(int)idx[1]][(int)idx[2]]).copyTo(e) - ); - } - - /** - * Copy a 5-dimensional array of doubles into the {@code dst} {@link NdArray} - * - * @param src source array - * @param dst destination rank-5 array - * @throws IllegalArgumentException if {@code dst} is not of rank-5 or has an incompatible shape - * with the source array - */ - public static void copyTo(double[][][][][] src, DoubleNdArray dst) { - dst.elements(3).forEachIndexed((idx, e) -> - NdArrays.vectorOf(src[(int)idx[0]][(int)idx[1]][(int)idx[2]][(int)idx[3]]).copyTo(e) - ); - } - - /** - * Copy a 6-dimensional array of doubles into the {@code dst} {@link NdArray} - * - * @param src source array - * @param dst destination rank-6 array - * @throws IllegalArgumentException if {@code dst} is not of rank-6 or has an incompatible shape - * with the source array - */ - public static void copyTo(double[][][][][][] src, DoubleNdArray dst) { - dst.elements(4).forEachIndexed((idx, e) -> - NdArrays.vectorOf(src[(int)idx[0]][(int)idx[1]][(int)idx[2]][(int)idx[3]][(int)idx[4]]).copyTo(e) - ); - } - - /** - * Copy an array of bytes into the {@code dst} {@link NdArray} - * - * @param src source array - * @param dst destination rank-1 array - * @throws IllegalArgumentException if {@code dst} is not of rank-1 or has an incompatible shape - * with the source array - */ - public static void copyTo(byte[] src, ByteNdArray dst) { - NdArrays.vectorOf(src).copyTo(dst); - } - - /** - * Copy a 2-dimensional array of bytes into the {@code dst} {@link NdArray} - * - * @param src source array - * @param dst destination rank-2 array - * @throws IllegalArgumentException if {@code dst} is not of rank-2 or has an incompatible shape - * with the source array - */ - public static void copyTo(byte[][] src, ByteNdArray dst) { - dst.elements(0).forEachIndexed((idx, e) -> - NdArrays.vectorOf(src[(int)idx[0]]).copyTo(e) - ); - } - - /** - * Copy a 3-dimensional array of bytes into the {@code dst} {@link NdArray} - * - * @param src source array - * @param dst destination rank-3 array - * @throws IllegalArgumentException if {@code dst} is not of rank-3 or has an incompatible shape - * with the source array - */ - public static void copyTo(byte[][][] src, ByteNdArray dst) { - dst.elements(1).forEachIndexed((idx, e) -> - NdArrays.vectorOf(src[(int)idx[0]][(int)idx[1]]).copyTo(e) - ); - } - - /** - * Copy a 4-dimensional array of bytes into the {@code dst} {@link NdArray} - * - * @param src source array - * @param dst destination rank-4 array - * @throws IllegalArgumentException if {@code dst} is not of rank-4 or has an incompatible shape - * with the source array - */ - public static void copyTo(byte[][][][] src, ByteNdArray dst) { - dst.elements(2).forEachIndexed((idx, e) -> - NdArrays.vectorOf(src[(int)idx[0]][(int)idx[1]][(int)idx[2]]).copyTo(e) - ); - } - - /** - * Copy a 5-dimensional array of bytes into the {@code dst} {@link NdArray} - * - * @param src source array - * @param dst destination rank-5 array - * @throws IllegalArgumentException if {@code dst} is not of rank-5 or has an incompatible shape - * with the source array - */ - public static void copyTo(byte[][][][][] src, ByteNdArray dst) { - dst.elements(3).forEachIndexed((idx, e) -> - NdArrays.vectorOf(src[(int)idx[0]][(int)idx[1]][(int)idx[2]][(int)idx[3]]).copyTo(e) - ); - } - - /** - * Copy a 6-dimensional array of bytes into the {@code dst} {@link NdArray} - * - * @param src source array - * @param dst destination rank-6 array - * @throws IllegalArgumentException if {@code dst} is not of rank-6 or has an incompatible shape - * with the source array - */ - public static void copyTo(byte[][][][][][] src, ByteNdArray dst) { - dst.elements(4).forEachIndexed((idx, e) -> - NdArrays.vectorOf(src[(int)idx[0]][(int)idx[1]][(int)idx[2]][(int)idx[3]][(int)idx[4]]).copyTo(e) - ); - } - - /** - * Copy an array of shorts into the {@code dst} {@link NdArray} - * - * @param src source array - * @param dst destination rank-1 array - * @throws IllegalArgumentException if {@code dst} is not of rank-1 or has an incompatible shape - * with the source array - */ - public static void copyTo(short[] src, ShortNdArray dst) { - NdArrays.vectorOf(src).copyTo(dst); - } - - /** - * Copy a 2-dimensional array of shorts into the {@code dst} {@link NdArray} - * - * @param src source array - * @param dst destination rank-2 array - * @throws IllegalArgumentException if {@code dst} is not of rank-2 or has an incompatible shape - * with the source array - */ - public static void copyTo(short[][] src, ShortNdArray dst) { - dst.elements(0).forEachIndexed((idx, e) -> - NdArrays.vectorOf(src[(int)idx[0]]).copyTo(e) - ); - } - - /** - * Copy a 3-dimensional array of shorts into the {@code dst} {@link NdArray} - * - * @param src source array - * @param dst destination rank-3 array - * @throws IllegalArgumentException if {@code dst} is not of rank-3 or has an incompatible shape - * with the source array - */ - public static void copyTo(short[][][] src, ShortNdArray dst) { - dst.elements(1).forEachIndexed((idx, e) -> - NdArrays.vectorOf(src[(int)idx[0]][(int)idx[1]]).copyTo(e) - ); - } - - /** - * Copy a 4-dimensional array of shorts into the {@code dst} {@link NdArray} - * - * @param src source array - * @param dst destination rank-4 array - * @throws IllegalArgumentException if {@code dst} is not of rank-4 or has an incompatible shape - * with the source array - */ - public static void copyTo(short[][][][] src, ShortNdArray dst) { - dst.elements(2).forEachIndexed((idx, e) -> - NdArrays.vectorOf(src[(int)idx[0]][(int)idx[1]][(int)idx[2]]).copyTo(e) - ); - } - - /** - * Copy a 5-dimensional array of shorts into the {@code dst} {@link NdArray} - * - * @param src source array - * @param dst destination rank-5 array - * @throws IllegalArgumentException if {@code dst} is not of rank-5 or has an incompatible shape - * with the source array - */ - public static void copyTo(short[][][][][] src, ShortNdArray dst) { - dst.elements(3).forEachIndexed((idx, e) -> - NdArrays.vectorOf(src[(int)idx[0]][(int)idx[1]][(int)idx[2]][(int)idx[3]]).copyTo(e) - ); - } - - /** - * Copy a 6-dimensional array of shorts into the {@code dst} {@link NdArray} - * - * @param src source array - * @param dst destination rank-6 array - * @throws IllegalArgumentException if {@code dst} is not of rank-6 or has an incompatible shape - * with the source array - */ - public static void copyTo(short[][][][][][] src, ShortNdArray dst) { - dst.elements(4).forEachIndexed((idx, e) -> - NdArrays.vectorOf(src[(int)idx[0]][(int)idx[1]][(int)idx[2]][(int)idx[3]][(int)idx[4]]).copyTo(e) - ); - } - - /** - * Copy an array of booleans into the {@code dst} {@link NdArray} - * - * @param src source array - * @param dst destination rank-1 array - * @throws IllegalArgumentException if {@code dst} is not of rank-1 or has an incompatible shape - * with the source array - */ - public static void copyTo(boolean[] src, BooleanNdArray dst) { - NdArrays.vectorOf(src).copyTo(dst); - } - - /** - * Copy a 2-dimensional array of booleans into the {@code dst} {@link NdArray} - * - * @param src source array - * @param dst destination rank-2 array - * @throws IllegalArgumentException if {@code dst} is not of rank-2 or has an incompatible shape - * with the source array - */ - public static void copyTo(boolean[][] src, BooleanNdArray dst) { - dst.elements(0).forEachIndexed((idx, e) -> - NdArrays.vectorOf(src[(int)idx[0]]).copyTo(e) - ); - } - - /** - * Copy a 3-dimensional array of booleans into the {@code dst} {@link NdArray} - * - * @param src source array - * @param dst destination rank-3 array - * @throws IllegalArgumentException if {@code dst} is not of rank-3 or has an incompatible shape - * with the source array - */ - public static void copyTo(boolean[][][] src, BooleanNdArray dst) { - dst.elements(1).forEachIndexed((idx, e) -> - NdArrays.vectorOf(src[(int)idx[0]][(int)idx[1]]).copyTo(e) - ); - } - - /** - * Copy a 4-dimensional array of booleans into the {@code dst} {@link NdArray} - * - * @param src source array - * @param dst destination rank-4 array - * @throws IllegalArgumentException if {@code dst} is not of rank-4 or has an incompatible shape - * with the source array - */ - public static void copyTo(boolean[][][][] src, BooleanNdArray dst) { - dst.elements(2).forEachIndexed((idx, e) -> - NdArrays.vectorOf(src[(int)idx[0]][(int)idx[1]][(int)idx[2]]).copyTo(e) - ); - } - - /** - * Copy a 5-dimensional array of booleans into the {@code dst} {@link NdArray} - * - * @param src source array - * @param dst destination rank-5 array - * @throws IllegalArgumentException if {@code dst} is not of rank-5 or has an incompatible shape - * with the source array - */ - public static void copyTo(boolean[][][][][] src, BooleanNdArray dst) { - dst.elements(3).forEachIndexed((idx, e) -> - NdArrays.vectorOf(src[(int)idx[0]][(int)idx[1]][(int)idx[2]][(int)idx[3]]).copyTo(e) - ); - } - - /** - * Copy a 6-dimensional array of booleans into the {@code dst} {@link NdArray} - * - * @param src source array - * @param dst destination rank-6 array - * @throws IllegalArgumentException if {@code dst} is not of rank-6 or has an incompatible shape - * with the source array - */ - public static void copyTo(boolean[][][][][][] src, BooleanNdArray dst) { - dst.elements(4).forEachIndexed((idx, e) -> - NdArrays.vectorOf(src[(int)idx[0]][(int)idx[1]][(int)idx[2]][(int)idx[3]][(int)idx[4]]).copyTo(e) - ); - } - - /** - * Copy an array of objects into the {@code dst} {@link NdArray} - * - * @param src source array - * @param dst destination rank-1 array - * @param data type - * @throws IllegalArgumentException if {@code dst} is not of rank-1 or has an incompatible shape - * with the source array - */ - public static void copyTo(T[] src, NdArray dst) { - NdArrays.vectorOfObjects(src).copyTo(dst); - } - - /** - * Copy a 2-dimensional array of objects into the {@code dst} {@link NdArray} - * - * @param src source array - * @param dst destination rank-2 array - * @param data type - * @throws IllegalArgumentException if {@code dst} is not of rank-2 or has an incompatible shape - * with the source array - */ - public static void copyTo(T[][] src, NdArray dst) { - dst.elements(0).forEachIndexed((idx, e) -> - NdArrays.vectorOfObjects(src[(int)idx[0]]).copyTo(e) - ); - } - - /** - * Copy a 3-dimensional array of objects into the {@code dst} {@link NdArray} - * - * @param src source array - * @param dst destination rank-3 array - * @param data type - * @throws IllegalArgumentException if {@code dst} is not of rank-3 or has an incompatible shape - * with the source array - */ - public static void copyTo(T[][][] src, NdArray dst) { - dst.elements(1).forEachIndexed((idx, e) -> - NdArrays.vectorOfObjects(src[(int)idx[0]][(int)idx[1]]).copyTo(e) - ); - } - - /** - * Copy a 4-dimensional array of objects into the {@code dst} {@link NdArray} - * - * @param src source array - * @param dst destination rank-4 array - * @param data type - * @throws IllegalArgumentException if {@code dst} is not of rank-4 or has an incompatible shape - * with the source array - */ - public static void copyTo(T[][][][] src, NdArray dst) { - dst.elements(2).forEachIndexed((idx, e) -> - NdArrays.vectorOfObjects(src[(int)idx[0]][(int)idx[1]][(int)idx[2]]).copyTo(e) - ); - } - - /** - * Copy a 5-dimensional array of objects into the {@code dst} {@link NdArray} - * - * @param src source array - * @param dst destination rank-5 array - * @param data type - * @throws IllegalArgumentException if {@code dst} is not of rank-5 or has an incompatible shape - * with the source array - */ - public static void copyTo(T[][][][][] src, NdArray dst) { - dst.elements(3).forEachIndexed((idx, e) -> - NdArrays.vectorOfObjects(src[(int)idx[0]][(int)idx[1]][(int)idx[2]][(int)idx[3]]).copyTo(e) - ); - } - - /** - * Copy a 6-dimensional array of objects into the {@code dst} {@link NdArray} - * - * @param src source array - * @param dst destination rank-6 array - * @param data type - * @throws IllegalArgumentException if {@code dst} is not of rank-6 or has an incompatible shape - * with the source array - */ - public static void copyTo(T[][][][][][] src, NdArray dst) { - dst.elements(4).forEachIndexed((idx, e) -> - NdArrays.vectorOfObjects(src[(int)idx[0]][(int)idx[1]][(int)idx[2]][(int)idx[3]][(int)idx[4]]).copyTo(e) - ); - } - - - /** - * Copy a {@link NdArray} to an array of ints - * - * @param src source rank-1 array - * @param dst destination array - * @throws IllegalArgumentException if {@code src} is not of rank-1 - * @throws ArrayIndexOutOfBoundsException if not all elements of {@code src} can fit it the destination array - */ - public static void copyFrom(IntNdArray src, int[] dst) { - if (src.rank() != 1) { - throw new IllegalArgumentException("Array cannot be copied from NdArray of rank " + src.rank()); - } - if (src.size() > dst.length) { - throw new ArrayIndexOutOfBoundsException(String.valueOf(src.size()) + " > " + dst.length); - } - src.read(DataBuffers.of(dst, false, false)); - } - - /** - * Copy a {@link NdArray} to a 2-dimensional array of ints - * - * @param src source rank-2 array - * @param dst destination array - * @throws IllegalArgumentException if {@code src} is not of rank-2 - * @throws ArrayIndexOutOfBoundsException if not all elements of {@code src} can fit it the destination array - */ - public static void copyFrom(IntNdArray src, int[][] dst) { - if (src.rank() != 2) { - throw new IllegalArgumentException("Array cannot be copied from NdArray of rank " + src.rank()); - } - src.elements(0).forEachIndexed((idx, e) -> - copyFrom(e, dst[(int)idx[0]]) - ); - } - - /** - * Copy a {@link NdArray} to a 3-dimensional array of ints - * - * @param src source rank-3 array - * @param dst destination array - * @throws IllegalArgumentException if {@code src} is not of rank-3 - * @throws ArrayIndexOutOfBoundsException if not all elements of {@code src} can fit it the destination array - */ - public static void copyFrom(IntNdArray src, int[][][] dst) { - if (src.rank() != 3) { - throw new IllegalArgumentException("Array cannot be copied from NdArray of rank " + src.rank()); - } - src.elements(1).forEachIndexed((idx, e) -> - copyFrom(e, dst[(int)idx[0]][(int)idx[1]]) - ); - } - - /** - * Copy a {@link NdArray} to a 4-dimensional array of ints - * - * @param src source rank-4 array - * @param dst destination array - * @throws IllegalArgumentException if {@code src} is not of rank-4 - * @throws ArrayIndexOutOfBoundsException if not all elements of {@code src} can fit it the destination array - */ - public static void copyFrom(IntNdArray src, int[][][][] dst) { - if (src.rank() != 4) { - throw new IllegalArgumentException("Array cannot be copied from NdArray of rank " + src.rank()); - } - src.elements(2).forEachIndexed((idx, e) -> - copyFrom(e, dst[(int)idx[0]][(int)idx[1]][(int)idx[2]]) - ); - } - - /** - * Copy a {@link NdArray} to a 5-dimensional array of ints - * - * @param src source rank-5 array - * @param dst destination array - * @throws IllegalArgumentException if {@code src} is not of rank-5 - * @throws ArrayIndexOutOfBoundsException if not all elements of {@code src} can fit it the destination array - */ - public static void copyFrom(IntNdArray src, int[][][][][] dst) { - if (src.rank() != 5) { - throw new IllegalArgumentException("Array cannot be copied from NdArray of rank " + src.rank()); - } - src.elements(3).forEachIndexed((idx, e) -> - copyFrom(e, dst[(int)idx[0]][(int)idx[1]][(int)idx[2]][(int)idx[3]]) - ); - } - - /** - * Copy a {@link NdArray} to a 6-dimensional array of ints - * - * @param src source rank-6 array - * @param dst destination array - * @throws IllegalArgumentException if {@code src} is not of rank-6 - * @throws ArrayIndexOutOfBoundsException if not all elements of {@code src} can fit it the destination array - */ - public static void copyFrom(IntNdArray src, int[][][][][][] dst) { - if (src.rank() != 6) { - throw new IllegalArgumentException("Array cannot be copied from NdArray of rank " + src.rank()); - } - src.elements(4).forEachIndexed((idx, e) -> - copyFrom(e, dst[(int)idx[0]][(int)idx[1]][(int)idx[2]][(int)idx[3]][(int)idx[4]]) - ); - } - - /** - * Copy a {@link NdArray} to an array of longs - * - * @param src source rank-1 array - * @param dst destination array - * @throws IllegalArgumentException if {@code src} is not of rank-1 - * @throws ArrayIndexOutOfBoundsException if not all elements of {@code src} can fit it the destination array - */ - public static void copyFrom(LongNdArray src, long[] dst) { - if (src.rank() != 1) { - throw new IllegalArgumentException("Array cannot be copied from NdArray of rank " + src.rank()); - } - if (src.size() > dst.length) { - throw new ArrayIndexOutOfBoundsException(String.valueOf(src.size()) + " > " + dst.length); - } - src.read(DataBuffers.of(dst, false, false)); - } - - /** - * Copy a {@link NdArray} to a 2-dimensional array of longs - * - * @param src source rank-2 array - * @param dst destination array - * @throws IllegalArgumentException if {@code src} is not of rank-2 - * @throws ArrayIndexOutOfBoundsException if not all elements of {@code src} can fit it the destination array - */ - public static void copyFrom(LongNdArray src, long[][] dst) { - if (src.rank() != 2) { - throw new IllegalArgumentException("Array cannot be copied from NdArray of rank " + src.rank()); - } - src.elements(0).forEachIndexed((idx, e) -> - copyFrom(e, dst[(int)idx[0]]) - ); - } - - /** - * Copy a {@link NdArray} to a 3-dimensional array of longs - * - * @param src source rank-3 array - * @param dst destination array - * @throws IllegalArgumentException if {@code src} is not of rank-3 - * @throws ArrayIndexOutOfBoundsException if not all elements of {@code src} can fit it the destination array - */ - public static void copyFrom(LongNdArray src, long[][][] dst) { - if (src.rank() != 3) { - throw new IllegalArgumentException("Array cannot be copied from NdArray of rank " + src.rank()); - } - src.elements(1).forEachIndexed((idx, e) -> - copyFrom(e, dst[(int)idx[0]][(int)idx[1]]) - ); - } - - /** - * Copy a {@link NdArray} to a 4-dimensional array of longs - * - * @param src source rank-4 array - * @param dst destination array - * @throws IllegalArgumentException if {@code src} is not of rank-4 - * @throws ArrayIndexOutOfBoundsException if not all elements of {@code src} can fit it the destination array - */ - public static void copyFrom(LongNdArray src, long[][][][] dst) { - if (src.rank() != 4) { - throw new IllegalArgumentException("Array cannot be copied from NdArray of rank " + src.rank()); - } - src.elements(2).forEachIndexed((idx, e) -> - copyFrom(e, dst[(int)idx[0]][(int)idx[1]][(int)idx[2]]) - ); - } - - /** - * Copy a {@link NdArray} to a 5-dimensional array of longs - * - * @param src source rank-5 array - * @param dst destination array - * @throws IllegalArgumentException if {@code src} is not of rank-5 - * @throws ArrayIndexOutOfBoundsException if not all elements of {@code src} can fit it the destination array - */ - public static void copyFrom(LongNdArray src, long[][][][][] dst) { - if (src.rank() != 5) { - throw new IllegalArgumentException("Array cannot be copied from NdArray of rank " + src.rank()); - } - src.elements(3).forEachIndexed((idx, e) -> - copyFrom(e, dst[(int)idx[0]][(int)idx[1]][(int)idx[2]][(int)idx[3]]) - ); - } - - /** - * Copy a {@link NdArray} to a 6-dimensional array of longs - * - * @param src source rank-6 array - * @param dst destination array - * @throws IllegalArgumentException if {@code src} is not of rank-6 - * @throws ArrayIndexOutOfBoundsException if not all elements of {@code src} can fit it the destination array - */ - public static void copyFrom(LongNdArray src, long[][][][][][] dst) { - if (src.rank() != 6) { - throw new IllegalArgumentException("Array cannot be copied from NdArray of rank " + src.rank()); - } - src.elements(4).forEachIndexed((idx, e) -> - copyFrom(e, dst[(int)idx[0]][(int)idx[1]][(int)idx[2]][(int)idx[3]][(int)idx[4]]) - ); - } - - /** - * Copy a {@link NdArray} to an array of floats - * - * @param src source rank-1 array - * @param dst destination array - * @throws IllegalArgumentException if {@code src} is not of rank-1 - * @throws ArrayIndexOutOfBoundsException if not all elements of {@code src} can fit it the destination array - */ - public static void copyFrom(FloatNdArray src, float[] dst) { - if (src.rank() != 1) { - throw new IllegalArgumentException("Array cannot be copied from NdArray of rank " + src.rank()); - } - if (src.size() > dst.length) { - throw new ArrayIndexOutOfBoundsException(String.valueOf(src.size()) + " > " + dst.length); - } - src.read(DataBuffers.of(dst, false, false)); - } - - /** - * Copy a {@link NdArray} to a 2-dimensional array of floats - * - * @param src source rank-2 array - * @param dst destination array - * @throws IllegalArgumentException if {@code src} is not of rank-2 - * @throws ArrayIndexOutOfBoundsException if not all elements of {@code src} can fit it the destination array - */ - public static void copyFrom(FloatNdArray src, float[][] dst) { - if (src.rank() != 2) { - throw new IllegalArgumentException("Array cannot be copied from NdArray of rank " + src.rank()); - } - src.elements(0).forEachIndexed((idx, e) -> - copyFrom(e, dst[(int)idx[0]]) - ); - } - - /** - * Copy a {@link NdArray} to a 3-dimensional array of floats - * - * @param src source rank-3 array - * @param dst destination array - * @throws IllegalArgumentException if {@code src} is not of rank-3 - * @throws ArrayIndexOutOfBoundsException if not all elements of {@code src} can fit it the destination array - */ - public static void copyFrom(FloatNdArray src, float[][][] dst) { - if (src.rank() != 3) { - throw new IllegalArgumentException("Array cannot be copied from NdArray of rank " + src.rank()); - } - src.elements(1).forEachIndexed((idx, e) -> - copyFrom(e, dst[(int)idx[0]][(int)idx[1]]) - ); - } - - /** - * Copy a {@link NdArray} to a 4-dimensional array of floats - * - * @param src source rank-4 array - * @param dst destination array - * @throws IllegalArgumentException if {@code src} is not of rank-4 - * @throws ArrayIndexOutOfBoundsException if not all elements of {@code src} can fit it the destination array - */ - public static void copyFrom(FloatNdArray src, float[][][][] dst) { - if (src.rank() != 4) { - throw new IllegalArgumentException("Array cannot be copied from NdArray of rank " + src.rank()); - } - src.elements(2).forEachIndexed((idx, e) -> - copyFrom(e, dst[(int)idx[0]][(int)idx[1]][(int)idx[2]]) - ); - } - - /** - * Copy a {@link NdArray} to a 5-dimensional array of floats - * - * @param src source rank-5 array - * @param dst destination array - * @throws IllegalArgumentException if {@code src} is not of rank-5 - * @throws ArrayIndexOutOfBoundsException if not all elements of {@code src} can fit it the destination array - */ - public static void copyFrom(FloatNdArray src, float[][][][][] dst) { - if (src.rank() != 5) { - throw new IllegalArgumentException("Array cannot be copied from NdArray of rank " + src.rank()); - } - src.elements(3).forEachIndexed((idx, e) -> - copyFrom(e, dst[(int)idx[0]][(int)idx[1]][(int)idx[2]][(int)idx[3]]) - ); - } - - /** - * Copy a {@link NdArray} to a 6-dimensional array of floats - * - * @param src source rank-6 array - * @param dst destination array - * @throws IllegalArgumentException if {@code src} is not of rank-6 - * @throws ArrayIndexOutOfBoundsException if not all elements of {@code src} can fit it the destination array - */ - public static void copyFrom(FloatNdArray src, float[][][][][][] dst) { - if (src.rank() != 6) { - throw new IllegalArgumentException("Array cannot be copied from NdArray of rank " + src.rank()); - } - src.elements(4).forEachIndexed((idx, e) -> - copyFrom(e, dst[(int)idx[0]][(int)idx[1]][(int)idx[2]][(int)idx[3]][(int)idx[4]]) - ); - } - - /** - * Copy a {@link NdArray} to an array of doubles - * - * @param src source rank-1 array - * @param dst destination array - * @throws IllegalArgumentException if {@code src} is not of rank-1 - * @throws ArrayIndexOutOfBoundsException if not all elements of {@code src} can fit it the destination array - */ - public static void copyFrom(DoubleNdArray src, double[] dst) { - if (src.rank() != 1) { - throw new IllegalArgumentException("Array cannot be copied from NdArray of rank " + src.rank()); - } - if (src.size() > dst.length) { - throw new ArrayIndexOutOfBoundsException(String.valueOf(src.size()) + " > " + dst.length); - } - src.read(DataBuffers.of(dst, false, false)); - } - - /** - * Copy a {@link NdArray} to a 2-dimensional array of doubles - * - * @param src source rank-2 array - * @param dst destination array - * @throws IllegalArgumentException if {@code src} is not of rank-2 - * @throws ArrayIndexOutOfBoundsException if not all elements of {@code src} can fit it the destination array - */ - public static void copyFrom(DoubleNdArray src, double[][] dst) { - if (src.rank() != 2) { - throw new IllegalArgumentException("Array cannot be copied from NdArray of rank " + src.rank()); - } - src.elements(0).forEachIndexed((idx, e) -> - copyFrom(e, dst[(int)idx[0]]) - ); - } - - /** - * Copy a {@link NdArray} to a 3-dimensional array of doubles - * - * @param src source rank-3 array - * @param dst destination array - * @throws IllegalArgumentException if {@code src} is not of rank-3 - * @throws ArrayIndexOutOfBoundsException if not all elements of {@code src} can fit it the destination array - */ - public static void copyFrom(DoubleNdArray src, double[][][] dst) { - if (src.rank() != 3) { - throw new IllegalArgumentException("Array cannot be copied from NdArray of rank " + src.rank()); - } - src.elements(1).forEachIndexed((idx, e) -> - copyFrom(e, dst[(int)idx[0]][(int)idx[1]]) - ); - } - - /** - * Copy a {@link NdArray} to a 4-dimensional array of doubles - * - * @param src source rank-4 array - * @param dst destination array - * @throws IllegalArgumentException if {@code src} is not of rank-4 - * @throws ArrayIndexOutOfBoundsException if not all elements of {@code src} can fit it the destination array - */ - public static void copyFrom(DoubleNdArray src, double[][][][] dst) { - if (src.rank() != 4) { - throw new IllegalArgumentException("Array cannot be copied from NdArray of rank " + src.rank()); - } - src.elements(2).forEachIndexed((idx, e) -> - copyFrom(e, dst[(int)idx[0]][(int)idx[1]][(int)idx[2]]) - ); - } - - /** - * Copy a {@link NdArray} to a 5-dimensional array of doubles - * - * @param src source rank-5 array - * @param dst destination array - * @throws IllegalArgumentException if {@code src} is not of rank-5 - * @throws ArrayIndexOutOfBoundsException if not all elements of {@code src} can fit it the destination array - */ - public static void copyFrom(DoubleNdArray src, double[][][][][] dst) { - if (src.rank() != 5) { - throw new IllegalArgumentException("Array cannot be copied from NdArray of rank " + src.rank()); - } - src.elements(3).forEachIndexed((idx, e) -> - copyFrom(e, dst[(int)idx[0]][(int)idx[1]][(int)idx[2]][(int)idx[3]]) - ); - } - - /** - * Copy a {@link NdArray} to a 6-dimensional array of doubles - * - * @param src source rank-6 array - * @param dst destination array - * @throws IllegalArgumentException if {@code src} is not of rank-6 - * @throws ArrayIndexOutOfBoundsException if not all elements of {@code src} can fit it the destination array - */ - public static void copyFrom(DoubleNdArray src, double[][][][][][] dst) { - if (src.rank() != 6) { - throw new IllegalArgumentException("Array cannot be copied from NdArray of rank " + src.rank()); - } - src.elements(4).forEachIndexed((idx, e) -> - copyFrom(e, dst[(int)idx[0]][(int)idx[1]][(int)idx[2]][(int)idx[3]][(int)idx[4]]) - ); - } - - /** - * Copy a {@link NdArray} to an array of bytes - * - * @param src source rank-1 array - * @param dst destination array - * @throws IllegalArgumentException if {@code src} is not of rank-1 - * @throws ArrayIndexOutOfBoundsException if not all elements of {@code src} can fit it the destination array - */ - public static void copyFrom(ByteNdArray src, byte[] dst) { - if (src.rank() != 1) { - throw new IllegalArgumentException("Array cannot be copied from NdArray of rank " + src.rank()); - } - if (src.size() > dst.length) { - throw new ArrayIndexOutOfBoundsException(String.valueOf(src.size()) + " > " + dst.length); - } - src.read(DataBuffers.of(dst, false, false)); - } - - /** - * Copy a {@link NdArray} to a 2-dimensional array of bytes - * - * @param src source rank-2 array - * @param dst destination array - * @throws IllegalArgumentException if {@code src} is not of rank-2 - * @throws ArrayIndexOutOfBoundsException if not all elements of {@code src} can fit it the destination array - */ - public static void copyFrom(ByteNdArray src, byte[][] dst) { - if (src.rank() != 2) { - throw new IllegalArgumentException("Array cannot be copied from NdArray of rank " + src.rank()); - } - src.elements(0).forEachIndexed((idx, e) -> - copyFrom(e, dst[(int)idx[0]]) - ); - } - - /** - * Copy a {@link NdArray} to a 3-dimensional array of bytes - * - * @param src source rank-3 array - * @param dst destination array - * @throws IllegalArgumentException if {@code src} is not of rank-3 - * @throws ArrayIndexOutOfBoundsException if not all elements of {@code src} can fit it the destination array - */ - public static void copyFrom(ByteNdArray src, byte[][][] dst) { - if (src.rank() != 3) { - throw new IllegalArgumentException("Array cannot be copied from NdArray of rank " + src.rank()); - } - src.elements(1).forEachIndexed((idx, e) -> - copyFrom(e, dst[(int)idx[0]][(int)idx[1]]) - ); - } - - /** - * Copy a {@link NdArray} to a 4-dimensional array of bytes - * - * @param src source rank-4 array - * @param dst destination array - * @throws IllegalArgumentException if {@code src} is not of rank-4 - * @throws ArrayIndexOutOfBoundsException if not all elements of {@code src} can fit it the destination array - */ - public static void copyFrom(ByteNdArray src, byte[][][][] dst) { - if (src.rank() != 4) { - throw new IllegalArgumentException("Array cannot be copied from NdArray of rank " + src.rank()); - } - src.elements(2).forEachIndexed((idx, e) -> - copyFrom(e, dst[(int)idx[0]][(int)idx[1]][(int)idx[2]]) - ); - } - - /** - * Copy a {@link NdArray} to a 5-dimensional array of bytes - * - * @param src source rank-5 array - * @param dst destination array - * @throws IllegalArgumentException if {@code src} is not of rank-5 - * @throws ArrayIndexOutOfBoundsException if not all elements of {@code src} can fit it the destination array - */ - public static void copyFrom(ByteNdArray src, byte[][][][][] dst) { - if (src.rank() != 5) { - throw new IllegalArgumentException("Array cannot be copied from NdArray of rank " + src.rank()); - } - src.elements(3).forEachIndexed((idx, e) -> - copyFrom(e, dst[(int)idx[0]][(int)idx[1]][(int)idx[2]][(int)idx[3]]) - ); - } - - /** - * Copy a {@link NdArray} to a 6-dimensional array of bytes - * - * @param src source rank-6 array - * @param dst destination array - * @throws IllegalArgumentException if {@code src} is not of rank-6 - * @throws ArrayIndexOutOfBoundsException if not all elements of {@code src} can fit it the destination array - */ - public static void copyFrom(ByteNdArray src, byte[][][][][][] dst) { - if (src.rank() != 6) { - throw new IllegalArgumentException("Array cannot be copied from NdArray of rank " + src.rank()); - } - src.elements(4).forEachIndexed((idx, e) -> - copyFrom(e, dst[(int)idx[0]][(int)idx[1]][(int)idx[2]][(int)idx[3]][(int)idx[4]]) - ); - } - - /** - * Copy a {@link NdArray} to an array of shorts - * - * @param src source rank-1 array - * @param dst destination array - * @throws IllegalArgumentException if {@code src} is not of rank-1 - * @throws ArrayIndexOutOfBoundsException if not all elements of {@code src} can fit it the destination array - */ - public static void copyFrom(ShortNdArray src, short[] dst) { - if (src.rank() != 1) { - throw new IllegalArgumentException("Array cannot be copied from NdArray of rank " + src.rank()); - } - if (src.size() > dst.length) { - throw new ArrayIndexOutOfBoundsException(String.valueOf(src.size()) + " > " + dst.length); - } - src.read(DataBuffers.of(dst, false, false)); - } - - /** - * Copy a {@link NdArray} to a 2-dimensional array of shorts - * - * @param src source rank-2 array - * @param dst destination array - * @throws IllegalArgumentException if {@code src} is not of rank-2 - * @throws ArrayIndexOutOfBoundsException if not all elements of {@code src} can fit it the destination array - */ - public static void copyFrom(ShortNdArray src, short[][] dst) { - if (src.rank() != 2) { - throw new IllegalArgumentException("Array cannot be copied from NdArray of rank " + src.rank()); - } - src.elements(0).forEachIndexed((idx, e) -> - copyFrom(e, dst[(int)idx[0]]) - ); - } - - /** - * Copy a {@link NdArray} to a 3-dimensional array of shorts - * - * @param src source rank-3 array - * @param dst destination array - * @throws IllegalArgumentException if {@code src} is not of rank-3 - * @throws ArrayIndexOutOfBoundsException if not all elements of {@code src} can fit it the destination array - */ - public static void copyFrom(ShortNdArray src, short[][][] dst) { - if (src.rank() != 3) { - throw new IllegalArgumentException("Array cannot be copied from NdArray of rank " + src.rank()); - } - src.elements(1).forEachIndexed((idx, e) -> - copyFrom(e, dst[(int)idx[0]][(int)idx[1]]) - ); - } - - /** - * Copy a {@link NdArray} to a 4-dimensional array of shorts - * - * @param src source rank-4 array - * @param dst destination array - * @throws IllegalArgumentException if {@code src} is not of rank-4 - * @throws ArrayIndexOutOfBoundsException if not all elements of {@code src} can fit it the destination array - */ - public static void copyFrom(ShortNdArray src, short[][][][] dst) { - if (src.rank() != 4) { - throw new IllegalArgumentException("Array cannot be copied from NdArray of rank " + src.rank()); - } - src.elements(2).forEachIndexed((idx, e) -> - copyFrom(e, dst[(int)idx[0]][(int)idx[1]][(int)idx[2]]) - ); - } - - /** - * Copy a {@link NdArray} to a 5-dimensional array of shorts - * - * @param src source rank-5 array - * @param dst destination array - * @throws IllegalArgumentException if {@code src} is not of rank-5 - * @throws ArrayIndexOutOfBoundsException if not all elements of {@code src} can fit it the destination array - */ - public static void copyFrom(ShortNdArray src, short[][][][][] dst) { - if (src.rank() != 5) { - throw new IllegalArgumentException("Array cannot be copied from NdArray of rank " + src.rank()); - } - src.elements(3).forEachIndexed((idx, e) -> - copyFrom(e, dst[(int)idx[0]][(int)idx[1]][(int)idx[2]][(int)idx[3]]) - ); - } - - /** - * Copy a {@link NdArray} to a 6-dimensional array of shorts - * - * @param src source rank-6 array - * @param dst destination array - * @throws IllegalArgumentException if {@code src} is not of rank-6 - * @throws ArrayIndexOutOfBoundsException if not all elements of {@code src} can fit it the destination array - */ - public static void copyFrom(ShortNdArray src, short[][][][][][] dst) { - if (src.rank() != 6) { - throw new IllegalArgumentException("Array cannot be copied from NdArray of rank " + src.rank()); - } - src.elements(4).forEachIndexed((idx, e) -> - copyFrom(e, dst[(int)idx[0]][(int)idx[1]][(int)idx[2]][(int)idx[3]][(int)idx[4]]) - ); - } - - /** - * Copy a {@link NdArray} to an array of booleans. - * - * @param src source rank-1 array - * @param dst destination array - * @throws IllegalArgumentException if {@code src} is not of rank-1 - * @throws ArrayIndexOutOfBoundsException if not all elements of {@code src} can fit it the destination array - */ - public static void copyFrom(BooleanNdArray src, boolean[] dst) { - if (src.rank() != 1) { - throw new IllegalArgumentException("Array cannot be copied from NdArray of rank " + src.rank()); - } - if (src.size() > dst.length) { - throw new ArrayIndexOutOfBoundsException(String.valueOf(src.size()) + " > " + dst.length); - } - src.read(DataBuffers.of(dst, false, false)); - } - - /** - * Copy a {@link NdArray} to a 2-dimensional array of booleans - * - * @param src source rank-2 array - * @param dst destination array - * @throws IllegalArgumentException if {@code src} is not of rank-2 - * @throws ArrayIndexOutOfBoundsException if not all elements of {@code src} can fit it the destination array - */ - public static void copyFrom(BooleanNdArray src, boolean[][] dst) { - if (src.rank() != 2) { - throw new IllegalArgumentException("Array cannot be copied from NdArray of rank " + src.rank()); - } - src.elements(0).forEachIndexed((idx, e) -> - copyFrom(e, dst[(int)idx[0]]) - ); - } - - /** - * Copy a {@link NdArray} to a 3-dimensional array of booleans - * - * @param src source rank-3 array - * @param dst destination array - * @throws IllegalArgumentException if {@code src} is not of rank-3 - * @throws ArrayIndexOutOfBoundsException if not all elements of {@code src} can fit it the destination array - */ - public static void copyFrom(BooleanNdArray src, boolean[][][] dst) { - if (src.rank() != 3) { - throw new IllegalArgumentException("Array cannot be copied from NdArray of rank " + src.rank()); - } - src.elements(1).forEachIndexed((idx, e) -> - copyFrom(e, dst[(int)idx[0]][(int)idx[1]]) - ); - } - - /** - * Copy a {@link NdArray} to a 4-dimensional array of booleans - * - * @param src source rank-4 array - * @param dst destination array - * @throws IllegalArgumentException if {@code src} is not of rank-4 - * @throws ArrayIndexOutOfBoundsException if not all elements of {@code src} can fit it the destination array - */ - public static void copyFrom(BooleanNdArray src, boolean[][][][] dst) { - if (src.rank() != 4) { - throw new IllegalArgumentException("Array cannot be copied from NdArray of rank " + src.rank()); - } - src.elements(2).forEachIndexed((idx, e) -> - copyFrom(e, dst[(int)idx[0]][(int)idx[1]][(int)idx[2]]) - ); - } - - /** - * Copy a {@link NdArray} to a 5-dimensional array of booleans - * - * @param src source rank-5 array - * @param dst destination array - * @throws IllegalArgumentException if {@code src} is not of rank-5 - * @throws ArrayIndexOutOfBoundsException if not all elements of {@code src} can fit it the destination array - */ - public static void copyFrom(BooleanNdArray src, boolean[][][][][] dst) { - if (src.rank() != 5) { - throw new IllegalArgumentException("Array cannot be copied from NdArray of rank " + src.rank()); - } - src.elements(3).forEachIndexed((idx, e) -> - copyFrom(e, dst[(int)idx[0]][(int)idx[1]][(int)idx[2]][(int)idx[3]]) - ); - } - - /** - * Copy a {@link NdArray} to a 6-dimensional array of booleans - * - * @param src source rank-6 array - * @param dst destination array - * @throws IllegalArgumentException if {@code src} is not of rank-6 - * @throws ArrayIndexOutOfBoundsException if not all elements of {@code src} can fit it the destination array - */ - public static void copyFrom(BooleanNdArray src, boolean[][][][][][] dst) { - if (src.rank() != 6) { - throw new IllegalArgumentException("Array cannot be copied from NdArray of rank " + src.rank()); - } - src.elements(4).forEachIndexed((idx, e) -> - copyFrom(e, dst[(int)idx[0]][(int)idx[1]][(int)idx[2]][(int)idx[3]][(int)idx[4]]) - ); - } - - /** - * Copy a {@link NdArray} to an array of objects - * - * @param src source rank-1 array - * @param dst destination array - * @param data type - * @throws IllegalArgumentException if {@code src} is not of rank-1 - * @throws ArrayIndexOutOfBoundsException if not all elements of {@code src} can fit it the destination array - */ - public static void copyFrom(NdArray src, T[] dst) { - if (src.rank() != 1) { - throw new IllegalArgumentException("Array cannot be copied from NdArray of rank " + src.rank()); - } - if (src.size() > dst.length) { - throw new ArrayIndexOutOfBoundsException(String.valueOf(src.size()) + " > " + dst.length); - } - src.read(DataBuffers.of(dst, false, false)); - } - - /** - * Copy a {@link NdArray} to a 2-dimensional array of objects - * - * @param src source rank-2 array - * @param dst destination array - * @param data type - * @throws IllegalArgumentException if {@code src} is not of rank-2 - * @throws ArrayIndexOutOfBoundsException if not all elements of {@code src} can fit it the destination array - */ - public static void copyFrom(NdArray src, T[][] dst) { - if (src.rank() != 2) { - throw new IllegalArgumentException("Array cannot be copied from NdArray of rank " + src.rank()); - } - src.elements(0).forEachIndexed((idx, e) -> - copyFrom(e, dst[(int)idx[0]]) - ); - } - - /** - * Copy a {@link NdArray} to a 3-dimensional array of objects - * - * @param src source rank-3 array - * @param dst destination array - * @param data type - * @throws IllegalArgumentException if {@code src} is not of rank-3 - * @throws ArrayIndexOutOfBoundsException if not all elements of {@code src} can fit it the destination array - */ - public static void copyFrom(NdArray src, T[][][] dst) { - if (src.rank() != 3) { - throw new IllegalArgumentException("Array cannot be copied from NdArray of rank " + src.rank()); - } - src.elements(1).forEachIndexed((idx, e) -> - copyFrom(e, dst[(int)idx[0]][(int)idx[1]]) - ); - } - - /** - * Copy a {@link NdArray} to a 4-dimensional array of objects - * - * @param src source rank-4 array - * @param dst destination array - * @param data type - * @throws IllegalArgumentException if {@code src} is not of rank-4 - * @throws ArrayIndexOutOfBoundsException if not all elements of {@code src} can fit it the destination array - */ - public static void copyFrom(NdArray src, T[][][][] dst) { - if (src.rank() != 4) { - throw new IllegalArgumentException("Array cannot be copied from NdArray of rank " + src.rank()); - } - src.elements(2).forEachIndexed((idx, e) -> - copyFrom(e, dst[(int)idx[0]][(int)idx[1]][(int)idx[2]]) - ); - } - - /** - * Copy a {@link NdArray} to a 5-dimensional array of objects - * - * @param src source rank-5 array - * @param dst destination array - * @param data type - * @throws IllegalArgumentException if {@code src} is not of rank-5 - * @throws ArrayIndexOutOfBoundsException if not all elements of {@code src} can fit it the destination array - */ - public static void copyFrom(NdArray src, T[][][][][] dst) { - if (src.rank() != 5) { - throw new IllegalArgumentException("Array cannot be copied from NdArray of rank " + src.rank()); - } - src.elements(3).forEachIndexed((idx, e) -> - copyFrom(e, dst[(int)idx[0]][(int)idx[1]][(int)idx[2]][(int)idx[3]]) - ); - } - - /** - * Copy a {@link NdArray} to a 6-dimensional array of objects - * - * @param src source rank-6 array - * @param dst destination array - * @param data type - * @throws IllegalArgumentException if {@code src} is not of rank-6 - * @throws ArrayIndexOutOfBoundsException if not all elements of {@code src} can fit it the destination array - */ - public static void copyFrom(NdArray src, T[][][][][][] dst) { - if (src.rank() != 6) { - throw new IllegalArgumentException("Array cannot be copied from NdArray of rank " + src.rank()); - } - src.elements(4).forEachIndexed((idx, e) -> - copyFrom(e, dst[(int)idx[0]][(int)idx[1]][(int)idx[2]][(int)idx[3]][(int)idx[4]]) - ); - } - - /** - * Compute the shape of an int array. - * - * @param array 1D array - * @return shape of the array - */ - public static Shape shapeOf(int[] array) { - return Shape.of(array.length); - } - - /** - * Compute the shape of a 2-dimensional int array. - * - * @param array 2D array - * @return shape of the array - */ - public static Shape shapeOf(int[][] array) { - return Shape.of(computeShape(array, new long[2])); - } - - /** - * Compute the shape of a 3-dimensional int array. - * - * @param array 3D array - * @return shape of the array - */ - public static Shape shapeOf(int[][][] array) { - return Shape.of(computeShape(array, new long[3])); - } - - /** - * Compute the shape of a 4-dimensional int array. - * - * @param array 4D array - * @return shape of the array - */ - public static Shape shapeOf(int[][][][] array) { - return Shape.of(computeShape(array, new long[4])); - } - - /** - * Compute the shape of a 5-dimensional int array. - * - * @param array 5D array - * @return shape of the array - */ - public static Shape shapeOf(int[][][][][] array) { - return Shape.of(computeShape(array, new long[5])); - } - - /** - * Compute the shape of a 6-dimensional int array. - * - * @param array 6D array - * @return shape of the array - */ - public static Shape shapeOf(int[][][][][][] array) { - return Shape.of(computeShape(array, new long[6])); - } - - /** - * Compute the shape of a long array. - * - * @param array 1D array - * @return shape of the array - */ - public static Shape shapeOf(long[] array) { - return Shape.of(array.length); - } - - /** - * Compute the shape of a 2-dimensional long array. - * - * @param array 2D array - * @return shape of the array - */ - public static Shape shapeOf(long[][] array) { - return Shape.of(computeShape(array, new long[2])); - } - - /** - * Compute the shape of a 3-dimensional long array. - * - * @param array 3D array - * @return shape of the array - */ - public static Shape shapeOf(long[][][] array) { - return Shape.of(computeShape(array, new long[3])); - } - - /** - * Compute the shape of a 4-dimensional long array. - * - * @param array 4D array - * @return shape of the array - */ - public static Shape shapeOf(long[][][][] array) { - return Shape.of(computeShape(array, new long[4])); - } - - /** - * Compute the shape of a 5-dimensional long array. - * - * @param array 5D array - * @return shape of the array - */ - public static Shape shapeOf(long[][][][][] array) { - return Shape.of(computeShape(array, new long[5])); - } - - /** - * Compute the shape of a 6-dimensional long array. - * - * @param array 6D array - * @return shape of the array - */ - public static Shape shapeOf(long[][][][][][] array) { - return Shape.of(computeShape(array, new long[6])); - } - - /** - * Compute the shape of a float array. - * - * @param array 1D array - * @return shape of the array - */ - public static Shape shapeOf(float[] array) { - return Shape.of(array.length); - } - - /** - * Compute the shape of a 2-dimensional float array. - * - * @param array 2D array - * @return shape of the array - */ - public static Shape shapeOf(float[][] array) { - return Shape.of(computeShape(array, new long[2])); - } - - /** - * Compute the shape of a 3-dimensional float array. - * - * @param array 3D array - * @return shape of the array - */ - public static Shape shapeOf(float[][][] array) { - return Shape.of(computeShape(array, new long[3])); - } - - /** - * Compute the shape of a 4-dimensional float array. - * - * @param array 4D array - * @return shape of the array - */ - public static Shape shapeOf(float[][][][] array) { - return Shape.of(computeShape(array, new long[4])); - } - - /** - * Compute the shape of a 5-dimensional float array. - * - * @param array 5D array - * @return shape of the array - */ - public static Shape shapeOf(float[][][][][] array) { - return Shape.of(computeShape(array, new long[5])); - } - - /** - * Compute the shape of a 6-dimensional float array. - * - * @param array 6D array - * @return shape of the array - */ - public static Shape shapeOf(float[][][][][][] array) { - return Shape.of(computeShape(array, new long[6])); - } - - /** - * Compute the shape of a double array. - * - * @param array 1D array - * @return shape of the array - */ - public static Shape shapeOf(double[] array) { - return Shape.of(array.length); - } - - /** - * Compute the shape of a 2-dimensional double array. - * - * @param array 2D array - * @return shape of the array - */ - public static Shape shapeOf(double[][] array) { - return Shape.of(computeShape(array, new long[2])); - } - - /** - * Compute the shape of a 3-dimensional double array. - * - * @param array 3D array - * @return shape of the array - */ - public static Shape shapeOf(double[][][] array) { - return Shape.of(computeShape(array, new long[3])); - } - - /** - * Compute the shape of a 4-dimensional double array. - * - * @param array 4D array - * @return shape of the array - */ - public static Shape shapeOf(double[][][][] array) { - return Shape.of(computeShape(array, new long[4])); - } - - /** - * Compute the shape of a 5-dimensional double array. - * - * @param array 5D array - * @return shape of the array - */ - public static Shape shapeOf(double[][][][][] array) { - return Shape.of(computeShape(array, new long[5])); - } - - /** - * Compute the shape of a 6-dimensional double array. - * - * @param array 6D array - * @return shape of the array - */ - public static Shape shapeOf(double[][][][][][] array) { - return Shape.of(computeShape(array, new long[6])); - } - - /** - * Compute the shape of a byte array. - * - * @param array 1D array - * @return shape of the array - */ - public static Shape shapeOf(byte[] array) { - return Shape.of(array.length); - } - - /** - * Compute the shape of a 2-dimensional byte array. - * - * @param array 2D array - * @return shape of the array - */ - public static Shape shapeOf(byte[][] array) { - return Shape.of(computeShape(array, new long[2])); - } - - /** - * Compute the shape of a 3-dimensional byte array. - * - * @param array 3D array - * @return shape of the array - */ - public static Shape shapeOf(byte[][][] array) { - return Shape.of(computeShape(array, new long[3])); - } - - /** - * Compute the shape of a 4-dimensional byte array. - * - * @param array 4D array - * @return shape of the array - */ - public static Shape shapeOf(byte[][][][] array) { - return Shape.of(computeShape(array, new long[4])); - } - - /** - * Compute the shape of a 5-dimensional byte array. - * - * @param array 5D array - * @return shape of the array - */ - public static Shape shapeOf(byte[][][][][] array) { - return Shape.of(computeShape(array, new long[5])); - } - - /** - * Compute the shape of a 6-dimensional byte array. - * - * @param array 6D array - * @return shape of the array - */ - public static Shape shapeOf(byte[][][][][][] array) { - return Shape.of(computeShape(array, new long[6])); - } - - /** - * Compute the shape of a short array. - * - * @param array 1D array - * @return shape of the array - */ - public static Shape shapeOf(short[] array) { - return Shape.of(array.length); - } - - /** - * Compute the shape of a 2-dimensional short array. - * - * @param array 2D array - * @return shape of the array - */ - public static Shape shapeOf(short[][] array) { - return Shape.of(computeShape(array, new long[2])); - } - - /** - * Compute the shape of a 3-dimensional short array. - * - * @param array 3D array - * @return shape of the array - */ - public static Shape shapeOf(short[][][] array) { - return Shape.of(computeShape(array, new long[3])); - } - - /** - * Compute the shape of a 4-dimensional short array. - * - * @param array 4D array - * @return shape of the array - */ - public static Shape shapeOf(short[][][][] array) { - return Shape.of(computeShape(array, new long[4])); - } - - /** - * Compute the shape of a 5-dimensional short array. - * - * @param array 5D array - * @return shape of the array - */ - public static Shape shapeOf(short[][][][][] array) { - return Shape.of(computeShape(array, new long[5])); - } - - /** - * Compute the shape of a 6-dimensional short array. - * - * @param array 6D array - * @return shape of the array - */ - public static Shape shapeOf(short[][][][][][] array) { - return Shape.of(computeShape(array, new long[6])); - } - - /** - * Compute the shape of a boolean array. - * - * @param array 1D array - * @return shape of the array - */ - public static Shape shapeOf(boolean[] array) { - return Shape.of(array.length); - } - - /** - * Compute the shape of a 2-dimensional boolean array. - * - * @param array 2D array - * @return shape of the array - */ - public static Shape shapeOf(boolean[][] array) { - return Shape.of(computeShape(array, new long[2])); - } - - /** - * Compute the shape of a 3-dimensional boolean array. - * - * @param array 3D array - * @return shape of the array - */ - public static Shape shapeOf(boolean[][][] array) { - return Shape.of(computeShape(array, new long[3])); - } - - /** - * Compute the shape of a 4-dimensional boolean array. - * - * @param array 4D array - * @return shape of the array - */ - public static Shape shapeOf(boolean[][][][] array) { - return Shape.of(computeShape(array, new long[4])); - } - - /** - * Compute the shape of a 5-dimensional boolean array. - * - * @param array 5D array - * @return shape of the array - */ - public static Shape shapeOf(boolean[][][][][] array) { - return Shape.of(computeShape(array, new long[5])); - } - - /** - * Compute the shape of a 6-dimensional boolean array. - * - * @param array 6D array - * @return shape of the array - */ - public static Shape shapeOf(boolean[][][][][][] array) { - return Shape.of(computeShape(array, new long[6])); - } - - /** - * Compute the shape of an object array. - * - * @param array 1D array - * @param data type - * @return shape of the array - */ - public static Shape shapeOf(T[] array) { - return Shape.of(array.length); - } - - /** - * Compute the shape of a 2-dimensional object array. - * - * @param array 2D array - * @param data type - * @return shape of the array - */ - public static Shape shapeOf(T[][] array) { - return Shape.of(computeShape(array, new long[2])); - } - - /** - * Compute the shape of a 3-dimensional object array. - * - * @param array 3D array - * @param data type - * @return shape of the array - */ - public static Shape shapeOf(T[][][] array) { - return Shape.of(computeShape(array, new long[3])); - } - - /** - * Compute the shape of a 4-dimensional object array. - * - * @param array 4D array - * @param data type - * @return shape of the array - */ - public static Shape shapeOf(T[][][][] array) { - return Shape.of(computeShape(array, new long[4])); - } - - /** - * Compute the shape of a 5-dimensional object array. - * - * @param array 5D array - * @param data type - * @return shape of the array - */ - public static Shape shapeOf(T[][][][][] array) { - return Shape.of(computeShape(array, new long[5])); - } - - /** - * Compute the shape of a 6-dimensional object array. - * - * @param array 6D array - * @param data type - * @return shape of the array - */ - public static Shape shapeOf(T[][][][][][] array) { - return Shape.of(computeShape(array, new long[6])); - } - - private static void dimSize(int arrayLength, long[] shape, int dimIdx) { - if (shape[dimIdx] == 0) { - shape[dimIdx] = arrayLength; - } else if (shape[dimIdx] != arrayLength) { - shape[dimIdx] = Shape.UNKNOWN_SIZE; - } - } - - private static long[] computeShape(int[][] array, long[] shape) { - if (array == null) { - throw new IllegalStateException("The array or one of its subarray is null"); - } - dimSize(array.length, shape, shape.length - 2); - for (int i = 0; i < array.length; ++i) { - if (array[i] == null) { - throw new IllegalStateException("One of the subarray is null"); - } - dimSize(array[i].length, shape, shape.length - 1); - } - return shape; - } - - private static long[] computeShape(int[][][] array, long[] shape) { - if (array == null) { - throw new IllegalStateException("The array or one of its subarray is null"); - } - dimSize(array.length, shape, shape.length - 3); - for (int i = 0; i < array.length; ++i) { - computeShape(array[i], shape); - } - return shape; - } - - private static long[] computeShape(int[][][][] array, long[] shape) { - if (array == null) { - throw new IllegalStateException("The array or one of its subarray is null"); - } - dimSize(array.length, shape, shape.length - 4); - for (int i = 0; i < array.length; ++i) { - computeShape(array[i], shape); - } - return shape; - } - - private static long[] computeShape(int[][][][][] array, long[] shape) { - if (array == null) { - throw new IllegalStateException("The array or one of its subarray is null"); - } - dimSize(array.length, shape, shape.length - 5); - for (int i = 0; i < array.length; ++i) { - computeShape(array[i], shape); - } - return shape; - } - - private static long[] computeShape(int[][][][][][] array, long[] shape) { - if (array == null) { - throw new IllegalStateException("The array or one of its subarray is null"); - } - dimSize(array.length, shape, shape.length - 6); - for (int i = 0; i < array.length; ++i) { - computeShape(array[i], shape); - } - return shape; - } - - private static long[] computeShape(long[][] array, long[] shape) { - if (array == null) { - throw new IllegalStateException("The array or one of its subarray is null"); - } - dimSize(array.length, shape, shape.length - 2); - for (int i = 0; i < array.length; ++i) { - if (array[i] == null) { - throw new IllegalStateException("One of the subarray is null"); - } - dimSize(array[i].length, shape, shape.length - 1); - } - return shape; - } - - private static long[] computeShape(long[][][] array, long[] shape) { - if (array == null) { - throw new IllegalStateException("The array or one of its subarray is null"); - } - dimSize(array.length, shape, shape.length - 3); - for (int i = 0; i < array.length; ++i) { - computeShape(array[i], shape); - } - return shape; - } - - private static long[] computeShape(long[][][][] array, long[] shape) { - if (array == null) { - throw new IllegalStateException("The array or one of its subarray is null"); - } - dimSize(array.length, shape, shape.length - 4); - for (int i = 0; i < array.length; ++i) { - computeShape(array[i], shape); - } - return shape; - } - - private static long[] computeShape(long[][][][][] array, long[] shape) { - if (array == null) { - throw new IllegalStateException("The array or one of its subarray is null"); - } - dimSize(array.length, shape, shape.length - 5); - for (int i = 0; i < array.length; ++i) { - computeShape(array[i], shape); - } - return shape; - } - - private static long[] computeShape(long[][][][][][] array, long[] shape) { - if (array == null) { - throw new IllegalStateException("The array or one of its subarray is null"); - } - dimSize(array.length, shape, shape.length - 6); - for (int i = 0; i < array.length; ++i) { - computeShape(array[i], shape); - } - return shape; - } - - private static long[] computeShape(float[][] array, long[] shape) { - if (array == null) { - throw new IllegalStateException("The array or one of its subarray is null"); - } - dimSize(array.length, shape, shape.length - 2); - for (int i = 0; i < array.length; ++i) { - if (array[i] == null) { - throw new IllegalStateException("One of the subarray is null"); - } - dimSize(array[i].length, shape, shape.length - 1); - } - return shape; - } - - private static long[] computeShape(float[][][] array, long[] shape) { - if (array == null) { - throw new IllegalStateException("The array or one of its subarray is null"); - } - dimSize(array.length, shape, shape.length - 3); - for (int i = 0; i < array.length; ++i) { - computeShape(array[i], shape); - } - return shape; - } - - private static long[] computeShape(float[][][][] array, long[] shape) { - if (array == null) { - throw new IllegalStateException("The array or one of its subarray is null"); - } - dimSize(array.length, shape, shape.length - 4); - for (int i = 0; i < array.length; ++i) { - computeShape(array[i], shape); - } - return shape; - } - - private static long[] computeShape(float[][][][][] array, long[] shape) { - if (array == null) { - throw new IllegalStateException("The array or one of its subarray is null"); - } - dimSize(array.length, shape, shape.length - 5); - for (int i = 0; i < array.length; ++i) { - computeShape(array[i], shape); - } - return shape; - } - - private static long[] computeShape(float[][][][][][] array, long[] shape) { - if (array == null) { - throw new IllegalStateException("The array or one of its subarray is null"); - } - dimSize(array.length, shape, shape.length - 6); - for (int i = 0; i < array.length; ++i) { - computeShape(array[i], shape); - } - return shape; - } - - private static long[] computeShape(double[][] array, long[] shape) { - if (array == null) { - throw new IllegalStateException("The array or one of its subarray is null"); - } - dimSize(array.length, shape, shape.length - 2); - for (int i = 0; i < array.length; ++i) { - if (array[i] == null) { - throw new IllegalStateException("One of the subarray is null"); - } - dimSize(array[i].length, shape, shape.length - 1); - } - return shape; - } - - private static long[] computeShape(double[][][] array, long[] shape) { - if (array == null) { - throw new IllegalStateException("The array or one of its subarray is null"); - } - dimSize(array.length, shape, shape.length - 3); - for (int i = 0; i < array.length; ++i) { - computeShape(array[i], shape); - } - return shape; - } - - private static long[] computeShape(double[][][][] array, long[] shape) { - if (array == null) { - throw new IllegalStateException("The array or one of its subarray is null"); - } - dimSize(array.length, shape, shape.length - 4); - for (int i = 0; i < array.length; ++i) { - computeShape(array[i], shape); - } - return shape; - } - - private static long[] computeShape(double[][][][][] array, long[] shape) { - if (array == null) { - throw new IllegalStateException("The array or one of its subarray is null"); - } - dimSize(array.length, shape, shape.length - 5); - for (int i = 0; i < array.length; ++i) { - computeShape(array[i], shape); - } - return shape; - } - - private static long[] computeShape(double[][][][][][] array, long[] shape) { - if (array == null) { - throw new IllegalStateException("The array or one of its subarray is null"); - } - dimSize(array.length, shape, shape.length - 6); - for (int i = 0; i < array.length; ++i) { - computeShape(array[i], shape); - } - return shape; - } - - private static long[] computeShape(byte[][] array, long[] shape) { - if (array == null) { - throw new IllegalStateException("The array or one of its subarray is null"); - } - dimSize(array.length, shape, shape.length - 2); - for (int i = 0; i < array.length; ++i) { - if (array[i] == null) { - throw new IllegalStateException("One of the subarray is null"); - } - dimSize(array[i].length, shape, shape.length - 1); - } - return shape; - } - - private static long[] computeShape(byte[][][] array, long[] shape) { - if (array == null) { - throw new IllegalStateException("The array or one of its subarray is null"); - } - dimSize(array.length, shape, shape.length - 3); - for (int i = 0; i < array.length; ++i) { - computeShape(array[i], shape); - } - return shape; - } - - private static long[] computeShape(byte[][][][] array, long[] shape) { - if (array == null) { - throw new IllegalStateException("The array or one of its subarray is null"); - } - dimSize(array.length, shape, shape.length - 4); - for (int i = 0; i < array.length; ++i) { - computeShape(array[i], shape); - } - return shape; - } - - private static long[] computeShape(byte[][][][][] array, long[] shape) { - if (array == null) { - throw new IllegalStateException("The array or one of its subarray is null"); - } - dimSize(array.length, shape, shape.length - 5); - for (int i = 0; i < array.length; ++i) { - computeShape(array[i], shape); - } - return shape; - } - - private static long[] computeShape(byte[][][][][][] array, long[] shape) { - if (array == null) { - throw new IllegalStateException("The array or one of its subarray is null"); - } - dimSize(array.length, shape, shape.length - 6); - for (int i = 0; i < array.length; ++i) { - computeShape(array[i], shape); - } - return shape; - } - - private static long[] computeShape(short[][] array, long[] shape) { - if (array == null) { - throw new IllegalStateException("The array or one of its subarray is null"); - } - dimSize(array.length, shape, shape.length - 2); - for (int i = 0; i < array.length; ++i) { - if (array[i] == null) { - throw new IllegalStateException("One of the subarray is null"); - } - dimSize(array[i].length, shape, shape.length - 1); - } - return shape; - } - - private static long[] computeShape(short[][][] array, long[] shape) { - if (array == null) { - throw new IllegalStateException("The array or one of its subarray is null"); - } - dimSize(array.length, shape, shape.length - 3); - for (int i = 0; i < array.length; ++i) { - computeShape(array[i], shape); - } - return shape; - } - - private static long[] computeShape(short[][][][] array, long[] shape) { - if (array == null) { - throw new IllegalStateException("The array or one of its subarray is null"); - } - dimSize(array.length, shape, shape.length - 4); - for (int i = 0; i < array.length; ++i) { - computeShape(array[i], shape); - } - return shape; - } - - private static long[] computeShape(short[][][][][] array, long[] shape) { - if (array == null) { - throw new IllegalStateException("The array or one of its subarray is null"); - } - dimSize(array.length, shape, shape.length - 5); - for (int i = 0; i < array.length; ++i) { - computeShape(array[i], shape); - } - return shape; - } - - private static long[] computeShape(short[][][][][][] array, long[] shape) { - if (array == null) { - throw new IllegalStateException("The array or one of its subarray is null"); - } - dimSize(array.length, shape, shape.length - 6); - for (int i = 0; i < array.length; ++i) { - computeShape(array[i], shape); - } - return shape; - } - - private static long[] computeShape(boolean[][] array, long[] shape) { - if (array == null) { - throw new IllegalStateException("The array or one of its subarray is null"); - } - dimSize(array.length, shape, shape.length - 2); - for (int i = 0; i < array.length; ++i) { - if (array[i] == null) { - throw new IllegalStateException("One of the subarray is null"); - } - dimSize(array[i].length, shape, shape.length - 1); - } - return shape; - } - - private static long[] computeShape(boolean[][][] array, long[] shape) { - if (array == null) { - throw new IllegalStateException("The array or one of its subarray is null"); - } - dimSize(array.length, shape, shape.length - 3); - for (int i = 0; i < array.length; ++i) { - computeShape(array[i], shape); - } - return shape; - } - - private static long[] computeShape(boolean[][][][] array, long[] shape) { - if (array == null) { - throw new IllegalStateException("The array or one of its subarray is null"); - } - dimSize(array.length, shape, shape.length - 4); - for (int i = 0; i < array.length; ++i) { - computeShape(array[i], shape); - } - return shape; - } - - private static long[] computeShape(boolean[][][][][] array, long[] shape) { - if (array == null) { - throw new IllegalStateException("The array or one of its subarray is null"); - } - dimSize(array.length, shape, shape.length - 5); - for (int i = 0; i < array.length; ++i) { - computeShape(array[i], shape); - } - return shape; - } - - private static long[] computeShape(boolean[][][][][][] array, long[] shape) { - if (array == null) { - throw new IllegalStateException("The array or one of its subarray is null"); - } - dimSize(array.length, shape, shape.length - 6); - for (int i = 0; i < array.length; ++i) { - computeShape(array[i], shape); - } - return shape; - } - - private static long[] computeShape(T[][] array, long[] shape) { - if (array == null) { - throw new IllegalStateException("The array or one of its subarray is null"); - } - dimSize(array.length, shape, shape.length - 2); - for (int i = 0; i < array.length; ++i) { - if (array[i] == null) { - throw new IllegalStateException("One of the subarray is null"); - } - dimSize(array[i].length, shape, shape.length - 1); - } - return shape; - } - - private static long[] computeShape(T[][][] array, long[] shape) { - if (array == null) { - throw new IllegalStateException("The array or one of its subarray is null"); - } - dimSize(array.length, shape, shape.length - 3); - for (int i = 0; i < array.length; ++i) { - computeShape(array[i], shape); - } - return shape; - } - - private static long[] computeShape(T[][][][] array, long[] shape) { - if (array == null) { - throw new IllegalStateException("The array or one of its subarray is null"); - } - dimSize(array.length, shape, shape.length - 4); - for (int i = 0; i < array.length; ++i) { - computeShape(array[i], shape); - } - return shape; - } - - private static long[] computeShape(T[][][][][] array, long[] shape) { - if (array == null) { - throw new IllegalStateException("The array or one of its subarray is null"); - } - dimSize(array.length, shape, shape.length - 5); - for (int i = 0; i < array.length; ++i) { - computeShape(array[i], shape); - } - return shape; - } - - private static long[] computeShape(T[][][][][][] array, long[] shape) { - if (array == null) { - throw new IllegalStateException("The array or one of its subarray is null"); - } - dimSize(array.length, shape, shape.length - 6); - for (int i = 0; i < array.length; ++i) { - computeShape(array[i], shape); - } - return shape; - } - - private static Class componentTypeOf(Object array) { - Class componentType = array.getClass().getComponentType(); - while (componentType.isArray()) { - componentType = componentType.getComponentType(); - } - return (Class)componentType; - } - - private static int[] computeArrayDims(NdArray ndArray, int expectedRank) { - Shape shape = ndArray.shape(); - if (shape.numDimensions() != expectedRank) { - throw new IllegalArgumentException("NdArray must be of rank " + expectedRank); - } - int[] arrayShape = new int[expectedRank]; - for (int i = 0; i < expectedRank; ++i) { - long dimSize = shape.size(i); - if (dimSize > Integer.MAX_VALUE) { - throw new IllegalArgumentException("Dimension " + i + " is too large to fit in a standard array (" + shape.size(i) + ")"); - } - arrayShape[i] = (int)dimSize; - } - return arrayShape; - } -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/buffer/BooleanDataBuffer.java b/ndarray/src/main/java/org/tensorflow/ndarray/buffer/BooleanDataBuffer.java deleted file mode 100644 index 73a570d4fe8..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/buffer/BooleanDataBuffer.java +++ /dev/null @@ -1,165 +0,0 @@ -/* - 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 - - 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.buffer; - -import java.nio.BufferOverflowException; -import java.nio.BufferUnderflowException; -import java.nio.ReadOnlyBufferException; - -/** - * A {@link DataBuffer} of booleans. - */ -public interface BooleanDataBuffer extends DataBuffer { - - /** - * Reads the boolean at the given index. - * - * @param index the index from which the float will be read - * @return the boolean at the given index - * @throws IndexOutOfBoundsException if index is negative or not smaller than the buffer size - */ - boolean getBoolean(long index); - - /** - * Writes the given boolean into this buffer at the given index. - * - * @param value the boolean to be written - * @param index the index at which the value will be written - * @return this buffer - * @throws IndexOutOfBoundsException if index is negative or not smaller than the buffer size - * @throws ReadOnlyBufferException if this buffer is read-only - */ - BooleanDataBuffer setBoolean(boolean value, long index); - - /** - * Bulk get method, using boolean arrays. - *

- * This method transfers values from this buffer into the given destination array. If there are - * fewer values in the buffer than are required to satisfy the request, that is, if - * {@code dst.length > size()}, then no values are transferred and a - * BufferUnderflowException is thrown. - *

- * Otherwise, this method copies {@code n = dst.length} values from this buffer into the given - * array. - * - * @param dst the array into which values are to be written - * @return this buffer - * @throws BufferUnderflowException if there are not enough values to copy from this buffer - */ - default BooleanDataBuffer read(boolean[] dst) { - return read(dst, 0, dst.length); - } - - /** - * Bulk get method, using boolean arrays. - *

- * This method transfers values from this buffer into the given destination array. If there are - * fewer values in the buffer than are required to satisfy the request, that is, if - * {@code length > size()}, then no values are transferred and a - * BufferUnderflowException is thrown. - *

- * Otherwise, this method copies {@code n = length} values from this buffer into the given array - * starting at the given offset. - * - * @param dst the array into which values are to be written - * @param offset the offset within the array of the first value to be written; must be - * non-negative and no larger than {@code dst.length} - * @param length the maximum number of values to be written to the given array; must be - * non-negative and no larger than {@code dst.length - offset} - * @return this buffer - * @throws BufferUnderflowException if there are fewer than length values remaining in this buffer - * @throws IndexOutOfBoundsException if the preconditions on the offset and length parameters do - * not hold - */ - BooleanDataBuffer read(boolean[] dst, int offset, int length); - - /** - * Bulk put method, using boolean arrays. - *

- * This method transfers the values in the given source array into this buffer. If there are - * more values in the source array than in this buffer, that is, if - * {@code src.length > size()}, then no values are transferred and a - * BufferOverflowException is thrown. - *

- * Otherwise, this method copies {@code n = src.length} values from the given array. - * - * @param src the source array from which values are to be read - * @return this buffer - * @throws BufferOverflowException if there is insufficient space in this buffer for the values in - * the source array - * @throws ReadOnlyBufferException if this buffer is read-only - */ - default BooleanDataBuffer write(boolean[] src) { - return write(src, 0, src.length); - } - - /** - * Bulk put method, using boolean arrays. - *

- * This method transfers the values in the given source array into this buffer. If there are - * more values in the source array than in this buffer, that is, if - * {@code length > size()}, then no values are transferred and a - * BufferOverflowException is thrown. - *

- * Otherwise, this method copies {@code n = length} values from the given array into this buffer, - * starting at the given offset. - * - * @param src the source array from which values are to be read - * @param offset the offset within the array of the first value to be read; must be non-negative - * and no larger than {@code src.length} - * @param length the number of values to be read from the given array; must be non-negative and no - * larger than {@code src.length - offset} - * @return this buffer - * @throws BufferOverflowException if there is insufficient space in this buffer for the values in - * the source array - * @throws IndexOutOfBoundsException if the preconditions on the offset and length parameters do - * not hold - * @throws ReadOnlyBufferException if this buffer is read-only - */ - BooleanDataBuffer write(boolean[] src, int offset, int length); - - @Override - default Boolean getObject(long index) { - return getBoolean(index); - } - - @Override - default BooleanDataBuffer setObject(Boolean value, long index) { - return setBoolean(value, index); - } - - @Override - BooleanDataBuffer copyTo(DataBuffer dst, long size); - - @Override - default BooleanDataBuffer offset(long index) { - return slice(index, size() - index); - } - - @Override - default BooleanDataBuffer narrow(long size) { - return slice(0, size); - } - - @Override - BooleanDataBuffer slice(long index, long size); - - @Override - default DataBufferWindow window(long size) { - throw new UnsupportedOperationException(); - } -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/buffer/ByteDataBuffer.java b/ndarray/src/main/java/org/tensorflow/ndarray/buffer/ByteDataBuffer.java deleted file mode 100644 index b1cce441b13..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/buffer/ByteDataBuffer.java +++ /dev/null @@ -1,232 +0,0 @@ -/* - 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 - - 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.buffer; - -import java.nio.BufferOverflowException; -import java.nio.BufferUnderflowException; -import java.nio.ReadOnlyBufferException; - -/** - * A {@link DataBuffer} of bytes. - */ -public interface ByteDataBuffer extends DataBuffer { - - /** - * Reads the byte at the given index. - * - * @param index the index from which the float will be read - * @return the byte at the given index - * @throws IndexOutOfBoundsException if index is negative or not smaller than the buffer size - */ - byte getByte(long index); - - /** - * Writes the given byte into this buffer at the given index. - * - * @param value the byte to be written - * @param index the index at which the value will be written - * @return this buffer - * @throws IndexOutOfBoundsException if index is negative or not smaller than the buffer size - * @throws ReadOnlyBufferException if this buffer is read-only - */ - ByteDataBuffer setByte(byte value, long index); - - /** - * Bulk get method, using byte arrays. - *

- * This method transfers values from this buffer into the given destination array. If there are - * fewer values in the buffer than are required to satisfy the request, that is, if - * {@code dst.length > size()}, then no values are transferred and a - * BufferUnderflowException is thrown. - *

- * Otherwise, this method copies {@code n = dst.length} values from this buffer into the given - * array. - * - * @param dst the array into which values are to be written - * @return this buffer - * @throws BufferUnderflowException if there are not enough values to copy from this buffer - */ - default ByteDataBuffer read(byte[] dst) { - return read(dst, 0, dst.length); - } - - /** - * Bulk get method, using byte arrays. - *

- * This method transfers values from this buffer into the given destination array. If there are - * fewer values in the buffer than are required to satisfy the request, that is, if - * {@code length > size()}, then no values are transferred and a - * BufferUnderflowException is thrown. - *

- * Otherwise, this method copies {@code n = length} values from this buffer into the given array - * starting at the given offset. - * - * @param dst the array into which values are to be written - * @param offset the offset within the array of the first value to be written; must be - * non-negative and no larger than {@code dst.length} - * @param length the maximum number of values to be written to the given array; must be - * non-negative and no larger than {@code dst.length - offset} - * @return this buffer - * @throws BufferUnderflowException if there are fewer than length values remaining in this buffer - * @throws IndexOutOfBoundsException if the preconditions on the offset and length parameters do - * not hold - */ - ByteDataBuffer read(byte[] dst, int offset, int length); - - /** - * Bulk put method, using byte arrays. - *

- * This method transfers the values in the given source array into this buffer. If there are - * more values in the source array than in this buffer, that is, if - * {@code src.length > size()}, then no values are transferred and a - * BufferOverflowException is thrown. - *

- * Otherwise, this method copies {@code n = src.length} values from the given array. - * - * @param src the source array from which values are to be read - * @return this buffer - * @throws BufferOverflowException if there is insufficient space in this buffer for the values in - * the source array - * @throws ReadOnlyBufferException if this buffer is read-only - */ - default ByteDataBuffer write(byte[] src) { - return write(src, 0, src.length); - } - - /** - * Bulk put method, using byte arrays. - *

- * This method transfers the values in the given source array into this buffer. If there are - * more values in the source array than in this buffer, that is, if - * {@code length > size()}, then no values are transferred and a - * BufferOverflowException is thrown. - *

- * Otherwise, this method copies {@code n = length} values from the given array into this buffer, - * starting at the given offset. - * - * @param src the source array from which values are to be read - * @param offset the offset within the array of the first value to be read; must be non-negative - * and no larger than {@code src.length} - * @param length the number of values to be read from the given array; must be non-negative and no - * larger than {@code src.length - offset} - * @return this buffer - * @throws BufferOverflowException if there is insufficient space in this buffer for the values in - * the source array - * @throws IndexOutOfBoundsException if the preconditions on the offset and length parameters do - * not hold - * @throws ReadOnlyBufferException if this buffer is read-only - */ - ByteDataBuffer write(byte[] src, int offset, int length); - - /** - * Return this byte buffer as a buffer of ints. - * - *

The returned buffer provides a different view on the same memory as the original byte buffer, - * meaning that changing a value in one will affect the other. - * - * @return this buffer as a {@link IntDataBuffer} - * @throws IllegalStateException if this buffer cannot be converted - */ - IntDataBuffer asInts(); - - /** - * Return this byte buffer as a buffer of shorts. - * - *

The returned buffer provides a different view on the same memory as the original byte buffer, - * meaning that changing a value in one will affect the other. - * - * @return this buffer as a {@link ShortDataBuffer} - * @throws IllegalStateException if this buffer cannot be converted - */ - ShortDataBuffer asShorts(); - - /** - * Return this byte buffer as a buffer of longs. - * - *

The returned buffer provides a different view on the same memory as the original byte buffer, - * meaning that changing a value in one will affect the other. - * - * @return this buffer as a {@link LongDataBuffer} - * @throws IllegalStateException if this buffer cannot be converted - */ - LongDataBuffer asLongs(); - - /** - * Return this byte buffer as a buffer of floats. - * - *

The returned buffer provides a different view on the same memory as the original byte buffer, - * meaning that changing a value in one will affect the other. - * - * @return this buffer as a {@link FloatDataBuffer} - * @throws IllegalStateException if this buffer cannot be converted - */ - FloatDataBuffer asFloats(); - - /** - * Return this byte buffer as a buffer of doubles. - * - *

The returned buffer provides a different view on the same memory as the original byte buffer, - * meaning that changing a value in one will affect the other. - * - * @return this buffer as a {@link DoubleDataBuffer} - * @throws IllegalStateException if this buffer cannot be converted - */ - DoubleDataBuffer asDoubles(); - - /** - * Return this byte buffer as a buffer of booleans. - * - *

The returned buffer provides a different view on the same memory as the original byte buffer, - * meaning that changing a value in one will affect the other. - * - * @return this buffer as a {@link BooleanDataBuffer} - * @throws IllegalStateException if this buffer cannot be converted - */ - BooleanDataBuffer asBooleans(); - - @Override - default Byte getObject(long index) { - return getByte(index); - } - - @Override - default ByteDataBuffer setObject(Byte value, long index) { - return setByte(value, index); - } - - @Override - ByteDataBuffer copyTo(DataBuffer dst, long size); - - - @Override - default ByteDataBuffer offset(long index) { - return slice(index, size() - index); - } - - @Override - default ByteDataBuffer narrow(long size) { - return slice(0, size); - } - - @Override - ByteDataBuffer slice(long index, long size); - - @Override - default DataBufferWindow window(long size) { - throw new UnsupportedOperationException(); - } -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/buffer/DataBuffer.java b/ndarray/src/main/java/org/tensorflow/ndarray/buffer/DataBuffer.java deleted file mode 100644 index e62ba87ce6e..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/buffer/DataBuffer.java +++ /dev/null @@ -1,324 +0,0 @@ -/* - 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 - - 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.buffer; - -import java.nio.BufferOverflowException; -import java.nio.BufferUnderflowException; -import java.nio.ReadOnlyBufferException; - -/** - * A container of data of a specific type. - * - *

Instances of {@code DataBuffer} map native or heap memory segments to a linear view that - * supports: - *

    - *
  • 64-bits indexing, allowing to work with buffer larger than 231 bytes
  • - *
  • Storage of object of any types and not only primitives
  • - *
  • Generic types allows to work directly with boxed types as well, which does not require - * explicit buffer types as with the standard JDK buffers. - *
- * It is important to note that there is no guarantee the memory managed by a {@code DataBuffer} - * is linear, specially when dealing with non-primitive types or large buffers. - * - * @param type of data stored in this buffer - */ -public interface DataBuffer { - - /** - * Size of the buffer, in elements. - *

- * For exemple, in case of a byte buffer, this value is equal to the number of bytes this buffer - * can hold. For an integer buffer, it is equal to the number of integers, therefore the size - * in bytes of this buffer is {@code size() * Integer.BYTES}. - * - * @return the buffer size - */ - long size(); - - /** - * Tells whether or not this buffer is backed by an accessible array. - * - * @return true if, and only if, this buffer is read-only - */ - boolean isReadOnly(); - - /** - * Reads the value at the given index. - * - * Important: Usage of this method should be limited to buffers of non-primitive types or - * when the data type is not deterministically known by the caller. In any other case, prefer - * the usage of its primitive variant which will significantly improve performances - * (e.g. {@code IntDataBuffer.getInt(idx)} - * - * @param index the index from which the float will be read - * @return the value at the given index - * @throws IndexOutOfBoundsException if index is negative or not smaller than the buffer size - */ - T getObject(long index); - - /** - * Writes the given value into this buffer at the given index. - * - * Important: Usage of this method should be limited to buffers of non-primitive types or - * when the data type is not deterministically known by the caller. In any other case, prefer - * the usage of its primitive variant which will significantly improve performances - * (e.g. {@code IntDataBuffer.setInt(idx)} - * - * @param value the value to be written - * @param index the index at which the value will be written - * @return this buffer - * @throws IndexOutOfBoundsException if index is negative or not smaller than the buffer size - * @throws ReadOnlyBufferException if this buffer is read-only - */ - DataBuffer setObject(T value, long index); - - /** - * Read the references of the objects in this buffer into the destination array. - *

- * This method transfers values from this buffer into the given destination array. If there are - * fewer values in the buffer than are required to satisfy the request, that is, if - * {@code dst.length > size()}, then no values are transferred and a - * BufferUnderflowException is thrown. - *

- * Otherwise, this method copies {@code n = dst.length} values from this buffer into the given - * array. - * - * @param dst the array into which values are to be written - * @return this buffer - * @throws BufferUnderflowException if there are not enough values to copy from this buffer - */ - default DataBuffer read(T[] dst) { - return read(dst, 0, dst.length); - } - - /** - * Read the references of the objects in this buffer into the destination array. - *

- * This method transfers values from this buffer into the given destination array. If there are - * fewer values in the buffer than are required to satisfy the request, that is, if - * {@code length > size()}, then no values are transferred and a - * BufferUnderflowException is thrown. - *

- * Otherwise, this method copies {@code n = length} values from this buffer into the given array - * starting at the given offset. - * - * @param dst the array into which values are to be written - * @param offset the offset within the array of the first value to be written; must be - * non-negative and no larger than {@code dst.length} - * @param length the maximum number of values to be written to the given array; must be - * non-negative and no larger than {@code dst.length - offset} - * @return this buffer - * @throws BufferUnderflowException if there are fewer than length values remaining in this buffer - * @throws IndexOutOfBoundsException if the preconditions on the offset and length parameters do - * not hold - */ - DataBuffer read(T[] dst, int offset, int length); - - /** - * Write the references of the objects in the source array into this buffer. - *

- * This method transfers the values in the given source array into this buffer. If there are - * more values in the source array than in this buffer, that is, if - * {@code src.length > size()}, then no values are transferred and a - * BufferOverflowException is thrown. - *

- * Otherwise, this method copies {@code n = src.length} values from the given array. - * - * @param src the source array from which values are to be read - * @return this buffer - * @throws BufferOverflowException if there is insufficient space in this buffer for the values in - * the source array - * @throws ReadOnlyBufferException if this buffer is read-only - */ - default DataBuffer write(T[] src) { - return write(src, 0, src.length); - } - - /** - * Bulk put method, using int arrays. - *

- * This method transfers the values in the given source array into this buffer. If there are - * more values in the source array than in this buffer, that is, if - * {@code length > size()}, then no values are transferred and a - * BufferOverflowException is thrown. - *

- * Otherwise, this method copies {@code n = length} values from the given array into this buffer, - * starting at the given offset. - * - * @param src the source array from which values are to be read - * @param offset the offset within the array of the first value to be read; must be non-negative - * and no larger than {@code src.length} - * @param length the number of values to be read from the given array; must be non-negative and no - * larger than {@code src.length - offset} - * @return this buffer - * @throws BufferOverflowException if there is insufficient space in this buffer for the values in - * the source array - * @throws IndexOutOfBoundsException if the preconditions on the offset and length parameters do - * not hold - * @throws ReadOnlyBufferException if this buffer is read-only - */ - DataBuffer write(T[] src, int offset, int length); - - /** - * Write the references of the objects in the source array into this buffer. - *

- * If there are more values to copy than the destination buffer size, i.e. - * {@code size > dst.size()}, then no values are transferred and a - * BufferOverflowException is thrown. On the other hand, if there are more values to copy that - * the source buffer size, i.e. {@code > src.size()}, then a BufferUnderfloatException is thrown. - *

- * Otherwise, this method copies {@code n = size} values from this buffer into - * the destination buffer. - * - * @param dst the destination buffer into which values are copied; must not be this buffer - * @param size number of values to copy to the destination buffer - * @return this buffer - * @throws IllegalArgumentException if the destination buffer is this buffer - * @throws ReadOnlyBufferException if the destination buffer is read-only - * @throws java.nio.BufferOverflowException if there is not enough space in destination buffer - * @throws java.nio.BufferUnderflowException if there are not enough values in the source buffer - */ - DataBuffer copyTo(DataBuffer dst, long size); - - /** - * Creates a new buffer whose content is a shared subsequence of this buffer's content, starting - * at the given index. - *

- * The index must not be greater than this buffer size. Changes to this buffer's content will - * be visible in the new buffer and vice versa. The new buffer will be read-only if, and only if, - * this buffer is read-only. - *

- * This call is equivalent to {@link #slice(long, long) slice(index, size() - index)} - * - * @param index index of the first value of the new buffer created, must not be greater than - * {@code size()} - * @return the new buffer - * @throws IllegalArgumentException if index do not pass validation checks - */ - default DataBuffer offset(long index) { - return slice(index, size() - index); - } - - /** - * Creates a new buffer whose content is a shared subsequence of this buffer's content, whose - * size is set to the given value. - *

- * The new size must not be greater than this buffer size. Changes to this buffer's - * content will be visible in the new buffer and vice versa. The new buffer will be read-only if, - * and only if, this buffer is read-only. - *

- * This call is equivalent to {@link #slice(long, long) slice(0, size)} - * - * @param size size of this new buffer - * @return the new buffer - * @throws IllegalArgumentException if index and/or size values do not pass validation checks - */ - default DataBuffer narrow(long size) { - return slice(0, size); - } - - /** - * Creates a new buffer whose content is a shared subsequence of this buffer's content, starting - * at the given index and of the given size. - *

- * The index plus the new size must not be greater than this buffer size. Changes to this - * buffer's content will be visible in the new buffer and vice versa. The new buffer will be - * read-only if, and only if, this buffer is read-only. - * - * @param index index of the first value of the new buffer created - * @param size size of this new buffer, must not be greater than {@code size()} - * @return the new buffer - * @throws IllegalArgumentException if size value do not pass validation checks - */ - DataBuffer slice(long index, long size); - - /** - * Creates a {@link DataBufferWindow} that provides a partial view of this buffer. - * - *

The created window has a fixed size and can {@link DataBufferWindow#slide(long) "slide"} - * along this buffer to provide different views of the data without allocating a new buffer - * instance, like {@link #offset(long)} does. This improves overall performance when this - * operation is repeated frequently. For example: - * - *

{@code
-   * IntDataBuffer bufferA = DataBuffers.ofInts(1024);
-   * // ... init buffer data
-   * IntDataBuffer bufferB = DataBuffers.ofInts(1, 2, 3, 4);
-   *
-   * // Return the index of the first occurrence of bufferB in bufferA using a sliding window
-   * DataBufferWindow windowA = bufferA.window(4);
-   * for (int i = 0; i < bufferA.size() - bufferB.size(); ++i) {
-   *     if (windowA.slideTo(i).buffer().equals(bufferB)) {
-   *         return i;
-   *     }
-   * }
-   * }
- * - *

The returned object is stateful and is not thread-safe. - * - * @param size size of the window - * @return a new window that starts at the index 0 of this buffer - * @throws UnsupportedOperationException if this type of buffer does not support buffer windows - */ - default DataBufferWindow> window(long size) { - throw new UnsupportedOperationException(); - } - - /** - * Visits the backing storage of this buffer. - * - *

The buffer implementation is responsible of passing back a reference to the actual data - * storage to the provided visitor. The visitor does not have to handle all possible types of - * data storage and can override only methods for storage it is actually interested in. For any - * other type of storage, this call will fallback to {@link DataStorageVisitor#fallback()} so the - * visitor can execute some generic routine if needed. - * - * @param visitor visits the data storage of this buffer - * @param type of value returned by the visitor - * @return the same value returned by the visitor - */ - default R accept(DataStorageVisitor visitor) { - return visitor.fallback(); - } - - /** - * Checks equality between data buffers. - * - *

A data buffer is equal to another object if this object is another {@link DataBuffer} of the - * same size, type and the elements are equal and in the same order. For example: - * - *

{@code
-   * IntDataBuffer buffer = DataBuffers.of(1, 2, 3);
-   *
-   * assertEquals(buffer, DataBuffers.of(1, 2, 3));  // true
-   * assertEquals(buffer, DataBuffers.ofObjects(1, 2, 3));  // true, as Integers are equal to ints
-   * assertNotEquals(buffer, DataBuffers.of(1, 2, 3, 0));  // false, different sizes
-   * assertNotEquals(buffer, DataBuffers.of(1, 3, 2));  // false, different order
-   * assertNotEquals(buffer, DataBuffers.of(1L, 2L, 3L));  // false, different types
-   * }
- * - *

Note that the computation required to verify equality between two buffers can be expensive - * in some cases and therefore, it is recommended to not use this method in a critical path - * where performances matter. - * - * @param obj object to compare this buffer with - * @return true if this buffer is equal to the provided object - */ - @Override - boolean equals(Object obj); -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/buffer/DataBufferWindow.java b/ndarray/src/main/java/org/tensorflow/ndarray/buffer/DataBufferWindow.java deleted file mode 100644 index 85fc8c43d05..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/buffer/DataBufferWindow.java +++ /dev/null @@ -1,93 +0,0 @@ -package org.tensorflow.ndarray.buffer; - -/** - * A mutable container for viewing part of a {@link DataBuffer}. - * - *

Data buffer windows have a fixed size and can {@link DataBufferWindow#slide(long) "slide"} - * along a buffer to provide different views of the data without allocating a new buffer instance, - * like {@link DataBuffer#offset(long)} does. This improves overall performance when this operation - * is repeated frequently. For example: - * - *

{@code
- * IntDataBuffer bufferA = DataBuffers.ofInts(1024);
- * // ... init buffer data
- * IntDataBuffer bufferB = DataBuffers.ofInts(1, 2, 3, 4);
- *
- * // Return the index of the first occurrence of bufferB in bufferA using a sliding window
- * DataBufferWindow windowA = bufferA.window(4);
- * for (int i = 0; i < bufferA.size() - bufferB.size(); ++i) {
- *     if (windowA.slideTo(i).buffer().equals(bufferB)) {
- *         return i;
- *     }
- * }
- * }
- * - *

{@code DataBufferWindow} instances are stateful and not thread-safe. - * - * @param the type of buffer being viewed - */ -public interface DataBufferWindow> { - - /** - * Returns the current offset of this window in the original buffer. - */ - long offset(); - - /** - * Returns the size of this buffer window. - */ - long size(); - - /** - * Moves the window at the given position in the original buffer. - * - *

The size of the window remains the same and its offset is set to {@code index}, so that - * accessing the value of {@link #buffer()} at index {@code x} will return the value at - * {@code index + x} in the original buffer. - * - * @param index new offset for this window - * @return this instance - * @throws IndexOutOfBoundsException if the window cannot be slid because it goes beyond - * the original buffer limits - */ - DataBufferWindow slideTo(long index); - - /** - * Moves the window of {@code step} elements in the original buffer. - * - *

The size of the window remains the same and its offset is set to {@code offset() + step}. - * If {@code step} is positive, then the window will slide forward. If it is negative, it will - * slide backward. - * - * @param step value to add to the current offset of this window - * @return this instance - * @throws IndexOutOfBoundsException if the window cannot be slid because it goes beyond - * the original buffer limits - */ - DataBufferWindow slide(long step); - - /** - * Returns the buffer backing this window. - * - *

Each window instance has it's own buffer providing a view onto the original - * {@link DataBuffer}. The buffers are mutated when the window slides to different offsets. - * For example: - * - *

{@code
-   * IntDataBuffer buffer = DataBuffers.of(0, 1, 2, 3);
-   * DataBufferWindow window = buffer.window(0, 2);
-   *
-   * IntDataBuffer windowBuffer = window.buffer();
-   * assertEquals(0, windowBuffer.getInt(0));
-   * assertEquals(1, windowBuffer.getInt(1));
-   *
-   * window.slideTo(2);
-   * assertEquals(2, windowBuffer.getInt(0));
-   * assertEquals(3, windowBuffer.getInt(1));
-   * assertSame(windowBuffer, window.buffer());
-   * }
- * - * @return this window's buffer - */ - B buffer(); -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/buffer/DataBuffers.java b/ndarray/src/main/java/org/tensorflow/ndarray/buffer/DataBuffers.java deleted file mode 100644 index a5feb2599d0..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/buffer/DataBuffers.java +++ /dev/null @@ -1,457 +0,0 @@ -/* - 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 - - 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.buffer; - -import java.lang.reflect.Array; -import java.nio.ByteBuffer; -import java.nio.DoubleBuffer; -import java.nio.FloatBuffer; -import java.nio.IntBuffer; -import java.nio.LongBuffer; -import java.nio.ShortBuffer; -import java.util.Arrays; -import java.util.BitSet; -import org.tensorflow.ndarray.impl.buffer.Validator; -import org.tensorflow.ndarray.impl.buffer.misc.MiscDataBufferFactory; -import org.tensorflow.ndarray.impl.buffer.nio.NioDataBufferFactory; -import org.tensorflow.ndarray.impl.buffer.raw.RawDataBufferFactory; - -/** - * Helper class for creating {@code DataBuffer} instances. - */ -public final class DataBuffers { - - /** - * Creates a buffer of bytes that can store up to {@code size} values - * - * @param size size of the buffer to allocate - * @return a new buffer - */ - public static ByteDataBuffer ofBytes(long size) { - Validator.createArgs(size, MAX_32BITS); - if (RawDataBufferFactory.canBeUsed()) { - return RawDataBufferFactory.create(new byte[(int)size], false); - } - return NioDataBufferFactory.create(ByteBuffer.allocate((int)size)); - } - - /** - * Creates a buffer of longs that can store up to {@code size} values - * - * @param size size of the buffer to allocate - * @return a new buffer - */ - public static LongDataBuffer ofLongs(long size) { - Validator.createArgs(size, MAX_32BITS); - if (RawDataBufferFactory.canBeUsed()) { - return RawDataBufferFactory.create(new long[(int)size], false); - } - return NioDataBufferFactory.create(LongBuffer.allocate((int)size)); - } - - /** - * Creates a buffer of integers that can store up to {@code size} values - * - * @param size size of the buffer to allocate - * @return a new buffer - */ - public static IntDataBuffer ofInts(long size) { - Validator.createArgs(size, MAX_32BITS); - if (RawDataBufferFactory.canBeUsed()) { - return RawDataBufferFactory.create(new int[(int)size], false); - } - return NioDataBufferFactory.create(IntBuffer.allocate((int)size)); - } - - /** - * Creates a buffer of shorts that can store up to {@code size} values - * - * @param size size of the buffer to allocate - * @return a new buffer - */ - public static ShortDataBuffer ofShorts(long size) { - Validator.createArgs(size, MAX_32BITS); - if (RawDataBufferFactory.canBeUsed()) { - return RawDataBufferFactory.create(new short[(int)size], false); - } - return NioDataBufferFactory.create(ShortBuffer.allocate((int)size)); - } - - /** - * Creates a buffer of doubles that can store up to {@code size} values - * - * @param size size of the buffer to allocate - * @return a new buffer - */ - public static DoubleDataBuffer ofDoubles(long size) { - Validator.createArgs(size, MAX_32BITS); - if (RawDataBufferFactory.canBeUsed()) { - return RawDataBufferFactory.create(new double[(int)size], false); - } - return NioDataBufferFactory.create(DoubleBuffer.allocate((int)size)); - } - - /** - * Creates a buffer of floats that can store up to {@code size} values - * - * @param size size of the buffer to allocate - * @return a new buffer - */ - public static FloatDataBuffer ofFloats(long size) { - Validator.createArgs(size, MAX_32BITS); - if (RawDataBufferFactory.canBeUsed()) { - return RawDataBufferFactory.create(new float[(int)size], false); - } - return NioDataBufferFactory.create(FloatBuffer.allocate((int)size)); - } - - /** - * Creates a buffer of booleans that can store up to {@code size} values - * - * @param size size of the buffer to allocate - * @return a new buffer - */ - public static BooleanDataBuffer ofBooleans(long size) { - Validator.createArgs(size, MAX_32BITS); - if (RawDataBufferFactory.canBeUsed()) { - return RawDataBufferFactory.create(new boolean[(int)size], false); - } - return MiscDataBufferFactory.create(new BitSet((int)size), size, false); - } - - /** - * Creates a buffer of references to objects of type {@code clazz` that can store up to `size} - * values. - * - * @param type the type of object stored in this buffer - * @param size size of the buffer to allocate - * @param data type - * @return a new buffer - */ - public static DataBuffer ofObjects(Class type, long size) { - Validator.createArgs(size, MAX_32BITS); - @SuppressWarnings("unchecked") - T[] array = (T[])Array.newInstance(type, (int)size); - return MiscDataBufferFactory.create(array, false); - } - - /** - * Create a buffer from an array of floats into a data buffer. - * - *

The returned buffer allows read and write operations and share the memory of the source - * array, which is equivalent to call {@link #of(float[], boolean, boolean) of(values, false, false}} - * - * @param values float values - * @return a new buffer - */ - public static FloatDataBuffer of(float... values) { - return of(values, false, false); - } - - /** - * Create a buffer from an array of bytes into a data buffer. - * - *

The returned buffer allows read and write operations and share the memory of the source - * array, which is equivalent to call {@link #of(byte[], boolean, boolean) of(values, false, false}} - * - * @param values byte values - * @return a new buffer - */ - public static ByteDataBuffer of(byte... values) { - return of(values, false, false); - } - - /** - * Create a buffer from an array of longs into a data buffer. - * - *

The returned buffer allows read and write operations and share the memory of the source - * array, which is equivalent to call {@link #of(long[], boolean, boolean) of(values, false, false}} - * - * @param values long values - * @return a new buffer - */ - public static LongDataBuffer of(long... values) { - return of(values, false, false); - } - - /** - * Create a buffer from an array of ints into a data buffer. - * - *

The returned buffer allows read and write operations and share the memory of the source - * array, which is equivalent to call {@link #of(int[], boolean, boolean) of(values, false, false}} - * - * @param values int values - * @return a new buffer - */ - public static IntDataBuffer of(int... values) { - return of(values, false, false); - } - - /** - * Create a buffer from an array of shorts into a data buffer. - * - *

The returned buffer allows read and write operations and share the memory of the source - * array, which is equivalent to call {@link #of(short[], boolean, boolean) of(values, false, false}} - * - * @param values short values - * @return a new buffer - */ - public static ShortDataBuffer of(short... values) { - return of(values, false, false); - } - - /** - * Create a buffer from an array of doubles into a data buffer. - * - *

The returned buffer allows read and write operations and share the memory of the source - * array, which is equivalent to call {@link #of(double[], boolean, boolean) of(array, false, false}} - * - * @param values double values - * @return a new buffer - */ - public static DoubleDataBuffer of(double... values) { - return of(values, false, false); - } - - /** - * Create a buffer from an array of booleans into a data buffer. - * - *

The returned buffer allows read and write operations and share the memory of the source - * array, which is equivalent to call {@link #of(boolean[], boolean, boolean) of(values, false, false}} - * - * @param values booleans values - * @return a new buffer - */ - public static BooleanDataBuffer of(boolean... values) { - return of(values, false, false); - } - - /** - * Create a buffer from an array of objects into a data buffer. - * - *

The returned buffer allows read and write operations and share the memory of the source - * array, which is equivalent to call {@link #of(Object[], boolean, boolean) of(values, false, false}} - * - * @param values objects values - * @param data type - * @return a new buffer - */ - @SafeVarargs - public static DataBuffer ofObjects(T... values) { - return of(values, false, false); - } - - /** - * Create a buffer from an array of floats into a data buffer. - * - * @param array array of floats - * @param readOnly true if the buffer created must be read-only - * @param makeCopy true if the array must be copied, false will wrap the provided array - * @return a new buffer - */ - public static FloatDataBuffer of(float[] array, boolean readOnly, boolean makeCopy) { - float[] bufferArray = makeCopy ? Arrays.copyOf(array, array.length) : array; - if (RawDataBufferFactory.canBeUsed()) { - return RawDataBufferFactory.create(bufferArray, readOnly); - } - FloatBuffer buf = FloatBuffer.wrap(bufferArray); - return NioDataBufferFactory.create(readOnly ? buf.asReadOnlyBuffer() : buf); - } - - /** - * Create a buffer from an array of bytes into a data buffer. - * - * @param array array of bytes - * @param readOnly true if the buffer created must be read-only - * @param makeCopy true if the array must be copied, false will wrap the provided array - * @return a new buffer - */ - public static ByteDataBuffer of(byte[] array, boolean readOnly, boolean makeCopy) { - byte[] bufferArray = makeCopy ? Arrays.copyOf(array, array.length) : array; - if (RawDataBufferFactory.canBeUsed()) { - return RawDataBufferFactory.create(bufferArray, readOnly); - } - ByteBuffer buf = ByteBuffer.wrap(bufferArray); - return NioDataBufferFactory.create(readOnly ? buf.asReadOnlyBuffer() : buf); - } - - /** - * Create a buffer from an array of longs into a data buffer. - * - * @param array array of longs - * @param readOnly true if the buffer created must be read-only - * @param makeCopy true if the array must be copied, false will wrap the provided array - * @return a new buffer - */ - public static LongDataBuffer of(long[] array, boolean readOnly, boolean makeCopy) { - long[] bufferArray = makeCopy ? Arrays.copyOf(array, array.length) : array; - if (RawDataBufferFactory.canBeUsed()) { - return RawDataBufferFactory.create(bufferArray, readOnly); - } - LongBuffer buf = LongBuffer.wrap(bufferArray); - return NioDataBufferFactory.create(readOnly ? buf.asReadOnlyBuffer() : buf); - } - - /** - * Create a buffer from an array of ints into a data buffer. - * - * @param array array of ints - * @param readOnly true if the buffer created must be read-only - * @param makeCopy true if the array must be copied, false will wrap the provided array - * @return a new buffer - */ - public static IntDataBuffer of(int[] array, boolean readOnly, boolean makeCopy) { - int[] bufferArray = makeCopy ? Arrays.copyOf(array, array.length) : array; - if (RawDataBufferFactory.canBeUsed()) { - return RawDataBufferFactory.create(bufferArray, readOnly); - } - IntBuffer buf = IntBuffer.wrap(bufferArray); - return NioDataBufferFactory.create(readOnly ? buf.asReadOnlyBuffer() : buf); - } - - /** - * Create a buffer from an array of shorts into a data buffer. - * - * @param array array of shorts - * @param readOnly true if the buffer created must be read-only - * @param makeCopy true if the array must be copied, false will wrap the provided array - * @return a new buffer - */ - public static ShortDataBuffer of(short[] array, boolean readOnly, boolean makeCopy) { - short[] bufferArray = makeCopy ? Arrays.copyOf(array, array.length) : array; - if (RawDataBufferFactory.canBeUsed()) { - return RawDataBufferFactory.create(bufferArray, readOnly); - } - ShortBuffer buf = ShortBuffer.wrap(bufferArray); - return NioDataBufferFactory.create(readOnly ? buf.asReadOnlyBuffer() : buf); - } - - /** - * Create a buffer from an array of doubles into a data buffer. - * - * @param array array of doubles - * @param readOnly true if the buffer created must be read-only - * @param makeCopy true if the array must be copied, false will wrap the provided array - * @return a new buffer - */ - public static DoubleDataBuffer of(double[] array, boolean readOnly, boolean makeCopy) { - double[] bufferArray = makeCopy ? Arrays.copyOf(array, array.length) : array; - if (RawDataBufferFactory.canBeUsed()) { - return RawDataBufferFactory.create(bufferArray, readOnly); - } - DoubleBuffer buf = DoubleBuffer.wrap(bufferArray); - return NioDataBufferFactory.create(readOnly ? buf.asReadOnlyBuffer() : buf); - } - - /** - * Create a buffer from an array of booleans into a data buffer. - * - * @param array array of booleans - * @param readOnly true if the buffer created must be read-only - * @param makeCopy true if the array must be copied, false will wrap the provided array - * @return a new buffer - */ - public static BooleanDataBuffer of(boolean[] array, boolean readOnly, boolean makeCopy) { - boolean[] bufferArray = makeCopy ? Arrays.copyOf(array, array.length) : array; - if (RawDataBufferFactory.canBeUsed()) { - return RawDataBufferFactory.create(bufferArray, readOnly); - } - return MiscDataBufferFactory.create(bufferArray, readOnly); - } - - /** - * Create a buffer from an array of objects into a data buffer. - * - * @param array array of objects - * @param readOnly true if the buffer created must be read-only - * @param makeCopy true if the array must be copied, false will wrap the provided array - * @param data type - * @return a new buffer - */ - public static DataBuffer of(T[] array, boolean readOnly, boolean makeCopy) { - T[] bufferArray = makeCopy ? Arrays.copyOf(array, array.length) : array; - return MiscDataBufferFactory.create(bufferArray, readOnly); - } - - /** - * Wraps a JDK NIO {@link ByteBuffer} into a data buffer. - * - * @param buf buffer to wrap - * @return a new buffer - */ - public static ByteDataBuffer of(ByteBuffer buf) { - return NioDataBufferFactory.create(buf.duplicate()); - } - - /** - * Wraps a JDK NIO {@link IntBuffer} into a data buffer. - * - * @param buf buffer to wrap - * @return a new buffer - */ - public static IntDataBuffer of(IntBuffer buf) { - return NioDataBufferFactory.create(buf.duplicate()); - } - - /** - * Wraps a JDK NIO {@link ShortBuffer} into a data buffer. - * - * @param buf buffer to wrap - * @return a new buffer - */ - public static ShortDataBuffer of(ShortBuffer buf) { - return NioDataBufferFactory.create(buf.duplicate()); - } - - /** - * Wraps a JDK NIO {@link LongBuffer} into a data buffer. - * - * @param buf buffer to wrap - * @return a new buffer - */ - public static LongDataBuffer of(LongBuffer buf) { - return NioDataBufferFactory.create(buf.duplicate()); - } - - /** - * Wraps a JDK NIO {@link FloatBuffer} into a data buffer. - * - * @param buf buffer to wrap - * @return a new buffer - */ - public static FloatDataBuffer of(FloatBuffer buf) { - return NioDataBufferFactory.create(buf.duplicate()); - } - - /** - * Wraps a JDK NIO {@link DoubleBuffer} into a data buffer. - * - * @param buf buffer to wrap - * @return a new buffer - */ - public static DoubleDataBuffer of(DoubleBuffer buf) { - return NioDataBufferFactory.create(buf.duplicate()); - } - - /* - * The maximum size for a buffer of this type, i.e. the maximum number of bytes it can store. - *

- * As the maximum size may vary depending on the JVM implementation and on the platform, this - * property returns a value that is safe for most of them. - */ - static long MAX_32BITS = Integer.MAX_VALUE - 10; -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/buffer/DataStorageVisitor.java b/ndarray/src/main/java/org/tensorflow/ndarray/buffer/DataStorageVisitor.java deleted file mode 100644 index 560320cd7eb..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/buffer/DataStorageVisitor.java +++ /dev/null @@ -1,147 +0,0 @@ -package org.tensorflow.ndarray.buffer; - -import java.nio.ByteBuffer; -import java.nio.DoubleBuffer; -import java.nio.FloatBuffer; -import java.nio.IntBuffer; -import java.nio.LongBuffer; -import java.nio.ShortBuffer; -import java.util.BitSet; - -/** - * Visit the backing storage of {@link DataBuffer} instances. - * - * @param value type returned by the visitor - */ -public interface DataStorageVisitor { - - /** - * Visit the {@link ByteBuffer} backing a given instance of a {@link DataBuffer} - * - * @param buffer underlying buffer - * @return any value - * @see DataBuffer#accept(DataStorageVisitor) - */ - default R visit(ByteBuffer buffer) { - return fallback(); - } - - /** - * Visit the {@link ShortBuffer} backing a given instance of a {@link DataBuffer} - * - * @param buffer underlying buffer - * @return any value - * @see DataBuffer#accept(DataStorageVisitor) - */ - default R visit(ShortBuffer buffer) { - return fallback(); - } - - /** - * Visit the {@link IntBuffer} backing a given instance of a {@link DataBuffer} - * - * @param buffer underlying buffer - * @return any value - * @see DataBuffer#accept(DataStorageVisitor) - */ - default R visit(IntBuffer buffer) { - return fallback(); - } - - /** - * Visit the {@link LongBuffer} backing a given instance of a {@link DataBuffer} - * - * @param buffer underlying buffer - * @return any value - * @see DataBuffer#accept(DataStorageVisitor) - */ - default R visit(LongBuffer buffer) { - return fallback(); - } - - /** - * Visit the {@link FloatBuffer} backing a given instance of a {@link DataBuffer} - * - * @param buffer underlying buffer - * @return any value - * @see DataBuffer#accept(DataStorageVisitor) - */ - default R visit(FloatBuffer buffer) { - return fallback(); - } - - /** - * Visit the {@link DoubleBuffer} backing a given instance of a {@link DataBuffer} - * - * @param buffer underlying buffer - * @return any value - * @see DataBuffer#accept(DataStorageVisitor) - */ - default R visit(DoubleBuffer buffer) { - return fallback(); - } - - /** - * Visit the boolean array backing a given instance of a {@link DataBuffer} - * - * @param array underlying array - * @param offset offset of the buffer within the array - * @param length length of the buffer within the array - * @return any value - * @see DataBuffer#accept(DataStorageVisitor) - */ - default R visit(boolean[] array, int offset, int length) { - return fallback(); - } - - /** - * Visit the bit set backing a given instance of a {@link DataBuffer} - * - * @param bitSet underlying bit set - * @param offset offset of the buffer within the bit set - * @param numBits number of bits used to represent the buffer within the bit set - * @return any value - * @see DataBuffer#accept(DataStorageVisitor) - */ - default R visit(BitSet bitSet, int offset, long numBits) { - return fallback(); - } - - /** - * Visit the object array backing a given instance of a {@link DataBuffer} - * - * @param array underlying array - * @param offset offset of the buffer within the array - * @param length length of the buffer within the array - * @return any value - * @see DataBuffer#accept(DataStorageVisitor) - */ - default R visit(Object[] array, int offset, int length) { - return fallback(); - } - - /** - * Visit the raw memory segment of a given instance of a {@link DataBuffer} - * - * @param address native address of the buffer - * @param length length of the buffer - * @param scale number of bytes required to store a single value of this buffer - * @return any value - * @see DataBuffer#accept(DataStorageVisitor) - */ - default R visit(long address, long length, long scale) { - return fallback(); - } - - /** - * Fallback method called if the visitor implementation does not support the type of backing storage - * for a given {@link DataBuffer} - * - *

The implementor of this interface must override the {@code visit} methods for type of storage - * it supports. If {@link DataBuffer#accept(DataStorageVisitor)} is called on a buffer - * using a different type of storage, the invocation will fallback to this method. - * - * @return any value - */ - R fallback(); -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/buffer/DoubleDataBuffer.java b/ndarray/src/main/java/org/tensorflow/ndarray/buffer/DoubleDataBuffer.java deleted file mode 100644 index f2db925eb78..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/buffer/DoubleDataBuffer.java +++ /dev/null @@ -1,165 +0,0 @@ -/* - 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 - - 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.buffer; - -import java.nio.BufferOverflowException; -import java.nio.BufferUnderflowException; -import java.nio.ReadOnlyBufferException; - -/** - * A {@link DataBuffer} of doubles. - */ -public interface DoubleDataBuffer extends DataBuffer { - - /** - * Reads the double at the given index. - * - * @param index the index from which the float will be read - * @return the double at the given index - * @throws IndexOutOfBoundsException if index is negative or not smaller than the buffer size - */ - double getDouble(long index); - - /** - * Writes the given double into this buffer at the given index. - * - * @param value the double to be written - * @param index the index at which the value will be written - * @return this buffer - * @throws IndexOutOfBoundsException if index is negative or not smaller than the buffer size - * @throws ReadOnlyBufferException if this buffer is read-only - */ - DoubleDataBuffer setDouble(double value, long index); - - /** - * Bulk get method, using double arrays. - *

- * This method transfers values from this buffer into the given destination array. If there are - * fewer values in the buffer than are required to satisfy the request, that is, if - * {@code dst.length > size()}, then no values are transferred and a - * BufferUnderflowException is thrown. - *

- * Otherwise, this method copies {@code n = dst.length} values from this buffer into the given - * array. - * - * @param dst the array into which values are to be written - * @return this buffer - * @throws BufferUnderflowException if there are not enough values to copy from this buffer - */ - default DoubleDataBuffer read(double[] dst) { - return read(dst, 0, dst.length); - } - - /** - * Bulk get method, using double arrays. - *

- * This method transfers values from this buffer into the given destination array. If there are - * fewer values in the buffer than are required to satisfy the request, that is, if - * {@code length > size()}, then no values are transferred and a - * BufferUnderflowException is thrown. - *

- * Otherwise, this method copies {@code n = length} values from this buffer into the given array - * starting at the given offset. - * - * @param dst the array into which values are to be written - * @param offset the offset within the array of the first value to be written; must be - * non-negative and no larger than {@code dst.length} - * @param length the maximum number of values to be written to the given array; must be - * non-negative and no larger than {@code dst.length - offset} - * @return this buffer - * @throws BufferUnderflowException if there are fewer than length values remaining in this buffer - * @throws IndexOutOfBoundsException if the preconditions on the offset and length parameters do - * not hold - */ - DoubleDataBuffer read(double[] dst, int offset, int length); - - /** - * Bulk put method, using double arrays. - *

- * This method transfers the values in the given source array into this buffer. If there are - * more values in the source array than in this buffer, that is, if - * {@code src.length > size()}, then no values are transferred and a - * BufferOverflowException is thrown. - *

- * Otherwise, this method copies {@code n = src.length} values from the given array. - * - * @param src the source array from which values are to be read - * @return this buffer - * @throws BufferOverflowException if there is insufficient space in this buffer for the values in - * the source array - * @throws ReadOnlyBufferException if this buffer is read-only - */ - default DoubleDataBuffer write(double[] src) { - return write(src, 0, src.length); - } - - /** - * Bulk put method, using double arrays. - *

- * This method transfers the values in the given source array into this buffer. If there are - * more values in the source array than in this buffer, that is, if - * {@code length > size()}, then no values are transferred and a - * BufferOverflowException is thrown. - *

- * Otherwise, this method copies {@code n = length} values from the given array into this buffer, - * starting at the given offset. - * - * @param src the source array from which values are to be read - * @param offset the offset within the array of the first value to be read; must be non-negative - * and no larger than {@code src.length} - * @param length the number of values to be read from the given array; must be non-negative and no - * larger than {@code src.length - offset} - * @return this buffer - * @throws BufferOverflowException if there is insufficient space in this buffer for the values in - * the source array - * @throws IndexOutOfBoundsException if the preconditions on the offset and length parameters do - * not hold - * @throws ReadOnlyBufferException if this buffer is read-only - */ - DoubleDataBuffer write(double[] src, int offset, int length); - - @Override - default Double getObject(long index) { - return getDouble(index); - } - - @Override - default DoubleDataBuffer setObject(Double value, long index) { - return setDouble(value, index); - } - - @Override - DoubleDataBuffer copyTo(DataBuffer dst, long size); - - @Override - default DoubleDataBuffer offset(long index) { - return slice(index, size() - index); - } - - @Override - default DoubleDataBuffer narrow(long size) { - return slice(0, size); - } - - @Override - DoubleDataBuffer slice(long index, long size); - - @Override - default DataBufferWindow window(long size) { - throw new UnsupportedOperationException(); - } -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/buffer/FloatDataBuffer.java b/ndarray/src/main/java/org/tensorflow/ndarray/buffer/FloatDataBuffer.java deleted file mode 100644 index 4961c1b3445..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/buffer/FloatDataBuffer.java +++ /dev/null @@ -1,165 +0,0 @@ -/* - 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 - - 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.buffer; - -import java.nio.BufferOverflowException; -import java.nio.BufferUnderflowException; -import java.nio.ReadOnlyBufferException; - -/** - * A {@link DataBuffer} of floats. - */ -public interface FloatDataBuffer extends DataBuffer { - - /** - * Reads the float at the given index. - * - * @param index the index from which the float will be read - * @return the float at the given index - * @throws IndexOutOfBoundsException if index is negative or not smaller than the buffer size - */ - float getFloat(long index); - - /** - * Writes the given float into this buffer at the given index. - * - * @param value the float to be written - * @param index the index at which the value will be written - * @return this buffer - * @throws IndexOutOfBoundsException if index is negative or not smaller than the buffer size - * @throws ReadOnlyBufferException if this buffer is read-only - */ - FloatDataBuffer setFloat(float value, long index); - - /** - * Bulk get method, using float arrays. - *

- * This method transfers values from this buffer into the given destination array. If there are - * fewer values in the buffer than are required to satisfy the request, that is, if - * {@code dst.length > size()}, then no values are transferred and a - * BufferUnderflowException is thrown. - *

- * Otherwise, this method copies {@code n = dst.length} values from this buffer into the given - * array. - * - * @param dst the array into which values are to be written - * @return this buffer - * @throws BufferUnderflowException if there are not enough values to copy from this buffer - */ - default FloatDataBuffer read(float[] dst) { - return read(dst, 0, dst.length); - } - - /** - * Bulk get method, using float arrays. - *

- * This method transfers values from this buffer into the given destination array. If there are - * fewer values in the buffer than are required to satisfy the request, that is, if - * {@code length > size()}, then no values are transferred and a - * BufferUnderflowException is thrown. - *

- * Otherwise, this method copies {@code n = length} values from this buffer into the given array - * starting at the given offset. - * - * @param dst the array into which values are to be written - * @param offset the offset within the array of the first value to be written; must be - * non-negative and no larger than {@code dst.length} - * @param length the maximum number of values to be written to the given array; must be - * non-negative and no larger than {@code dst.length - offset} - * @return this buffer - * @throws BufferUnderflowException if there are fewer than length values remaining in this buffer - * @throws IndexOutOfBoundsException if the preconditions on the offset and length parameters do - * not hold - */ - FloatDataBuffer read(float[] dst, int offset, int length); - - /** - * Bulk put method, using float arrays. - *

- * This method transfers the values in the given source array into this buffer. If there are - * more values in the source array than in this buffer, that is, if - * {@code src.length > size()}, then no values are transferred and a - * BufferOverflowException is thrown. - *

- * Otherwise, this method copies {@code n = src.length} values from the given array. - * - * @param src the source array from which values are to be read - * @return this buffer - * @throws BufferOverflowException if there is insufficient space in this buffer for the values in - * the source array - * @throws ReadOnlyBufferException if this buffer is read-only - */ - default FloatDataBuffer write(float[] src) { - return write(src, 0, src.length); - } - - /** - * Bulk put method, using float arrays. - *

- * This method transfers the values in the given source array into this buffer. If there are - * more values in the source array than in this buffer, that is, if - * {@code length > size()}, then no values are transferred and a - * BufferOverflowException is thrown. - *

- * Otherwise, this method copies {@code n = length} values from the given array into this buffer, - * starting at the given offset. - * - * @param src the source array from which values are to be read - * @param offset the offset within the array of the first value to be read; must be non-negative - * and no larger than {@code src.length} - * @param length the number of values to be read from the given array; must be non-negative and no - * larger than {@code src.length - offset} - * @return this buffer - * @throws BufferOverflowException if there is insufficient space in this buffer for the values in - * the source array - * @throws IndexOutOfBoundsException if the preconditions on the offset and length parameters do - * not hold - * @throws ReadOnlyBufferException if this buffer is read-only - */ - FloatDataBuffer write(float[] src, int offset, int length); - - @Override - default Float getObject(long index) { - return getFloat(index); - } - - @Override - default FloatDataBuffer setObject(Float value, long index) { - return setFloat(value, index); - } - - @Override - FloatDataBuffer copyTo(DataBuffer dst, long size); - - @Override - default FloatDataBuffer offset(long index) { - return slice(index, size() - index); - } - - @Override - default FloatDataBuffer narrow(long size) { - return slice(0, size); - } - - @Override - FloatDataBuffer slice(long index, long size); - - @Override - default DataBufferWindow window(long size) { - throw new UnsupportedOperationException(); - } -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/buffer/IntDataBuffer.java b/ndarray/src/main/java/org/tensorflow/ndarray/buffer/IntDataBuffer.java deleted file mode 100644 index 2d660756e09..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/buffer/IntDataBuffer.java +++ /dev/null @@ -1,165 +0,0 @@ -/* - 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 - - 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.buffer; - -import java.nio.BufferOverflowException; -import java.nio.BufferUnderflowException; -import java.nio.ReadOnlyBufferException; - -/** - * A {@link DataBuffer} of ints. - */ -public interface IntDataBuffer extends DataBuffer { - - /** - * Reads the int at the given index. - * - * @param index the index from which the float will be read - * @return the int at the given index - * @throws IndexOutOfBoundsException if index is negative or not smaller than the buffer size - */ - int getInt(long index); - - /** - * Writes the given int into this buffer at the given index. - * - * @param value the int to be written - * @param index the index at which the value will be written - * @return this buffer - * @throws IndexOutOfBoundsException if index is negative or not smaller than the buffer size - * @throws ReadOnlyBufferException if this buffer is read-only - */ - IntDataBuffer setInt(int value, long index); - - /** - * Bulk get method, using int arrays. - *

- * This method transfers values from this buffer into the given destination array. If there are - * fewer values in the buffer than are required to satisfy the request, that is, if - * {@code dst.length > size()}, then no values are transferred and a - * BufferUnderflowException is thrown. - *

- * Otherwise, this method copies {@code n = dst.length} values from this buffer into the given - * array. - * - * @param dst the array into which values are to be written - * @return this buffer - * @throws BufferUnderflowException if there are not enough values to copy from this buffer - */ - default IntDataBuffer read(int[] dst) { - return read(dst, 0, dst.length); - } - - /** - * Bulk get method, using int arrays. - *

- * This method transfers values from this buffer into the given destination array. If there are - * fewer values in the buffer than are required to satisfy the request, that is, if - * {@code length > size()}, then no values are transferred and a - * BufferUnderflowException is thrown. - *

- * Otherwise, this method copies {@code n = length} values from this buffer into the given array - * starting at the given offset. - * - * @param dst the array into which values are to be written - * @param offset the offset within the array of the first value to be written; must be - * non-negative and no larger than {@code dst.length} - * @param length the maximum number of values to be written to the given array; must be - * non-negative and no larger than {@code dst.length - offset} - * @return this buffer - * @throws BufferUnderflowException if there are fewer than length values remaining in this buffer - * @throws IndexOutOfBoundsException if the preconditions on the offset and length parameters do - * not hold - */ - IntDataBuffer read(int[] dst, int offset, int length); - - /** - * Bulk put method, using int arrays. - *

- * This method transfers the values in the given source array into this buffer. If there are - * more values in the source array than in this buffer, that is, if - * {@code src.length > size()}, then no values are transferred and a - * BufferOverflowException is thrown. - *

- * Otherwise, this method copies {@code n = src.length} values from the given array. - * - * @param src the source array from which values are to be read - * @return this buffer - * @throws BufferOverflowException if there is insufficient space in this buffer for the values in - * the source array - * @throws ReadOnlyBufferException if this buffer is read-only - */ - default IntDataBuffer write(int[] src) { - return write(src, 0, src.length); - } - - /** - * Bulk put method, using int arrays. - *

- * This method transfers the values in the given source array into this buffer. If there are - * more values in the source array than in this buffer, that is, if - * {@code length > size()}, then no values are transferred and a - * BufferOverflowException is thrown. - *

- * Otherwise, this method copies {@code n = length} values from the given array into this buffer, - * starting at the given offset. - * - * @param src the source array from which values are to be read - * @param offset the offset within the array of the first value to be read; must be non-negative - * and no larger than {@code src.length} - * @param length the number of values to be read from the given array; must be non-negative and no - * larger than {@code src.length - offset} - * @return this buffer - * @throws BufferOverflowException if there is insufficient space in this buffer for the values in - * the source array - * @throws IndexOutOfBoundsException if the preconditions on the offset and length parameters do - * not hold - * @throws ReadOnlyBufferException if this buffer is read-only - */ - IntDataBuffer write(int[] src, int offset, int length); - - @Override - default Integer getObject(long index) { - return getInt(index); - } - - @Override - default IntDataBuffer setObject(Integer value, long index) { - return setInt(value, index); - } - - @Override - IntDataBuffer copyTo(DataBuffer dst, long size); - - @Override - default IntDataBuffer offset(long index) { - return slice(index, size() - index); - } - - @Override - default IntDataBuffer narrow(long size) { - return slice(0, size); - } - - @Override - IntDataBuffer slice(long index, long size); - - @Override - default DataBufferWindow window(long size) { - throw new UnsupportedOperationException(); - } -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/buffer/LongDataBuffer.java b/ndarray/src/main/java/org/tensorflow/ndarray/buffer/LongDataBuffer.java deleted file mode 100644 index f88ae4a80b4..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/buffer/LongDataBuffer.java +++ /dev/null @@ -1,165 +0,0 @@ -/* - 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 - - 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.buffer; - -import java.nio.BufferOverflowException; -import java.nio.BufferUnderflowException; -import java.nio.ReadOnlyBufferException; - -/** - * A {@link DataBuffer} of longs. - */ -public interface LongDataBuffer extends DataBuffer { - - /** - * Reads the long at the given index. - * - * @param index the index from which the float will be read - * @return the long at the given index - * @throws IndexOutOfBoundsException if index is negative or not smaller than the buffer size - */ - long getLong(long index); - - /** - * Writes the given long into this buffer at the given index. - * - * @param value the long to be written - * @param index the index at which the value will be written - * @return this buffer - * @throws IndexOutOfBoundsException if index is negative or not smaller than the buffer size - * @throws ReadOnlyBufferException if this buffer is read-only - */ - LongDataBuffer setLong(long value, long index); - - /** - * Bulk get method, using long arrays. - *

- * This method transfers values from this buffer into the given destination array. If there are - * fewer values in the buffer than are required to satisfy the request, that is, if - * {@code dst.length > size()}, then no values are transferred and a - * BufferUnderflowException is thrown. - *

- * Otherwise, this method copies {@code n = dst.length} values from this buffer into the given - * array. - * - * @param dst the array into which values are to be written - * @return this buffer - * @throws BufferUnderflowException if there are not enough values to copy from this buffer - */ - default LongDataBuffer read(long[] dst) { - return read(dst, 0, dst.length); - } - - /** - * Bulk get method, using long arrays. - *

- * This method transfers values from this buffer into the given destination array. If there are - * fewer values in the buffer than are required to satisfy the request, that is, if - * {@code length > size()}, then no values are transferred and a - * BufferUnderflowException is thrown. - *

- * Otherwise, this method copies {@code n = length} values from this buffer into the given array - * starting at the given offset. - * - * @param dst the array into which values are to be written - * @param offset the offset within the array of the first value to be written; must be - * non-negative and no larger than {@code dst.length} - * @param length the maximum number of values to be written to the given array; must be - * non-negative and no larger than {@code dst.length - offset} - * @return this buffer - * @throws BufferUnderflowException if there are fewer than length values remaining in this buffer - * @throws IndexOutOfBoundsException if the preconditions on the offset and length parameters do - * not hold - */ - LongDataBuffer read(long[] dst, int offset, int length); - - /** - * Bulk put method, using long arrays. - *

- * This method transfers the values in the given source array into this buffer. If there are - * more values in the source array than in this buffer, that is, if - * {@code src.length > size()}, then no values are transferred and a - * BufferOverflowException is thrown. - *

- * Otherwise, this method copies {@code n = src.length} values from the given array. - * - * @param src the source array from which values are to be read - * @return this buffer - * @throws BufferOverflowException if there is insufficient space in this buffer for the values in - * the source array - * @throws ReadOnlyBufferException if this buffer is read-only - */ - default LongDataBuffer write(long[] src) { - return write(src, 0, src.length); - } - - /** - * Bulk put method, using long arrays. - *

- * This method transfers the values in the given source array into this buffer. If there are - * more values in the source array than in this buffer, that is, if - * {@code length > size()}, then no values are transferred and a - * BufferOverflowException is thrown. - *

- * Otherwise, this method copies {@code n = length} values from the given array into this buffer, - * starting at the given offset. - * - * @param src the source array from which values are to be read - * @param offset the offset within the array of the first value to be read; must be non-negative - * and no larger than {@code src.length} - * @param length the number of values to be read from the given array; must be non-negative and no - * larger than {@code src.length - offset} - * @return this buffer - * @throws BufferOverflowException if there is insufficient space in this buffer for the values in - * the source array - * @throws IndexOutOfBoundsException if the preconditions on the offset and length parameters do - * not hold - * @throws ReadOnlyBufferException if this buffer is read-only - */ - LongDataBuffer write(long[] src, int offset, int length); - - @Override - default Long getObject(long index) { - return getLong(index); - } - - @Override - default LongDataBuffer setObject(Long value, long index) { - return setLong(value, index); - } - - @Override - LongDataBuffer copyTo(DataBuffer dst, long size); - - @Override - default LongDataBuffer offset(long index) { - return slice(index, size() - index); - } - - @Override - default LongDataBuffer narrow(long size) { - return slice(0, size); - } - - @Override - LongDataBuffer slice(long index, long size); - - @Override - default DataBufferWindow window(long size) { - throw new UnsupportedOperationException(); - } -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/buffer/ShortDataBuffer.java b/ndarray/src/main/java/org/tensorflow/ndarray/buffer/ShortDataBuffer.java deleted file mode 100644 index 290e2d57619..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/buffer/ShortDataBuffer.java +++ /dev/null @@ -1,165 +0,0 @@ -/* - 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 - - 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.buffer; - -import java.nio.BufferOverflowException; -import java.nio.BufferUnderflowException; -import java.nio.ReadOnlyBufferException; - -/** - * A {@link DataBuffer} of shorts. - */ -public interface ShortDataBuffer extends DataBuffer { - - /** - * Reads the short at the given index. - * - * @param index the index from which the float will be read - * @return the short at the given index - * @throws IndexOutOfBoundsException if index is negative or not smaller than the buffer size - */ - short getShort(long index); - - /** - * Writes the given short into this buffer at the given index. - * - * @param value the short to be written - * @param index the index at which the value will be written - * @return this buffer - * @throws IndexOutOfBoundsException if index is negative or not smaller than the buffer size - * @throws ReadOnlyBufferException if this buffer is read-only - */ - ShortDataBuffer setShort(short value, long index); - - /** - * Bulk get method, using short arrays. - *

- * This method transfers values from this buffer into the given destination array. If there are - * fewer values in the buffer than are required to satisfy the request, that is, if - * {@code dst.length > size()}, then no values are transferred and a - * BufferUnderflowException is thrown. - *

- * Otherwise, this method copies {@code n = dst.length} values from this buffer into the given - * array. - * - * @param dst the array into which values are to be written - * @return this buffer - * @throws BufferUnderflowException if there are not enough values to copy from this buffer - */ - default ShortDataBuffer read(short[] dst) { - return read(dst, 0, dst.length); - } - - /** - * Bulk get method, using short arrays. - *

- * This method transfers values from this buffer into the given destination array. If there are - * fewer values in the buffer than are required to satisfy the request, that is, if - * {@code length > size()}, then no values are transferred and a - * BufferUnderflowException is thrown. - *

- * Otherwise, this method copies {@code n = length} values from this buffer into the given array - * starting at the given offset. - * - * @param dst the array into which values are to be written - * @param offset the offset within the array of the first value to be written; must be - * non-negative and no larger than {@code dst.length} - * @param length the maximum number of values to be written to the given array; must be - * non-negative and no larger than {@code dst.length - offset} - * @return this buffer - * @throws BufferUnderflowException if there are fewer than length values remaining in this buffer - * @throws IndexOutOfBoundsException if the preconditions on the offset and length parameters do - * not hold - */ - ShortDataBuffer read(short[] dst, int offset, int length); - - /** - * Bulk put method, using short arrays. - *

- * This method transfers the values in the given source array into this buffer. If there are - * more values in the source array than in this buffer, that is, if - * {@code src.length > size()}, then no values are transferred and a - * BufferOverflowException is thrown. - *

- * Otherwise, this method copies {@code n = src.length} values from the given array. - * - * @param src the source array from which values are to be read - * @return this buffer - * @throws BufferOverflowException if there is insufficient space in this buffer for the values in - * the source array - * @throws ReadOnlyBufferException if this buffer is read-only - */ - default ShortDataBuffer write(short[] src) { - return write(src, 0, src.length); - } - - /** - * Bulk put method, using short arrays. - *

- * This method transfers the values in the given source array into this buffer. If there are - * more values in the source array than in this buffer, that is, if - * {@code length > size()}, then no values are transferred and a - * BufferOverflowException is thrown. - *

- * Otherwise, this method copies {@code n = length} values from the given array into this buffer, - * starting at the given offset. - * - * @param src the source array from which values are to be read - * @param offset the offset within the array of the first value to be read; must be non-negative - * and no larger than {@code src.length} - * @param length the number of values to be read from the given array; must be non-negative and no - * larger than {@code src.length - offset} - * @return this buffer - * @throws BufferOverflowException if there is insufficient space in this buffer for the values in - * the source array - * @throws IndexOutOfBoundsException if the preconditions on the offset and length parameters do - * not hold - * @throws ReadOnlyBufferException if this buffer is read-only - */ - ShortDataBuffer write(short[] src, int offset, int length); - - @Override - default Short getObject(long index) { - return getShort(index); - } - - @Override - default ShortDataBuffer setObject(Short value, long index) { - return setShort(value, index); - } - - @Override - ShortDataBuffer copyTo(DataBuffer dst, long size); - - @Override - default ShortDataBuffer offset(long index) { - return slice(index, size() - index); - } - - @Override - default ShortDataBuffer narrow(long size) { - return slice(0, size); - } - - @Override - ShortDataBuffer slice(long index, long size); - - @Override - default DataBufferWindow window(long size) { - throw new UnsupportedOperationException(); - } -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/buffer/layout/BooleanDataLayout.java b/ndarray/src/main/java/org/tensorflow/ndarray/buffer/layout/BooleanDataLayout.java deleted file mode 100644 index c7092c8720d..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/buffer/layout/BooleanDataLayout.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * 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 - * - * 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.buffer.layout; - -import org.tensorflow.ndarray.buffer.BooleanDataBuffer; -import org.tensorflow.ndarray.buffer.DataBuffer; -import org.tensorflow.ndarray.impl.buffer.adapter.DataBufferAdapterFactory; - -/** - * A {@link DataLayout} that converts data stored in a buffer to booleans. - * - * @param type of buffer this layout can be applied to - * @see DataLayout - */ -public interface BooleanDataLayout> extends DataLayout { - - @Override - default BooleanDataBuffer applyTo(S buffer) { - return DataBufferAdapterFactory.create(buffer, this); - } - - /** - * Writes a boolean into the buffer at the given index after converting it to the buffer type. - * - * @param buffer the buffer to write to - * @param value the boolean to convert and write - * @param index index in the buffer where the converted value should be written - * @see #writeObject(DataBuffer, Boolean, long) - */ - void writeBoolean(S buffer, boolean value, long index); - - /** - * Reads {@code n = scale()} values from the buffer at the given index and return them as a boolean. - * - * @param buffer the buffer to read from - * @param index position of the buffer to read in the buffer - * @return the boolean value - * @see #readObject(DataBuffer, long) - */ - boolean readBoolean(S buffer, long index); - - @Override - default void writeObject(S buffer, Boolean value, long index) { - writeBoolean(buffer, value, index); - } - - @Override - default Boolean readObject(S buffer, long index) { - return readBoolean(buffer, index); - } -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/buffer/layout/ByteDataLayout.java b/ndarray/src/main/java/org/tensorflow/ndarray/buffer/layout/ByteDataLayout.java deleted file mode 100644 index e4d4bf9c8cf..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/buffer/layout/ByteDataLayout.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - 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 - - 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.buffer.layout; - -import org.tensorflow.ndarray.buffer.ByteDataBuffer; -import org.tensorflow.ndarray.buffer.DataBuffer; -import org.tensorflow.ndarray.impl.buffer.adapter.DataBufferAdapterFactory; - -/** - * A {@link DataLayout} that converts data stored in a buffer to bytes. - * - * @param type of buffer this layout can be applied to - * @see DataLayout - */ -public interface ByteDataLayout> extends DataLayout { - - @Override - default ByteDataBuffer applyTo(S buffer) { - return DataBufferAdapterFactory.create(buffer, this); - } - - /** - * Writes a byte into the buffer at the given index after converting it to the buffer type. - * - * @param buffer the buffer to write to - * @param value the byte to convert and write - * @param index index in the buffer where the converted value should be written - * @see #writeObject(DataBuffer, Byte, long) - */ - void writeByte(S buffer, byte value, long index); - - /** - * Reads {@code n = scale()} values from the buffer at the given index and return them as a byte. - * - * @param buffer the buffer to read from - * @param index position of the buffer to read in the buffer - * @return the byte value - * @see #readObject(DataBuffer, long) - */ - byte readByte(S buffer, long index); - - @Override - default void writeObject(S buffer, Byte value, long index) { - writeByte(buffer, value, index); - } - - @Override - default Byte readObject(S buffer, long index) { - return readByte(buffer, index); - } -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/buffer/layout/DataLayout.java b/ndarray/src/main/java/org/tensorflow/ndarray/buffer/layout/DataLayout.java deleted file mode 100644 index 93cc542e07a..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/buffer/layout/DataLayout.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * 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 - * - * 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.buffer.layout; - -import org.tensorflow.ndarray.buffer.DataBuffer; -import org.tensorflow.ndarray.impl.buffer.adapter.DataBufferAdapterFactory; - -/** - * Converts data stored in a buffer to a given type. - * - *

{@code DataLayout} instances are used to define a custom format for storing and reading data - * of a {@link DataBuffer}. They provide a segregation layer between the type of data stored in the - * buffer (the buffer type) and the type of data manipulated by the end user (the - * user type). - * - *

Since the conversion methods are invoked for every value that is written or read, working - * with data layouts may have a negative impact on the performances so using primitive types directly - * should be preferred whenever possible. - * - *

It is also recommended to implement immutable data layouts so they can be reapplied to multiple - * buffers without reallocating a new instance for each of them. For example: - * - *

- * class BigIntegerBufferAllocator {
- *
- *     public DataBuffer<BigInteger> allocate(long size) {
- *         return LAYOUT.applyTo(DataBuffers.ofLongs(size * LAYOUT.scale()));  // scale is 1 by default
- *     }
- *
- *     private static final DataLayout<LongDataBuffer, BigInteger> LAYOUT = new DataLayout<LongDataBuffer, BigInteger>() {
- *
- *         @Override
- *         public void writeObject(LongDataBuffer buffer, BigInteger value, long index) {
- *             buffer.setLong(value.longValue(), index);
- *         }
- *
- *         @Override
- *         public BigInteger readObject(LongDataBuffer buffer, long index) {
- *             return BigInteger.valueOf(buffer.getLong(index));
- *         }
- *     };
- * }
- * 
- * - * @param type of buffer this layout can be applied to - * @param user data type of this layout - */ -public interface DataLayout, T> { - - /** - * Apply this layout to the provided buffer. - * - *

The returned {@link DataBuffer} instance is simply a wrapper to the original buffer and does - * not have a backing storage of his own. - * - * @param buffer the target buffer to apply this layout to - * @return a buffer with this layout - */ - default DataBuffer applyTo(S buffer) { - return DataBufferAdapterFactory.create(buffer, this); - } - - /** - * Writes a user value into the buffer at the given index after converting it to the buffer type. - * - *

It is the responsibility of the implementors of this interface to write the converted value - * to the given buffer before this call returns, using the most appropriate method. For example, - * for a layout converting a {@code BigInteger} to a single {@code long}, - *

-   * @Override
-   * public void writeObject(LongDataBuffer buffer, BigInteger value, long index) {
-   *   buffer.setLong(value.longValue(), index);
-   * }
-   * 
- * If a single user value scales over more than one buffer values, {@code index} indicates the - * starting position of the sequence to be written to the buffer. - * - * @param buffer the buffer to write to - * @param value the value in the user type to convert and write - * @param index index in the buffer where the converted value should be written - */ - void writeObject(S buffer, T value, long index); - - /** - * Reads {@code n = scale()} values from the buffer at the given index and return them as a single - * value in the user type. - * - *

It is the responsibility of the implementors of this interface to read the value to be - * converted from the given buffer, using the most appropriate method. For example, for a layout - * that converting a single {@code long} to a {@code BigInteger}, - *

-   * @Override
-   * public BigInteger readObject(LongDataBuffer buffer, long index) {
-   *   return BigInteger.valueOf(buffer.getLong(index));
-   * }
-   * 
- * If a single user value scales over more than one buffer values, {@code index} indicates the - * starting position of the sequence to be read from the buffer. - * - * @param buffer the buffer to read from - * @param index position of the buffer to read in the buffer - * @return the converted value - */ - T readObject(S buffer, long index); - - /** - * Indicates the number of buffer values are required to represent a single user value, default is 1. - * - *

Scale must be positive and must be an integer, meaning that a single buffer value in a buffer cannot - * be used to represent more than one user value. - */ - default int scale() { - return 1; - } -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/buffer/layout/DataLayouts.java b/ndarray/src/main/java/org/tensorflow/ndarray/buffer/layout/DataLayouts.java deleted file mode 100644 index 8f69168930c..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/buffer/layout/DataLayouts.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright 2020 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.buffer.layout; - -import java.nio.charset.Charset; -import org.tensorflow.ndarray.buffer.ByteDataBuffer; -import org.tensorflow.ndarray.buffer.DataBuffer; -import org.tensorflow.ndarray.buffer.ShortDataBuffer; -import org.tensorflow.ndarray.impl.buffer.layout.Bfloat16Layout; -import org.tensorflow.ndarray.impl.buffer.layout.BoolLayout; -import org.tensorflow.ndarray.impl.buffer.layout.Float16Layout; -import org.tensorflow.ndarray.impl.buffer.layout.StringLayout; - -/** - * Exposes {@link DataLayout} instances of data formats frequently used in linear algebra computation. - * - *

Example of usage: - *

{@code
- * // Storing boolean values in a ByteDataBuffer
- * BooleanDataBuffer boolBuffer = DataLayouts.BOOL.applyTo(byteDataBuffer);
- *
- * // Allocating a new buffer of 256 half floats
- * FloatDataBuffer halfBuffer = DataLayouts.FLOAT16.applyTo(DataBuffers.ofShorts(256 * DataLayouts.FLOAT16.scale());
- * }
- */ -public final class DataLayouts { - - /** - * Data layout for converting 16-bit bfloats to/from short values. - * - *

This format used to be specific to TensorFlow but has now been adopted more broadly in the - * machine learning field. It is optimized for fast conversion with single-precision 32-bit - * floating points by simply shifting their value and truncating the mantissa to only 7 bits. - * - *

Therefore, this is a lost of precision in the fraction part compared to the IEEE-754 - * half-precision floating point specification (see {@link #FLOAT16} but it has a larger range of - * possible values in the whole part as it preserves the 8-bit exponent and uses the same bias, - * (i.e. an absolute range above 0 of approximately [10-40, 3.39 × 1038] - * - *

Some CPUs support the bfloat16 format natively for better performances. - */ - public static final FloatDataLayout BFLOAT16 = new Bfloat16Layout(); - - /** - * Data layout for converting 16-bit half floats to/from short values. - * - *

Half floats are stored in memory accordingly to the IEEE-754 half-precision floating point - * specification, and are converted to/from 32-bit floats in the user space. - * - *

There is a potential loss of precision when converting a single float (32-bit) to a half - * float (16-bit). Absolute range of values above 0 for a half float is approximately - * [5.96 × 10-8, 6.55 × 104] and their decimal part is rounded up - * to a 10 bits mantissa. - * - *

In general, half float computation perform better on GPUs since, in general, CPUs do not - * support this format natively.

- */ - public static final FloatDataLayout FLOAT16 = new Float16Layout(); - - /** - * Data layout for converting booleans to/from byte values. - * - *

Since there is no Java NIO boolean buffer, this layout is particularly useful for mapping - * booleans values to standard byte buffers. The conversion between a boolean and a byte requires - * explicit type casting. - */ - public static final BooleanDataLayout BOOL = new BoolLayout(); - - /** - * Creates a data layout for converting strings to/from byte sequences. - * - *

This layout requires a {@code charset} in parameter to specify how the strings must be - * encoded/decoded as byte sequences. So a new layout instance is always returned. - * - * @param charset charset to use - * @return a new string layout - */ - public static DataLayout, String> ofStrings(Charset charset) { - return StringLayout.of(charset); - } -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/buffer/layout/DoubleDataLayout.java b/ndarray/src/main/java/org/tensorflow/ndarray/buffer/layout/DoubleDataLayout.java deleted file mode 100644 index efd1e461802..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/buffer/layout/DoubleDataLayout.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * 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 - * - * 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.buffer.layout; - -import org.tensorflow.ndarray.buffer.DataBuffer; -import org.tensorflow.ndarray.buffer.DoubleDataBuffer; -import org.tensorflow.ndarray.impl.buffer.adapter.DataBufferAdapterFactory; - -/** - * A {@link DataLayout} that converts data stored in a buffer to doubles. - * - * @param type of buffer this layout can be applied to - * @see DataLayout - */ -public interface DoubleDataLayout> extends DataLayout { - - @Override - default DoubleDataBuffer applyTo(S buffer) { - return DataBufferAdapterFactory.create(buffer, this); - } - - /** - * Writes a double into the buffer at the given index after converting it to the buffer type. - * - * @param buffer the buffer to write to - * @param value the double to convert and write - * @param index index in the buffer where the converted value should be written - * @see #writeObject(DataBuffer, Double, long) - */ - void writeDouble(S buffer, double value, long index); - - /** - * Reads {@code n = scale()} buffer values at the given index and return them as a double. - * - * @param buffer the buffer to read from - * @param index position of the buffer to read in the buffer - * @return the double value - * @see #readObject(DataBuffer, long) - */ - double readDouble(S buffer, long index); - - @Override - default void writeObject(S buffer, Double value, long index) { - writeDouble(buffer, value, index); - } - - @Override - default Double readObject(S buffer, long index) { - return readDouble(buffer, index); - } -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/buffer/layout/FloatDataLayout.java b/ndarray/src/main/java/org/tensorflow/ndarray/buffer/layout/FloatDataLayout.java deleted file mode 100644 index a57f525d69f..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/buffer/layout/FloatDataLayout.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * 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 - * - * 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.buffer.layout; - -import org.tensorflow.ndarray.buffer.DataBuffer; -import org.tensorflow.ndarray.buffer.FloatDataBuffer; -import org.tensorflow.ndarray.impl.buffer.adapter.DataBufferAdapterFactory; - -/** - * A {@link DataLayout} that converts data stored in a buffer to floats. - * - * @param type of buffer this layout can be applied to - * @see DataLayout - */ -public interface FloatDataLayout> extends DataLayout { - - @Override - default FloatDataBuffer applyTo(S buffer) { - return DataBufferAdapterFactory.create(buffer, this); - } - - /** - * Writes a float into the buffer at the given index after converting it to the buffer type. - * - * @param buffer the buffer to write to - * @param value the float to convert and write - * @param index index in the buffer where the converted value should be written - * @see #writeObject(DataBuffer, Float, long) - */ - void writeFloat(S buffer, float value, long index); - - /** - * Reads {@code n = scale()} values from the buffer at the given index and return them as a float. - * - * @param buffer the buffer to read from - * @param index position of the buffer to read in the buffer - * @return the float value - * @see #readObject(DataBuffer, long) - */ - float readFloat(S buffer, long index); - - @Override - default void writeObject(S buffer, Float value, long index) { - writeFloat(buffer, value, index); - } - - @Override - default Float readObject(S buffer, long index) { - return readFloat(buffer, index); - } -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/buffer/layout/IntDataLayout.java b/ndarray/src/main/java/org/tensorflow/ndarray/buffer/layout/IntDataLayout.java deleted file mode 100644 index 718deac9b9f..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/buffer/layout/IntDataLayout.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * 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 - * - * 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.buffer.layout; - -import org.tensorflow.ndarray.buffer.DataBuffer; -import org.tensorflow.ndarray.buffer.IntDataBuffer; -import org.tensorflow.ndarray.impl.buffer.adapter.DataBufferAdapterFactory; - -/** - * A {@link DataLayout} that converts data stored in a buffer to ints. - * - * @param type of buffer this layout can be applied to - * @see DataLayout - */ -public interface IntDataLayout> extends DataLayout { - - @Override - default IntDataBuffer applyTo(S buffer) { - return DataBufferAdapterFactory.create(buffer, this); - } - - /** - * Writes a int into the buffer at the given index after converting it to the buffer type. - * - * @param buffer the buffer to write to - * @param value the int to convert and write - * @param index index in the buffer where the converted value should be written - * @see #writeObject(DataBuffer, Integer, long) - */ - void writeInt(S buffer, int value, long index); - - /** - * Reads {@code n = scale()} values from the buffer at the given index and return them as an int. - * - * @param buffer the buffer to read from - * @param index position of the buffer to read in the buffer - * @return the int value - * @see #readObject(DataBuffer, long) - */ - int readInt(S buffer, long index); - - @Override - default void writeObject(S buffer, Integer value, long index) { - writeInt(buffer, value, index); - } - - @Override - default Integer readObject(S buffer, long index) { - return readInt(buffer, index); - } -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/buffer/layout/LongDataLayout.java b/ndarray/src/main/java/org/tensorflow/ndarray/buffer/layout/LongDataLayout.java deleted file mode 100644 index f5c86ddd378..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/buffer/layout/LongDataLayout.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * 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 - * - * 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.buffer.layout; - -import org.tensorflow.ndarray.buffer.DataBuffer; -import org.tensorflow.ndarray.buffer.LongDataBuffer; -import org.tensorflow.ndarray.impl.buffer.adapter.DataBufferAdapterFactory; - -/** - * A {@link DataLayout} that converts data stored in a buffer to longs. - * - * @param type of buffer this layout can be applied to - * @see DataLayout - */ -public interface LongDataLayout> extends DataLayout { - - @Override - default LongDataBuffer applyTo(S buffer) { - return DataBufferAdapterFactory.create(buffer, this); - } - - /** - * Writes a long into the buffer at the given index after converting it to the buffer type. - * - * @param buffer the buffer to write to - * @param value the long to convert and write - * @param index index in the buffer where the converted value should be written - * @see #writeObject(DataBuffer, Long, long) - */ - void writeLong(S buffer, long value, long index); - - /** - * Reads {@code n = scale()} values from the buffer at the given index and return them as a long. - * - * @param buffer the buffer to read from - * @param index position of the buffer to read in the buffer - * @return the long value - * @see #readObject(DataBuffer, long) - */ - long readLong(S buffer, long index); - - @Override - default void writeObject(S buffer, Long value, long index) { - writeLong(buffer, value, index); - } - - @Override - default Long readObject(S buffer, long index) { - return readLong(buffer, index); - } -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/buffer/layout/ShortDataLayout.java b/ndarray/src/main/java/org/tensorflow/ndarray/buffer/layout/ShortDataLayout.java deleted file mode 100644 index 89c1fd0dec4..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/buffer/layout/ShortDataLayout.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * 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 - * - * 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.buffer.layout; - -import org.tensorflow.ndarray.buffer.DataBuffer; -import org.tensorflow.ndarray.buffer.ShortDataBuffer; -import org.tensorflow.ndarray.impl.buffer.adapter.DataBufferAdapterFactory; - -/** - * A {@link DataLayout} that converts data stored in a buffer to shorts. - * - * @param type of buffer this layout can be applied to - * @see DataLayout - */ -public interface ShortDataLayout> extends DataLayout { - - @Override - default ShortDataBuffer applyTo(S buffer) { - return DataBufferAdapterFactory.create(buffer, this); - } - - /** - * Writes a short into the buffer at the given index after converting it to the buffer type. - * - * @param buffer the buffer to write to - * @param value the short to convert and write - * @param index index in the buffer where the converted value should be written - * @see #writeObject(DataBuffer, Short, long) - */ - void writeShort(S buffer, short value, long index); - - /** - * Reads {@code n = scale()} buffer values at the given index and return them as a short. - * - * @param buffer the buffer to read from - * @param index position of the value to read in the buffer - * @return the short value - * @see #readObject(DataBuffer, long) - */ - short readShort(S buffer, long index); - - @Override - default void writeObject(S buffer, Short value, long index) { - writeShort(buffer, value, index); - } - - @Override - default Short readObject(S buffer, long index) { - return readShort(buffer, index); - } -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/AbstractNdArray.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/AbstractNdArray.java deleted file mode 100644 index 690dedc2042..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/AbstractNdArray.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - 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 - - 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; - -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; - -@SuppressWarnings("unchecked") -public abstract class AbstractNdArray> implements NdArray { - - public abstract U slice(long position, DimensionalSpace dimensions); - - public DimensionalSpace dimensions() { - return dimensions; - } - - @Override - public Shape shape() { - return dimensions.shape(); - } - - @Override - public NdArraySequence scalars() { - // negative if this array is a scalar, should be handled in `elements(dimIdx)` - return (NdArraySequence)elements(shape().numDimensions() - 1); - } - - @Override - public int hashCode() { - return slowHashCode(); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (!(obj instanceof NdArray)) { - return false; - } - return slowEquals((NdArray)obj); - } - - protected AbstractNdArray(DimensionalSpace dimensions) { - this.dimensions = dimensions; - } - - protected void slowCopyTo(NdArray array) { - scalars().forEachIndexed((coords, e) -> array.setObject(e.getObject(), coords)); - } - - protected int slowHashCode() { - final int prime = 31; - int result = 1; - for (NdArray scalar : scalars()) { - result = prime * result + scalar.getObject().hashCode(); - } - result = prime * result + shape().hashCode(); - return result; - } - - protected boolean slowEquals(NdArray array) { - 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())) { - 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 deleted file mode 100644 index 285d09966de..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/Validator.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - 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 - - 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; - -import java.nio.BufferOverflowException; -import java.nio.BufferUnderflowException; -import org.tensorflow.ndarray.NdArray; -import org.tensorflow.ndarray.buffer.DataBuffer; - -public class Validator { - - public static void copyToNdArrayArgs(NdArray ndArray, NdArray otherNdArray) { - if (!ndArray.shape().equals(otherNdArray.shape())) { - throw new IllegalArgumentException("Can only copy to arrays of the same shape (" + - ndArray.shape() + " != " + otherNdArray.shape() + ")"); - } - } - - public static void readToBufferArgs(NdArray ndArray, DataBuffer dst) { - if (dst.size() < ndArray.size()) { - throw new BufferOverflowException(); - } - } - - public static void writeFromBufferArgs(NdArray ndArray, DataBuffer src) { - if (src.size() < ndArray.size()) { - throw new BufferUnderflowException(); - } - } - - private static void copyArrayArgs(int arrayLength, int arrayOffset) { - if (arrayOffset < 0) { - throw new IndexOutOfBoundsException("Offset must be non-negative"); - } - if (arrayOffset > arrayLength) { - throw new IndexOutOfBoundsException("Offset must be no larger than array length"); - } - } - - protected Validator() {} -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/AbstractDataBuffer.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/AbstractDataBuffer.java deleted file mode 100644 index e5103a2c17a..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/AbstractDataBuffer.java +++ /dev/null @@ -1,182 +0,0 @@ -/* - 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 - - 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.buffer; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; -import org.tensorflow.ndarray.buffer.DataBuffer; - -public abstract class AbstractDataBuffer implements DataBuffer { - - @Override - public DataBuffer read(T[] dst, int offset, int length) { - Validator.readArgs(this, dst.length, offset, length); - for (int i = 0; i < length; ++i) { - dst[i + offset] = getObject(i); - } - return this; - } - - @Override - public DataBuffer write(T[] src, int offset, int length) { - Validator.writeArgs(this, src.length, offset, length); - for (int i = 0; i < length; ++i) { - setObject(src[i + offset], i); - } - return this; - } - - @Override - public DataBuffer copyTo(DataBuffer dst, long size) { - return slowCopyTo(dst, size); - } - - @Override - public int hashCode() { - // This hash code computation is generic to all types of data buffers and accurate but not optimized - // for performances, it needs to be improved if there is a present use case for such hash codes. - return slowHashCode(); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (!(obj instanceof DataBuffer)) { - return false; - } - return slowEquals((DataBuffer)obj); - } - - @SuppressWarnings("unchecked") - protected > U slowCopyTo(DataBuffer dst, long size) { - Validator.copyToArgs(this, dst, size); - for (long idx = 0L; idx < size; ++idx) { - dst.setObject(getObject(idx), idx); - } - return (U)this; - } - - protected int slowHashCode() { - final int prime = 31; - int result = 1; - - // First check from the first non-null element if we are dealing with a buffer of arrays - long idx = 0L; - for (; idx < size(); ++idx) { - T o = getObject(idx); - if (o != null) { - if (o.getClass().isArray()) { - result = prime * result + arrayHashCode(idx, o.getClass()); // compute hash codes based on array elements - return result; - } - result = prime * result + o.hashCode(); - break; // continue hash code computation without array type check - } - result = prime * result; - } - while (++idx < size()) { - result = prime * result + Objects.hashCode(getObject(idx)); - } - return result; - } - - protected boolean slowEquals(DataBuffer other) { - if (other.size() != size()) { - return false; - } - long idx = 0L; - for (; idx < size(); ++idx) { - Object thisObject = getObject(idx); - if (thisObject != null) { - if (thisObject.getClass().isArray()) { - return arrayEquals(idx, thisObject.getClass(), other); - } - if (!Objects.equals(other.getObject(idx), thisObject)) { - return false; - } - break; // continue equality comparison without array type check - } - if (other.getObject(idx) != null) { - return false; - } - } - while (++idx < size()) { - if (!Objects.equals(other.getObject(idx), getObject(idx))) { - return false; - } - } - return true; - } - - private int arrayHashCode(long startIdx, Class arrayClass) { - ArrayHashCoder hashCoder = ARRAY_HASH_CODERS.getOrDefault(arrayClass, DEFAULT_ARRAY_HASH_CODER); - final int prime = 31; - int result = 1; - for (long idx = startIdx; idx < size(); ++idx) { - result = prime * result + hashCoder.hashCode(this, idx); - } - return result; - } - - private boolean arrayEquals(long startIdx, Class arrayClass, DataBuffer other) { - ArrayComparator comparator = ARRAY_COMPARATORS.getOrDefault(arrayClass, DEFAULT_ARRAY_COMPARATOR); - for (long idx = startIdx; idx < size(); ++idx) { - if (!comparator.equals(this, other, idx)) { - return false; - } - } - return true; - } - - @FunctionalInterface - private static interface ArrayHashCoder { - int hashCode(DataBuffer buffer, long index); - } - private static final Map, ArrayHashCoder> ARRAY_HASH_CODERS = new HashMap<>(); - private static final ArrayHashCoder DEFAULT_ARRAY_HASH_CODER; - - @FunctionalInterface - private static interface ArrayComparator { - boolean equals(DataBuffer buffer, DataBuffer otherBuffer, long index); - } - private static final Map, ArrayComparator> ARRAY_COMPARATORS = new HashMap<>(); - private static final ArrayComparator DEFAULT_ARRAY_COMPARATOR; - - static { - ARRAY_HASH_CODERS.put(byte[].class, (b, idx) -> Arrays.hashCode((byte[])b.getObject(idx))); - ARRAY_HASH_CODERS.put(int[].class, (b, idx) -> Arrays.hashCode((int[])b.getObject(idx))); - ARRAY_HASH_CODERS.put(short[].class, (b, idx) -> Arrays.hashCode((short[])b.getObject(idx))); - ARRAY_HASH_CODERS.put(long[].class, (b, idx) -> Arrays.hashCode((long[])b.getObject(idx))); - ARRAY_HASH_CODERS.put(float[].class, (b, idx) -> Arrays.hashCode((float[])b.getObject(idx))); - ARRAY_HASH_CODERS.put(double[].class, (b, idx) -> Arrays.hashCode((double[])b.getObject(idx))); - ARRAY_HASH_CODERS.put(boolean[].class, (b, idx) -> Arrays.hashCode((boolean[])b.getObject(idx))); - DEFAULT_ARRAY_HASH_CODER = (b, idx) -> Arrays.deepHashCode((Object[])b.getObject(idx)); - - ARRAY_COMPARATORS.put(byte[].class, (b1, b2, idx) -> Arrays.equals((byte[])b1.getObject(idx), (byte[])b2.getObject(idx))); - ARRAY_COMPARATORS.put(int[].class, (b1, b2, idx) -> Arrays.equals((int[])b1.getObject(idx), (int[])b2.getObject(idx))); - ARRAY_COMPARATORS.put(short[].class, (b1, b2, idx) -> Arrays.equals((short[])b1.getObject(idx), (short[])b2.getObject(idx))); - ARRAY_COMPARATORS.put(long[].class, (b1, b2, idx) -> Arrays.equals((long[])b1.getObject(idx), (long[])b2.getObject(idx))); - ARRAY_COMPARATORS.put(float[].class, (b1, b2, idx) -> Arrays.equals((float[])b1.getObject(idx), (float[])b2.getObject(idx))); - ARRAY_COMPARATORS.put(double[].class, (b1, b2, idx) -> Arrays.equals((double[])b1.getObject(idx), (double[])b2.getObject(idx))); - ARRAY_COMPARATORS.put(boolean[].class, (b1, b2, idx) -> Arrays.equals((boolean[])b1.getObject(idx), (boolean[])b2.getObject(idx))); - DEFAULT_ARRAY_COMPARATOR = (b1, b2, idx) -> Arrays.deepEquals((Object[])b1.getObject(idx), (Object[])b2.getObject(idx)); - } -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/AbstractDataBufferWindow.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/AbstractDataBufferWindow.java deleted file mode 100644 index 55d4fd56021..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/AbstractDataBufferWindow.java +++ /dev/null @@ -1,48 +0,0 @@ -package org.tensorflow.ndarray.impl.buffer; - -import org.tensorflow.ndarray.buffer.DataBuffer; -import org.tensorflow.ndarray.buffer.DataBufferWindow; - -public abstract class AbstractDataBufferWindow> implements DataBufferWindow { - - @Override - public final long offset() { - return offset; - } - - @Override - public final long size() { - return windowBuffer.size(); - } - - @Override - public final DataBufferWindow slideTo(long index) { - if (index < 0 || index > maxOffset) { - throw new IndexOutOfBoundsException(); - } - offset(index); - offset = index; - return this; - } - - @Override - public final DataBufferWindow slide(long step) { - return slideTo(offset + step); - } - - @Override - public final B buffer() { - return windowBuffer; - } - - protected abstract void offset(long offset); - - protected AbstractDataBufferWindow(B windowBuffer, long bufferLimit) { - this.windowBuffer = windowBuffer; - maxOffset = bufferLimit - windowBuffer.size(); - } - - private final B windowBuffer; - private final long maxOffset; - private long offset = 0; -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/Validator.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/Validator.java deleted file mode 100644 index 8f18e620b90..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/Validator.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - 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 - - 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.buffer; - -import java.nio.BufferOverflowException; -import java.nio.BufferUnderflowException; -import java.nio.ReadOnlyBufferException; -import org.tensorflow.ndarray.buffer.DataBuffer; - -public class Validator { - - public static void createArgs(long size, long maxSize) { - if (size < 0) { - throw new IllegalArgumentException("Size must be non-negative"); - } - if (size > maxSize) { - throw new IllegalArgumentException("Buffer size must be no greater than maximum size allowed (" + maxSize + ")"); - } - } - - public static void getArgs(DataBuffer buffer, long index) { - if (index < 0) { - throw new IndexOutOfBoundsException("Index must be non-negative"); - } - if (index >= buffer.size()) { - throw new IndexOutOfBoundsException("Index must be smaller than the buffer size"); - } - } - - public static void setArgs(DataBuffer buffer, long index) { - if (index < 0) { - throw new IndexOutOfBoundsException("Index must be non-negative"); - } - if (index >= buffer.size()) { - throw new IndexOutOfBoundsException("Index must be smaller than the buffer size"); - } - if (buffer.isReadOnly()) { - throw new ReadOnlyBufferException(); - } - } - - public static void copyToArgs(DataBuffer src, DataBuffer dst, long size) { - if (dst == src) { - throw new IllegalArgumentException("Source cannot be the same buffer as destination"); - } - if (size > dst.size()) { - throw new BufferOverflowException(); - } - if (size > src.size()) { - throw new BufferUnderflowException(); - } - if (dst.isReadOnly()) { - throw new ReadOnlyBufferException(); - } - } - - public static void readArgs(DataBuffer buffer, int arrayLength, int offset, int length) { - if (length > buffer.size()) { - throw new BufferUnderflowException(); - } - arrayArgs(arrayLength, offset, length); - } - - public static void writeArgs(DataBuffer buffer, int arrayLength, int offset, int length) { - if (length > buffer.size()) { - throw new BufferOverflowException(); - } - if (buffer.isReadOnly()) { - throw new ReadOnlyBufferException(); - } - arrayArgs(arrayLength, offset, length); - } - - public static void offsetArgs(DataBuffer buffer, long index) { - if (index < 0) { - throw new IllegalArgumentException("Index must be non-negative"); - } - if (index > buffer.size()) { - throw new IllegalArgumentException("Index must not exceed buffer size"); - } - } - - public static void narrowArgs(DataBuffer buffer, long size) { - if (size < 0) { - throw new IllegalArgumentException("Size must be non-negative"); - } - if (size > buffer.size()) { - throw new IllegalArgumentException("Cannot narrow a buffer of size " + buffer.size() + " to " + size); - } - } - - public static void sliceArgs(DataBuffer buffer, long index, long size) { - if (index < 0) { - throw new IllegalArgumentException("Index must be non-negative"); - } - if (size < 0) { - throw new IllegalArgumentException("Size must be non-negative"); - } - if (index + size > buffer.size()) { - throw new IllegalArgumentException("Buffer view must not exceed original buffer limits"); - } - } - - private static void arrayArgs(int arrayLength, int offset, int length) { - if (offset < 0) { - throw new IndexOutOfBoundsException("Offset must be non-negative"); - } - if (offset > arrayLength) { - throw new IndexOutOfBoundsException("Offset must be no larger than array length"); - } - if (length < 0) { - throw new IndexOutOfBoundsException("Length must be non-negative"); - } - if (length > arrayLength - offset) { - throw new IndexOutOfBoundsException("Length must be no larger than array length minus the offset"); - } - } -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/adapter/AbstractDataBufferAdapter.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/adapter/AbstractDataBufferAdapter.java deleted file mode 100644 index 3c9b6df1e93..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/adapter/AbstractDataBufferAdapter.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * 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 - * - * 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.buffer.adapter; - -import org.tensorflow.ndarray.impl.buffer.Validator; -import org.tensorflow.ndarray.buffer.DataBuffer; -import org.tensorflow.ndarray.impl.buffer.AbstractDataBuffer; -import org.tensorflow.ndarray.buffer.layout.DataLayout; - -@SuppressWarnings("unchecked") -abstract class AbstractDataBufferAdapter, T, U extends DataBuffer> extends AbstractDataBuffer { - - @Override - public long size() { - return size; - } - - @Override - public boolean isReadOnly() { - return buffer.isReadOnly(); - } - - @Override - public T getObject(long index) { - Validator.getArgs(this, index); - return layout.readObject(buffer, index * layout.scale()); - } - - @Override - public U setObject(T value, long index) { - Validator.setArgs(this, index); - layout.writeObject(buffer, value, index * layout.scale()); - return (U)this; - } - - AbstractDataBufferAdapter(S buffer, DataLayout layout) { - this.buffer = buffer; - this.layout = layout; - size = buffer.size() / layout.scale(); - } - - DataLayout layout() { - return layout; - } - - S buffer() { - return buffer; - } - - private final S buffer; - private final DataLayout layout; - private final long size; -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/adapter/BooleanDataBufferAdapter.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/adapter/BooleanDataBufferAdapter.java deleted file mode 100644 index 40217b57d6a..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/adapter/BooleanDataBufferAdapter.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * 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 - * - * 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.buffer.adapter; - -import org.tensorflow.ndarray.impl.buffer.Validator; -import org.tensorflow.ndarray.buffer.BooleanDataBuffer; -import org.tensorflow.ndarray.buffer.DataBuffer; -import org.tensorflow.ndarray.buffer.layout.BooleanDataLayout; - -class BooleanDataBufferAdapter> extends AbstractDataBufferAdapter - implements BooleanDataBuffer { - - @Override - public boolean getBoolean(long index) { - Validator.getArgs(this, index); - return layout.readBoolean(buffer(), index * layout.scale()); - } - - @Override - public BooleanDataBuffer setBoolean(boolean value, long index) { - Validator.setArgs(this, index); - layout.writeBoolean(buffer(), value, index * layout.scale()); - return this; - } - - @Override - public BooleanDataBuffer read(boolean[] dst, int offset, int length) { - Validator.readArgs(this, dst.length, offset, length); - for (int i = 0, j = offset; i < length; ++i, ++j) { - dst[j] = layout.readBoolean(buffer(), i * layout.scale()); - } - return this; - } - - @Override - public BooleanDataBuffer write(boolean[] src, int offset, int length) { - Validator.writeArgs(this, src.length, offset, length); - for (int i = 0, j = offset; i < length; ++i, ++j) { - layout.writeBoolean(buffer(), src[j], i * layout.scale()); - } - return this; - } - - @Override - public BooleanDataBuffer copyTo(DataBuffer dst, long size) { - Validator.copyToArgs(this, dst, size); - if (dst instanceof BooleanDataBuffer) { - BooleanDataBuffer booleanDst = (BooleanDataBuffer)dst; - for (long idx = 0L; idx < size; ++idx) { - booleanDst.setBoolean(getBoolean(idx), idx); - } - return this; - } - return slowCopyTo(dst, size); - } - - @Override - @SuppressWarnings("unchecked") - public BooleanDataBuffer offset(long index) { - return new BooleanDataBufferAdapter<>((S)buffer().offset(index * layout.scale()), layout); - } - - @Override - @SuppressWarnings("unchecked") - public BooleanDataBuffer narrow(long size) { - return new BooleanDataBufferAdapter<>((S)buffer().narrow(size * layout.scale()), layout); - } - - @Override - @SuppressWarnings("unchecked") - public BooleanDataBuffer slice(long index, long size) { - return new BooleanDataBufferAdapter<>((S)buffer().slice(index * layout.scale(), size * layout.scale()), layout); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (!(obj instanceof BooleanDataBuffer)) { - return super.equals(obj); - } - BooleanDataBuffer other = (BooleanDataBuffer)obj; - if (other.size() != size()) { - return false; - } - for (long idx = 0L; idx < size(); ++idx) { - if (other.getBoolean(idx) != getBoolean(idx)) { - return false; - } - } - return true; - } - - BooleanDataBufferAdapter(S buffer, BooleanDataLayout layout) { - super(buffer, layout); - this.layout = layout; - } - - private BooleanDataLayout layout; -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/adapter/ByteDataBufferAdapter.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/adapter/ByteDataBufferAdapter.java deleted file mode 100644 index c120a3ba810..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/adapter/ByteDataBufferAdapter.java +++ /dev/null @@ -1,135 +0,0 @@ -package org.tensorflow.ndarray.impl.buffer.adapter; - -import org.tensorflow.ndarray.impl.buffer.Validator; -import org.tensorflow.ndarray.buffer.BooleanDataBuffer; -import org.tensorflow.ndarray.buffer.ByteDataBuffer; -import org.tensorflow.ndarray.buffer.DataBuffer; -import org.tensorflow.ndarray.buffer.DoubleDataBuffer; -import org.tensorflow.ndarray.buffer.FloatDataBuffer; -import org.tensorflow.ndarray.buffer.IntDataBuffer; -import org.tensorflow.ndarray.buffer.LongDataBuffer; -import org.tensorflow.ndarray.buffer.ShortDataBuffer; -import org.tensorflow.ndarray.buffer.layout.ByteDataLayout; - -class ByteDataBufferAdapter> extends AbstractDataBufferAdapter - implements ByteDataBuffer { - - @Override - public byte getByte(long index) { - Validator.getArgs(this, index); - return layout.readByte(buffer(), index * layout.scale()); - } - - @Override - public ByteDataBuffer setByte(byte value, long index) { - Validator.setArgs(this, index); - layout.writeByte(buffer(), value, index * layout.scale()); - return this; - } - - @Override - public ByteDataBuffer read(byte[] dst, int offset, int length) { - Validator.readArgs(this, dst.length, offset, length); - for (int i = 0, j = offset; i < length; ++i, ++j) { - dst[j] = layout.readByte(buffer(), i * layout.scale()); - } - return this; - } - - @Override - public ByteDataBuffer write(byte[] src, int offset, int length) { - Validator.writeArgs(this, src.length, offset, length); - for (int i = 0, j = offset; i < length; ++i, ++j) { - layout.writeByte(buffer(), src[j], i * layout.scale()); - } - return this; - } - - @Override - public ByteDataBuffer copyTo(DataBuffer dst, long size) { - Validator.copyToArgs(this, dst, size); - if (dst instanceof ByteDataBuffer) { - ByteDataBuffer byteDst = (ByteDataBuffer)dst; - for (long idx = 0L; idx < size; ++idx) { - byteDst.setByte(getByte(idx), idx); - } - return this; - } - return slowCopyTo(dst, size); - } - - @Override - public IntDataBuffer asInts() { - throw new IllegalStateException("Byte buffers with layout cannot be converted"); - } - - @Override - public ShortDataBuffer asShorts() { - throw new IllegalStateException("Byte buffers with layout cannot be converted"); - } - - @Override - public LongDataBuffer asLongs() { - throw new IllegalStateException("Byte buffers with layout cannot be converted"); - } - - @Override - public FloatDataBuffer asFloats() { - throw new IllegalStateException("Byte buffers with layout cannot be converted"); - } - - @Override - public DoubleDataBuffer asDoubles() { - throw new IllegalStateException("Byte buffers with layout cannot be converted"); - } - - @Override - public BooleanDataBuffer asBooleans() { - throw new IllegalStateException("Byte buffers with layout cannot be converted"); - } - - @Override - @SuppressWarnings("unchecked") - public ByteDataBuffer offset(long index) { - return new ByteDataBufferAdapter<>((S)buffer().offset(index * layout.scale()), layout); - } - - @Override - @SuppressWarnings("unchecked") - public ByteDataBuffer narrow(long size) { - return new ByteDataBufferAdapter<>((S)buffer().narrow(size * layout.scale()), layout); - } - - @Override - @SuppressWarnings("unchecked") - public ByteDataBuffer slice(long index, long size) { - return new ByteDataBufferAdapter<>((S)buffer().slice(index * layout.scale(), size * layout.scale()), layout); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (!(obj instanceof ByteDataBuffer)) { - return super.equals(obj); - } - ByteDataBuffer other = (ByteDataBuffer)obj; - if (other.size() != size()) { - return false; - } - for (long idx = 0L; idx < size(); ++idx) { - if (other.getByte(idx) != getByte(idx)) { - return false; - } - } - return true; - } - - ByteDataBufferAdapter(S buffer, ByteDataLayout layout) { - super(buffer, layout); - this.layout = layout; - } - - private ByteDataLayout layout; -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/adapter/DataBufferAdapter.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/adapter/DataBufferAdapter.java deleted file mode 100644 index de20ab2cfd7..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/adapter/DataBufferAdapter.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * 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 - * - * 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.buffer.adapter; - -import org.tensorflow.ndarray.buffer.DataBuffer; -import org.tensorflow.ndarray.buffer.layout.DataLayout; - -@SuppressWarnings("unchecked") -class DataBufferAdapter, T> extends AbstractDataBufferAdapter> { - - @Override - @SuppressWarnings("unchecked") - public DataBuffer offset(long index) { - return new DataBufferAdapter<>((S)buffer().offset(index * layout().scale()), layout()); - } - - @Override - @SuppressWarnings("unchecked") - public DataBuffer narrow(long size) { - return new DataBufferAdapter<>((S)buffer().narrow(size * layout().scale()), layout()); - } - - @Override - @SuppressWarnings("unchecked") - public DataBuffer slice(long index, long size) { - return new DataBufferAdapter<>((S)buffer().slice(index * layout().scale(), size * layout().scale()), layout()); - } - - DataBufferAdapter(S buffer, DataLayout layout) { - super(buffer, layout); - } -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/adapter/DataBufferAdapterFactory.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/adapter/DataBufferAdapterFactory.java deleted file mode 100644 index 468051e1b46..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/adapter/DataBufferAdapterFactory.java +++ /dev/null @@ -1,142 +0,0 @@ -/* - * 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 - * - * 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.buffer.adapter; - -import org.tensorflow.ndarray.buffer.BooleanDataBuffer; -import org.tensorflow.ndarray.buffer.ByteDataBuffer; -import org.tensorflow.ndarray.buffer.DataBuffer; -import org.tensorflow.ndarray.buffer.DoubleDataBuffer; -import org.tensorflow.ndarray.buffer.FloatDataBuffer; -import org.tensorflow.ndarray.buffer.IntDataBuffer; -import org.tensorflow.ndarray.buffer.LongDataBuffer; -import org.tensorflow.ndarray.buffer.ShortDataBuffer; -import org.tensorflow.ndarray.buffer.layout.BooleanDataLayout; -import org.tensorflow.ndarray.buffer.layout.ByteDataLayout; -import org.tensorflow.ndarray.buffer.layout.DataLayout; -import org.tensorflow.ndarray.buffer.layout.DoubleDataLayout; -import org.tensorflow.ndarray.buffer.layout.FloatDataLayout; -import org.tensorflow.ndarray.buffer.layout.IntDataLayout; -import org.tensorflow.ndarray.buffer.layout.LongDataLayout; -import org.tensorflow.ndarray.buffer.layout.ShortDataLayout; - -/** - * Factory of data buffer adapters. - * - *

Data buffer adapters are used to apply a {@link DataLayout} to a buffer. Conceptually, they act - * as a proxy that intercept each I/O call and perform the required type conversions after/before - * delegating the task to the underlying buffer. - */ -public class DataBufferAdapterFactory { - - /** - * Creates an adapter that applies a byte data layout to the given buffer. - * - * @param buffer the delegate buffer - * @param layout layout to apply - * @param the type of the buffer - * @return buffer adapter - */ - public static > ByteDataBuffer create(S buffer, ByteDataLayout layout) { - return new ByteDataBufferAdapter<>(buffer, layout); - } - - /** - * Creates an adapter that applies a boolean data layout to the given buffer. - * - * @param buffer the delegate buffer - * @param layout layout to apply - * @param the type of the buffer - * @return buffer adapter - */ - public static > BooleanDataBuffer create(S buffer, BooleanDataLayout layout) { - return new BooleanDataBufferAdapter<>(buffer, layout); - } - - /** - * Creates an adapter that applies a double data layout to the given buffer. - * - * @param buffer the delegate buffer - * @param layout layout to apply - * @param the type of the buffer - * @return buffer adapter - */ - public static > DoubleDataBuffer create(S buffer, DoubleDataLayout layout) { - return new DoubleDataBufferAdapter<>(buffer, layout); - } - - /** - * Creates an adapter that applies a float data layout to the given buffer. - * - * @param buffer the delegate buffer - * @param layout layout to apply - * @param the type of the buffer - * @return buffer adapter - */ - public static > FloatDataBuffer create(S buffer, FloatDataLayout layout) { - return new FloatDataBufferAdapter<>(buffer, layout); - } - - /** - * Creates an adapter that applies a integer data layout to the given buffer. - * - * @param buffer the delegate buffer - * @param layout layout to apply - * @param the type of the buffer - * @return buffer adapter - */ - public static > IntDataBuffer create(S buffer, IntDataLayout layout) { - return new IntDataBufferAdapter<>(buffer, layout); - } - - /** - * Creates an adapter that applies a long data layout to the given buffer. - * - * @param buffer the delegate buffer - * @param layout layout to apply - * @param the type of the buffer - * @return buffer adapter - */ - public static > LongDataBuffer create(S buffer, LongDataLayout layout) { - return new LongDataBufferAdapter<>(buffer, layout); - } - - /** - * Creates an adapter that applies a short data layout to the given buffer. - * - * @param buffer the delegate buffer - * @param layout layout to apply - * @param the type of the buffer - * @return buffer adapter - */ - public static > ShortDataBuffer create(S buffer, ShortDataLayout layout) { - return new ShortDataBufferAdapter<>(buffer, layout); - } - - /** - * Creates an adapter that applies a data layout to the given buffer. - * - * @param buffer the delegate buffer - * @param layout layout to apply - * @param the type of the buffer - * @param the type of data returned by the layout - * @return buffer adapter - */ - public static , T> DataBuffer create(S buffer, DataLayout layout) { - return new DataBufferAdapter<>(buffer, layout); - } -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/adapter/DoubleDataBufferAdapter.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/adapter/DoubleDataBufferAdapter.java deleted file mode 100644 index 253acbac269..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/adapter/DoubleDataBufferAdapter.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * 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 - * - * 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.buffer.adapter; - -import org.tensorflow.ndarray.impl.buffer.Validator; -import org.tensorflow.ndarray.buffer.DataBuffer; -import org.tensorflow.ndarray.buffer.DoubleDataBuffer; -import org.tensorflow.ndarray.buffer.layout.DoubleDataLayout; - -class DoubleDataBufferAdapter> extends AbstractDataBufferAdapter - implements DoubleDataBuffer { - - @Override - public double getDouble(long index) { - Validator.getArgs(this, index); - return layout.readDouble(buffer(), index * layout.scale()); - } - - @Override - public DoubleDataBuffer setDouble(double value, long index) { - Validator.setArgs(this, index); - layout.writeDouble(buffer(), value, index * layout.scale()); - return this; - } - - @Override - public DoubleDataBuffer read(double[] dst, int offset, int length) { - Validator.readArgs(this, dst.length, offset, length); - for (int i = 0, j = offset; i < length; ++i, ++j) { - dst[j] = layout.readDouble(buffer(), i * layout.scale()); - } - return this; - } - - @Override - public DoubleDataBuffer write(double[] src, int offset, int length) { - Validator.writeArgs(this, src.length, offset, length); - for (int i = 0, j = offset; i < length; ++i, ++j) { - layout.writeDouble(buffer(), src[j], i * layout.scale()); - } - return this; - } - - @Override - public DoubleDataBuffer copyTo(DataBuffer dst, long size) { - Validator.copyToArgs(this, dst, size); - if (dst instanceof DoubleDataBuffer) { - DoubleDataBuffer doubleDst = (DoubleDataBuffer)dst; - for (long idx = 0L; idx < size; ++idx) { - doubleDst.setDouble(getDouble(idx), idx); - } - return this; - } - return slowCopyTo(dst, size); - } - - @Override - @SuppressWarnings("unchecked") - public DoubleDataBuffer offset(long index) { - return new DoubleDataBufferAdapter<>((S)buffer().offset(index * layout.scale()), layout); - } - - @Override - @SuppressWarnings("unchecked") - public DoubleDataBuffer narrow(long size) { - return new DoubleDataBufferAdapter<>((S)buffer().narrow(size * layout.scale()), layout); - } - - @Override - @SuppressWarnings("unchecked") - public DoubleDataBuffer slice(long index, long size) { - return new DoubleDataBufferAdapter<>((S)buffer().slice(index * layout.scale(), size * layout.scale()), layout); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (!(obj instanceof DoubleDataBuffer)) { - return super.equals(obj); - } - DoubleDataBuffer other = (DoubleDataBuffer)obj; - if (other.size() != size()) { - return false; - } - for (long idx = 0L; idx < size(); ++idx) { - if (other.getDouble(idx) != getDouble(idx)) { - return false; - } - } - return true; - } - - DoubleDataBufferAdapter(S buffer, DoubleDataLayout layout) { - super(buffer, layout); - this.layout = layout; - } - - private DoubleDataLayout layout; -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/adapter/FloatDataBufferAdapter.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/adapter/FloatDataBufferAdapter.java deleted file mode 100644 index 69928bf0ccb..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/adapter/FloatDataBufferAdapter.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * 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 - * - * 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.buffer.adapter; - -import org.tensorflow.ndarray.impl.buffer.Validator; -import org.tensorflow.ndarray.buffer.DataBuffer; -import org.tensorflow.ndarray.buffer.FloatDataBuffer; -import org.tensorflow.ndarray.buffer.layout.FloatDataLayout; - -class FloatDataBufferAdapter> extends AbstractDataBufferAdapter - implements FloatDataBuffer { - - @Override - public float getFloat(long index) { - Validator.getArgs(this, index); - return layout.readFloat(buffer(), index * layout.scale()); - } - - @Override - public FloatDataBuffer setFloat(float value, long index) { - Validator.setArgs(this, index); - layout.writeFloat(buffer(), value, index * layout.scale()); - return this; - } - - @Override - public FloatDataBuffer read(float[] dst, int offset, int length) { - Validator.readArgs(this, dst.length, offset, length); - for (int i = 0, j = offset; i < length; ++i, ++j) { - dst[j] = layout.readFloat(buffer(), i * layout.scale()); - } - return this; - } - - @Override - public FloatDataBuffer write(float[] src, int offset, int length) { - Validator.writeArgs(this, src.length, offset, length); - for (int i = 0, j = offset; i < length; ++i, ++j) { - layout.writeFloat(buffer(), src[j], i * layout.scale()); - } - return this; - } - - @Override - public FloatDataBuffer copyTo(DataBuffer dst, long size) { - Validator.copyToArgs(this, dst, size); - if (dst instanceof FloatDataBuffer) { - FloatDataBuffer floatDst = (FloatDataBuffer)dst; - for (long idx = 0L; idx < size; ++idx) { - floatDst.setFloat(getFloat(idx), idx); - } - return this; - } - return slowCopyTo(dst, size); - } - - @Override - @SuppressWarnings("unchecked") - public FloatDataBuffer offset(long index) { - return new FloatDataBufferAdapter<>((S)buffer().offset(index * layout.scale()), layout); - } - - @Override - @SuppressWarnings("unchecked") - public FloatDataBuffer narrow(long size) { - return new FloatDataBufferAdapter<>((S)buffer().narrow(size * layout.scale()), layout); - } - - @Override - @SuppressWarnings("unchecked") - public FloatDataBuffer slice(long index, long size) { - return new FloatDataBufferAdapter<>((S)buffer().slice(index * layout.scale(), size * layout.scale()), layout); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (!(obj instanceof FloatDataBuffer)) { - return super.equals(obj); - } - FloatDataBuffer other = (FloatDataBuffer)obj; - if (other.size() != size()) { - return false; - } - for (long idx = 0L; idx < size(); ++idx) { - if (other.getFloat(idx) != getFloat(idx)) { - return false; - } - } - return true; - } - - FloatDataBufferAdapter(S buffer, FloatDataLayout layout) { - super(buffer, layout); - this.layout = layout; - } - - private FloatDataLayout layout; -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/adapter/IntDataBufferAdapter.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/adapter/IntDataBufferAdapter.java deleted file mode 100644 index 052b63fe0f3..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/adapter/IntDataBufferAdapter.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * 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 - * - * 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.buffer.adapter; - -import org.tensorflow.ndarray.impl.buffer.Validator; -import org.tensorflow.ndarray.buffer.DataBuffer; -import org.tensorflow.ndarray.buffer.IntDataBuffer; -import org.tensorflow.ndarray.buffer.layout.IntDataLayout; - -class IntDataBufferAdapter> extends AbstractDataBufferAdapter - implements IntDataBuffer { - - @Override - public int getInt(long index) { - Validator.getArgs(this, index); - return layout.readInt(buffer(), index * layout.scale()); - } - - @Override - public IntDataBuffer setInt(int value, long index) { - Validator.setArgs(this, index); - layout.writeInt(buffer(), value, index * layout.scale()); - return this; - } - - @Override - public IntDataBuffer read(int[] dst, int offset, int length) { - Validator.readArgs(this, dst.length, offset, length); - for (int i = 0, j = offset; i < length; ++i, ++j) { - dst[j] = layout.readInt(buffer(), i * layout.scale()); - } - return this; - } - - @Override - public IntDataBuffer write(int[] src, int offset, int length) { - Validator.writeArgs(this, src.length, offset, length); - for (int i = 0, j = offset; i < length; ++i, ++j) { - layout.writeInt(buffer(), src[j], i * layout.scale()); - } - return this; - } - - @Override - public IntDataBuffer copyTo(DataBuffer dst, long size) { - Validator.copyToArgs(this, dst, size); - if (dst instanceof IntDataBuffer) { - IntDataBuffer intDst = (IntDataBuffer)dst; - for (long idx = 0L; idx < size; ++idx) { - intDst.setInt(getInt(idx), idx); - } - return this; - } - return slowCopyTo(dst, size); - } - - @Override - @SuppressWarnings("unchecked") - public IntDataBuffer offset(long index) { - return new IntDataBufferAdapter<>((S)buffer().offset(index * layout.scale()), layout); - } - - @Override - @SuppressWarnings("unchecked") - public IntDataBuffer narrow(long size) { - return new IntDataBufferAdapter<>((S)buffer().narrow(size * layout.scale()), layout); - } - - @Override - @SuppressWarnings("unchecked") - public IntDataBuffer slice(long index, long size) { - return new IntDataBufferAdapter<>((S)buffer().slice(index * layout.scale(), size * layout.scale()), layout); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (!(obj instanceof IntDataBuffer)) { - return super.equals(obj); - } - IntDataBuffer other = (IntDataBuffer)obj; - if (other.size() != size()) { - return false; - } - for (long idx = 0L; idx < size(); ++idx) { - if (other.getInt(idx) != getInt(idx)) { - return false; - } - } - return true; - } - - IntDataBufferAdapter(S buffer, IntDataLayout layout) { - super(buffer, layout); - this.layout = layout; - } - - private IntDataLayout layout; -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/adapter/LongDataBufferAdapter.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/adapter/LongDataBufferAdapter.java deleted file mode 100644 index aea154d4b9f..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/adapter/LongDataBufferAdapter.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * 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 - * - * 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.buffer.adapter; - -import org.tensorflow.ndarray.impl.buffer.Validator; -import org.tensorflow.ndarray.buffer.DataBuffer; -import org.tensorflow.ndarray.buffer.LongDataBuffer; -import org.tensorflow.ndarray.buffer.layout.LongDataLayout; - -class LongDataBufferAdapter> extends AbstractDataBufferAdapter - implements LongDataBuffer { - - @Override - public long getLong(long index) { - Validator.getArgs(this, index); - return layout.readLong(buffer(), index * layout.scale()); - } - - @Override - public LongDataBuffer setLong(long value, long index) { - Validator.setArgs(this, index); - layout.writeLong(buffer(), value, index * layout.scale()); - return this; - } - - @Override - public LongDataBuffer read(long[] dst, int offset, int length) { - Validator.readArgs(this, dst.length, offset, length); - for (int i = 0, j = offset; i < length; ++i, ++j) { - dst[j] = layout.readLong(buffer(), i * layout.scale()); - } - return this; - } - - @Override - public LongDataBuffer write(long[] src, int offset, int length) { - Validator.writeArgs(this, src.length, offset, length); - for (int i = 0, j = offset; i < length; ++i, ++j) { - layout.writeLong(buffer(), src[j], i * layout.scale()); - } - return this; - } - - @Override - public LongDataBuffer copyTo(DataBuffer dst, long size) { - Validator.copyToArgs(this, dst, size); - if (dst instanceof LongDataBuffer) { - LongDataBuffer longDst = (LongDataBuffer)dst; - for (long idx = 0L; idx < size; ++idx) { - longDst.setLong(getLong(idx), idx); - } - return this; - } - return slowCopyTo(dst, size); - } - - @Override - @SuppressWarnings("unchecked") - public LongDataBuffer offset(long index) { - return new LongDataBufferAdapter<>((S)buffer().offset(index * layout.scale()), layout); - } - - @Override - @SuppressWarnings("unchecked") - public LongDataBuffer narrow(long size) { - return new LongDataBufferAdapter<>((S)buffer().narrow(size * layout.scale()), layout); - } - - @Override - @SuppressWarnings("unchecked") - public LongDataBuffer slice(long index, long size) { - return new LongDataBufferAdapter<>((S)buffer().slice(index * layout.scale(), size * layout.scale()), layout); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (!(obj instanceof LongDataBuffer)) { - return super.equals(obj); - } - LongDataBuffer other = (LongDataBuffer)obj; - if (other.size() != size()) { - return false; - } - for (long idx = 0L; idx < size(); ++idx) { - if (other.getLong(idx) != getLong(idx)) { - return false; - } - } - return true; - } - - LongDataBufferAdapter(S buffer, LongDataLayout layout) { - super(buffer, layout); - this.layout = layout; - } - - private LongDataLayout layout; -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/adapter/ShortDataBufferAdapter.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/adapter/ShortDataBufferAdapter.java deleted file mode 100644 index 6b6b1bb24b7..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/adapter/ShortDataBufferAdapter.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * 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 - * - * 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.buffer.adapter; - -import org.tensorflow.ndarray.impl.buffer.Validator; -import org.tensorflow.ndarray.buffer.DataBuffer; -import org.tensorflow.ndarray.buffer.ShortDataBuffer; -import org.tensorflow.ndarray.buffer.layout.ShortDataLayout; - -class ShortDataBufferAdapter> extends AbstractDataBufferAdapter - implements ShortDataBuffer { - - @Override - public short getShort(long index) { - Validator.getArgs(this, index); - return layout.readShort(buffer(), index * layout.scale()); - } - - @Override - public ShortDataBuffer setShort(short value, long index) { - Validator.setArgs(this, index); - layout.writeShort(buffer(), value, index * layout.scale()); - return this; - } - - @Override - public ShortDataBuffer read(short[] dst, int offset, int length) { - Validator.readArgs(this, dst.length, offset, length); - for (int i = 0, j = offset; i < length; ++i, ++j) { - dst[j] = layout.readShort(buffer(), i * layout.scale()); - } - return this; - } - - @Override - public ShortDataBuffer write(short[] src, int offset, int length) { - Validator.writeArgs(this, src.length, offset, length); - for (int i = 0, j = offset; i < length; ++i, ++j) { - layout.writeShort(buffer(), src[j], i * layout.scale()); - } - return this; - } - - @Override - public ShortDataBuffer copyTo(DataBuffer dst, long size) { - Validator.copyToArgs(this, dst, size); - if (dst instanceof ShortDataBuffer) { - ShortDataBuffer shortDst = (ShortDataBuffer)dst; - for (long idx = 0L; idx < size; ++idx) { - shortDst.setShort(getShort(idx), idx); - } - return this; - } - return slowCopyTo(dst, size); - } - - @Override - @SuppressWarnings("unchecked") - public ShortDataBuffer offset(long index) { - return new ShortDataBufferAdapter<>((S)buffer().offset(index * layout.scale()), layout); - } - - @Override - @SuppressWarnings("unchecked") - public ShortDataBuffer narrow(long size) { - return new ShortDataBufferAdapter<>((S)buffer().narrow(size * layout.scale()), layout); - } - - @Override - @SuppressWarnings("unchecked") - public ShortDataBuffer slice(long index, long size) { - return new ShortDataBufferAdapter<>((S)buffer().slice(index * layout.scale(), size * layout.scale()), layout); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (!(obj instanceof ShortDataBuffer)) { - return super.equals(obj); - } - ShortDataBuffer other = (ShortDataBuffer)obj; - if (other.size() != size()) { - return false; - } - for (long idx = 0L; idx < size(); ++idx) { - if (other.getShort(idx) != getShort(idx)) { - return false; - } - } - return true; - } - - ShortDataBufferAdapter(S buffer, ShortDataLayout layout) { - super(buffer, layout); - this.layout = layout; - } - - private ShortDataLayout layout; -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/layout/Bfloat16Layout.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/layout/Bfloat16Layout.java deleted file mode 100644 index 3f8171088cb..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/layout/Bfloat16Layout.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2020 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.buffer.layout; - -import org.tensorflow.ndarray.buffer.ShortDataBuffer; -import org.tensorflow.ndarray.buffer.layout.FloatDataLayout; - -/** - * Data layout that converts 32-bit floats from/to 16-bit, truncating their mantissa to 7 bits but - * preserving the 8-bit exponent with the same bias. - */ -public final class Bfloat16Layout implements FloatDataLayout { - - @Override - public void writeFloat(ShortDataBuffer buffer, float value, long index) { - buffer.setShort(float32to16(value), index); - } - - @Override - public float readFloat(ShortDataBuffer buffer, long index) { - return float16to32(buffer.getShort(index)); - } - - // - // FLOAT 32-bit to/from BFLOAT 16-bit conversions - // - // We simply shift the value from 32-bit to 16-bit and vice-versa. NaN special case is ignored. - // - - // VisibleForTesting - static short float32to16(float f32) { - return (short)(Float.floatToIntBits(f32) >>> 16); - } - - // Visible for testing - static float float16to32(short i16) { - return Float.intBitsToFloat((int)i16 << 16); - } -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/layout/BoolLayout.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/layout/BoolLayout.java deleted file mode 100644 index 0358d60d662..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/layout/BoolLayout.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2020 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.buffer.layout; - -import org.tensorflow.ndarray.buffer.ByteDataBuffer; -import org.tensorflow.ndarray.buffer.layout.BooleanDataLayout; - -/** - * Data layout that converts booleans from/to bytes. - */ -public final class BoolLayout implements BooleanDataLayout { - - @Override - public void writeBoolean(ByteDataBuffer buffer, boolean value, long index) { - buffer.setByte(booleanToByte(value), index); - } - - @Override - public boolean readBoolean(ByteDataBuffer buffer, long index) { - return byteToBoolean(buffer.getByte(index)); - } - - // Visible for testing - static byte booleanToByte(boolean b) { - return (byte)(b ? 0x1 : 0x0); - } - - // Visible for testing - static boolean byteToBoolean(byte b) { - return b != 0x0; - } -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/layout/Float16Layout.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/layout/Float16Layout.java deleted file mode 100644 index b19744bbd13..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/layout/Float16Layout.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright 2020 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.buffer.layout; - -import org.tensorflow.ndarray.buffer.ShortDataBuffer; -import org.tensorflow.ndarray.buffer.layout.FloatDataLayout; - -/** - * Data layout that converts 32-bit floats from/to 16-bit, accordingly to the IEEE-754 half-precision - * floating point specification. - */ -public final class Float16Layout implements FloatDataLayout { - - @Override - public void writeFloat(ShortDataBuffer buffer, float value, long index) { - buffer.setShort(float32to16(value), index); - } - - @Override - public float readFloat(ShortDataBuffer buffer, long index) { - return float16to32(buffer.getShort(index)); - } - - // - // FLOAT 32-bit to/from 16-bit conversions - // - // The following conversion algorithms are issued from the C++ implementation found in the - // Eigen library used by TensorFlow native library. - // See https://eigen.tuxfamily.org/dox-devel/Half_8h_source.html for more details. - // - - // VisibleForTesting - static short float32to16(float f32) { - int i16; - int i32 = Float.floatToIntBits(f32); - short sign16 = (short) ((i32 >>> 16) & 0x8000); - i32 &= 0x7FFFFFFF; // remove sign - - if (i32 >= (E32BIAS + E16MAX + 1) << E32SHIFT) { - // float32 value is higher than float16 max value (max16 -> 2^15 * 2 -> 2^16) - // - if float32 value is higher than infinite (i.e. s32 > 0), then it is NaN and should also - // be NaN in float16 (0x7e00) - // - else, float16 value is forced to infinite (0x7c00) - i16 = i32 > E32MASK ? 0x7E00 : 0x7C00; - - } else if (i32 < (E32BIAS + E16MIN) << E32SHIFT){ - // float32 abs value is smaller than float16 min abs value (min16 = 2^-14), could also be 0 - // - apply magic number to align significand 10 bits at the bottom on the float and subtract bias - i16 = Float.floatToIntBits(Float.intBitsToFloat(i32) + MAGIC_32_16_FLOAT) - MAGIC_32_16; - - } else { - // float32 value can be rounded up to a normalized float16 value (i.e. exp32 = [113(-14), 142(15)]) - // - rebase exponent to float16 - // - round up significand to the 13nd bit if s16 is even, on the 12nd bit if it is odd - int round = 0xFFF + ((i32 >>> 13) & 0x1); - i16 = (i32 + ((E16BIAS - E32BIAS) << E32SHIFT) + round) >>> 13; - } - return (short)(i16 | sign16); - } - - // Visible for testing - static float float16to32(short i16) { - int i32 = (i16 & 0x7FFF) << (S32BITS - S16BITS); // remove sign and align in float32 - i32 += (E32BIAS - E16BIAS) << E32SHIFT; // rebase exponent to float32 - - // Handle float16 exponent special cases - switch (i16 & E16MASK) { - case E16MASK: - // float16 value is infinite or NaN - // - adjust float32 exponent one more time - i32 += (E32BIAS - E16BIAS) << E32SHIFT; - break; - case 0x0: - // float16 value is zero or subnormal - // - adjust float32 exponent - // - renormalize using magic number - i32 = Float.floatToIntBits(Float.intBitsToFloat(i32 + (1 << E32SHIFT)) - MAGIC_16_32_FLOAT); - break; - default: - break; - } - return Float.intBitsToFloat(i32 | ((i16 & 0x8000) << 16)); // reapply sign - } - - // float32 format - private static final int E32SHIFT = 23; // position of the exponent in float32 - private static final int E32MASK = 0xFF << E32SHIFT; // mask for float32 exponent (== Infinity) - private static final int E32BIAS = 127; // exponent bias for float32 - private static final int S32BITS = 23; // number of bits in float32 significand - - // float16 format - private static final int E16SHIFT = 10; // position of the exponent in float16 - private static final int E16MASK = 0x1F << E16SHIFT; // mask for float16 exponent (== Infinity) - private static final int E16BIAS = 15; // exponent bias for float16 - private static final int E16MAX = 15; // max value for float16 exponent - private static final int E16MIN = -14; // min value for float16 exponent - private static final int S16BITS = 10; // number of bits in float16 significand - - // magic numbers used when converting denormalized values - private static final int MAGIC_32_16 = ((E32BIAS - E16BIAS) + (S32BITS - S16BITS) + 1) << E32SHIFT; - private static final float MAGIC_32_16_FLOAT = Float.intBitsToFloat(MAGIC_32_16); - private static final int MAGIC_16_32 = (E32BIAS - E16BIAS + 1) << E32SHIFT; - private static final float MAGIC_16_32_FLOAT = Float.intBitsToFloat(MAGIC_16_32); -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/layout/StringLayout.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/layout/StringLayout.java deleted file mode 100644 index 51576c0100b..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/layout/StringLayout.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2020 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.buffer.layout; - -import java.nio.charset.Charset; -import org.tensorflow.ndarray.buffer.DataBuffer; -import org.tensorflow.ndarray.buffer.layout.DataLayout; - -/** - * Data layout that converts a String to/from a sequence of bytes applying a given charset. - */ -public final class StringLayout implements DataLayout, String> { - - public static StringLayout of(Charset charset) { - return new StringLayout(charset); - } - - @Override - public void writeObject(DataBuffer buffer, String value, long index) { - buffer.setObject(value.getBytes(charset), index); - } - - @Override - public String readObject(DataBuffer buffer, long index) { - return new String(buffer.getObject(index), charset); - } - - private StringLayout(Charset charset) { - this.charset = charset; - } - - private final Charset charset; -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/misc/ArrayDataBuffer.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/misc/ArrayDataBuffer.java deleted file mode 100644 index 676e291357a..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/misc/ArrayDataBuffer.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - 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 - - 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.buffer.misc; - -import java.util.Arrays; -import org.tensorflow.ndarray.impl.buffer.AbstractDataBuffer; -import org.tensorflow.ndarray.impl.buffer.Validator; -import org.tensorflow.ndarray.buffer.DataBuffer; -import org.tensorflow.ndarray.buffer.DataStorageVisitor; - -class ArrayDataBuffer extends AbstractDataBuffer { - - @Override - public long size() { - return length; - } - - @Override - public boolean isReadOnly() { - return readOnly; - } - - @Override - public T getObject(long index) { - Validator.getArgs(this, index); - return values[(int)index + offset]; - } - - @Override - public DataBuffer setObject(T value, long index) { - Validator.setArgs(this, index); - values[(int)index + offset] = value; - return this; - } - - @Override - public DataBuffer copyTo(DataBuffer dst, long size) { - Validator.copyToArgs(this, dst, size); - return dst.accept(new DataStorageVisitor>() { - - @Override - public DataBuffer visit(Object[] array, int arrayOffset, int arrayLength) { - System.arraycopy(values, offset, array, arrayOffset, (int)size); - return ArrayDataBuffer.this; - } - - @Override - public DataBuffer fallback() { - for (int idx = 0; idx < size; ++idx) { - dst.setObject(values[idx + offset], idx); - } - return ArrayDataBuffer.this; - } - }); - } - - @Override - public DataBuffer slice(long index, long size) { - Validator.sliceArgs(this, index, size); - return new ArrayDataBuffer<>(values, readOnly, offset + (int)index, (int)size); - } - - @Override - public R accept(DataStorageVisitor visitor) { - return visitor.visit(values, offset, length); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (!(obj instanceof DataBuffer)) { - return false; - } - DataBuffer other = (DataBuffer)obj; - if (size() != other.size()) { - return false; - } - return other.accept(new DataStorageVisitor() { - - @Override - public Boolean visit(Object[] array, int arrayOffset, int arrayLength) { - if (offset == 0 && values.length == length && arrayOffset == 0 && array.length == arrayLength) { - return Arrays.deepEquals(array, values); - } - return slowEquals(other); - } - - @Override - public Boolean fallback() { - return slowEquals(other); - } - }); - } - - ArrayDataBuffer(T[] values, boolean readOnly) { - this(values, readOnly, 0, values.length); - } - - private ArrayDataBuffer(T[] values, boolean readOnly, int offset, int length) { - this.values = values; - this.readOnly = readOnly; - this.offset = offset; - this.length = length; - } - - private final T[] values; - private final boolean readOnly; - private final int offset; - private final int length; -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/misc/BitSetDataBuffer.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/misc/BitSetDataBuffer.java deleted file mode 100644 index 5b5ec15294b..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/misc/BitSetDataBuffer.java +++ /dev/null @@ -1,183 +0,0 @@ -/* - * 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 - * - * 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.buffer.misc; - -import java.util.BitSet; -import org.tensorflow.ndarray.impl.buffer.AbstractDataBuffer; -import org.tensorflow.ndarray.impl.buffer.Validator; -import org.tensorflow.ndarray.buffer.BooleanDataBuffer; -import org.tensorflow.ndarray.buffer.DataBuffer; -import org.tensorflow.ndarray.buffer.DataStorageVisitor; - -class BitSetDataBuffer extends AbstractDataBuffer implements BooleanDataBuffer { - - @Override - public long size() { - return numBits; - } - - @Override - public boolean isReadOnly() { - return readOnly; - } - - @Override - public boolean getBoolean(long index) { - Validator.getArgs(this, index); - return bitSet.get((int)index + offset); - } - - @Override - public BooleanDataBuffer setBoolean(boolean value, long index) { - Validator.setArgs(this, index); - bitSet.set((int)index + offset, value); - return this; - } - - @Override - public BooleanDataBuffer read(boolean[] dst, int offset, int length) { - Validator.readArgs(this, dst.length, offset, length); - for (int i = this.offset, j = offset; i < this.offset + length; ++i, ++j) { - dst[j] = bitSet.get(i); - } - return this; - } - - @Override - public BooleanDataBuffer write(boolean[] src, int offset, int length) { - Validator.readArgs(this, src.length, offset, length); - for (int i = this.offset, j = offset; i < this.offset + length; ++i, ++j) { - bitSet.set(i, src[j]); - } - return this; - } - - @Override - public BooleanDataBuffer copyTo(DataBuffer dst, long size) { - Validator.copyToArgs(this, dst, size); - return dst.accept(new DataStorageVisitor() { - - @Override - public BooleanDataBuffer visit(boolean[] array, int arrayOffset, int arrayLength) { - for (int idx = 0; idx < size; ++idx) { - array[idx + arrayOffset] = bitSet.get(idx + offset); - } - return BitSetDataBuffer.this; - } - - @Override - public BooleanDataBuffer visit(BitSet dstBitSet, int dstOffset, long numBits) { - for (int idx = 0; idx < size; ++idx) { - dstBitSet.set(idx + dstOffset, bitSet.get(idx + offset)); - } - return BitSetDataBuffer.this; - } - - @Override - public BooleanDataBuffer fallback() { - if (dst instanceof BooleanDataBuffer) { - BooleanDataBuffer booleanDst = (BooleanDataBuffer)dst; - for (int idx = 0; idx < size; ++idx) { - booleanDst.setBoolean(bitSet.get(idx + offset), idx); - } - } else { - for (int idx = 0; idx < size; ++idx) { - dst.setObject(bitSet.get(idx + offset), idx); - } - } - return BitSetDataBuffer.this; - } - }); - } - - @Override - public BooleanDataBuffer slice(long index, long size) { - Validator.sliceArgs(this, index, size); - return new BitSetDataBuffer(bitSet, size, readOnly, offset + (int)index); - } - - @Override - public R accept(DataStorageVisitor visitor) { - return visitor.visit(bitSet, offset, numBits); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (!(obj instanceof BooleanDataBuffer)) { - return super.equals(obj); - } - BooleanDataBuffer other = (BooleanDataBuffer)obj; - if (size() != other.size()) { - return false; - } - return other.accept(new DataStorageVisitor() { - - @Override - public Boolean visit(boolean[] array, int arrayOffset, int length) { - for (int idx = 0; idx < size(); ++idx) { - if (array[idx + arrayOffset] != bitSet.get(idx + offset)) { - return false; - } - } - return true; - } - - @Override - public Boolean visit(BitSet otherBitSet, int otherOffset, long otherNumBits) { - if (offset == 0 && otherOffset == 0 && numBits == otherNumBits) { - return bitSet.equals(otherBitSet); - } - for (int idx = 0; idx < size(); ++idx) { - if (otherBitSet.get(idx + otherOffset) != bitSet.get(idx + offset)) { - return false; - } - } - return true; - } - - @Override - public Boolean fallback() { - for (int idx = 0; idx < size(); ++idx) { - if (other.getBoolean(idx) != bitSet.get(idx + offset)) { - return false; - } - } - return true; - } - }); - } - - BitSetDataBuffer(BitSet bitSet, long numBits, boolean readOnly) { - this(bitSet, numBits, readOnly, 0); - } - - private BitSetDataBuffer(BitSet bitSet, long numBits, boolean readOnly, int offset) { - this.bitSet = bitSet; - this.numBits = numBits; - this.readOnly = readOnly; - this.offset = offset; - } - - private final BitSet bitSet; - private final long numBits; - private final boolean readOnly; - private final int offset; -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/misc/BooleanArrayDataBuffer.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/misc/BooleanArrayDataBuffer.java deleted file mode 100644 index f8d033519ec..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/misc/BooleanArrayDataBuffer.java +++ /dev/null @@ -1,176 +0,0 @@ -/* - 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 - - 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.buffer.misc; - -import java.util.Arrays; -import java.util.BitSet; -import org.tensorflow.ndarray.impl.buffer.AbstractDataBuffer; -import org.tensorflow.ndarray.impl.buffer.Validator; -import org.tensorflow.ndarray.buffer.BooleanDataBuffer; -import org.tensorflow.ndarray.buffer.DataBuffer; -import org.tensorflow.ndarray.buffer.DataStorageVisitor; - -class BooleanArrayDataBuffer extends AbstractDataBuffer implements - BooleanDataBuffer { - - @Override - public long size() { - return length; - } - - @Override - public boolean isReadOnly() { - return readOnly; - } - - @Override - public boolean getBoolean(long index) { - Validator.getArgs(this, index); - return values[(int)index + offset]; - } - - @Override - public BooleanDataBuffer setBoolean(boolean value, long index) { - Validator.setArgs(this, index); - values[(int)index + offset] = value; - return this; - } - - @Override - public BooleanDataBuffer read(boolean[] dst, int offset, int length) { - System.arraycopy(values, this.offset, dst, offset, length); - return this; - } - - @Override - public BooleanDataBuffer write(boolean[] src, int offset, int length) { - System.arraycopy(src, offset, values, this.offset, length); - return null; - } - - @Override - public BooleanDataBuffer copyTo(DataBuffer dst, long size) { - Validator.copyToArgs(this, dst, size); - return dst.accept(new DataStorageVisitor() { - - @Override - public BooleanDataBuffer visit(boolean[] array, int arrayOffset, int arrayLength) { - System.arraycopy(values, offset, array, arrayOffset, (int)size); - return BooleanArrayDataBuffer.this; - } - - @Override - public BooleanDataBuffer visit(BitSet bitSet, int bitSetOffset, long numBits) { - for (int idx = 0; idx < size; ++idx) { - bitSet.set(idx + bitSetOffset, values[idx + offset]); - } - return BooleanArrayDataBuffer.this; - } - - @Override - public BooleanDataBuffer fallback() { - if (dst instanceof BooleanDataBuffer) { - BooleanDataBuffer booleanDst = (BooleanDataBuffer)dst; - for (int idx = 0; idx < size; ++idx) { - booleanDst.setBoolean(values[idx + offset], idx); - } - } else { - for (int idx = 0; idx < size; ++idx) { - dst.setObject(values[idx + offset], idx); - } - } - return BooleanArrayDataBuffer.this; - } - }); - } - - @Override - public BooleanDataBuffer slice(long index, long size) { - Validator.sliceArgs(this, index, size); - return new BooleanArrayDataBuffer(values, readOnly, offset + (int)index, (int)size); - } - - @Override - public R accept(DataStorageVisitor visitor) { - return visitor.visit(values, offset, length); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (!(obj instanceof BooleanDataBuffer)) { - return super.equals(obj); - } - BooleanDataBuffer other = (BooleanDataBuffer)obj; - if (size() != other.size()) { - return false; - } - return other.accept(new DataStorageVisitor() { - - @Override - public Boolean visit(boolean[] array, int arrayOffset, int arrayLength) { - if (offset == 0 && values.length == length && arrayOffset == 0 && array.length == arrayLength) { - return Arrays.equals(array, values); - } - for (int idx = 0; idx < size(); ++idx) { - if (array[idx + arrayOffset] != values[idx + offset]) { - return false; - } - } - return true; - } - - @Override - public Boolean visit(BitSet bitSet, int bitSetOffset, long numBits) { - for (int idx = 0; idx < size(); ++idx) { - if (bitSet.get(idx + bitSetOffset) != values[idx + offset]) { - return false; - } - } - return true; - } - - @Override - public Boolean fallback() { - for (int idx = 0; idx < size(); ++idx) { - if (other.getBoolean(idx) != values[idx + offset]) { - return false; - } - } - return true; - } - }); - } - - BooleanArrayDataBuffer(boolean[] values, boolean readOnly) { - this(values, readOnly, 0, values.length); - } - - private BooleanArrayDataBuffer(boolean[] values, boolean readOnly, int offset, int length) { - this.values = values; - this.readOnly = readOnly; - this.offset = offset; - this.length = length; - } - - private final boolean[] values; - private final boolean readOnly; - private final int offset; - private final int length; -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/misc/MiscDataBufferFactory.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/misc/MiscDataBufferFactory.java deleted file mode 100644 index 84cfce6bc66..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/misc/MiscDataBufferFactory.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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 - * - * 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.buffer.misc; - -import java.util.BitSet; -import org.tensorflow.ndarray.buffer.BooleanDataBuffer; -import org.tensorflow.ndarray.buffer.DataBuffer; - -/** - * Factory of miscellaneous data buffers - */ -public class MiscDataBufferFactory { - - public static BooleanDataBuffer create(BitSet bitSet, long numBits, boolean readOnly) { - return new BitSetDataBuffer(bitSet, numBits, readOnly); - } - - public static BooleanDataBuffer create(boolean[] array, boolean readOnly) { - return new BooleanArrayDataBuffer(array, readOnly); - } - - public static DataBuffer create(T[] array, boolean readOnly) { - return new ArrayDataBuffer<>(array, readOnly); - } -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/nio/AbstractNioDataBuffer.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/nio/AbstractNioDataBuffer.java deleted file mode 100644 index 82bc981ad46..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/nio/AbstractNioDataBuffer.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - 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 - - 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.buffer.nio; - -import java.nio.Buffer; -import org.tensorflow.ndarray.impl.buffer.AbstractDataBuffer; - -/** - * Base class for all JDK-based data buffers. - * - * @param type of elements (or values) stored in this buffer - */ -abstract class AbstractNioDataBuffer extends AbstractDataBuffer { - - @Override - public long size() { - return buf().capacity(); - } - - @Override - public boolean isReadOnly() { - return buf().isReadOnly(); - } - - abstract Buffer buf(); -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/nio/ByteNioDataBuffer.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/nio/ByteNioDataBuffer.java deleted file mode 100644 index 5ede97cef78..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/nio/ByteNioDataBuffer.java +++ /dev/null @@ -1,185 +0,0 @@ -/* - 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 - - 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.buffer.nio; - -import java.nio.ByteBuffer; -import org.tensorflow.ndarray.impl.buffer.Validator; -import org.tensorflow.ndarray.impl.buffer.adapter.DataBufferAdapterFactory; -import org.tensorflow.ndarray.buffer.BooleanDataBuffer; -import org.tensorflow.ndarray.buffer.ByteDataBuffer; -import org.tensorflow.ndarray.buffer.DataBuffer; -import org.tensorflow.ndarray.buffer.DataStorageVisitor; -import org.tensorflow.ndarray.buffer.DoubleDataBuffer; -import org.tensorflow.ndarray.buffer.FloatDataBuffer; -import org.tensorflow.ndarray.buffer.IntDataBuffer; -import org.tensorflow.ndarray.buffer.LongDataBuffer; -import org.tensorflow.ndarray.buffer.ShortDataBuffer; -import org.tensorflow.ndarray.buffer.layout.DataLayouts; - -/** - * A buffer of bytes using a JDK {@link ByteBuffer} for storage. - */ -final class ByteNioDataBuffer extends AbstractNioDataBuffer - implements ByteDataBuffer { - - @Override - public byte getByte(long index) { - return buf.get((int)index); - } - - @Override - public ByteDataBuffer setByte(byte value, long index) { - buf.put((int)index, value); - return this; - } - - @Override - public ByteDataBuffer read(byte[] dst, int offset, int length) { - buf.duplicate().get(dst, offset, length); - return this; - } - - @Override - public ByteDataBuffer write(byte[] src, int offset, int length) { - buf.duplicate().put(src, offset, length); - return this; - } - - @Override - public ByteDataBuffer copyTo(DataBuffer dst, long size) { - Validator.copyToArgs(this, dst, size); - return dst.accept(new DataStorageVisitor() { - - @Override - public ByteDataBuffer visit(ByteBuffer buffer) { - buffer.duplicate().put((ByteBuffer)buf.duplicate().limit((int)size)); - return ByteNioDataBuffer.this; - } - - @Override - public ByteDataBuffer fallback() { - if (dst instanceof ByteDataBuffer) { - ByteDataBuffer byteDst = (ByteDataBuffer)dst; - for (long idx = 0L; idx < size; ++idx) { - byteDst.setByte(getByte(idx), idx); - } - return ByteNioDataBuffer.this; - } - return slowCopyTo(dst, size); - } - }); - } - - @Override - public IntDataBuffer asInts() { - return new IntNioDataBuffer(buf.asIntBuffer()); - } - - @Override - public ShortDataBuffer asShorts() { - return new ShortNioDataBuffer(buf.asShortBuffer()); - } - - @Override - public LongDataBuffer asLongs() { - return new LongNioDataBuffer(buf.asLongBuffer()); - } - - @Override - public FloatDataBuffer asFloats() { - return new FloatNioDataBuffer(buf.asFloatBuffer()); - } - - @Override - public DoubleDataBuffer asDoubles() { - return new DoubleNioDataBuffer(buf.asDoubleBuffer()); - } - - @Override - public BooleanDataBuffer asBooleans() { - return DataBufferAdapterFactory.create(this, DataLayouts.BOOL); - } - - @Override - public ByteDataBuffer offset(long index) { - Validator.offsetArgs(this, index); - return new ByteNioDataBuffer(((ByteBuffer)buf.duplicate().position((int)index)).slice()); - } - - @Override - public ByteDataBuffer narrow(long size) { - Validator.narrowArgs(this, size); - return new ByteNioDataBuffer(((ByteBuffer)buf.duplicate().limit((int)size)).slice()); - } - - @Override - public ByteDataBuffer slice(long index, long size) { - Validator.sliceArgs(this, index, size); - ByteBuffer sliceBuf = buf.duplicate(); - sliceBuf.position((int)index); - sliceBuf.limit((int)index + (int)size); - return new ByteNioDataBuffer(sliceBuf.slice()); - } - - @Override - public R accept(DataStorageVisitor visitor) { - return visitor.visit(buf); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (!(obj instanceof ByteDataBuffer)) { - return super.equals(obj); - } - ByteDataBuffer other = (ByteDataBuffer)obj; - if (size() != other.size()) { - return false; - } - return other.accept(new DataStorageVisitor() { - - @Override - public Boolean visit(ByteBuffer buffer) { - return buf.equals(buffer); - } - - @Override - public Boolean fallback() { - for (int idx = 0; idx < size(); ++idx) { - if (other.getByte(idx) != getByte(idx)) { - return false; - } - } - return true; - } - }); - } - - @Override - ByteBuffer buf() { - return buf; - } - - ByteNioDataBuffer(ByteBuffer buf) { - this.buf = buf; - } - - private ByteBuffer buf; -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/nio/DoubleNioDataBuffer.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/nio/DoubleNioDataBuffer.java deleted file mode 100644 index bddc5db1e3f..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/nio/DoubleNioDataBuffer.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - 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 - - 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.buffer.nio; - -import java.nio.DoubleBuffer; -import org.tensorflow.ndarray.impl.buffer.Validator; -import org.tensorflow.ndarray.buffer.DataBuffer; -import org.tensorflow.ndarray.buffer.DataStorageVisitor; -import org.tensorflow.ndarray.buffer.DoubleDataBuffer; - -/** - * A buffer of bytes using a JDK {@link DoubleBuffer} for storage. - */ -final class DoubleNioDataBuffer extends AbstractNioDataBuffer - implements DoubleDataBuffer { - - @Override - public double getDouble(long index) { - return buf.get((int)index); - } - - @Override - public DoubleDataBuffer setDouble(double value, long index) { - buf.put((int)index, value); - return this; - } - - @Override - public DoubleDataBuffer read(double[] dst, int offset, int length) { - buf.duplicate().get(dst, offset, length); - return this; - } - - @Override - public DoubleDataBuffer write(double[] src, int offset, int length) { - buf.duplicate().put(src, offset, length); - return this; - } - - @Override - public DoubleDataBuffer copyTo(DataBuffer dst, long size) { - Validator.copyToArgs(this, dst, size); - return dst.accept(new DataStorageVisitor() { - - @Override - public DoubleDataBuffer visit(DoubleBuffer buffer) { - buffer.duplicate().put((DoubleBuffer)buf.duplicate().limit((int)size)); - return DoubleNioDataBuffer.this; - } - - @Override - public DoubleDataBuffer fallback() { - if (dst instanceof DoubleDataBuffer) { - DoubleDataBuffer doubleDst = (DoubleDataBuffer)dst; - for (long idx = 0L; idx < size; ++idx) { - doubleDst.setDouble(getDouble(idx), idx); - } - return DoubleNioDataBuffer.this; - } - return slowCopyTo(dst, size); - } - }); - } - - @Override - public DoubleDataBuffer offset(long index) { - Validator.offsetArgs(this, index); - return new DoubleNioDataBuffer(((DoubleBuffer)buf.duplicate().position((int)index)).slice()); - } - - @Override - public DoubleDataBuffer narrow(long size) { - Validator.narrowArgs(this, size); - return new DoubleNioDataBuffer(((DoubleBuffer)buf.duplicate().limit((int)size)).slice()); - } - - @Override - public DoubleDataBuffer slice(long index, long size) { - Validator.sliceArgs(this, index, size); - DoubleBuffer sliceBuf = buf.duplicate(); - sliceBuf.position((int)index); - sliceBuf.limit((int)index + (int)size); - return new DoubleNioDataBuffer(sliceBuf.slice()); - } - - @Override - public R accept(DataStorageVisitor visitor) { - return visitor.visit(buf); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (!(obj instanceof DoubleDataBuffer)) { - return super.equals(obj); - } - DoubleDataBuffer other = (DoubleDataBuffer)obj; - if (size() != other.size()) { - return false; - } - return other.accept(new DataStorageVisitor() { - - @Override - public Boolean visit(DoubleBuffer buffer) { - return buf.equals(buffer); - } - - @Override - public Boolean fallback() { - for (int idx = 0; idx < size(); ++idx) { - if (other.getDouble(idx) != getDouble(idx)) { - return false; - } - } - return true; - } - }); - } - - @Override - DoubleBuffer buf() { - return buf; - } - - DoubleNioDataBuffer(DoubleBuffer buf) { - this.buf = buf; - } - - private DoubleBuffer buf; -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/nio/FloatNioDataBuffer.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/nio/FloatNioDataBuffer.java deleted file mode 100644 index 06a9a31b56a..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/nio/FloatNioDataBuffer.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - 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 - - 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.buffer.nio; - -import java.nio.FloatBuffer; -import org.tensorflow.ndarray.impl.buffer.Validator; -import org.tensorflow.ndarray.buffer.DataBuffer; -import org.tensorflow.ndarray.buffer.DataStorageVisitor; -import org.tensorflow.ndarray.buffer.FloatDataBuffer; - -/** - * A buffer of bytes using a JDK {@link FloatBuffer} for storage. - */ -final class FloatNioDataBuffer extends AbstractNioDataBuffer - implements FloatDataBuffer { - - @Override - public float getFloat(long index) { - return buf.get((int)index); - } - - @Override - public FloatDataBuffer setFloat(float value, long index) { - buf.put((int)index, value); - return this; - } - - @Override - public FloatDataBuffer read(float[] dst, int offset, int length) { - buf.duplicate().get(dst, offset, length); - return this; - } - - @Override - public FloatDataBuffer write(float[] src, int offset, int length) { - buf.duplicate().put(src, offset, length); - return this; - } - - @Override - public FloatDataBuffer copyTo(DataBuffer dst, long size) { - Validator.copyToArgs(this, dst, size); - return dst.accept(new DataStorageVisitor() { - - @Override - public FloatDataBuffer visit(FloatBuffer buffer) { - buffer.duplicate().put((FloatBuffer)buf.duplicate().limit((int)size)); - return FloatNioDataBuffer.this; - } - - @Override - public FloatDataBuffer fallback() { - if (dst instanceof FloatDataBuffer) { - FloatDataBuffer floatDst = (FloatDataBuffer)dst; - for (long idx = 0L; idx < size; ++idx) { - floatDst.setFloat(getFloat(idx), idx); - } - return FloatNioDataBuffer.this; - } - return slowCopyTo(dst, size); - } - }); - } - - @Override - public FloatDataBuffer offset(long index) { - Validator.offsetArgs(this, index); - return new FloatNioDataBuffer(((FloatBuffer)buf.duplicate().position((int)index)).slice()); - } - - @Override - public FloatDataBuffer narrow(long size) { - Validator.narrowArgs(this, size); - return new FloatNioDataBuffer(((FloatBuffer)buf.duplicate().limit((int)size)).slice()); - } - - @Override - public FloatDataBuffer slice(long index, long size) { - Validator.sliceArgs(this, index, size); - FloatBuffer sliceBuf = buf.duplicate(); - sliceBuf.position((int)index); - sliceBuf.limit((int)index + (int)size); - return new FloatNioDataBuffer(sliceBuf.slice()); - } - - @Override - public R accept(DataStorageVisitor visitor) { - return visitor.visit(buf); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (!(obj instanceof FloatDataBuffer)) { - return super.equals(obj); - } - FloatDataBuffer other = (FloatDataBuffer)obj; - if (size() != other.size()) { - return false; - } - return other.accept(new DataStorageVisitor() { - - @Override - public Boolean visit(FloatBuffer buffer) { - return buf.equals(buffer); - } - - @Override - public Boolean fallback() { - for (int idx = 0; idx < size(); ++idx) { - if (other.getFloat(idx) != getFloat(idx)) { - return false; - } - } - return true; - } - }); - } - - @Override - FloatBuffer buf() { - return buf; - } - - FloatNioDataBuffer(FloatBuffer buf) { - this.buf = buf; - } - - private FloatBuffer buf; -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/nio/IntNioDataBuffer.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/nio/IntNioDataBuffer.java deleted file mode 100644 index cea729e86a7..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/nio/IntNioDataBuffer.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - 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 - - 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.buffer.nio; - -import java.nio.IntBuffer; -import org.tensorflow.ndarray.impl.buffer.Validator; -import org.tensorflow.ndarray.buffer.DataBuffer; -import org.tensorflow.ndarray.buffer.DataStorageVisitor; -import org.tensorflow.ndarray.buffer.IntDataBuffer; - -/** - * A buffer of bytes using a JDK {@link IntBuffer} for storage. - */ -final class IntNioDataBuffer extends AbstractNioDataBuffer - implements IntDataBuffer { - - @Override - public int getInt(long index) { - return buf.get((int)index); - } - - @Override - public IntDataBuffer setInt(int value, long index) { - buf.put((int)index, value); - return this; - } - - @Override - public IntDataBuffer read(int[] dst, int offset, int length) { - buf.duplicate().get(dst, offset, length); - return this; - } - - @Override - public IntDataBuffer write(int[] src, int offset, int length) { - buf.duplicate().put(src, offset, length); - return this; - } - - @Override - public IntDataBuffer copyTo(DataBuffer dst, long size) { - Validator.copyToArgs(this, dst, size); - return dst.accept(new DataStorageVisitor() { - - @Override - public IntDataBuffer visit(IntBuffer buffer) { - buffer.duplicate().put((IntBuffer)buf.duplicate().limit((int)size)); - return IntNioDataBuffer.this; - } - - @Override - public IntDataBuffer fallback() { - if (dst instanceof IntDataBuffer) { - IntDataBuffer intDst = (IntDataBuffer)dst; - for (long idx = 0L; idx < size; ++idx) { - intDst.setInt(getInt(idx), idx); - } - return IntNioDataBuffer.this; - } - return slowCopyTo(dst, size); - } - }); - } - - @Override - public IntDataBuffer offset(long index) { - Validator.offsetArgs(this, index); - return new IntNioDataBuffer(((IntBuffer)buf.duplicate().position((int)index)).slice()); - } - - @Override - public IntDataBuffer narrow(long size) { - Validator.narrowArgs(this, size); - return new IntNioDataBuffer(((IntBuffer)buf.duplicate().limit((int)size)).slice()); - } - - @Override - public IntDataBuffer slice(long index, long size) { - Validator.sliceArgs(this, index, size); - IntBuffer sliceBuf = buf.duplicate(); - sliceBuf.position((int)index); - sliceBuf.limit((int)index + (int)size); - return new IntNioDataBuffer(sliceBuf.slice()); - } - - @Override - public R accept(DataStorageVisitor visitor) { - return visitor.visit(buf); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (!(obj instanceof IntDataBuffer)) { - return super.equals(obj); - } - IntDataBuffer other = (IntDataBuffer)obj; - if (size() != other.size()) { - return false; - } - return other.accept(new DataStorageVisitor() { - - @Override - public Boolean visit(IntBuffer buffer) { - return buf.equals(buffer); - } - - @Override - public Boolean fallback() { - for (int idx = 0; idx < size(); ++idx) { - if (other.getInt(idx) != getInt(idx)) { - return false; - } - } - return true; - } - }); - } - - @Override - IntBuffer buf() { - return buf; - } - - IntNioDataBuffer(IntBuffer buf) { - this.buf = buf; - } - - private IntBuffer buf; -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/nio/LongNioDataBuffer.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/nio/LongNioDataBuffer.java deleted file mode 100644 index 7231ee7d408..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/nio/LongNioDataBuffer.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - 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 - - 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.buffer.nio; - -import java.nio.LongBuffer; -import org.tensorflow.ndarray.impl.buffer.Validator; -import org.tensorflow.ndarray.buffer.DataBuffer; -import org.tensorflow.ndarray.buffer.DataStorageVisitor; -import org.tensorflow.ndarray.buffer.LongDataBuffer; - -/** - * A buffer of bytes using a JDK {@link LongBuffer} for storage. - */ -final class LongNioDataBuffer extends AbstractNioDataBuffer - implements LongDataBuffer { - - @Override - public long getLong(long index) { - return buf.get((int)index); - } - - @Override - public LongDataBuffer setLong(long value, long index) { - buf.put((int)index, value); - return this; - } - - @Override - public LongDataBuffer read(long[] dst, int offset, int length) { - buf.duplicate().get(dst, offset, length); - return this; - } - - @Override - public LongDataBuffer write(long[] src, int offset, int length) { - buf.duplicate().put(src, offset, length); - return this; - } - - @Override - public LongDataBuffer copyTo(DataBuffer dst, long size) { - Validator.copyToArgs(this, dst, size); - return dst.accept(new DataStorageVisitor() { - - @Override - public LongDataBuffer visit(LongBuffer buffer) { - buffer.duplicate().put((LongBuffer)buf.duplicate().limit((int)size)); - return LongNioDataBuffer.this; - } - - @Override - public LongDataBuffer fallback() { - if (dst instanceof LongDataBuffer) { - LongDataBuffer longDst = (LongDataBuffer)dst; - for (long idx = 0L; idx < size; ++idx) { - longDst.setLong(getLong(idx), idx); - } - return LongNioDataBuffer.this; - } - return slowCopyTo(dst, size); - } - }); - } - - @Override - public LongDataBuffer offset(long index) { - Validator.offsetArgs(this, index); - return new LongNioDataBuffer(((LongBuffer)buf.duplicate().position((int)index)).slice()); - } - - @Override - public LongDataBuffer narrow(long size) { - Validator.narrowArgs(this, size); - return new LongNioDataBuffer(((LongBuffer)buf.duplicate().limit((int)size)).slice()); - } - - @Override - public LongDataBuffer slice(long index, long size) { - Validator.sliceArgs(this, index, size); - LongBuffer sliceBuf = buf.duplicate(); - sliceBuf.position((int)index); - sliceBuf.limit((int)index + (int)size); - return new LongNioDataBuffer(sliceBuf.slice()); - } - - @Override - public R accept(DataStorageVisitor visitor) { - return visitor.visit(buf); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (!(obj instanceof LongDataBuffer)) { - return super.equals(obj); - } - LongDataBuffer other = (LongDataBuffer)obj; - if (size() != other.size()) { - return false; - } - return other.accept(new DataStorageVisitor() { - - @Override - public Boolean visit(LongBuffer buffer) { - return buf.equals(buffer); - } - - @Override - public Boolean fallback() { - for (int idx = 0; idx < size(); ++idx) { - if (other.getLong(idx) != getLong(idx)) { - return false; - } - } - return true; - } - }); - } - - @Override - LongBuffer buf() { - return buf; - } - - LongNioDataBuffer(LongBuffer buf) { - this.buf = buf; - } - - private LongBuffer buf; -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/nio/NioDataBufferFactory.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/nio/NioDataBufferFactory.java deleted file mode 100644 index 4e84fc9bc17..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/nio/NioDataBufferFactory.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * 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 - * - * 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.buffer.nio; - -import java.nio.ByteBuffer; -import java.nio.DoubleBuffer; -import java.nio.FloatBuffer; -import java.nio.IntBuffer; -import java.nio.LongBuffer; -import java.nio.ShortBuffer; -import org.tensorflow.ndarray.buffer.ByteDataBuffer; -import org.tensorflow.ndarray.buffer.DoubleDataBuffer; -import org.tensorflow.ndarray.buffer.FloatDataBuffer; -import org.tensorflow.ndarray.buffer.IntDataBuffer; -import org.tensorflow.ndarray.buffer.LongDataBuffer; -import org.tensorflow.ndarray.buffer.ShortDataBuffer; - -/** - * Factory of JDK NIO-based data buffers - */ -public class NioDataBufferFactory { - - public static ByteDataBuffer create(ByteBuffer buffer) { - return new ByteNioDataBuffer(buffer); - } - - public static DoubleDataBuffer create(DoubleBuffer buffer) { - return new DoubleNioDataBuffer(buffer); - } - - public static FloatDataBuffer create(FloatBuffer buffer) { - return new FloatNioDataBuffer(buffer); - } - - public static IntDataBuffer create(IntBuffer buffer) { - return new IntNioDataBuffer(buffer); - } - - public static LongDataBuffer create(LongBuffer buffer) { - return new LongNioDataBuffer(buffer); - } - - public static ShortDataBuffer create(ShortBuffer buffer) { - return new ShortNioDataBuffer(buffer); - } -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/nio/ShortNioDataBuffer.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/nio/ShortNioDataBuffer.java deleted file mode 100644 index 776faa103c2..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/nio/ShortNioDataBuffer.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - 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 - - 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.buffer.nio; - -import java.nio.ShortBuffer; -import org.tensorflow.ndarray.impl.buffer.Validator; -import org.tensorflow.ndarray.buffer.DataBuffer; -import org.tensorflow.ndarray.buffer.DataStorageVisitor; -import org.tensorflow.ndarray.buffer.ShortDataBuffer; - -/** - * A buffer of bytes using a JDK {@link ShortBuffer} for storage. - */ -final class ShortNioDataBuffer extends AbstractNioDataBuffer - implements ShortDataBuffer { - - @Override - public short getShort(long index) { - return buf.get((int)index); - } - - @Override - public ShortDataBuffer setShort(short value, long index) { - buf.put((int)index, value); - return this; - } - - @Override - public ShortDataBuffer read(short[] dst, int offset, int length) { - buf.duplicate().get(dst, offset, length); - return this; - } - - @Override - public ShortDataBuffer write(short[] src, int offset, int length) { - buf.duplicate().put(src, offset, length); - return this; - } - - @Override - public ShortDataBuffer copyTo(DataBuffer dst, long size) { - Validator.copyToArgs(this, dst, size); - return dst.accept(new DataStorageVisitor() { - - @Override - public ShortDataBuffer visit(ShortBuffer buffer) { - buffer.duplicate().put((ShortBuffer)buf.duplicate().limit((int)size)); - return ShortNioDataBuffer.this; - } - - @Override - public ShortDataBuffer fallback() { - if (dst instanceof ShortDataBuffer) { - ShortDataBuffer shortDst = (ShortDataBuffer)dst; - for (long idx = 0L; idx < size; ++idx) { - shortDst.setShort(getShort(idx), idx); - } - return ShortNioDataBuffer.this; - } - return slowCopyTo(dst, size); - } - }); - } - - @Override - public ShortDataBuffer offset(long index) { - Validator.offsetArgs(this, index); - return new ShortNioDataBuffer(((ShortBuffer)buf.duplicate().position((int)index)).slice()); - } - - @Override - public ShortDataBuffer narrow(long size) { - Validator.narrowArgs(this, size); - return new ShortNioDataBuffer(((ShortBuffer)buf.duplicate().limit((int)size)).slice()); - } - - @Override - public ShortDataBuffer slice(long index, long size) { - Validator.sliceArgs(this, index, size); - ShortBuffer sliceBuf = buf.duplicate(); - sliceBuf.position((int)index); - sliceBuf.limit((int)index + (int)size); - return new ShortNioDataBuffer(sliceBuf.slice()); - } - - @Override - public R accept(DataStorageVisitor visitor) { - return visitor.visit(buf); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (!(obj instanceof ShortDataBuffer)) { - return super.equals(obj); - } - ShortDataBuffer other = (ShortDataBuffer)obj; - if (size() != other.size()) { - return false; - } - return other.accept(new DataStorageVisitor() { - - @Override - public Boolean visit(ShortBuffer buffer) { - return buf.equals(buffer); - } - - @Override - public Boolean fallback() { - for (int idx = 0; idx < size(); ++idx) { - if (other.getShort(idx) != getShort(idx)) { - return false; - } - } - return true; - } - }); - } - - @Override - ShortBuffer buf() { - return buf; - } - - ShortNioDataBuffer(ShortBuffer buf) { - this.buf = buf; - } - - private ShortBuffer buf; -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/raw/AbstractRawDataBuffer.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/raw/AbstractRawDataBuffer.java deleted file mode 100644 index c6050385b77..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/raw/AbstractRawDataBuffer.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * 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 - * - * 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.buffer.raw; - -import org.tensorflow.ndarray.impl.buffer.AbstractDataBuffer; -import org.tensorflow.ndarray.impl.buffer.Validator; -import org.tensorflow.ndarray.buffer.DataBuffer; -import org.tensorflow.ndarray.buffer.DataBufferWindow; - -@SuppressWarnings("unchecked") -abstract class AbstractRawDataBuffer> extends AbstractDataBuffer { - - public long size() { - return memory.size(); - } - - @Override - public boolean isReadOnly() { - return readOnly; - } - - public B read(Object dst, int dstLength) { - Validator.readArgs(this, dstLength, 0, dstLength); - memory.copyTo(UnsafeMemoryHandle.fromArray(dst, dstLength), dstLength); - return (B)this; - } - - public B read(Object dst, int dstLength, int offset, int length) { - Validator.readArgs(this, dstLength, offset, length); - memory.copyTo(UnsafeMemoryHandle.fromArray(dst, dstLength).offset(offset), length); - return (B)this; - } - - public B write(Object src, int srcLength) { - Validator.writeArgs(this, srcLength, 0, srcLength); - UnsafeMemoryHandle.fromArray(src, srcLength).copyTo(memory, srcLength); - return (B)this; - } - - public B write(Object src, int srcLength, int offset, int length) { - Validator.writeArgs(this, srcLength, offset, length); - UnsafeMemoryHandle.fromArray(src, srcLength).offset(offset).copyTo(memory, length); - return (B)this; - } - - @Override - public B copyTo(DataBuffer dst, long size) { - Validator.copyToArgs(this, dst, size); - if (dst instanceof AbstractRawDataBuffer) { - AbstractRawDataBuffer unsafeDst = (AbstractRawDataBuffer)dst; - memory.copyTo(unsafeDst.memory, size); - } else { - super.copyTo(dst, size); - } - return (B)this; - } - - @Override - public B slice(long index, long size) { - Validator.sliceArgs(this, index, size); - return instantiate(memory.slice(index, size)); - } - - @Override - public DataBufferWindow window(long size) { - B windowBuffer = instantiate(memory.slice(0, size)); - return new RawDataBufferWindow<>((AbstractRawDataBuffer)windowBuffer, size()); - } - - protected final UnsafeMemoryHandle memory; - protected final boolean readOnly; - - protected abstract B instantiate(UnsafeMemoryHandle region); - - AbstractRawDataBuffer(UnsafeMemoryHandle memory, boolean readOnly) { - this.memory = memory; - this.readOnly = readOnly; - } -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/raw/BooleanRawDataBuffer.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/raw/BooleanRawDataBuffer.java deleted file mode 100644 index e7e825ea505..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/raw/BooleanRawDataBuffer.java +++ /dev/null @@ -1,146 +0,0 @@ -/* - * 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 - * - * 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.buffer.raw; - -import java.util.Arrays; -import org.tensorflow.ndarray.impl.buffer.Validator; -import org.tensorflow.ndarray.buffer.BooleanDataBuffer; -import org.tensorflow.ndarray.buffer.DataBuffer; -import org.tensorflow.ndarray.buffer.DataStorageVisitor; - -final class BooleanRawDataBuffer extends AbstractRawDataBuffer - implements BooleanDataBuffer { - - @Override - public boolean getBoolean(long index) { - Validator.getArgs(this, index); - return memory.getBoolean(index); - } - - @Override - public BooleanDataBuffer setBoolean(boolean value, long index) { - Validator.setArgs(this, index); - memory.setBoolean(value, index); - return this; - } - - @Override - public BooleanDataBuffer read(boolean[] dst) { - return read(dst, dst.length); - } - - @Override - public BooleanDataBuffer read(boolean[] dst, int offset, int length) { - return read(dst, dst.length, offset, length); - } - - @Override - public BooleanDataBuffer write(boolean[] src) { - return write(src, src.length); - } - - @Override - public BooleanDataBuffer write(boolean[] src, int offset, int length) { - return write(src, src.length, offset, length); - } - - @Override - public BooleanDataBuffer copyTo(DataBuffer dst, long size) { - Validator.copyToArgs(this, dst, size); - return dst.accept(new DataStorageVisitor() { - - @Override - public BooleanDataBuffer visit(boolean[] array, int offset, int length) { - memory.copyTo(UnsafeMemoryHandle.fromArray(array, offset, length), size); - return BooleanRawDataBuffer.this; - } - - @Override - public BooleanDataBuffer visit(long address, long length, long scale) { - memory.copyTo(UnsafeMemoryHandle.fromAddress(address, length, scale), size); - return BooleanRawDataBuffer.this; - } - - @Override - public BooleanDataBuffer fallback() { - if (dst instanceof BooleanDataBuffer) { - BooleanDataBuffer booleanDst = (BooleanDataBuffer)dst; - for (long idx = 0L; idx < size; ++idx) { - booleanDst.setBoolean(getBoolean(idx), idx); - } - return BooleanRawDataBuffer.this; - } - return slowCopyTo(dst, size); - } - }); - } - - @Override - public R accept(DataStorageVisitor visitor) { - if (memory.isArray()) { - return visitor.visit((boolean[])memory.object, memory.arrayOffset(boolean[].class), (int)memory.size()); - } - return visitor.visit(memory.byteOffset, memory.byteSize, memory.scale); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (!(obj instanceof BooleanDataBuffer)) { - return super.equals(obj); - } - BooleanDataBuffer other = (BooleanDataBuffer)obj; - if (size() != other.size()) { - return false; - } - return other.accept(new DataStorageVisitor() { - - @Override - public Boolean visit(boolean[] array, int offset, int length) { - if (memory.isArray() && memory.arrayOffset(boolean[].class) == 0 && offset == 0) { - boolean[] thisArray = memory.array(); - if (thisArray.length == array.length) { - return Arrays.equals(thisArray, array); - } - } - return fallback(); - } - - @Override - public Boolean fallback() { - for (long idx = 0L; idx < size(); ++idx) { - if (other.getBoolean(idx) != getBoolean(idx)) { - return false; - } - } - return true; - } - }); - } - - @Override - protected BooleanDataBuffer instantiate(UnsafeMemoryHandle memory) { - return new BooleanRawDataBuffer(memory, readOnly); - } - - BooleanRawDataBuffer(UnsafeMemoryHandle memory, boolean readOnly) { - super(memory, readOnly); - } -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/raw/ByteRawDataBuffer.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/raw/ByteRawDataBuffer.java deleted file mode 100644 index b4b490e98ed..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/raw/ByteRawDataBuffer.java +++ /dev/null @@ -1,185 +0,0 @@ -/* - * 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 - * - * 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.buffer.raw; - -import java.nio.ByteBuffer; -import org.tensorflow.ndarray.impl.buffer.Validator; -import org.tensorflow.ndarray.buffer.BooleanDataBuffer; -import org.tensorflow.ndarray.buffer.ByteDataBuffer; -import org.tensorflow.ndarray.buffer.DataBuffer; -import org.tensorflow.ndarray.buffer.DataStorageVisitor; -import org.tensorflow.ndarray.buffer.DoubleDataBuffer; -import org.tensorflow.ndarray.buffer.FloatDataBuffer; -import org.tensorflow.ndarray.buffer.IntDataBuffer; -import org.tensorflow.ndarray.buffer.LongDataBuffer; -import org.tensorflow.ndarray.buffer.ShortDataBuffer; - -final class ByteRawDataBuffer extends AbstractRawDataBuffer - implements ByteDataBuffer { - - @Override - public byte getByte(long index) { - Validator.getArgs(this, index); - return memory.getByte(index); - } - - @Override - public ByteDataBuffer setByte(byte value, long index) { - Validator.setArgs(this, index); - memory.setByte(value, index); - return this; - } - - @Override - public ByteDataBuffer read(byte[] dst) { - return read(dst, dst.length); - } - - @Override - public ByteDataBuffer read(byte[] dst, int offset, int length) { - return read(dst, dst.length, offset, length); - } - - @Override - public ByteDataBuffer write(byte[] src) { - return write(src, src.length); - } - - @Override - public ByteDataBuffer write(byte[] src, int offset, int length) { - return write(src, src.length, offset, length); - } - - @Override - public ByteDataBuffer copyTo(DataBuffer dst, long size) { - Validator.copyToArgs(this, dst, size); - return dst.accept(new DataStorageVisitor() { - - @Override - public ByteDataBuffer visit(ByteBuffer buffer) { - if (buffer.hasArray()) { - memory.copyTo(UnsafeMemoryHandle.fromArray(buffer.array(), buffer.position(), buffer.limit()), size); - } else if (memory.isArray()) { - buffer.put(memory.toArrayByteBuffer()); - } else { - slowCopyTo(dst, size); - } - return ByteRawDataBuffer.this; - } - - @Override - public ByteDataBuffer visit(long address, long length, long scale) { - memory.copyTo(UnsafeMemoryHandle.fromAddress(address, length, scale), size); - return ByteRawDataBuffer.this; - } - - @Override - public ByteDataBuffer fallback() { - if (dst instanceof ByteDataBuffer) { - ByteDataBuffer byteDst = (ByteDataBuffer)dst; - for (long idx = 0L; idx < size; ++idx) { - byteDst.setByte(getByte(idx), idx); - } - return ByteRawDataBuffer.this; - } - return slowCopyTo(dst, size); - } - }); - } - - @Override - public IntDataBuffer asInts() { - return new IntRawDataBuffer(memory.rescale(Integer.BYTES), readOnly); - } - - @Override - public ShortDataBuffer asShorts() { - return new ShortRawDataBuffer(memory.rescale(Short.BYTES), readOnly); - } - - @Override - public LongDataBuffer asLongs() { - return new LongRawDataBuffer(memory.rescale(Long.BYTES), readOnly); - } - - @Override - public FloatDataBuffer asFloats() { - return new FloatRawDataBuffer(memory.rescale(Float.BYTES), readOnly); - } - - @Override - public DoubleDataBuffer asDoubles() { - return new DoubleRawDataBuffer(memory.rescale(Double.BYTES), readOnly); - } - - @Override - public BooleanDataBuffer asBooleans() { - return new BooleanRawDataBuffer(memory.rescale(Byte.BYTES), readOnly); - } - - @Override - public R accept(DataStorageVisitor visitor) { - if (memory.isArray()) { - return visitor.visit(memory.toArrayByteBuffer()); - } - return visitor.visit(memory.byteOffset, memory.byteSize, memory.scale); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (!(obj instanceof ByteDataBuffer)) { - return super.equals(obj); - } - ByteDataBuffer other = (ByteDataBuffer)obj; - if (size() != other.size()) { - return false; - } - return other.accept(new DataStorageVisitor() { - - @Override - public Boolean visit(ByteBuffer buffer) { - if (memory.isArray()) { - return buffer.equals(memory.toArrayByteBuffer()); - } - return fallback(); - } - - @Override - public Boolean fallback() { - for (long idx = 0L; idx < size(); ++idx) { - if (other.getByte(idx) != getByte(idx)) { - return false; - } - } - return true; - } - }); - } - - @Override - protected ByteDataBuffer instantiate(UnsafeMemoryHandle memory) { - return new ByteRawDataBuffer(memory, readOnly); - } - - ByteRawDataBuffer(UnsafeMemoryHandle memory, boolean readOnly) { - super(memory, readOnly); - } -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/raw/DoubleRawDataBuffer.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/raw/DoubleRawDataBuffer.java deleted file mode 100644 index 680d9565184..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/raw/DoubleRawDataBuffer.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * 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 - * - * 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.buffer.raw; - -import java.nio.DoubleBuffer; -import org.tensorflow.ndarray.impl.buffer.Validator; -import org.tensorflow.ndarray.buffer.DataBuffer; -import org.tensorflow.ndarray.buffer.DataStorageVisitor; -import org.tensorflow.ndarray.buffer.DoubleDataBuffer; - -final class DoubleRawDataBuffer extends AbstractRawDataBuffer - implements DoubleDataBuffer { - - @Override - public double getDouble(long index) { - Validator.getArgs(this, index); - return memory.getDouble(index); - } - - @Override - public DoubleDataBuffer setDouble(double value, long index) { - Validator.setArgs(this, index); - memory.setDouble(value, index); - return this; - } - - @Override - public DoubleDataBuffer read(double[] dst) { - return read(dst, dst.length); - } - - @Override - public DoubleDataBuffer read(double[] dst, int offset, int length) { - return read(dst, dst.length, offset, length); - } - - @Override - public DoubleDataBuffer write(double[] src) { - return write(src, src.length); - } - - @Override - public DoubleDataBuffer write(double[] src, int offset, int length) { - return write(src, src.length, offset, length); - } - - @Override - public DoubleDataBuffer copyTo(DataBuffer dst, long size) { - Validator.copyToArgs(this, dst, size); - return dst.accept(new DataStorageVisitor() { - - @Override - public DoubleDataBuffer visit(DoubleBuffer buffer) { - if (buffer.hasArray()) { - memory.copyTo(UnsafeMemoryHandle.fromArray(buffer.array(), buffer.position(), buffer.limit()), size); - } else if (memory.isArray()) { - buffer.put(memory.toArrayDoubleBuffer()); - } else { - slowCopyTo(dst, size); - } - return DoubleRawDataBuffer.this; - } - - @Override - public DoubleDataBuffer visit(long address, long length, long scale) { - memory.copyTo(UnsafeMemoryHandle.fromAddress(address, length, scale), size); - return DoubleRawDataBuffer.this; - } - - @Override - public DoubleDataBuffer fallback() { - if (dst instanceof DoubleDataBuffer) { - DoubleDataBuffer doubleDst = (DoubleDataBuffer)dst; - for (long idx = 0L; idx < size; ++idx) { - doubleDst.setDouble(getDouble(idx), idx); - } - return DoubleRawDataBuffer.this; - } - return slowCopyTo(dst, size); - } - }); - } - - @Override - public R accept(DataStorageVisitor visitor) { - if (memory.isArray()) { - return visitor.visit(memory.toArrayDoubleBuffer()); - } - return visitor.visit(memory.byteOffset, memory.byteSize, memory.scale); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (!(obj instanceof DoubleDataBuffer)) { - return super.equals(obj); - } - DoubleDataBuffer other = (DoubleDataBuffer)obj; - if (size() != other.size()) { - return false; - } - return other.accept(new DataStorageVisitor() { - - @Override - public Boolean visit(DoubleBuffer buffer) { - if (memory.isArray()) { - return buffer.equals(memory.toArrayDoubleBuffer()); - } - return fallback(); - } - - @Override - public Boolean fallback() { - for (long idx = 0L; idx < size(); ++idx) { - if (other.getDouble(idx) != getDouble(idx)) { - return false; - } - } - return true; - } - }); - } - - @Override - protected DoubleDataBuffer instantiate(UnsafeMemoryHandle memory) { - return new DoubleRawDataBuffer(memory, readOnly); - } - - DoubleRawDataBuffer(UnsafeMemoryHandle memory, boolean readOnly) { - super(memory, readOnly); - } -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/raw/FloatRawDataBuffer.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/raw/FloatRawDataBuffer.java deleted file mode 100644 index 43bd370a88b..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/raw/FloatRawDataBuffer.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * 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 - * - * 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.buffer.raw; - -import java.nio.FloatBuffer; - -import org.tensorflow.ndarray.impl.buffer.Validator; -import org.tensorflow.ndarray.buffer.DataBuffer; -import org.tensorflow.ndarray.buffer.DataStorageVisitor; -import org.tensorflow.ndarray.buffer.FloatDataBuffer; - -final class FloatRawDataBuffer extends AbstractRawDataBuffer - implements FloatDataBuffer { - - @Override - public float getFloat(long index) { - Validator.getArgs(this, index); - return memory.getFloat(index); - } - - @Override - public FloatDataBuffer setFloat(float value, long index) { - Validator.setArgs(this, index); - memory.setFloat(value, index); - return this; - } - - @Override - public FloatDataBuffer read(float[] dst) { - return read(dst, dst.length); - } - - @Override - public FloatDataBuffer read(float[] dst, int offset, int length) { - return read(dst, dst.length, offset, length); - } - - @Override - public FloatDataBuffer write(float[] src) { - return write(src, src.length); - } - - @Override - public FloatDataBuffer write(float[] src, int offset, int length) { - return write(src, src.length, offset, length); - } - - @Override - public FloatDataBuffer copyTo(DataBuffer dst, long size) { - Validator.copyToArgs(this, dst, size); - return dst.accept(new DataStorageVisitor() { - - @Override - public FloatDataBuffer visit(FloatBuffer buffer) { - if (buffer.hasArray()) { - memory.copyTo(UnsafeMemoryHandle.fromArray(buffer.array(), buffer.position(), buffer.limit()), size); - } else if (memory.isArray()) { - buffer.put(memory.toArrayFloatBuffer()); - } else { - slowCopyTo(dst, size); - } - return FloatRawDataBuffer.this; - } - - @Override - public FloatDataBuffer visit(long address, long length, long scale) { - memory.copyTo(UnsafeMemoryHandle.fromAddress(address, length, scale), size); - return FloatRawDataBuffer.this; - } - - @Override - public FloatDataBuffer fallback() { - if (dst instanceof FloatDataBuffer) { - FloatDataBuffer floatDst = (FloatDataBuffer)dst; - for (long idx = 0L; idx < size; ++idx) { - floatDst.setFloat(getFloat(idx), idx); - } - return FloatRawDataBuffer.this; - } - return slowCopyTo(dst, size); - } - }); - } - - @Override - public R accept(DataStorageVisitor visitor) { - if (memory.isArray()) { - return visitor.visit(memory.toArrayFloatBuffer()); - } - return visitor.visit(memory.byteOffset, memory.byteSize, memory.scale); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (!(obj instanceof FloatDataBuffer)) { - return super.equals(obj); - } - FloatDataBuffer other = (FloatDataBuffer)obj; - if (size() != other.size()) { - return false; - } - return other.accept(new DataStorageVisitor() { - - @Override - public Boolean visit(FloatBuffer buffer) { - if (memory.isArray()) { - return buffer.equals(memory.toArrayFloatBuffer()); - } - return fallback(); - } - - @Override - public Boolean fallback() { - for (long idx = 0L; idx < size(); ++idx) { - if (other.getFloat(idx) != getFloat(idx)) { - return false; - } - } - return true; - } - }); - } - - @Override - protected FloatDataBuffer instantiate(UnsafeMemoryHandle memory) { - return new FloatRawDataBuffer(memory, readOnly); - } - - FloatRawDataBuffer(UnsafeMemoryHandle memory, boolean readOnly) { - super(memory, readOnly); - } -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/raw/IntRawDataBuffer.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/raw/IntRawDataBuffer.java deleted file mode 100644 index 1c905e2756d..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/raw/IntRawDataBuffer.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * 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 - * - * 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.buffer.raw; - -import java.nio.IntBuffer; -import org.tensorflow.ndarray.impl.buffer.Validator; -import org.tensorflow.ndarray.buffer.DataBuffer; -import org.tensorflow.ndarray.buffer.DataStorageVisitor; -import org.tensorflow.ndarray.buffer.IntDataBuffer; - -final class IntRawDataBuffer extends AbstractRawDataBuffer - implements IntDataBuffer { - - @Override - public int getInt(long index) { - Validator.getArgs(this, index); - return memory.getInt(index); - } - - @Override - public IntDataBuffer setInt(int value, long index) { - Validator.setArgs(this, index); - memory.setInt(value, index); - return this; - } - - @Override - public IntDataBuffer read(int[] dst) { - return read(dst, dst.length); - } - - @Override - public IntDataBuffer read(int[] dst, int offset, int length) { - return read(dst, dst.length, offset, length); - } - - @Override - public IntDataBuffer write(int[] src) { - return write(src, src.length); - } - - @Override - public IntDataBuffer write(int[] src, int offset, int length) { - return write(src, src.length, offset, length); - } - - @Override - public IntDataBuffer copyTo(DataBuffer dst, long size) { - Validator.copyToArgs(this, dst, size); - return dst.accept(new DataStorageVisitor() { - - @Override - public IntDataBuffer visit(IntBuffer buffer) { - if (buffer.hasArray()) { - memory.copyTo(UnsafeMemoryHandle.fromArray(buffer.array(), buffer.position(), buffer.limit()), size); - } else if (memory.isArray()) { - buffer.put(memory.toArrayIntBuffer()); - } else { - slowCopyTo(dst, size); - } - return IntRawDataBuffer.this; - } - - @Override - public IntDataBuffer visit(long address, long length, long scale) { - memory.copyTo(UnsafeMemoryHandle.fromAddress(address, length, scale), size); - return IntRawDataBuffer.this; - } - - @Override - public IntDataBuffer fallback() { - if (dst instanceof IntDataBuffer) { - IntDataBuffer intDst = (IntDataBuffer)dst; - for (long idx = 0L; idx < size; ++idx) { - intDst.setInt(getInt(idx), idx); - } - return IntRawDataBuffer.this; - } - return slowCopyTo(dst, size); - } - }); - } - - @Override - public R accept(DataStorageVisitor visitor) { - if (memory.isArray()) { - return visitor.visit(memory.toArrayIntBuffer()); - } - return visitor.visit(memory.byteOffset, memory.byteSize, memory.scale); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (!(obj instanceof IntDataBuffer)) { - return super.equals(obj); - } - IntDataBuffer other = (IntDataBuffer)obj; - if (size() != other.size()) { - return false; - } - return other.accept(new DataStorageVisitor() { - - @Override - public Boolean visit(IntBuffer buffer) { - if (memory.isArray()) { - return buffer.equals(memory.toArrayIntBuffer()); - } - return fallback(); - } - - @Override - public Boolean fallback() { - for (long idx = 0L; idx < size(); ++idx) { - if (other.getInt(idx) != getInt(idx)) { - return false; - } - } - return true; - } - }); - } - - @Override - protected IntDataBuffer instantiate(UnsafeMemoryHandle memory) { - return new IntRawDataBuffer(memory, readOnly); - } - - IntRawDataBuffer(UnsafeMemoryHandle memory, boolean readOnly) { - super(memory, readOnly); - } -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/raw/LongRawDataBuffer.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/raw/LongRawDataBuffer.java deleted file mode 100644 index 724868fe81f..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/raw/LongRawDataBuffer.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * 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 - * - * 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.buffer.raw; - -import java.nio.LongBuffer; -import org.tensorflow.ndarray.impl.buffer.Validator; -import org.tensorflow.ndarray.buffer.DataBuffer; -import org.tensorflow.ndarray.buffer.DataStorageVisitor; -import org.tensorflow.ndarray.buffer.LongDataBuffer; - -final class LongRawDataBuffer extends AbstractRawDataBuffer - implements LongDataBuffer { - - @Override - public long getLong(long index) { - Validator.getArgs(this, index); - return memory.getLong(index); - } - - @Override - public LongDataBuffer setLong(long value, long index) { - Validator.setArgs(this, index); - memory.setLong(value, index); - return this; - } - - @Override - public LongDataBuffer read(long[] dst) { - return read(dst, dst.length); - } - - @Override - public LongDataBuffer read(long[] dst, int offset, int length) { - return read(dst, dst.length, offset, length); - } - - @Override - public LongDataBuffer write(long[] src) { - return write(src, src.length); - } - - @Override - public LongDataBuffer write(long[] src, int offset, int length) { - return write(src, src.length, offset, length); - } - - @Override - public LongDataBuffer copyTo(DataBuffer dst, long size) { - Validator.copyToArgs(this, dst, size); - return dst.accept(new DataStorageVisitor() { - - @Override - public LongDataBuffer visit(LongBuffer buffer) { - if (buffer.hasArray()) { - memory.copyTo(UnsafeMemoryHandle.fromArray(buffer.array(), buffer.position(), buffer.limit()), size); - } else if (memory.isArray()) { - buffer.put(memory.toArrayLongBuffer()); - } else { - slowCopyTo(dst, size); - } - return LongRawDataBuffer.this; - } - - @Override - public LongDataBuffer visit(long address, long length, long scale) { - memory.copyTo(UnsafeMemoryHandle.fromAddress(address, length, scale), size); - return LongRawDataBuffer.this; - } - - @Override - public LongDataBuffer fallback() { - if (dst instanceof LongDataBuffer) { - LongDataBuffer longDst = (LongDataBuffer)dst; - for (long idx = 0L; idx < size; ++idx) { - longDst.setLong(getLong(idx), idx); - } - return LongRawDataBuffer.this; - } - return slowCopyTo(dst, size); - } - }); - } - - @Override - public R accept(DataStorageVisitor visitor) { - if (memory.isArray()) { - return visitor.visit(memory.toArrayLongBuffer()); - } - return visitor.visit(memory.byteOffset, memory.byteSize, memory.scale); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (!(obj instanceof LongDataBuffer)) { - return super.equals(obj); - } - LongDataBuffer other = (LongDataBuffer)obj; - if (size() != other.size()) { - return false; - } - return other.accept(new DataStorageVisitor() { - - @Override - public Boolean visit(LongBuffer buffer) { - if (memory.isArray()) { - return buffer.equals(memory.toArrayLongBuffer()); - } - return fallback(); - } - - @Override - public Boolean fallback() { - for (long idx = 0L; idx < size(); ++idx) { - if (other.getLong(idx) != getLong(idx)) { - return false; - } - } - return true; - } - }); - } - - @Override - protected LongDataBuffer instantiate(UnsafeMemoryHandle memory) { - return new LongRawDataBuffer(memory, readOnly); - } - - LongRawDataBuffer(UnsafeMemoryHandle memory, boolean readOnly) { - super(memory, readOnly); - } -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/raw/RawDataBufferFactory.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/raw/RawDataBufferFactory.java deleted file mode 100644 index 7253b239af2..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/raw/RawDataBufferFactory.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * 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 - * - * 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.buffer.raw; - -import org.tensorflow.ndarray.impl.buffer.Validator; -import org.tensorflow.ndarray.buffer.BooleanDataBuffer; -import org.tensorflow.ndarray.buffer.ByteDataBuffer; -import org.tensorflow.ndarray.buffer.DoubleDataBuffer; -import org.tensorflow.ndarray.buffer.FloatDataBuffer; -import org.tensorflow.ndarray.buffer.IntDataBuffer; -import org.tensorflow.ndarray.buffer.LongDataBuffer; -import org.tensorflow.ndarray.buffer.ShortDataBuffer; - -/** - * Factory of raw data buffers - */ -public class RawDataBufferFactory { - - public static boolean canBeUsed() { - return UnsafeReference.isAvailable(); - } - - public static BooleanDataBuffer create(boolean[] array, boolean readOnly) { - return new BooleanRawDataBuffer(UnsafeMemoryHandle.fromArray(array, array.length), readOnly); - } - - public static ByteDataBuffer create(byte[] array, boolean readOnly) { - if (!canBeUsed()) { - throw new IllegalStateException("Raw data buffers are not available"); - } - return new ByteRawDataBuffer(UnsafeMemoryHandle.fromArray(array, array.length), readOnly); - } - - public static DoubleDataBuffer create(double[] array, boolean readOnly) { - if (!canBeUsed()) { - throw new IllegalStateException("Raw data buffers are not available"); - } - return new DoubleRawDataBuffer(UnsafeMemoryHandle.fromArray(array, array.length), readOnly); - } - - public static FloatDataBuffer create(float[] array, boolean readOnly) { - if (!canBeUsed()) { - throw new IllegalStateException("Raw data buffers are not available"); - } - return new FloatRawDataBuffer(UnsafeMemoryHandle.fromArray(array, array.length), readOnly); - } - - public static IntDataBuffer create(int[] array, boolean readOnly) { - if (!canBeUsed()) { - throw new IllegalStateException("Raw data buffers are not available"); - } - return new IntRawDataBuffer(UnsafeMemoryHandle.fromArray(array, array.length), readOnly); - } - - public static LongDataBuffer create(long[] array, boolean readOnly) { - if (!canBeUsed()) { - throw new IllegalStateException("Raw data buffers are not available"); - } - return new LongRawDataBuffer(UnsafeMemoryHandle.fromArray(array, array.length), readOnly); - } - - public static ShortDataBuffer create(short[] array, boolean readOnly) { - if (!canBeUsed()) { - throw new IllegalStateException("Raw data buffers are not available"); - } - return new ShortRawDataBuffer(UnsafeMemoryHandle.fromArray(array, array.length), readOnly); - } - - protected static BooleanDataBuffer mapNativeBooleans(long address, long size, boolean readOnly) { - if (!canBeUsed()) { - throw new IllegalStateException("Raw data buffers are not available"); - } - Validator.createArgs(size, MAX_64BITS); - return new BooleanRawDataBuffer(UnsafeMemoryHandle.fromAddress(address, size, Byte.BYTES), readOnly); - } - - protected static ByteDataBuffer mapNativeBytes(long address, long size, boolean readOnly) { - if (!canBeUsed()) { - throw new IllegalStateException("Raw data buffers are not available"); - } - Validator.createArgs(size, MAX_64BITS); - return new ByteRawDataBuffer(UnsafeMemoryHandle.fromAddress(address, size, Byte.BYTES), readOnly); - } - - protected static DoubleDataBuffer mapNativeDoubles(long address, long size, boolean readOnly) { - if (!canBeUsed()) { - throw new IllegalStateException("Raw data buffers are not available"); - } - Validator.createArgs(size, MAX_64BITS); - return new DoubleRawDataBuffer(UnsafeMemoryHandle.fromAddress(address, size, Double.BYTES), readOnly); - } - - protected static FloatDataBuffer mapNativeFloats(long address, long size, boolean readOnly) { - if (!canBeUsed()) { - throw new IllegalStateException("Raw data buffers are not available"); - } - Validator.createArgs(size, MAX_64BITS); - return new FloatRawDataBuffer(UnsafeMemoryHandle.fromAddress(address, size, Float.BYTES), readOnly); - } - - protected static IntDataBuffer mapNativeInts(long address, long size, boolean readOnly) { - if (!canBeUsed()) { - throw new IllegalStateException("Raw data buffers are not available"); - } - Validator.createArgs(size, MAX_64BITS); - return new IntRawDataBuffer(UnsafeMemoryHandle.fromAddress(address, size, Integer.BYTES), readOnly); - } - - protected static LongDataBuffer mapNativeLongs(long address, long size, boolean readOnly) { - if (!canBeUsed()) { - throw new IllegalStateException("Raw data buffers are not available"); - } - Validator.createArgs(size, MAX_64BITS); - return new LongRawDataBuffer(UnsafeMemoryHandle.fromAddress(address, size, Long.BYTES), readOnly); - } - - protected static ShortDataBuffer mapNativeShorts(long address, long size, boolean readOnly) { - if (!canBeUsed()) { - throw new IllegalStateException("Raw data buffers are not available"); - } - Validator.createArgs(size, MAX_64BITS); - return new ShortRawDataBuffer(UnsafeMemoryHandle.fromAddress(address, size, Short.BYTES), readOnly); - } - - /* - * The maximum size for a buffer of this type, i.e. the maximum number of bytes it can store. - *

- * As the maximum size may vary depending on the JVM implementation and on the platform, this - * property returns a value that is safe for most of them. - */ - static long MAX_32BITS = Integer.MAX_VALUE - 10; - static long MAX_64BITS = Long.MAX_VALUE - 10; -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/raw/RawDataBufferWindow.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/raw/RawDataBufferWindow.java deleted file mode 100644 index dc18a6caa6e..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/raw/RawDataBufferWindow.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.tensorflow.ndarray.impl.buffer.raw; - -import org.tensorflow.ndarray.buffer.DataBuffer; -import org.tensorflow.ndarray.impl.buffer.AbstractDataBufferWindow; - -final class RawDataBufferWindow> extends AbstractDataBufferWindow { - - @Override - public void offset(long offset) { - windowMemory.rebase(offset); - } - - > RawDataBufferWindow(R windowBuffer, long bufferLimit) { - super((B)windowBuffer, bufferLimit); - this.windowMemory = windowBuffer.memory; - } - - private final UnsafeMemoryHandle windowMemory; -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/raw/ShortRawDataBuffer.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/raw/ShortRawDataBuffer.java deleted file mode 100644 index 80f9c289852..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/raw/ShortRawDataBuffer.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * 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 - * - * 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.buffer.raw; - -import java.nio.ShortBuffer; -import org.tensorflow.ndarray.impl.buffer.Validator; -import org.tensorflow.ndarray.buffer.DataBuffer; -import org.tensorflow.ndarray.buffer.DataStorageVisitor; -import org.tensorflow.ndarray.buffer.ShortDataBuffer; - -final class ShortRawDataBuffer extends AbstractRawDataBuffer - implements ShortDataBuffer { - - @Override - public short getShort(long index) { - Validator.getArgs(this, index); - return memory.getShort(index); - } - - @Override - public ShortDataBuffer setShort(short value, long index) { - Validator.setArgs(this, index); - memory.setShort(value, index); - return this; - } - - @Override - public ShortDataBuffer read(short[] dst) { - return read(dst, dst.length); - } - - @Override - public ShortDataBuffer read(short[] dst, int offset, int length) { - return read(dst, dst.length, offset, length); - } - - @Override - public ShortDataBuffer write(short[] src) { - return write(src, src.length); - } - - @Override - public ShortDataBuffer write(short[] src, int offset, int length) { - return write(src, src.length, offset, length); - } - - @Override - public ShortDataBuffer copyTo(DataBuffer dst, long size) { - Validator.copyToArgs(this, dst, size); - return dst.accept(new DataStorageVisitor() { - - @Override - public ShortDataBuffer visit(ShortBuffer buffer) { - if (buffer.hasArray()) { - memory.copyTo(UnsafeMemoryHandle.fromArray(buffer.array(), buffer.position(), buffer.limit()), size); - } else if (memory.isArray()) { - buffer.put(memory.toArrayShortBuffer()); - } else { - slowCopyTo(dst, size); - } - return ShortRawDataBuffer.this; - } - - @Override - public ShortDataBuffer visit(long address, long length, long scale) { - memory.copyTo(UnsafeMemoryHandle.fromAddress(address, length, scale), size); - return ShortRawDataBuffer.this; - } - - @Override - public ShortDataBuffer fallback() { - if (dst instanceof ShortDataBuffer) { - ShortDataBuffer shortDst = (ShortDataBuffer)dst; - for (long idx = 0L; idx < size; ++idx) { - shortDst.setShort(getShort(idx), idx); - } - return ShortRawDataBuffer.this; - } - return slowCopyTo(dst, size); - } - }); - } - - @Override - public R accept(DataStorageVisitor visitor) { - if (memory.isArray()) { - return visitor.visit(memory.toArrayShortBuffer()); - } - return visitor.visit(memory.byteOffset, memory.byteSize, memory.scale); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (!(obj instanceof ShortDataBuffer)) { - return super.equals(obj); - } - ShortDataBuffer other = (ShortDataBuffer)obj; - if (size() != other.size()) { - return false; - } - return other.accept(new DataStorageVisitor() { - - @Override - public Boolean visit(ShortBuffer buffer) { - if (memory.isArray()) { - return buffer.equals(memory.toArrayShortBuffer()); - } - return fallback(); - } - - @Override - public Boolean fallback() { - for (long idx = 0L; idx < size(); ++idx) { - if (other.getShort(idx) != getShort(idx)) { - return false; - } - } - return true; - } - }); - } - - @Override - protected ShortDataBuffer instantiate(UnsafeMemoryHandle memory) { - return new ShortRawDataBuffer(memory, readOnly); - } - - ShortRawDataBuffer(UnsafeMemoryHandle memory, boolean readOnly) { - super(memory, readOnly); - } -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/raw/UnsafeMemoryHandle.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/raw/UnsafeMemoryHandle.java deleted file mode 100644 index bd61e53a128..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/raw/UnsafeMemoryHandle.java +++ /dev/null @@ -1,195 +0,0 @@ -/* - * 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 - * - * 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.buffer.raw; - -import java.nio.ByteBuffer; -import java.nio.DoubleBuffer; -import java.nio.FloatBuffer; -import java.nio.IntBuffer; -import java.nio.LongBuffer; -import java.nio.ShortBuffer; - -final class UnsafeMemoryHandle { - - static UnsafeMemoryHandle fromArray(Object array, int length) { - return fromArray(array, 0, length); - } - - static UnsafeMemoryHandle fromArray(Object array, int arrayOffset, int length) { - long scale = UnsafeReference.UNSAFE.arrayIndexScale(array.getClass()); - int baseOffset = UnsafeReference.UNSAFE.arrayBaseOffset(array.getClass()); - return new UnsafeMemoryHandle(array, baseOffset + (arrayOffset * scale), length * scale, scale); - } - - static UnsafeMemoryHandle fromAddress(long address, long byteSize, long scale) { - return new UnsafeMemoryHandle(address, byteSize, scale); - } - - long size() { - return size; - } - - byte getByte(long index) { - return UnsafeReference.UNSAFE.getByte(object, align(index)); - } - - void setByte(byte value, long index) { - UnsafeReference.UNSAFE.putByte(object, align(index), value); - } - - boolean getBoolean(long index) { - return UnsafeReference.UNSAFE.getBoolean(object, align(index)); - } - - void setBoolean(boolean value, long index) { - UnsafeReference.UNSAFE.putBoolean(object, align(index), value); - } - - short getShort(long index) { - return UnsafeReference.UNSAFE.getShort(object, align(index)); - } - - void setShort(short value, long index) { - UnsafeReference.UNSAFE.putShort(object, align(index), value); - } - - int getInt(long index) { - return UnsafeReference.UNSAFE.getInt(object, align(index)); - } - - void setInt(int value, long index) { - UnsafeReference.UNSAFE.putInt(object, align(index), value); - } - - float getFloat(long index) { - return UnsafeReference.UNSAFE.getFloat(object, align(index)); - } - - void setFloat(float value, long index) { - UnsafeReference.UNSAFE.putFloat(object, align(index), value); - } - - double getDouble(long index) { - return UnsafeReference.UNSAFE.getDouble(object, align(index)); - } - - void setDouble(double value, long index) { - UnsafeReference.UNSAFE.putDouble(object, align(index), value); - } - - long getLong(long index) { - return UnsafeReference.UNSAFE.getLong(object, align(index)); - } - - void setLong(long value, long index) { - UnsafeReference.UNSAFE.putLong(object, align(index), value); - } - - void copyTo(UnsafeMemoryHandle memory, long length) { - UnsafeReference.UNSAFE.copyMemory(object, byteOffset, memory.object, memory.byteOffset, length * scale); - } - - UnsafeMemoryHandle offset(long index) { - long offset = scale(index); - return new UnsafeMemoryHandle(object, this.byteOffset + offset, byteSize - offset, scale); - } - - UnsafeMemoryHandle narrow(long size) { - return new UnsafeMemoryHandle(object, byteOffset, scale(size), scale); - } - - UnsafeMemoryHandle slice(long index, long size) { - return new UnsafeMemoryHandle(object, this.byteOffset + scale(index), scale(size), scale); - } - - UnsafeMemoryHandle rescale(long scale) { - if (object != null) { - throw new IllegalStateException("Raw heap memory cannot be rescaled"); - } - return new UnsafeMemoryHandle(null, byteOffset, byteSize, scale); - } - - void rebase(long index) { - byteOffset = baseOffset + scale(index); - } - - boolean isArray() { - return object != null; - } - - @SuppressWarnings("unchecked") - A array() { - return (A)object; - } - - int arrayOffset(Class arrayClass) { - return (int)((byteOffset - UnsafeReference.UNSAFE.arrayBaseOffset(arrayClass)) / scale); - } - - ByteBuffer toArrayByteBuffer() { - return ByteBuffer.wrap((byte[])object, (int) byteOffset - UnsafeReference.UNSAFE.arrayBaseOffset(byte[].class), (int)size); - } - - ShortBuffer toArrayShortBuffer() { - return ShortBuffer.wrap((short[])object, (int)((byteOffset - UnsafeReference.UNSAFE.arrayBaseOffset(short[].class)) / scale), (int)size); - } - - IntBuffer toArrayIntBuffer() { - return IntBuffer.wrap((int[])object, (int)((byteOffset - UnsafeReference.UNSAFE.arrayBaseOffset(int[].class)) / scale), (int)size); - } - - LongBuffer toArrayLongBuffer() { - return LongBuffer.wrap((long[])object, (int)((byteOffset - UnsafeReference.UNSAFE.arrayBaseOffset(long[].class)) / scale), (int)size); - } - - FloatBuffer toArrayFloatBuffer() { - return FloatBuffer.wrap((float[])object, (int)((byteOffset - UnsafeReference.UNSAFE.arrayBaseOffset(float[].class)) / scale), (int)size); - } - - DoubleBuffer toArrayDoubleBuffer() { - return DoubleBuffer.wrap((double[])object, (int)((byteOffset - UnsafeReference.UNSAFE.arrayBaseOffset(double[].class)) / scale), (int)size); - } - - final Object object; - final long baseOffset; - long byteOffset; - final long byteSize; - final long scale; - final long size; - - private UnsafeMemoryHandle(Object object, long baseOffset, long byteSize, long scale) { - this.object = object; - this.baseOffset = baseOffset; - byteOffset = baseOffset; - this.byteSize = byteSize; - this.scale = scale; - size = byteSize / scale; - } - - private UnsafeMemoryHandle(long address, long byteSize, long scale) { - this(null, address, byteSize, scale); - } - - private long align(long index) { - return byteOffset + index * scale; - } - - private long scale(long value) { - return value * scale; - } -} 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 deleted file mode 100644 index 7b95eac7349..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/buffer/raw/UnsafeReference.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * 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 - * - * 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.buffer.raw; - -import java.lang.reflect.Field; -import sun.misc.Unsafe; - -final class UnsafeReference { - - static boolean isAvailable() { - return UNSAFE != null; - } - - static final Unsafe UNSAFE; - - static { - Unsafe unsafe = null; - try { - Class clazz = Class.forName("sun.misc.Unsafe"); - Field theUnsafe = clazz.getDeclaredField("theUnsafe"); - 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); - unsafe = (Unsafe) instance; - } - } catch (ClassNotFoundException | NoSuchMethodException | NoSuchFieldException | SecurityException | IllegalAccessException | ClassCastException ex) { - // Do nothing, keep unsafe as null - } - UNSAFE = unsafe; - } -} 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 deleted file mode 100644 index 0497095116e..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/dense/AbstractDenseNdArray.java +++ /dev/null @@ -1,163 +0,0 @@ -/* - 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 - - 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.dense; - -import org.tensorflow.ndarray.NdArray; -import org.tensorflow.ndarray.NdArraySequence; -import org.tensorflow.ndarray.impl.AbstractNdArray; -import org.tensorflow.ndarray.impl.dimension.RelativeDimensionalSpace; -import org.tensorflow.ndarray.impl.sequence.FastElementSequence; -import org.tensorflow.ndarray.index.Index; -import org.tensorflow.ndarray.buffer.DataBuffer; -import org.tensorflow.ndarray.buffer.DataBufferWindow; -import org.tensorflow.ndarray.impl.dimension.DimensionalSpace; -import org.tensorflow.ndarray.impl.sequence.SlicingElementSequence; -import org.tensorflow.ndarray.impl.sequence.SingleElementSequence; - -@SuppressWarnings("unchecked") -public abstract class AbstractDenseNdArray> extends AbstractNdArray { - - @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); - try { - DataBufferWindow> elemWindow = buffer().window(elemDims.physicalSize()); - U element = instantiate(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 - return new SlicingElementSequence<>(this, dimensionIdx, elemDims); - } - } - - @Override - public U slice(long position, DimensionalSpace sliceDimensions) { - DataBuffer sliceBuffer = buffer().slice(position, sliceDimensions.physicalSize()); - return instantiate(sliceBuffer, sliceDimensions); - } - - @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); - } - - @Override - public U get(long... coords) { - return slice(positionOf(coords, false), dimensions().from(coords.length)); - } - - @Override - public T getObject(long... coords) { - return buffer().getObject(positionOf(coords, true)); - } - - @Override - public U set(NdArray src, long... coordinates) { - src.copyTo((coordinates == null || coordinates.length == 0) ? this : get(coordinates)); - return (U)this; - } - - @Override - public U setObject(T value, long... coords) { - buffer().setObject(value, positionOf(coords, true)); - return (U)this; - } - - @Override - public U read(DataBuffer dst) { - Validator.readToBufferArgs(this, dst); - DataTransfer.execute(buffer(), dimensions(), dst, DataTransfer::ofValue); - return (U)this; - } - - @Override - public U write(DataBuffer src) { - Validator.writeFromBufferArgs(this, src); - DataTransfer.execute(src, buffer(), dimensions(), DataTransfer::ofValue); - return (U)this; - } - - @Override - public int hashCode() { - if (dimensions().isSegmented()) { - return slowHashCode(); - } - final int prime = 31; - int result = 1; - result = prime * result + buffer().hashCode(); - result = prime * result + shape().hashCode(); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (!(obj instanceof AbstractDenseNdArray)) { - return super.equals(obj); - } - AbstractDenseNdArray other = (AbstractDenseNdArray)obj; - if (dimensions().isSegmented() || other.dimensions().isSegmented()) { - return slowEquals(other); - } - if (!shape().equals(other.shape())) { - return false; - } - return buffer().equals(other.buffer()); - } - - protected AbstractDenseNdArray(DimensionalSpace dimensions) { - super(dimensions); - } - - abstract protected DataBuffer buffer(); - - abstract U instantiate(DataBuffer buffer, DimensionalSpace dimensions); - - long positionOf(long[] coords, boolean isValue) { - if (coords == null || coords.length == 0) { - return 0; - } - Validator.coordinates(dimensions, coords, isValue); - return dimensions.positionOf(coords); - } - - @Override - protected void slowCopyTo(NdArray array) { - if (array instanceof AbstractDenseNdArray) { - AbstractDenseNdArray dst = (AbstractDenseNdArray)array; - long offset = 0L; - for (NdArray s : scalars()) { - dst.buffer().setObject(s.getObject(), offset++); - } - } else { - super.slowCopyTo(array); - } - } -} 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 deleted file mode 100644 index 0764146f962..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/dense/BooleanDenseNdArray.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - 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 - - 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.dense; - -import org.tensorflow.ndarray.BooleanNdArray; -import org.tensorflow.ndarray.NdArray; -import org.tensorflow.ndarray.Shape; -import org.tensorflow.ndarray.buffer.BooleanDataBuffer; -import org.tensorflow.ndarray.buffer.DataBuffer; -import org.tensorflow.ndarray.impl.dimension.DimensionalSpace; - -public class BooleanDenseNdArray extends AbstractDenseNdArray - implements BooleanNdArray { - - public static BooleanNdArray create(BooleanDataBuffer buffer, Shape shape) { - Validator.denseShape(buffer, shape); - return new BooleanDenseNdArray(buffer, shape); - } - - @Override - public boolean getBoolean(long... indices) { - return buffer.getBoolean(positionOf(indices, true)); - } - - @Override - public BooleanNdArray setBoolean(boolean value, long... indices) { - buffer.setBoolean(value, positionOf(indices, true)); - return this; - } - - @Override - public BooleanNdArray copyTo(NdArray dst) { - Validator.copyToNdArrayArgs(this, dst); - if (dst instanceof BooleanDenseNdArray) { - BooleanDenseNdArray booleanDst = (BooleanDenseNdArray)dst; - DataTransfer.execute(buffer, dimensions(), booleanDst.buffer, booleanDst.dimensions(), DataTransfer::ofBoolean); - } else { - slowCopyTo(dst); - } - return this; - } - - @Override - public BooleanNdArray read(BooleanDataBuffer dst) { - Validator.readToBufferArgs(this, dst); - DataTransfer.execute(buffer, dimensions(), dst, DataTransfer::ofBoolean); - return this; - } - - @Override - public BooleanNdArray write(BooleanDataBuffer src) { - Validator.writeFromBufferArgs(this, src); - DataTransfer.execute(src, buffer, dimensions(), DataTransfer::ofBoolean); - return this; - } - - protected BooleanDenseNdArray(BooleanDataBuffer buffer, Shape shape) { - this(buffer, DimensionalSpace.create(shape)); - } - - @Override - BooleanDenseNdArray instantiate(DataBuffer buffer, DimensionalSpace dimensions) { - return new BooleanDenseNdArray((BooleanDataBuffer)buffer, dimensions); - } - - @Override - protected BooleanDataBuffer buffer() { - return buffer; - } - - private final BooleanDataBuffer buffer; - - private BooleanDenseNdArray(BooleanDataBuffer buffer, DimensionalSpace dimensions) { - super(dimensions); - this.buffer = buffer; - } -} \ No newline at end of file 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 deleted file mode 100644 index 172432b5939..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/dense/ByteDenseNdArray.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - 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 - - 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.dense; - -import org.tensorflow.ndarray.ByteNdArray; -import org.tensorflow.ndarray.NdArray; -import org.tensorflow.ndarray.Shape; -import org.tensorflow.ndarray.buffer.ByteDataBuffer; -import org.tensorflow.ndarray.buffer.DataBuffer; -import org.tensorflow.ndarray.impl.dimension.DimensionalSpace; - -public class ByteDenseNdArray extends AbstractDenseNdArray - implements ByteNdArray { - - public static ByteNdArray create(ByteDataBuffer buffer, Shape shape) { - Validator.denseShape(buffer, shape); - return new ByteDenseNdArray(buffer, shape); - } - - @Override - public byte getByte(long... indices) { - return buffer.getByte(positionOf(indices, true)); - } - - @Override - public ByteNdArray setByte(byte value, long... indices) { - buffer.setByte(value, positionOf(indices, true)); - return this; - } - - @Override - public ByteNdArray copyTo(NdArray dst) { - Validator.copyToNdArrayArgs(this, dst); - if (dst instanceof ByteDenseNdArray) { - ByteDenseNdArray byteDst = (ByteDenseNdArray)dst; - DataTransfer.execute(buffer, dimensions(), byteDst.buffer, byteDst.dimensions(), DataTransfer::ofByte); - } else { - slowCopyTo(dst); - } - return this; - } - - @Override - public ByteNdArray read(ByteDataBuffer dst) { - Validator.readToBufferArgs(this, dst); - DataTransfer.execute(buffer, dimensions(), dst, DataTransfer::ofByte); - return this; - } - - @Override - public ByteNdArray write(ByteDataBuffer src) { - Validator.writeFromBufferArgs(this, src); - DataTransfer.execute(src, buffer, dimensions(), DataTransfer::ofByte); - return this; - } - - protected ByteDenseNdArray(ByteDataBuffer buffer, Shape shape) { - this(buffer, DimensionalSpace.create(shape)); - } - - @Override - ByteDenseNdArray instantiate(DataBuffer buffer, DimensionalSpace dimensions) { - return new ByteDenseNdArray((ByteDataBuffer)buffer, dimensions); - } - - @Override - protected ByteDataBuffer buffer() { - return buffer; - } - - private final ByteDataBuffer buffer; - - private ByteDenseNdArray(ByteDataBuffer buffer, DimensionalSpace dimensions) { - super(dimensions); - this.buffer = buffer; - } -} \ No newline at end of file diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/dense/DataTransfer.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/dense/DataTransfer.java deleted file mode 100644 index d3afa223231..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/dense/DataTransfer.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * 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 - * - * 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.dense; - -import org.tensorflow.ndarray.buffer.BooleanDataBuffer; -import org.tensorflow.ndarray.buffer.ByteDataBuffer; -import org.tensorflow.ndarray.buffer.DataBuffer; -import org.tensorflow.ndarray.buffer.DoubleDataBuffer; -import org.tensorflow.ndarray.buffer.FloatDataBuffer; -import org.tensorflow.ndarray.buffer.IntDataBuffer; -import org.tensorflow.ndarray.buffer.LongDataBuffer; -import org.tensorflow.ndarray.buffer.ShortDataBuffer; -import org.tensorflow.ndarray.impl.dimension.DimensionalSpace; -import org.tensorflow.ndarray.impl.sequence.PositionIterator; - -final class DataTransfer { - - @FunctionalInterface - interface OfValue> { - void copy(B srcBuffer, long srcIndex, B dstBuffer, long dstIndex); - } - - static > void ofValue(B srcBuf, long srcIdx, B dstBuf, long dstIdx) { - dstBuf.setObject(srcBuf.getObject(srcIdx), dstIdx); - } - - static void ofByte(ByteDataBuffer srcBuf, long srcIdx, ByteDataBuffer dstBuf, long dstIdx) { - dstBuf.setByte(srcBuf.getByte(srcIdx), dstIdx); - } - - static void ofInt(IntDataBuffer srcBuf, long srcIdx, IntDataBuffer dstBuf, long dstIdx) { - dstBuf.setInt(srcBuf.getInt(srcIdx), dstIdx); - } - - static void ofLong(LongDataBuffer srcBuf, long srcIdx, LongDataBuffer dstBuf, long dstIdx) { - dstBuf.setLong(srcBuf.getLong(srcIdx), dstIdx); - } - - static void ofDouble(DoubleDataBuffer srcBuf, long srcIdx, DoubleDataBuffer dstBuf, long dstIdx) { - dstBuf.setDouble(srcBuf.getDouble(srcIdx), dstIdx); - } - - static void ofFloat(FloatDataBuffer srcBuf, long srcIdx, FloatDataBuffer dstBuf, long dstIdx) { - dstBuf.setFloat(srcBuf.getFloat(srcIdx), dstIdx); - } - - static void ofShort(ShortDataBuffer srcBuf, long srcIdx, ShortDataBuffer dstBuf, long dstIdx) { - dstBuf.setShort(srcBuf.getShort(srcIdx), dstIdx); - } - - static void ofBoolean(BooleanDataBuffer srcBuf, long srcIdx, BooleanDataBuffer dstBuf, long dstIdx) { - dstBuf.setBoolean(srcBuf.getBoolean(srcIdx), dstIdx); - } - - static > void execute(B srcBuffer, DimensionalSpace srcDimensions, B dstBuffer, DimensionalSpace dstDimensions, OfValue valueTransfer) { - if (srcDimensions.isSegmented() || dstDimensions.isSegmented()) { - int segmentationIdx = Math.max(srcDimensions.segmentationIdx(), dstDimensions.segmentationIdx()); - copyByElement( - srcBuffer, - PositionIterator.create(srcDimensions, segmentationIdx), - dstBuffer, - PositionIterator.create(dstDimensions, segmentationIdx), - srcDimensions.get(segmentationIdx).elementSize(), - valueTransfer - ); - } else { - srcBuffer.copyTo(dstBuffer, srcDimensions.physicalSize()); - } - } - - static > void execute(B srcBuffer, B dstBuffer, DimensionalSpace dstDimensions, OfValue valueTransfer) { - if (dstDimensions.isSegmented()) { - long elementSize = dstDimensions.get(dstDimensions.segmentationIdx()).elementSize(); - copyByElement( - srcBuffer, - PositionIterator.sequence(elementSize, srcBuffer.size()), - dstBuffer, - PositionIterator.create(dstDimensions, dstDimensions.segmentationIdx()), - elementSize, - valueTransfer - ); - } else { - srcBuffer.copyTo(dstBuffer, dstDimensions.physicalSize()); - } - } - - static > void execute(B srcBuffer, DimensionalSpace srcDimensions, B dstBuffer, OfValue valueTransfer) { - if (srcDimensions.isSegmented()) { - long elementSize = srcDimensions.get(srcDimensions.segmentationIdx()).elementSize(); - copyByElement( - srcBuffer, - PositionIterator.create(srcDimensions, srcDimensions.segmentationIdx()), - dstBuffer, - PositionIterator.sequence(elementSize, dstBuffer.size()), - elementSize, - valueTransfer - ); - } else { - srcBuffer.copyTo(dstBuffer, srcDimensions.physicalSize()); - } - } - - private static > void copyByElement( - B srcBuffer, - PositionIterator srcIterator, - B dstBuffer, - PositionIterator dstIterator, - long elementSize, - OfValue valueTransfer - ) { - if (elementSize == 1) { - while (srcIterator.hasNext()) { - valueTransfer.copy(srcBuffer, srcIterator.nextLong(), dstBuffer, dstIterator.nextLong()); - } - } else { - while (srcIterator.hasNext()) { - srcBuffer.offset(srcIterator.nextLong()).copyTo(dstBuffer.offset(dstIterator.nextLong()), elementSize); - } - } - } -} 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 deleted file mode 100644 index 819d95de2fc..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/dense/DenseNdArray.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - 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 - - 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.dense; - -import org.tensorflow.ndarray.Shape; -import org.tensorflow.ndarray.buffer.DataBuffer; -import org.tensorflow.ndarray.NdArray; -import org.tensorflow.ndarray.impl.dimension.DimensionalSpace; - -public class DenseNdArray extends AbstractDenseNdArray> { - - public static NdArray wrap(DataBuffer buffer, Shape shape) { - Validator.denseShape(buffer, shape); - return new DenseNdArray<>(buffer, shape); - } - - @Override - public NdArray copyTo(NdArray dst) { - Validator.copyToNdArrayArgs(this, dst); - if (dst instanceof DenseNdArray) { - DenseNdArray denseDst = (DenseNdArray)dst; - DataTransfer.execute(buffer, dimensions(), denseDst.buffer, denseDst.dimensions(), DataTransfer::ofValue); - } else { - slowCopyTo(dst); - } - return this; - } - - protected DenseNdArray(DataBuffer buffer, Shape shape) { - this(buffer, DimensionalSpace.create(shape)); - } - - @Override - DenseNdArray instantiate(DataBuffer buffer, DimensionalSpace dimensions) { - return new DenseNdArray<>(buffer, dimensions); - } - - @Override - protected DataBuffer buffer() { - return buffer; - } - - private final DataBuffer buffer; - - private DenseNdArray(DataBuffer buffer, DimensionalSpace dimensions) { - super(dimensions); - this.buffer = buffer; - } -} \ No newline at end of file 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 deleted file mode 100644 index f54b8d0347a..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/dense/DoubleDenseNdArray.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - 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 - - 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.dense; - -import org.tensorflow.ndarray.DoubleNdArray; -import org.tensorflow.ndarray.NdArray; -import org.tensorflow.ndarray.Shape; -import org.tensorflow.ndarray.buffer.DataBuffer; -import org.tensorflow.ndarray.buffer.DoubleDataBuffer; -import org.tensorflow.ndarray.impl.dimension.DimensionalSpace; - -public class DoubleDenseNdArray extends AbstractDenseNdArray - implements DoubleNdArray { - - public static DoubleNdArray create(DoubleDataBuffer buffer, Shape shape) { - Validator.denseShape(buffer, shape); - return new DoubleDenseNdArray(buffer, shape); - } - - @Override - public double getDouble(long... indices) { - return buffer.getDouble(positionOf(indices, true)); - } - - @Override - public DoubleNdArray setDouble(double value, long... indices) { - buffer.setDouble(value, positionOf(indices, true)); - return this; - } - - @Override - public DoubleNdArray copyTo(NdArray dst) { - Validator.copyToNdArrayArgs(this, dst); - if (dst instanceof DoubleDenseNdArray) { - DoubleDenseNdArray doubleDst = (DoubleDenseNdArray)dst; - DataTransfer.execute(buffer, dimensions(), doubleDst.buffer, doubleDst.dimensions(), DataTransfer::ofDouble); - } else { - slowCopyTo(dst); - } - return this; - } - - @Override - public DoubleNdArray read(DoubleDataBuffer dst) { - Validator.readToBufferArgs(this, dst); - DataTransfer.execute(buffer, dimensions(), dst, DataTransfer::ofDouble); - return this; - } - - @Override - public DoubleNdArray write(DoubleDataBuffer src) { - Validator.writeFromBufferArgs(this, src); - DataTransfer.execute(src, buffer, dimensions(), DataTransfer::ofDouble); - return this; - } - - protected DoubleDenseNdArray(DoubleDataBuffer buffer, Shape shape) { - this(buffer, DimensionalSpace.create(shape)); - } - - @Override - DoubleDenseNdArray instantiate(DataBuffer buffer, DimensionalSpace dimensions) { - return new DoubleDenseNdArray((DoubleDataBuffer)buffer, dimensions); - } - - @Override - protected DoubleDataBuffer buffer() { - return buffer; - } - - private final DoubleDataBuffer buffer; - - private DoubleDenseNdArray(DoubleDataBuffer buffer, DimensionalSpace dimensions) { - super(dimensions); - this.buffer = buffer; - } -} \ No newline at end of file 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 deleted file mode 100644 index 196b5ef8b11..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/dense/FloatDenseNdArray.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - 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 - - 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.dense; - -import org.tensorflow.ndarray.FloatNdArray; -import org.tensorflow.ndarray.NdArray; -import org.tensorflow.ndarray.Shape; -import org.tensorflow.ndarray.buffer.DataBuffer; -import org.tensorflow.ndarray.buffer.FloatDataBuffer; -import org.tensorflow.ndarray.impl.dimension.DimensionalSpace; - -public class FloatDenseNdArray extends AbstractDenseNdArray - implements FloatNdArray { - - public static FloatNdArray create(FloatDataBuffer buffer, Shape shape) { - Validator.denseShape(buffer, shape); - return new FloatDenseNdArray(buffer, shape); - } - - @Override - public float getFloat(long... indices) { - return buffer.getFloat(positionOf(indices, true)); - } - - @Override - public FloatNdArray setFloat(float value, long... indices) { - buffer.setFloat(value, positionOf(indices, true)); - return this; - } - - @Override - public FloatNdArray copyTo(NdArray dst) { - Validator.copyToNdArrayArgs(this, dst); - if (dst instanceof FloatDenseNdArray) { - FloatDenseNdArray floatDst = (FloatDenseNdArray)dst; - DataTransfer.execute(buffer, dimensions(), floatDst.buffer, floatDst.dimensions(), DataTransfer::ofFloat); - } else { - slowCopyTo(dst); - } - return this; - } - - @Override - public FloatNdArray read(FloatDataBuffer dst) { - Validator.readToBufferArgs(this, dst); - DataTransfer.execute(buffer, dimensions(), dst, DataTransfer::ofFloat); - return this; - } - - @Override - public FloatNdArray write(FloatDataBuffer src) { - Validator.writeFromBufferArgs(this, src); - DataTransfer.execute(src, buffer, dimensions(), DataTransfer::ofFloat); - return this; - } - - protected FloatDenseNdArray(FloatDataBuffer buffer, Shape shape) { - this(buffer, DimensionalSpace.create(shape)); - } - - @Override - FloatDenseNdArray instantiate(DataBuffer buffer, DimensionalSpace dimensions) { - return new FloatDenseNdArray((FloatDataBuffer) buffer, dimensions); - } - - @Override - public FloatDataBuffer buffer() { - return buffer; - } - - private final FloatDataBuffer buffer; - - private FloatDenseNdArray(FloatDataBuffer buffer, DimensionalSpace dimensions) { - super(dimensions); - this.buffer = buffer; - } -} 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 deleted file mode 100644 index a7af498dd6f..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/dense/IntDenseNdArray.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - 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 - - 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.dense; - -import org.tensorflow.ndarray.Shape; -import org.tensorflow.ndarray.buffer.DataBuffer; -import org.tensorflow.ndarray.buffer.IntDataBuffer; -import org.tensorflow.ndarray.IntNdArray; -import org.tensorflow.ndarray.NdArray; -import org.tensorflow.ndarray.impl.dimension.DimensionalSpace; - -public class IntDenseNdArray extends AbstractDenseNdArray - implements IntNdArray { - - public static IntNdArray create(IntDataBuffer buffer, Shape shape) { - Validator.denseShape(buffer, shape); - return new IntDenseNdArray(buffer, shape); - } - - @Override - public int getInt(long... indices) { - return buffer.getInt(positionOf(indices, true)); - } - - @Override - public IntNdArray setInt(int value, long... indices) { - buffer.setInt(value, positionOf(indices, true)); - return this; - } - - @Override - public IntNdArray copyTo(NdArray dst) { - Validator.copyToNdArrayArgs(this, dst); - if (dst instanceof IntDenseNdArray) { - IntDenseNdArray intDst = (IntDenseNdArray)dst; - DataTransfer.execute(buffer, dimensions(), intDst.buffer, intDst.dimensions(), DataTransfer::ofInt); - } else { - slowCopyTo(dst); - } - return this; - } - - @Override - public IntNdArray read(IntDataBuffer dst) { - Validator.readToBufferArgs(this, dst); - DataTransfer.execute(buffer, dimensions(), dst, DataTransfer::ofInt); - return this; - } - - @Override - public IntNdArray write(IntDataBuffer src) { - Validator.writeFromBufferArgs(this, src); - DataTransfer.execute(src, buffer, dimensions(), DataTransfer::ofInt); - return this; - } - - protected IntDenseNdArray(IntDataBuffer buffer, Shape shape) { - this(buffer, DimensionalSpace.create(shape)); - } - - @Override - IntDenseNdArray instantiate(DataBuffer buffer, DimensionalSpace dimensions) { - return new IntDenseNdArray((IntDataBuffer)buffer, dimensions); - } - - @Override - protected IntDataBuffer buffer() { - return buffer; - } - - private final IntDataBuffer buffer; - - private IntDenseNdArray(IntDataBuffer buffer, DimensionalSpace dimensions) { - super(dimensions); - this.buffer = buffer; - } -} \ No newline at end of file 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 deleted file mode 100644 index cd56dadfb2b..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/dense/LongDenseNdArray.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - 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 - - 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.dense; - -import org.tensorflow.ndarray.LongNdArray; -import org.tensorflow.ndarray.NdArray; -import org.tensorflow.ndarray.Shape; -import org.tensorflow.ndarray.buffer.DataBuffer; -import org.tensorflow.ndarray.buffer.LongDataBuffer; -import org.tensorflow.ndarray.impl.dimension.DimensionalSpace; - -public class LongDenseNdArray extends AbstractDenseNdArray - implements LongNdArray { - - public static LongNdArray create(LongDataBuffer buffer, Shape shape) { - Validator.denseShape(buffer, shape); - return new LongDenseNdArray(buffer, shape); - } - - @Override - public long getLong(long... indices) { - return buffer.getLong(positionOf(indices, true)); - } - - @Override - public LongNdArray setLong(long value, long... indices) { - buffer.setLong(value, positionOf(indices, true)); - return this; - } - - @Override - public LongNdArray copyTo(NdArray dst) { - Validator.copyToNdArrayArgs(this, dst); - if (dst instanceof LongDenseNdArray) { - LongDenseNdArray longDst = (LongDenseNdArray)dst; - DataTransfer.execute(buffer, dimensions(), longDst.buffer, longDst.dimensions(), DataTransfer::ofLong); - } else { - slowCopyTo(dst); - } - return this; - } - - @Override - public LongNdArray read(LongDataBuffer dst) { - Validator.readToBufferArgs(this, dst); - DataTransfer.execute(buffer, dimensions(), dst, DataTransfer::ofLong); - return this; - } - - @Override - public LongNdArray write(LongDataBuffer src) { - Validator.writeFromBufferArgs(this, src); - DataTransfer.execute(src, buffer, dimensions(), DataTransfer::ofLong); - return this; - } - - protected LongDenseNdArray(LongDataBuffer buffer, Shape shape) { - this(buffer, DimensionalSpace.create(shape)); - } - - @Override - LongDenseNdArray instantiate(DataBuffer buffer, DimensionalSpace dimensions) { - return new LongDenseNdArray((LongDataBuffer)buffer, dimensions); - } - - @Override - protected LongDataBuffer buffer() { - return buffer; - } - - private final LongDataBuffer buffer; - - private LongDenseNdArray(LongDataBuffer buffer, DimensionalSpace dimensions) { - super(dimensions); - this.buffer = buffer; - } -} \ No newline at end of file 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 deleted file mode 100644 index 291c01ac8e1..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/dense/ShortDenseNdArray.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - 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 - - 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.dense; - -import org.tensorflow.ndarray.NdArray; -import org.tensorflow.ndarray.ShortNdArray; -import org.tensorflow.ndarray.Shape; -import org.tensorflow.ndarray.buffer.DataBuffer; -import org.tensorflow.ndarray.buffer.ShortDataBuffer; -import org.tensorflow.ndarray.impl.dimension.DimensionalSpace; - -public class ShortDenseNdArray extends AbstractDenseNdArray - implements ShortNdArray { - - public static ShortNdArray create(ShortDataBuffer buffer, Shape shape) { - Validator.denseShape(buffer, shape); - return new ShortDenseNdArray(buffer, shape); - } - - @Override - public short getShort(long... indices) { - return buffer.getShort(positionOf(indices, true)); - } - - @Override - public ShortNdArray setShort(short value, long... indices) { - buffer.setShort(value, positionOf(indices, true)); - return this; - } - - @Override - public ShortNdArray copyTo(NdArray dst) { - Validator.copyToNdArrayArgs(this, dst); - if (dst instanceof ShortDenseNdArray) { - ShortDenseNdArray shortDst = (ShortDenseNdArray)dst; - DataTransfer.execute(buffer, dimensions(), shortDst.buffer, shortDst.dimensions(), DataTransfer::ofShort); - } else { - slowCopyTo(dst); - } - return this; - } - - @Override - public ShortNdArray read(ShortDataBuffer dst) { - Validator.readToBufferArgs(this, dst); - DataTransfer.execute(buffer, dimensions(), dst, DataTransfer::ofShort); - return this; - } - - @Override - public ShortNdArray write(ShortDataBuffer src) { - Validator.writeFromBufferArgs(this, src); - DataTransfer.execute(src, buffer, dimensions(), DataTransfer::ofShort); - return this; - } - - protected ShortDenseNdArray(ShortDataBuffer buffer, Shape shape) { - this(buffer, DimensionalSpace.create(shape)); - } - - @Override - ShortDenseNdArray instantiate(DataBuffer buffer, DimensionalSpace dimensions) { - return new ShortDenseNdArray((ShortDataBuffer)buffer, dimensions); - } - - @Override - protected ShortDataBuffer buffer() { - return buffer; - } - - private final ShortDataBuffer buffer; - - private ShortDenseNdArray(ShortDataBuffer buffer, DimensionalSpace dimensions) { - super(dimensions); - this.buffer = buffer; - } -} \ No newline at end of file diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/dense/Validator.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/dense/Validator.java deleted file mode 100644 index b0bcae252fb..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/dense/Validator.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - 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 - - 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.dense; - -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 { - - 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("Dense arrays cannot have unknown dimension(s)"); - } - if (buffer.size() < shape.size()) { - throw new IllegalArgumentException("Buffer size is smaller than the shape size"); - }; - } - - private Validator() {} -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/dimension/AbstractDimension.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/dimension/AbstractDimension.java deleted file mode 100644 index f2a0fb05b6e..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/dimension/AbstractDimension.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - 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 - - 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.dimension; - -abstract class AbstractDimension implements Dimension { - - /** - * Dimensions are known to be equal if they have the same number of elements - */ - @Override - public int hashCode() { - final int prime = 17; - long numElements = numElements(); - return 31 * prime + (int)(numElements ^ (numElements >>> 32)); - } - - /** - * Dimensions are known to be equal if they have the same number of elements - */ - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj instanceof Dimension) { - Dimension otherDimension = (Dimension) obj; - return numElements() == otherDimension.numElements(); - } - return false; - } -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/dimension/Axis.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/dimension/Axis.java deleted file mode 100644 index 91973c752a1..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/dimension/Axis.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - 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 - - 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.dimension; - -final class Axis extends AbstractDimension { - - @Override - public long numElements() { - return numElements; - } - - @Override - public long positionOf(long coord) { - if (coord >= numElements) { - throw new IndexOutOfBoundsException(); - } - return elementSize * coord; - } - - @Override - public boolean isSegmented() { - return false; // all axis are continuous - } - - @Override - public long elementSize() { - return elementSize; - } - - @Override - public long physicalSize() { - return elementSize * numElements; - } - - @Override - public String toString() { - return String.valueOf(numElements); - } - - Axis(long numElements, long elementSize) { - this.numElements = numElements; - this.elementSize = elementSize; - } - - private final long numElements; - private final long elementSize; -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/dimension/Dimension.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/dimension/Dimension.java deleted file mode 100644 index cbae2d897a8..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/dimension/Dimension.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - 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 - - 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.dimension; - -import org.tensorflow.ndarray.index.Index; - -public interface Dimension { - - default Dimension withIndex(Index index) { - return new IndexedDimension(index, this); - } - - long numElements(); - - long elementSize(); - - long physicalSize(); - - long positionOf(long coord); - - boolean isSegmented(); -} 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 deleted file mode 100644 index 7d0f0222bbe..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/dimension/DimensionalSpace.java +++ /dev/null @@ -1,224 +0,0 @@ -/* - 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 - - 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.dimension; - -import java.util.Arrays; -import org.tensorflow.ndarray.Shape; -import org.tensorflow.ndarray.index.Index; - -public class DimensionalSpace { - - public static DimensionalSpace create(Shape shape) { - Dimension[] dimensions = new Dimension[shape.numDimensions()]; - - // 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); - elementSize *= dimensions[i].numElements(); - } - return new DimensionalSpace(dimensions, shape); - } - - public RelativeDimensionalSpace mapTo(Index[] indices) { - if (dimensions == null) { - throw new ArrayIndexOutOfBoundsException(); - } - int dimIdx = 0; - int indexIdx = 0; - int newDimIdx = 0; - int segmentationIdx = -1; - long initialOffset = 0; - - int newAxes = 0; - boolean seenEllipsis = false; - for (Index idx : indices) { - if (idx.isNewAxis()) { - newAxes += 1; - } - if (idx.isEllipsis()) { - if (seenEllipsis) { - throw new IllegalArgumentException("Only one ellipsis allowed"); - } else { - seenEllipsis = true; - } - } - } - int newLength = dimensions.length + newAxes; - - Dimension[] newDimensions = new Dimension[newLength]; - while (indexIdx < indices.length) { - - if (indices[indexIdx].isPoint()) { - // When an index targets a single point in a given dimension, calculate the offset of this - // point and cumulate the offset of any subsequent point as well - long offset = 0; - do { - offset += indices[indexIdx].mapCoordinate(0, dimensions[dimIdx]); - dimIdx++; - } while (++indexIdx < indices.length && indices[indexIdx].isPoint()); - - // If this is the first index, then the offset is the position of the whole dimension - // space within the original one. If not, then we apply the offset to the last vectorial - // dimension - if (newDimIdx == 0) { - initialOffset = offset; - } else { - long reducedSize = dimensions[dimIdx - 1].elementSize(); - newDimensions[newDimIdx - 1] = new ReducedDimension(newDimensions[newDimIdx - 1], offset, reducedSize); - segmentationIdx = newDimIdx - 1; - } - - } else if (indices[indexIdx].isNewAxis()) { - long newSize; - if (dimIdx == 0) { - // includes everything. Should really include future reduction (at()) but that doesn't seem to cause issues - // elsewhere - newSize = dimensions[0].numElements() * dimensions[0].elementSize(); - } else { - newSize = dimensions[dimIdx - 1].elementSize(); - } - - newDimensions[newDimIdx] = new Axis(1, newSize); - segmentationIdx = newDimIdx; // is this correct? - ++newDimIdx; - ++indexIdx; - } else if (indices[indexIdx].isEllipsis()) { - int remainingDimensions = dimensions.length - dimIdx; - int requiredDimensions = 0; - for (int i = indexIdx + 1; i < indices.length; i++) { - if (!indices[i].isNewAxis()) { - requiredDimensions++; - } - } - // while the number of dimensions left < the number of indices that consume axes - while (remainingDimensions > requiredDimensions) { - Dimension dim = dimensions[dimIdx++]; - if (dim.isSegmented()) { - segmentationIdx = newDimIdx; - } - newDimensions[newDimIdx++] = dim; - remainingDimensions--; - } - indexIdx++; - } else { - // Map any other index to the appropriate dimension of this space - Dimension newDimension = indices[indexIdx].apply(dimensions[dimIdx++]); - newDimensions[newDimIdx] = newDimension; - if (newDimension.isSegmented()) { - segmentationIdx = newDimIdx; - } - ++newDimIdx; - ++indexIdx; - } - } - - // When the number of indices provided is smaller than the number of dimensions in this space, - // we copy the remaining dimensions directly to the new space as well. - for (; dimIdx < dimensions.length; ++dimIdx, ++newDimIdx) { - Dimension dim = dimensions[dimIdx]; - newDimensions[newDimIdx] = dim; - if (dim.isSegmented()) { - segmentationIdx = newDimIdx; - } - } - return new RelativeDimensionalSpace(Arrays.copyOf(newDimensions, newDimIdx), segmentationIdx, initialOffset); - } - - public DimensionalSpace from(int dimensionStart) { - if (dimensionStart > dimensions.length) { - throw new IndexOutOfBoundsException(); - } - Dimension[] newDimensions = Arrays.copyOfRange(dimensions, dimensionStart, dimensions.length); - if (segmentationIdx > dimensionStart) { - return new DimensionalSpace(newDimensions, segmentationIdx - dimensionStart); - } - return new DimensionalSpace(newDimensions); - } - - public Shape shape() { - if (shape == null) { - shape = toShape(dimensions); - } - return shape; - } - - public int numDimensions() { - return dimensions.length; - } - - public long numElements(int i) { - return dimensions[i].numElements(); - } - - public long physicalSize() { - return dimensions.length > 0 ? dimensions[0].physicalSize() : 1; // dimensions.length == 0 for scalars - } - - public Dimension get(int i) { - return dimensions[i]; - } - - public boolean isSegmented() { - return segmentationIdx >= 0; - } - - public int segmentationIdx() { - return segmentationIdx; - } - - public long positionOf(long[] coords) { - long position = 0L; - for (int i = 0; i < coords.length; ++i) { - position += dimensions[i].positionOf(coords[i]); - } - return position; - } - - /** Succinct description of the shape meant for debugging. */ - @Override - public String toString() { - return Arrays.toString(dimensions); - } - - DimensionalSpace(Dimension[] dimensions, int segmentationIdx) { - this.dimensions = dimensions; - this.segmentationIdx = segmentationIdx; - } - - private DimensionalSpace(Dimension[] dimensions) { - this(dimensions, -1); - } - - private DimensionalSpace(Dimension[] dimensions, Shape shape) { - this(dimensions); - this.shape = shape; - } - - private final Dimension[] dimensions; - private final int segmentationIdx; - private Shape shape; - - private static Shape toShape(Dimension[] dimensions) { - long[] shapeDimSizes = new long[dimensions.length]; - int i = 0; - for (Dimension dimension : dimensions) { - shapeDimSizes[i++] = dimension.numElements(); - } - return Shape.of(shapeDimSizes); - } -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/dimension/IndexedDimension.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/dimension/IndexedDimension.java deleted file mode 100644 index 2b609bc3535..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/dimension/IndexedDimension.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - 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 - - 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.dimension; - -import org.tensorflow.ndarray.index.Index; - -final class IndexedDimension extends AbstractDimension { - - @Override - public long numElements() { - return numElements; - } - - @Override - public long positionOf(long coord) { - if (coord >= numElements()) { - throw new IndexOutOfBoundsException(); - } - return originalDimension.positionOf(index.mapCoordinate(coord, originalDimension)); - } - - @Override - public boolean isSegmented() { - // TODO (karllessard) for now we consider all indexed dimensions as segmented but might depend - // on the actual index - return true; - } - - @Override - public long elementSize() { - return originalDimension.elementSize(); // indices do not change the size of an inner element - } - - @Override - public long physicalSize() { - // TODO (karllessard) we consider this dimension takes the same amount of memory that the - // original one but might depend on the actual index - return originalDimension.physicalSize(); - } - - @Override - public String toString() { - return String.valueOf(numElements()); - } - - IndexedDimension(Index index, Dimension originalDimension) { - this.index = index; - this.originalDimension = originalDimension; - this.numElements = index.numElements(originalDimension); - } - - private final Index index; - private final Dimension originalDimension; - private final long numElements; -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/dimension/ReducedDimension.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/dimension/ReducedDimension.java deleted file mode 100644 index 4b5cb1adcf8..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/dimension/ReducedDimension.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - 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 - - 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.dimension; - -final class ReducedDimension extends AbstractDimension { - - @Override - public long numElements() { - return originalDimension.numElements(); - } - - @Override - public long positionOf(long coord) { - return originalDimension.positionOf(coord) + offset; - } - - @Override - public boolean isSegmented() { - return true; - } - - @Override - public long elementSize() { - return elementSize; - } - - @Override - public long physicalSize() { - // We simplify the computation by assuming that a reduced dimension takes the same amount of - // memory than the original one - return originalDimension.physicalSize(); - } - - @Override - public String toString() { - return String.valueOf(numElements()); - } - - ReducedDimension(Dimension originalDimension, long offset, long elementSize) { - this.originalDimension = originalDimension; - this.offset = offset; - this.elementSize = elementSize; - } - - private final Dimension originalDimension; - private final long offset; - private final long elementSize; -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/dimension/RelativeDimensionalSpace.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/dimension/RelativeDimensionalSpace.java deleted file mode 100644 index 4259bbcf76e..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/dimension/RelativeDimensionalSpace.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - 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 - - 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.dimension; - -public class RelativeDimensionalSpace extends DimensionalSpace { - - public long position() { - return position; - } - - RelativeDimensionalSpace(Dimension[] dimensions, int segmentationIdx, long position) { - super(dimensions, segmentationIdx); - this.position = position; - } - - private long position; -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/sequence/CoordinatesIncrementor.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/sequence/CoordinatesIncrementor.java deleted file mode 100644 index 8c9c9f86f4c..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/sequence/CoordinatesIncrementor.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2020 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.sequence; - -final class CoordinatesIncrementor { - - boolean increment() { - for (int i = coords.length - 1; i >= 0; --i) { - if ((coords[i] = (coords[i] + 1) % shape[i]) > 0) { - return true; - } - } - return false; - } - - CoordinatesIncrementor(long[] shape, int dimensionIdx) { - this.shape = shape; - this.coords = new long[dimensionIdx + 1]; - } - - final long[] shape; - final long[] coords; -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/sequence/FastElementSequence.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/sequence/FastElementSequence.java deleted file mode 100644 index 92cebeb2338..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/sequence/FastElementSequence.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2020 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.sequence; - -import java.util.Iterator; -import java.util.function.BiConsumer; - -import org.tensorflow.ndarray.NdArray; -import org.tensorflow.ndarray.NdArraySequence; -import org.tensorflow.ndarray.buffer.DataBufferWindow; -import org.tensorflow.ndarray.impl.AbstractNdArray; - -/** - * A sequence recycling the same {@code NdArray} instance when iterating its elements - * - * @param Type of the elements - * @param Type of the {@code NdArray} with this sequence - */ -public final class FastElementSequence> implements NdArraySequence { - - public FastElementSequence(AbstractNdArray ndArray, int dimensionIdx, U element, DataBufferWindow elementWindow) { - this.ndArray = ndArray; - this.dimensionIdx = dimensionIdx; - this.element = element; - this.elementWindow = elementWindow; - } - - @Override - public Iterator iterator() { - return new SequenceIterator(); - } - - @Override - public void forEachIndexed(BiConsumer consumer) { - PositionIterator.createIndexed(ndArray.dimensions(), dimensionIdx).forEachIndexed((long[] coords, long position) -> { - elementWindow.slideTo(position); - consumer.accept(coords, element); - }); - } - - @Override - public NdArraySequence asSlices() { - return new SlicingElementSequence(ndArray, dimensionIdx); - } - - private class SequenceIterator implements Iterator { - - @Override - public boolean hasNext() { - return positionIterator.hasNext(); - } - - @Override - public U next() { - elementWindow.slideTo(positionIterator.nextLong()); - return element; - } - - private final PositionIterator positionIterator = PositionIterator.create(ndArray.dimensions(), dimensionIdx); - } - - private final AbstractNdArray ndArray; - private final int dimensionIdx; - private final U element; - private final DataBufferWindow elementWindow; -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/sequence/IndexedPositionIterator.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/sequence/IndexedPositionIterator.java deleted file mode 100644 index 30ece1599b6..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/sequence/IndexedPositionIterator.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * 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 - * - * 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.sequence; - -public interface IndexedPositionIterator extends PositionIterator { - - @FunctionalInterface - interface CoordsLongConsumer { - void consume(long[] coords, long position); - } - - void forEachIndexed(CoordsLongConsumer consumer); -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/sequence/IndexedSequentialPositionIterator.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/sequence/IndexedSequentialPositionIterator.java deleted file mode 100644 index 80b3de681bd..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/sequence/IndexedSequentialPositionIterator.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * 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 - * - * 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.sequence; - -import org.tensorflow.ndarray.impl.dimension.DimensionalSpace; - -class IndexedSequentialPositionIterator extends SequentialPositionIterator implements IndexedPositionIterator { - - @Override - public void forEachIndexed(CoordsLongConsumer consumer) { - while (hasNext()) { - consumer.consume(coords, nextLong()); - incrementCoords(); - } - } - - private void incrementCoords() { - for (int i = coords.length - 1; i >= 0; --i) { - if (coords[i] < shape[i] - 1) { - coords[i] += 1L; - return; - } - coords[i] = 0L; - } - } - - IndexedSequentialPositionIterator(DimensionalSpace dimensions, int dimensionIdx) { - super(dimensions, dimensionIdx); - this.shape = dimensions.shape().asArray(); - this.coords = new long[dimensionIdx + 1]; - //this.coordsIncrementor = new CoordinatesIncrementor(dimensions.shape().asArray(), dimensionIdx); - } - - private final long[] shape; - private final long[] coords; -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/sequence/NdPositionIterator.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/sequence/NdPositionIterator.java deleted file mode 100644 index 789474c58ae..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/sequence/NdPositionIterator.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * 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 - * - * 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.sequence; - -import java.util.NoSuchElementException; -import org.tensorflow.ndarray.impl.dimension.DimensionalSpace; - -class NdPositionIterator implements IndexedPositionIterator { - - @Override - public boolean hasNext() { - return coords != null; - } - - @Override - public long nextLong() { - if (!hasNext()) { - throw new NoSuchElementException(); - } - long position = dimensions.positionOf(coords); - increment(); - return position; - } - - @Override - public void forEachIndexed(CoordsLongConsumer consumer) { - while (hasNext()) { - consumer.consume(coords, dimensions.positionOf(coords)); - increment(); - } - } - - private void increment() { - if (!increment(coords, dimensions)) { - coords = null; - } - } - - static boolean increment(long[] coords, DimensionalSpace dimensions) { - for (int i = coords.length - 1; i >= 0; --i) { - if ((coords[i] = (coords[i] + 1) % dimensions.get(i).numElements()) > 0) { - return true; - } - } - return false; - } - - NdPositionIterator(DimensionalSpace dimensions, int dimensionIdx) { - this.dimensions = dimensions; - this.coords = new long[dimensionIdx + 1]; - } - - private final DimensionalSpace dimensions; - private long[] coords; -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/sequence/PositionIterator.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/sequence/PositionIterator.java deleted file mode 100644 index 83ed940563c..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/sequence/PositionIterator.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * 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 - * - * 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.sequence; - -import java.util.PrimitiveIterator; -import org.tensorflow.ndarray.impl.dimension.DimensionalSpace; - -public interface PositionIterator extends PrimitiveIterator.OfLong { - - static PositionIterator create(DimensionalSpace dimensions, int dimensionIdx) { - if (dimensions.isSegmented()) { - return new NdPositionIterator(dimensions, dimensionIdx); - } - return new SequentialPositionIterator(dimensions, dimensionIdx); - } - - static IndexedPositionIterator createIndexed(DimensionalSpace dimensions, int dimensionIdx) { - if (dimensions.isSegmented()) { - return new NdPositionIterator(dimensions, dimensionIdx); - } - return new IndexedSequentialPositionIterator(dimensions, dimensionIdx); - } - - static PositionIterator sequence(long stride, long end) { - return new SequentialPositionIterator(stride, end); - } -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/sequence/SequentialPositionIterator.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/sequence/SequentialPositionIterator.java deleted file mode 100644 index 65c6fc966cc..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/sequence/SequentialPositionIterator.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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 - * - * 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.sequence; - -import java.util.NoSuchElementException; -import org.tensorflow.ndarray.impl.dimension.DimensionalSpace; - -class SequentialPositionIterator implements PositionIterator { - - @Override - public boolean hasNext() { - return index < end; - } - - @Override - public long nextLong() { - if (!hasNext()) { - throw new NoSuchElementException(); - } - return stride * index++; - } - - SequentialPositionIterator(DimensionalSpace dimensions, int dimensionIdx) { - long size = 1; - for (int i = 0; i <= dimensionIdx; ++i) { - size *= dimensions.get(i).numElements(); - } - this.stride = dimensions.get(dimensionIdx).elementSize(); - this.end = size; - } - - SequentialPositionIterator(long stride, long end) { - this.stride = stride; - this.end = end; - } - - private final long stride; - private final long end; - private long index; -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/sequence/SingleElementSequence.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/sequence/SingleElementSequence.java deleted file mode 100644 index 59525bf486b..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/sequence/SingleElementSequence.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * 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 - * - * 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.sequence; - -import java.util.Iterator; -import java.util.function.BiConsumer; -import org.tensorflow.ndarray.IllegalRankException; -import org.tensorflow.ndarray.NdArray; -import org.tensorflow.ndarray.NdArraySequence; -import org.tensorflow.ndarray.impl.AbstractNdArray; - -/** - * A sequence of one single element - * - * @param Type of the element - * @param Type of the {@code NdArray} with this sequence - */ -public final class SingleElementSequence> implements NdArraySequence { - - public SingleElementSequence(AbstractNdArray ndArray) { - this.ndArray = ndArray; - } - - @Override - public Iterator iterator() { - return new Iterator() { - - @Override - public boolean hasNext() { - return element != null; - } - - @Override - public U next() { - U ret = element; - element = null; - return ret; - } - - @SuppressWarnings("unchecked") - private U element = (U)ndArray; - }; - } - - @Override - public NdArraySequence asSlices() { - return this; // no need to slice, as there are only one element - } - - @Override - public void forEachIndexed(BiConsumer consumer) { - throw new IllegalRankException("Single element has no coordinates to iterate on, use forEach()"); - } - - private final AbstractNdArray ndArray; -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/impl/sequence/SlicingElementSequence.java b/ndarray/src/main/java/org/tensorflow/ndarray/impl/sequence/SlicingElementSequence.java deleted file mode 100644 index 6fe8398ea70..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/impl/sequence/SlicingElementSequence.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * 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 - * - * 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.sequence; - -import java.util.Iterator; -import java.util.function.BiConsumer; -import org.tensorflow.ndarray.NdArray; -import org.tensorflow.ndarray.NdArraySequence; -import org.tensorflow.ndarray.impl.AbstractNdArray; -import org.tensorflow.ndarray.impl.dimension.DimensionalSpace; - -/** - * A sequence creating a new {@code NdArray} instance (slice) for each element of an iteration - * - * @param Type of the element - * @param Type of the {@code NdArray} with this sequence - */ -public final class SlicingElementSequence> implements NdArraySequence { - - public SlicingElementSequence(AbstractNdArray ndArray, int dimensionIdx) { - this(ndArray, dimensionIdx, ndArray.dimensions().from(dimensionIdx + 1)); - } - - public SlicingElementSequence(AbstractNdArray ndArray, int dimensionIdx, DimensionalSpace elementDimensions) { - this.ndArray = ndArray; - this.dimensionIdx = dimensionIdx; - this.elementDimensions = elementDimensions; - } - - @Override - public Iterator iterator() { - PositionIterator positionIterator = PositionIterator.create(ndArray.dimensions(), dimensionIdx); - return new Iterator() { - - @Override - public boolean hasNext() { - return positionIterator.hasNext(); - } - - @Override - public U next() { - return ndArray.slice(positionIterator.next(), elementDimensions); - } - }; - } - - @Override - public void forEachIndexed(BiConsumer consumer) { - PositionIterator.createIndexed(ndArray.dimensions(), dimensionIdx).forEachIndexed((long[] coords, long position) -> - consumer.accept(coords, ndArray.slice(position, elementDimensions)) - ); - } - - @Override - public NdArraySequence asSlices() { - return this; - } - - private final AbstractNdArray ndArray; - private final int dimensionIdx; - private final DimensionalSpace elementDimensions; -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/index/All.java b/ndarray/src/main/java/org/tensorflow/ndarray/index/All.java deleted file mode 100644 index 9d3139f3248..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/index/All.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - 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 - - 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.index; - -import org.tensorflow.ndarray.impl.dimension.Dimension; - -final class All implements Index { - - static final All INSTANCE = new All(); - - @Override - public long numElements(Dimension dim) { - return dim.numElements(); - } - - @Override - public long mapCoordinate(long coordinate, Dimension dim) { - return coordinate; - } - - @Override - public Dimension apply(Dimension dim) { - return dim; - } - - private All() { - } - - @Override - public boolean beginMask() { - return true; - } - - @Override - public boolean endMask() { - return true; - } - - @Override - public String toString() { - return All.class.getSimpleName() + "()"; - } -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/index/At.java b/ndarray/src/main/java/org/tensorflow/ndarray/index/At.java deleted file mode 100644 index 31ce021ddc8..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/index/At.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - 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 - - 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.index; - -import java.util.StringJoiner; -import org.tensorflow.ndarray.impl.dimension.Dimension; - -final class At implements Index { - - @Override - public long numElements(Dimension dim) { - return 1; - } - - @Override - public long mapCoordinate(long coordinate, Dimension dim) { - long coord = this.coord >= 0 ? this.coord : dim.numElements() + this.coord; - return dim.positionOf(coord); - } - - @Override - public Dimension apply(Dimension dim) { - if (!keepDim) { - throw new UnsupportedOperationException("Should be handled in DimensionalSpace."); - } - - return dim.withIndex(this); - } - - @Override - public boolean isPoint() { - return !keepDim; - } - - At(long coord, boolean keepDim) { - this.coord = coord; - this.keepDim = keepDim; - } - - private final long coord; - private final boolean keepDim; - - @Override - public long begin() { - return coord; - } - - @Override - public long end() { - return coord + 1; - } - - @Override - public String toString() { - return new StringJoiner(", ", At.class.getSimpleName() + "(", ")") - .add("coord=" + coord) - .add("keepDim=" + keepDim) - .toString(); - } -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/index/Ellipsis.java b/ndarray/src/main/java/org/tensorflow/ndarray/index/Ellipsis.java deleted file mode 100644 index d4085735df2..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/index/Ellipsis.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - Copyright 2020 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.index; - -import org.tensorflow.ndarray.impl.dimension.Dimension; - -final class Ellipsis implements Index { - - static final Ellipsis INSTANCE = new Ellipsis(); - - private Ellipsis() { - - } - - @Override - public long numElements(Dimension dim) { - throw new UnsupportedOperationException("Should be handled in DimensionalSpace."); - } - - @Override - public long mapCoordinate(long coordinate, Dimension dim) { - throw new UnsupportedOperationException("Should be handled in DimensionalSpace."); - } - - @Override - public boolean isEllipsis() { - return true; - } - - @Override - public String toString() { - return Ellipsis.class.getSimpleName() + "()"; - } -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/index/Hyperslab.java b/ndarray/src/main/java/org/tensorflow/ndarray/index/Hyperslab.java deleted file mode 100644 index 55c4e510748..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/index/Hyperslab.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright 2020 Matteo Di Giovinazzo. - * - * 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.index; - -import java.util.StringJoiner; -import org.tensorflow.ndarray.impl.dimension.Dimension; - -/** - * A hyperslab is a rectangular pattern defined by four arrays. - * - * The {@code start} defines the origin of the hyperslab in the original coordinates. - * The {@code stride} is the number of elements to increment between selected elements. - * A stride of '1' is every element, a stride of '2' is every second element, etc. - * The default stride is 1. - * The {@code count} is the number of elements in the hyperslab selection. - * When the stride is 1, the selection is a hyper rectangle with a corner at {@code start} - * and size {@code count[0]} by {@code count[1]} by ... - * When stride is greater than one, the hyperslab bounded by start and the corners - * defined by {@code stride[n] * count[n]}. - * The {@code block} is a count on the number of repetitions of the hyperslab. - * The default block size is '1', which is one hyperslab. A block of 2 would be - * two hyperslabs in that dimension, with the second starting at {@code start[n]+ (count[n] * stride[n]) + 1}. - * - * @see https://portal.hdfgroup.org/display/HDF5/Reading+From+or+Writing+To+a+Subset+of+a+Dataset - * @see https://portal.hdfgroup.org/display/HDF5/H5S_SELECT_HYPERSLAB - * @see https://support.hdfgroup.org/HDF5/doc1.6/UG/12_Dataspaces.html - * @author Matteo Di Giovinazzo - */ -final class Hyperslab implements Index { - - @Override - public long numElements(Dimension dimension) { - return count * block; - } - - @Override - public long mapCoordinate(long coordinate, Dimension dimension) { - return start + stride * (coordinate / block) + (coordinate % block); - } - - @Override - public Dimension apply(Dimension dim) { - return dim.withIndex(this); - } - - @Override - public boolean isPoint() { - return false; - } - - Hyperslab(long start, long stride, long count, long block) { - this.start = start; - this.stride = stride; - this.count = count; - this.block = block; - } - - private final long start; - private final long stride; - private final long count; - private final long block; - - @Override - public String toString() { - return new StringJoiner(", ", Hyperslab.class.getSimpleName() + "Hyperslab(", ")") - .add("start=" + start) - .add("stride=" + stride) - .add("count=" + count) - .add("block=" + block) - .toString(); - } - - @Override - public boolean isStridedSlicingCompliant() { - return false; - } -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/index/Index.java b/ndarray/src/main/java/org/tensorflow/ndarray/index/Index.java deleted file mode 100644 index 617ca4d474b..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/index/Index.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - 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 - - 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.index; - -import org.tensorflow.ndarray.NdArray; -import org.tensorflow.ndarray.impl.dimension.Dimension; - -/** - * An index used for slicing a view out of an N-dimensional array. - * - *

A slice, i.e. a reduced view, of an N-dimensional array is obtain by calling - * {@link NdArray#slice(Index...)}, given a list of indices that select which elements on a given dimension should be - * included/excluded from that view. - */ -public interface Index { - - /** - * Returns the number of elements that can be retrieved using this index on the given dimension. - * - *

An index that maps one-by-one all elements of the dimensions will return a value - * equal to {@code dim.numElements()}, while an index that only maps a subset of these will return a smaller value. - * - * @param dim the indexed dimension - * @return number of elements accessible - */ - long numElements(Dimension dim); - - /** - * Transforms an element coordinate to a new coordinate by applying this index to the given dimension. - * - *

For example, if the coordinate is 0 and this index flips the {@code n} elements on this - * dimension, then the returned value will be {@code n-1}. - * - * @param coordinate coordinate to transform - * @param dim dimension the indexed dimension - * @return transformed coordinate - */ - long mapCoordinate(long coordinate, Dimension dim); - - /** - * Applies this index to the given dimension. - * - *

When accessing the elements from the returned dimension, this index will automatically - * apply and may transform the original position. - * - * @param dim dimension to apply this index to - * @return an indexed dimension - */ - default Dimension apply(Dimension dim) { - return dim.withIndex(this); - } - - /** - * Returns true if this index is a single point, reducing the number of dimensions by one - */ - default boolean isPoint() { - return false; - } - - /** - * Returns true if this index is a new axis, adding a dimension of size 1 - */ - default boolean isNewAxis() { - return false; - } - - /** - * Returns true if this index is an ellipsis, expanding to take as many dimensions as possible (and applying all() to - * them) - */ - default boolean isEllipsis() { - return false; - } - - /** - * Get whether the Index supports strided slice style indexing (using start, end, stride, and flags, i.e. TensorFlow's). - */ - default boolean isStridedSlicingCompliant() { - return true; - } - - /** - * Get the start of the index, for strided slice style indexing. - */ - default long begin() { - return 0; - } - - /** - * Get the end of the index, strided slice style indexing. - */ - default long end() { - return 0; - } - - /** - * Get the stride of the index, for strided slice style indexing. - */ - default long stride() { - return 1; - } - - /** - * Get whether the Index should start at the beginning of the dimension, for strided slice style indexing. - */ - default boolean beginMask() { - return false; - } - - /** - * Get whether the Index should end at the beginning of the dimension, for strided slice style indexing. - */ - default boolean endMask() { - return false; - } -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/index/Indices.java b/ndarray/src/main/java/org/tensorflow/ndarray/index/Indices.java deleted file mode 100644 index 346ab705595..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/index/Indices.java +++ /dev/null @@ -1,363 +0,0 @@ -/* - 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 - - 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.index; - -import org.tensorflow.ndarray.IllegalRankException; -import org.tensorflow.ndarray.NdArray; -import org.tensorflow.ndarray.NdArrays; -import org.tensorflow.ndarray.Shape; -import org.tensorflow.ndarray.buffer.DataBuffers; - -/** - * Helper class for instantiating {@link Index} objects. - */ -public final class Indices { - - /** - * A coordinate that selects a specific element on a given dimension. - * - *

When this index is applied to a given dimension, the dimension is resolved as a - * single element and therefore is excluded from the computation of the rank. - * - *

For example, given a 3D matrix on the axis [x, y, z], if - * {@code matrix.slice(all(), at(0), at(0)}, then the rank of the returned slice is 1 and its number of elements is - * {@code x.numElements()} - * - * @param coord coordinate of the element on the indexed axis - * @return index - */ - public static Index at(long coord) { - return new At(coord, false); - } - - /** - * A coordinate that selects a specific element on a given dimension. - * - *

This is equivalent to call {@link #at(long)} but where the value of the coordinate is - * provided by an N-dimensional array. - * - * @param coord scalar indicating the coordinate of the element on the indexed axis - * @return index - * @throws IllegalRankException if {@code coord} is not a scalar (rank 0) - */ - public static Index at(NdArray coord) { - if (coord.rank() > 0) { - throw new IllegalRankException("Only scalars are accepted as a value index"); - } - return new At(coord.getObject().longValue(), false); - } - - /** - * A coordinate that selects a specific element on a given dimension. - * - *

When this index is applied to a given dimension, the dimension is resolved as a - * single element and therefore, if {@code keepDim} is false, is excluded from the computation of the rank. If {@code} - * keepDim is true, the dimension is collapsed down to one element. - * - *

For example, given a 3D matrix on the axis [x, y, z], if - * {@code matrix.slice(all(), at(0), at(0)}, then the rank of the returned slice is 1 and its number of elements is - * {@code x.numElements()} - * - * @param coord coordinate of the element on the indexed axis - * @param keepDim whether to remove the dimension. - * @return index - */ - public static Index at(long coord, boolean keepDim) { - return new At(coord, keepDim); - } - - /** - * A coordinate that selects a specific element on a given dimension. - * - *

This is equivalent to call {@link #at(long, boolean)} but where the value of the coordinate is - * provided by an N-dimensional array. - *

- * If {@code} keepDim is true, the dimension is collapsed down to one element instead of being removed. - * - * @param coord scalar indicating the coordinate of the element on the indexed axis - * @param keepDim whether to remove the dimension. - * @return index - * @throws IllegalRankException if {@code coord} is not a scalar (rank 0) - */ - public static Index at(NdArray coord, boolean keepDim) { - if (coord.rank() > 0) { - throw new IllegalRankException("Only scalars are accepted as a value index"); - } - return new At(coord.getObject().longValue(), keepDim); - } - - /** - * An index that returns all elements of a dimension in the original order. - * - *

Applying this index to a given dimension will return the original dimension - * directly. - * - *

For example, given a vector with {@code n} elements, {@code all()} returns - * x0, x1, ..., xn-1 - * - * @return index - */ - public static Index all() { - return All.INSTANCE; - } - - /** - * An index that returns only specific elements on a given dimension. - * - *

For example, given a vector with {@code n} elements on the {@code x} axis, and {@code n > 10}, - * {@code seq(8, 0, 3)} returns x8, x0, x3 - * - * @param coords coordinates of the elements in the sequence - * @return index - */ - public static Index seq(long... coords) { - if (coords == null) { - throw new IllegalArgumentException(); - } - return new Sequence(NdArrays.wrap(Shape.of(coords.length), DataBuffers.of(coords, true, false))); - } - - /** - * An index that returns only specific elements on a given dimension. - * - *

This is equivalent to {@link #seq(long...)} but where the coordinates of the elements in - * the sequence are provided by an N-dimensional array. - * - * @param coords vector of coordinates of the elements in the sequence - * @return index - * @throws IllegalRankException if {@code coords} is not a vector (rank 1) - */ - public static Index seq(NdArray coords) { - if (coords.rank() != 1) { - throw new IllegalRankException("Only vectors are accepted as an element index"); - } - return new Sequence(coords); - } - - /** - * An index that returns only elements found at an even position in the original dimension. - * - *

For example, given a vector with {@code n} elements on the {@code x} axis, and n is even, - * {@code even()} returns x0, x2, ..., xn-2 - * - * @return index - */ - public static Index even() { - return step(2); - } - - /** - * An index that returns only elements found at an odd position in the original dimension. - * - *

For example, given a vector with {@code n} elements on the {@code x} axis, and n is even, - * {@code odd()} returns x1, x3, ..., xn-1 - * - * @return index - */ - public static Index odd() { - return sliceFrom(1, 2); - } - - /** - * An index that skips a fixed amount of coordinates between each values returned. - * - *

For example, given a vector with {@code n} elements on the {@code x} axis, - * {@code step(k)} returns x0, xk, xk*2, ... - * - * @param stride the number of elements between each steps - * @return index - */ - public static Index step(long stride) { - return new Step(stride); - } - - /** - * An index that returns only elements on a given dimension starting at a specific coordinate. - * - *

For example, given a vector with {@code n} elements on the {@code x} axis, and {@code n > k}, - * {@code from(k)} returns xk, xk+1, ..., xn-1 - * - * @param start coordinate of the first element of the sequence - * @return index - */ - public static Index sliceFrom(long start) { - return sliceFrom(start, 1); - } - - /** - * An index that returns only elements on a given dimension starting at a specific coordinate, using the given - * stride. - * - *

For example, given a vector with {@code n} elements on the {@code x} axis, and {@code n > k}, - * {@code from(k)} returns xk, xk+1, ..., xn-1 - * - * @param start coordinate of the first element of the sequence - * @param stride the stride to use - * @return index - * @see #slice(long, long, long) - */ - public static Index sliceFrom(long start, long stride) { - return new SliceFrom(start, stride); - } - - /** - * An index that returns only elements on a given dimension up to a specific coordinate. - * - *

For example, given a vector with {@code n} elements on the {@code x} axis, and {@code n > k}, - * {@code to(k)} returns x0, x1, ..., xk - * - * @param end coordinate of the last element of the sequence (exclusive) - * @return index - */ - public static Index sliceTo(long end) { - return sliceTo(end, 1); - } - - /** - * An index that returns only elements on a given dimension up to a specific coordinate, using the given stride. - * - *

For example, given a vector with {@code n} elements on the {@code x} axis, and {@code n > k}, - * {@code to(k)} returns x0, x1, ..., xk - * - * @param end coordinate of the last element of the sequence (exclusive) - * @param stride the stride to use - * @return index - * @see #slice(long, long, long) - */ - public static Index sliceTo(long end, long stride) { - return new SliceTo(end, stride); - } - - /** - * An index that returns only elements on a given dimension between two coordinates. - * - *

For example, given a vector with {@code n} elements on the {@code x} axis, and {@code n > k > j}, - * {@code range(j, k)} returns xj, xj+1, ..., xk - * - * @param start coordinate of the first element of the sequence - * @param end coordinate of the last element of the sequence (exclusive) - * @return index - */ - public static Index range(long start, long end) { - return slice(start, end); - } - - /** - * An index that returns only elements on a given dimension between two coordinates. - * - *

For example, given a vector with {@code n} elements on the {@code x} axis, and {@code n > k > j}, - * {@code range(j, k)} returns xj, xj+1, ..., xk - * - * @return index - */ - public static Index flip() { - return slice(null, null, -1); - } - - /** - * An index that returns elements according to an hyperslab defined by {@code start}, {@code stride}, {@code count}, - * {@code block}. See {@link Hyperslab}. - * - * @param start Starting location for the hyperslab. - * @param stride The number of elements to separate each element or block to be selected. - * @param count The number of elements or blocks to select along the dimension. - * @param block The size of the block selected from the dimension. - * @return index - */ - public static Index hyperslab(long start, long stride, long count, long block) { - return new Hyperslab(start, stride, count, block); - } - - /** - * An index that inserts a new dimension of size 1 into the resulting array. - * - * @return index - */ - public static Index newAxis() { - return NewAxis.INSTANCE; - } - - /** - * An index that expands to fill all available source dimensions. Works the same as Python's {@code ...}. - * - * @return index - */ - public static Index ellipsis() { - return Ellipsis.INSTANCE; - } - - /** - * An index that returns elements between {@code start} and {@code end}. If {@code start} or {@code end} is {@code - * null}, starts or ends at the beginning or the end, respectively. - *

- * Analogous to Python's {@code :} slice syntax. - * - * @return index - */ - public static Index slice(long start, long end) { - return slice(start, end, 1); - } - - /** - * An index that returns every {@code stride}-th element between {@code start} and {@code end}. If {@code start} or - * {@code end} is {@code null}, starts or ends at the beginning or the end, respectively. - *

- * Analogous to Python's {@code :} slice syntax. - * - * @return index - */ - public static Index slice(long start, long end, long stride) { - return new Slice(start, end, stride); - } - - /** - * An index that returns elements between {@code start} and {@code end}. If {@code start} or {@code end} is {@code - * null}, starts or ends at the beginning or the end, respectively. - *

- * Analogous to Python's {@code :} slice syntax. - * - * @return index - */ - public static Index slice(Long start, Long end) { - return slice(start, end, 1); - } - - /** - * An index that returns every {@code stride}-th element between {@code start} and {@code end}. If {@code start} or - * {@code end} is {@code null}, starts or ends at the beginning or the end, respectively. - *

- * Analogous to Python's {@code :} slice syntax. - * - * @return index - */ - public static Index slice(Long start, Long end, long stride) { - if (start == null && end == null) { - if (stride == 1) { - return Indices.all(); - } else { - return Indices.step(stride); - } - } else if (start == null) { - return Indices.sliceTo(end, stride); - } else if (end == null) { - return Indices.sliceFrom(start, stride); - } - - return slice(start.longValue(), end.longValue(), stride); - } - -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/index/NewAxis.java b/ndarray/src/main/java/org/tensorflow/ndarray/index/NewAxis.java deleted file mode 100644 index a68b1ed9ad1..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/index/NewAxis.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - Copyright 2020 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.index; - -import org.tensorflow.ndarray.impl.dimension.Dimension; - -final class NewAxis implements Index { - - static final NewAxis INSTANCE = new NewAxis(); - - private NewAxis() { - - } - - @Override - public long numElements(Dimension dim) { - return 1; - } - - @Override - public long mapCoordinate(long coordinate, Dimension dim) { - return coordinate; - } - - @Override - public Dimension apply(Dimension dim) { - throw new IllegalStateException(); - } - - @Override - public boolean isNewAxis() { - return true; - } - - @Override - public String toString() { - return NewAxis.class.getSimpleName() + "()"; - } -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/index/Sequence.java b/ndarray/src/main/java/org/tensorflow/ndarray/index/Sequence.java deleted file mode 100644 index 5b93e434e54..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/index/Sequence.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - 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 - - 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.index; - -import java.util.StringJoiner; -import org.tensorflow.ndarray.NdArray; -import org.tensorflow.ndarray.impl.dimension.Dimension; - -final class Sequence implements Index { - - @Override - public long numElements(Dimension dim) { - return coords.size(); - } - - @Override - public long mapCoordinate(long coordinate, Dimension dim) { - return coords.getObject(coordinate).longValue(); - } - - Sequence(NdArray coords) { - this.coords = coords; - } - - private final NdArray coords; - - @Override - public String toString() { - return new StringJoiner(", ", Sequence.class.getSimpleName() + "(", ")") - .add("coords=" + coords) - .toString(); - } - - @Override - public boolean isStridedSlicingCompliant() { - return false; - } -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/index/Slice.java b/ndarray/src/main/java/org/tensorflow/ndarray/index/Slice.java deleted file mode 100644 index 1be4368261c..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/index/Slice.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - Copyright 2020 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.index; - -import java.util.StringJoiner; -import org.tensorflow.ndarray.impl.dimension.Dimension; - -final class Slice implements Index { - - Slice(long start, long end, long stride) { - this.start = start; - this.end = end; - this.stride = stride; - - if (stride == 0) { - throw new IllegalArgumentException("Can not have a stride of 0"); - } - } - - @Override - public long numElements(Dimension dim) { - long length = end(dim) - start(dim); - - return (length / stride) + (length % stride != 0 ? 1 : 0); - } - - @Override - public long mapCoordinate(long coordinate, Dimension dim) { - return start(dim) + stride * coordinate; - } - - @Override - public long begin() { - return start; - } - - @Override - public long end() { - return end; - } - - @Override - public long stride() { - return stride; - } - - @Override - public String toString() { - return new StringJoiner(", ", Slice.class.getSimpleName() + "(", ")") - .add("start=" + start) - .add("end=" + end) - .add("stride=" + stride) - .toString(); - } - - private long start(Dimension dim) { - if (start < 0) { - return dim.numElements() + start; - } - - return start; - } - - private long end(Dimension dim) { - if (end < 0) { - return dim.numElements() + end; - } else { - return end; - } - } - - private final long start; - private final long end; - private final long stride; -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/index/SliceFrom.java b/ndarray/src/main/java/org/tensorflow/ndarray/index/SliceFrom.java deleted file mode 100644 index c968a325cf7..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/index/SliceFrom.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - Copyright 2020 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.index; - -import java.util.StringJoiner; -import org.tensorflow.ndarray.impl.dimension.Dimension; - -final class SliceFrom implements Index { - - SliceFrom(long start, long stride) { - this.start = start; - this.stride = stride; - - if (stride == 0) { - throw new IllegalArgumentException("Can not have a stride of 0"); - } - } - - @Override - public long numElements(Dimension dim) { - long length = end(dim) - start(dim); - - return (length / stride) + (length % stride != 0 ? 1 : 0); - } - - @Override - public long mapCoordinate(long coordinate, Dimension dim) { - return start(dim) + stride * coordinate; - } - - @Override - public long begin() { - return start; - } - - @Override - public boolean endMask() { - return true; - } - - @Override - public long stride() { - return stride; - } - - @Override - public String toString() { - return new StringJoiner(", ", SliceFrom.class.getSimpleName() + "(", ")") - .add("start=" + start) - .add("stride=" + stride) - .toString(); - } - - private long start(Dimension dim) { - if (start < 0) { - return dim.numElements() + start; - } - - return start; - } - - private long end(Dimension dim) { - if (stride > 0) { - return dim.numElements(); - } else { - return -1; // it's exclusive - } - } - - private final long start; - private final long stride; -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/index/SliceTo.java b/ndarray/src/main/java/org/tensorflow/ndarray/index/SliceTo.java deleted file mode 100644 index 761d1d52a3a..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/index/SliceTo.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - Copyright 2020 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.index; - -import java.util.StringJoiner; -import org.tensorflow.ndarray.impl.dimension.Dimension; - -final class SliceTo implements Index { - - SliceTo(long end, long stride) { - this.end = end; - this.stride = stride; - - if (stride == 0) { - throw new IllegalArgumentException("Can not have a stride of 0"); - } - } - - @Override - public long numElements(Dimension dim) { - long length = end(dim) - start(dim); - - return (length / stride) + (length % stride != 0 ? 1 : 0); - } - - @Override - public long mapCoordinate(long coordinate, Dimension dim) { - return start(dim) + stride * coordinate; - } - - @Override - public long end() { - return end; - } - - @Override - public boolean beginMask() { - return true; - } - - @Override - public long stride() { - return stride; - } - - @Override - public String toString() { - return new StringJoiner(", ", SliceTo.class.getSimpleName() + "(", ")") - .add("end=" + end) - .add("stride=" + stride) - .toString(); - } - - private long start(Dimension dim) { - if (stride > 0) { - return 0; - } - - return dim.numElements() - 1; // it's inclusive - } - - private long end(Dimension dim) { - if (end < 0) { - return dim.numElements() + end; - } else { - return end; - } - } - - private final long end; - private final long stride; -} diff --git a/ndarray/src/main/java/org/tensorflow/ndarray/index/Step.java b/ndarray/src/main/java/org/tensorflow/ndarray/index/Step.java deleted file mode 100644 index c9a21c507b6..00000000000 --- a/ndarray/src/main/java/org/tensorflow/ndarray/index/Step.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - Copyright 2020 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.index; - -import java.util.StringJoiner; -import org.tensorflow.ndarray.impl.dimension.Dimension; - -final class Step implements Index { - - Step(long stride) { - this.stride = stride; - - if (stride == 0) { - throw new IllegalArgumentException("Can not have a stride of 0"); - } - } - - @Override - public long numElements(Dimension dim) { - long length = end(dim) - start(dim); - - return (length / stride) + (length % stride != 0 ? 1 : 0); - } - - @Override - public long mapCoordinate(long coordinate, Dimension dim) { - return start(dim) + stride * coordinate; - } - - @Override - public boolean beginMask() { - return true; - } - - @Override - public boolean endMask() { - return true; - } - - @Override - public long stride() { - return stride; - } - - @Override - public String toString() { - return new StringJoiner(", ", Step.class.getSimpleName() + "(", ")") - .add("stride=" + stride) - .toString(); - } - - private long start(Dimension dim) { - if (stride > 0) { - return 0; - } - - return dim.numElements() - 1; // it's inclusive - } - - private long end(Dimension dim) { - if (stride > 0) { - return dim.numElements(); - } else { - return -1; // it's exclusive - } - } - - private final long stride; -} diff --git a/ndarray/src/test/java/org/tensorflow/ndarray/BooleanNdArrayTestBase.java b/ndarray/src/test/java/org/tensorflow/ndarray/BooleanNdArrayTestBase.java deleted file mode 100644 index 6426ff5a1c2..00000000000 --- a/ndarray/src/test/java/org/tensorflow/ndarray/BooleanNdArrayTestBase.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - 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 - - 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 static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.tensorflow.ndarray.NdArrays.vectorOf; - -import org.junit.jupiter.api.Test; - -public abstract class BooleanNdArrayTestBase extends NdArrayTestBase { - - @Override - protected abstract BooleanNdArray allocate(Shape shape); - - @Override - protected Boolean valueOf(Long val) { - return val > 0; - } - - @Test - public void iteratePrimitiveElements() { - BooleanNdArray matrix3d = allocate(Shape.of(5, 4, 5)); - - matrix3d.scalars().forEachIndexed((coords, scalar) -> - scalar.setBoolean(coords[2] > 0) - ); - - assertFalse(matrix3d.getBoolean(0, 0, 0)); - assertTrue(matrix3d.getBoolean(0, 0, 1)); - assertTrue(matrix3d.getBoolean(0, 0, 4)); - assertTrue(matrix3d.getBoolean(0, 1, 2)); - - matrix3d.elements(1).forEach(vector -> - vector.set(vectorOf(true, false, true, false, true)) - ); - - assertTrue(matrix3d.getBoolean(0, 0, 0)); - assertFalse(matrix3d.getBoolean(0, 0, 1)); - assertTrue(matrix3d.getBoolean(0, 0, 4)); - assertTrue(matrix3d.getBoolean(0, 1, 2)); - } -} diff --git a/ndarray/src/test/java/org/tensorflow/ndarray/ByteNdArrayTestBase.java b/ndarray/src/test/java/org/tensorflow/ndarray/ByteNdArrayTestBase.java deleted file mode 100644 index 407efffda94..00000000000 --- a/ndarray/src/test/java/org/tensorflow/ndarray/ByteNdArrayTestBase.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - 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 - - 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 static org.junit.jupiter.api.Assertions.assertEquals; - -import org.junit.jupiter.api.Test; - -public abstract class ByteNdArrayTestBase extends NdArrayTestBase { - - @Override - protected abstract ByteNdArray allocate(Shape shape); - - @Override - protected Byte valueOf(Long val) { - return val.byteValue(); - } - - @Test - public void iteratePrimitiveElements() { - ByteNdArray matrix3d = allocate(Shape.of(5, 4, 5)); - - matrix3d.scalars().forEachIndexed((coords, scalar) -> - scalar.setByte((byte)coords[2]) - ); - - assertEquals(0, matrix3d.getByte(0, 0, 0)); - assertEquals(1, matrix3d.getByte(0, 0, 1)); - assertEquals(4, matrix3d.getByte(0, 0, 4)); - assertEquals(2, matrix3d.getByte(0, 1, 2)); - - matrix3d.elements(1).forEach(vector -> - vector.set(NdArrays.vectorOf((byte)5, (byte)6, (byte)7, (byte)8, (byte)9)) - ); - - assertEquals(5, matrix3d.getByte(0, 0, 0)); - assertEquals(6, matrix3d.getByte(0, 0, 1)); - assertEquals(9, matrix3d.getByte(0, 0, 4)); - assertEquals(7, matrix3d.getByte(0, 1, 2)); - } -} diff --git a/ndarray/src/test/java/org/tensorflow/ndarray/DoubleNdArrayTestBase.java b/ndarray/src/test/java/org/tensorflow/ndarray/DoubleNdArrayTestBase.java deleted file mode 100644 index d4f98e2caa0..00000000000 --- a/ndarray/src/test/java/org/tensorflow/ndarray/DoubleNdArrayTestBase.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - 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 - - 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 static org.junit.jupiter.api.Assertions.assertEquals; - -import org.junit.jupiter.api.Test; - -public abstract class DoubleNdArrayTestBase extends NdArrayTestBase { - - @Override - protected abstract DoubleNdArray allocate(Shape shape); - - @Override - protected Double valueOf(Long val) { - return val.doubleValue(); - } - - @Test - public void iteratePrimitiveElements() { - DoubleNdArray matrix3d = allocate(Shape.of(5, 4, 5)); - - matrix3d.scalars().forEachIndexed((coords, scalar) -> - scalar.setDouble((double)coords[2]) - ); - - assertEquals(0.0, matrix3d.getDouble(0, 0, 0), 0.0); - assertEquals(1.0, matrix3d.getDouble(0, 0, 1), 0.0); - assertEquals(4.0, matrix3d.getDouble(0, 0, 4), 0.0); - assertEquals(2.0, matrix3d.getDouble(0, 1, 2), 0.0); - - matrix3d.elements(1).forEach(vector -> - vector.set(NdArrays.vectorOf(5.0, 6.0, 7.0, 8.0, 9.0)) - ); - - assertEquals(5, matrix3d.getDouble(0, 0, 0), 0.0); - assertEquals(6, matrix3d.getDouble(0, 0, 1), 0.0); - assertEquals(9, matrix3d.getDouble(0, 0, 4), 0.0); - assertEquals(7, matrix3d.getDouble(0, 1, 2), 0.0); - } -} diff --git a/ndarray/src/test/java/org/tensorflow/ndarray/FloatNdArrayTestBase.java b/ndarray/src/test/java/org/tensorflow/ndarray/FloatNdArrayTestBase.java deleted file mode 100644 index 55f05ae3de1..00000000000 --- a/ndarray/src/test/java/org/tensorflow/ndarray/FloatNdArrayTestBase.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - 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 - - 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 static org.junit.jupiter.api.Assertions.assertEquals; - -import org.junit.jupiter.api.Test; - -public abstract class FloatNdArrayTestBase extends NdArrayTestBase { - - @Override - protected abstract FloatNdArray allocate(Shape shape); - - @Override - protected Float valueOf(Long val) { - return val.floatValue(); - } - - @Test - public void iteratePrimitiveElements() { - FloatNdArray matrix3d = allocate(Shape.of(5, 4, 5)); - - matrix3d.scalars().forEachIndexed((coords, scalar) -> - scalar.setFloat((float)coords[2]) - ); - - assertEquals(0.0f, matrix3d.getFloat(0, 0, 0), 0.0f); - assertEquals(1.0f, matrix3d.getFloat(0, 0, 1), 0.0f); - assertEquals(4.0f, matrix3d.getFloat(0, 0, 4), 0.0f); - assertEquals(2.0f, matrix3d.getFloat(0, 1, 2), 0.0f); - - matrix3d.elements(1).forEach(vector -> - vector.set(NdArrays.vectorOf(5.0f, 6.0f, 7.0f, 8.0f, 9.0f)) - ); - - assertEquals(5, matrix3d.getFloat(0, 0, 0), 0.0f); - assertEquals(6, matrix3d.getFloat(0, 0, 1), 0.0f); - assertEquals(9, matrix3d.getFloat(0, 0, 4), 0.0f); - assertEquals(7, matrix3d.getFloat(0, 1, 2), 0.0f); - } -} diff --git a/ndarray/src/test/java/org/tensorflow/ndarray/IndexTest.java b/ndarray/src/test/java/org/tensorflow/ndarray/IndexTest.java deleted file mode 100644 index 6f92dab9b99..00000000000 --- a/ndarray/src/test/java/org/tensorflow/ndarray/IndexTest.java +++ /dev/null @@ -1,205 +0,0 @@ -/* - Copyright 2020 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 static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import org.junit.jupiter.api.Test; -import org.tensorflow.ndarray.index.Indices; - -public class IndexTest { - @Test - public void testNullConversions(){ - assertTrue(Indices.slice(null, 0L).beginMask(), - "Passed null for slice start but didn't set begin mask"); - - assertTrue(Indices.slice(null, 0L).beginMask(), - "Passed null for slice start but didn't set begin mask"); - - assertTrue(Indices.slice(null, null).beginMask(), - "Passed null for slice start but didn't set begin mask"); - - assertTrue(Indices.slice(0L, null).endMask(), - "Passed null for slice end but didn't set end mask"); - - assertTrue(Indices.slice(0L, null).endMask(), - "Passed null for slice end but didn't set end mask"); - - assertTrue(Indices.slice(null, null).endMask(), - "Passed null for slice end but didn't set end mask"); - } - - @Test - public void testNewaxis(){ - IntNdArray matrix3d = NdArrays.ofInts(Shape.of(5, 4, 5)); - - matrix3d.scalars().forEachIndexed((coords, scalar) -> - scalar.setInt((int)coords[2]) - ); - - IntNdArray slice1 = matrix3d.slice(Indices.all(), Indices.all(), Indices.all(), Indices.newAxis()); - - assertEquals(Shape.of(5, 4, 5, 1), slice1.shape()); - assertEquals(0, slice1.getInt(0, 0, 0, 0)); - assertEquals(1, slice1.getInt(0, 0, 1, 0)); - assertEquals(4, slice1.getInt(0, 0, 4, 0)); - assertEquals(2, slice1.getInt(0, 1, 2, 0)); - - IntNdArray slice2 = matrix3d.slice(Indices.all(), Indices.all(), Indices.newAxis(), Indices.all()); - - assertEquals(Shape.of(5, 4, 1, 5), slice2.shape()); - assertEquals(0, slice2.getInt(0, 0, 0, 0)); - assertEquals(1, slice2.getInt(0, 0, 0, 1)); - assertEquals(4, slice2.getInt(0, 0, 0, 4)); - assertEquals(2, slice2.getInt(0, 1, 0, 2)); - - IntNdArray slice3 = matrix3d.slice(Indices.all(), Indices.newAxis(), Indices.all(), Indices.all()); - - assertEquals(Shape.of(5, 1, 4, 5), slice3.shape()); - assertEquals(0, slice3.getInt(0, 0, 0, 0)); - assertEquals(1, slice3.getInt(0, 0, 0, 1)); - assertEquals(4, slice3.getInt(0, 0, 0, 4)); - assertEquals(2, slice3.getInt(0, 0, 1, 2)); - - IntNdArray slice4 = matrix3d.slice(Indices.newAxis(), Indices.all(), Indices.all(), Indices.all()); - - assertEquals(Shape.of(1, 5, 4, 5), slice4.shape()); - assertEquals(0, slice4.getInt(0, 0, 0, 0)); - assertEquals(1, slice4.getInt(0, 0, 0, 1)); - assertEquals(4, slice4.getInt(0, 0, 0, 4)); - assertEquals(2, slice4.getInt(0, 0, 1, 2)); - - } - - @Test - public void testEllipsis(){ - IntNdArray matrix3d = NdArrays.ofInts(Shape.of(5, 4, 5)); - - matrix3d.scalars().forEachIndexed((coords, scalar) -> - scalar.setInt((int)coords[2]) - ); - - assertEquals( - matrix3d.slice(Indices.all(), Indices.all(), Indices.at(0)), - matrix3d.slice(Indices.ellipsis(), Indices.at(0)) - ); - - assertEquals( - matrix3d.slice(Indices.at(0), Indices.all(), Indices.all()), - matrix3d.slice(Indices.at(0), Indices.ellipsis()) - ); - - assertEquals( - matrix3d.slice(Indices.at(0), Indices.all(), Indices.at(0)), - matrix3d.slice(Indices.at(0), Indices.ellipsis(), Indices.at(0)) - ); - - // newaxis interacts specially with ellipsis (since it doesn't consume a dimension), test this - - assertEquals( - matrix3d.slice(Indices.all(), Indices.all(), Indices.newAxis(), Indices.at(0)), - matrix3d.slice(Indices.ellipsis(), Indices.newAxis(), Indices.at(0)) - ); - - assertEquals( - matrix3d.slice(Indices.newAxis(), Indices.all(), Indices.all(), Indices.at(0)), - matrix3d.slice(Indices.newAxis(), Indices.ellipsis(), Indices.at(0)) - ); - - assertEquals( - matrix3d.slice(Indices.all(), Indices.all(), Indices.at(0), Indices.newAxis()), - matrix3d.slice(Indices.ellipsis(), Indices.at(0), Indices.newAxis()) - ); - } - - @Test - public void testSlice(){ - IntNdArray matrix3d = NdArrays.ofInts(Shape.of(5, 4, 5)); - - matrix3d.scalars().forEachIndexed((coords, scalar) -> - scalar.setInt((int)coords[2]) - ); - - IntNdArray slice1 = matrix3d.slice(Indices.all(), Indices.sliceTo(3), Indices.all()); - - assertEquals(Shape.of(5, 3, 5), slice1.shape()); - assertEquals(0, slice1.getInt(0, 0, 0)); - assertEquals(1, slice1.getInt(0, 0, 1)); - assertEquals(2, slice1.getInt(0, 1, 2)); - - IntNdArray slice2 = matrix3d.slice(Indices.all(), Indices.all(), Indices.slice(1, 4)); - - assertEquals(Shape.of(5, 4, 3), slice2.shape()); - assertEquals(1, slice2.getInt(0, 0, 0)); - assertEquals(3, slice2.getInt(0, 0, 2)); - assertEquals(2, slice2.getInt(0, 1, 1)); - - assertEquals(slice2, matrix3d.slice(Indices.all(), Indices.all(), Indices.slice(1, -1))); - - assertEquals(slice2, matrix3d.slice(Indices.all(), Indices.all(), Indices.slice(-4, -1))); - - assertEquals(Shape.of(5, 4, 0), matrix3d.slice(Indices.all(), Indices.all(), Indices.slice(1, 4, -2)).shape()); - - IntNdArray slice3 = matrix3d.slice(Indices.all(), Indices.all(), Indices.slice(4, 1, -2)); - - assertEquals(Shape.of(5, 4, 2), slice3.shape()); - assertEquals(4, slice3.getInt(0, 0, 0)); - assertEquals(2, slice3.getInt(0, 1, 1)); - - assertEquals(slice3, matrix3d.slice(Indices.all(), Indices.all(), Indices.slice(-1, 1, -2))); - - assertEquals(slice3, matrix3d.slice(Indices.all(), Indices.all(), Indices.slice(-1, -4, -2))); - - IntNdArray slice4 = matrix3d.slice(Indices.all(), Indices.all(), Indices.slice(null, null, -1)); - - assertEquals(Shape.of(5, 4, 5), slice4.shape()); - assertEquals(4, slice4.getInt(0, 0, 0)); - assertEquals(3, slice4.getInt(0, 0, 1)); - assertEquals(2, slice4.getInt(0, 1, 2)); - } - - @Test - public void testAt(){ - IntNdArray matrix3d = NdArrays.ofInts(Shape.of(5, 4, 5)); - - matrix3d.scalars().forEachIndexed((coords, scalar) -> - scalar.setInt((int)coords[2]) - ); - - IntNdArray slice1 = matrix3d.slice(Indices.all(), Indices.all(), Indices.at(0)); - - assertEquals(Shape.of(5, 4), slice1.shape()); - assertEquals(0, slice1.getInt(0, 0)); - - IntNdArray slice2 = matrix3d.slice(Indices.all(), Indices.all(), Indices.at(3)); - - assertEquals(Shape.of(5, 4), slice2.shape()); - assertEquals(3, slice2.getInt(0, 0)); - - IntNdArray slice3 = matrix3d.slice(Indices.all(), Indices.all(), Indices.at(-3)); - - assertEquals(Shape.of(5, 4), slice3.shape()); - assertEquals(2, slice3.getInt(0, 0)); - - IntNdArray slice4 = matrix3d.slice(Indices.all(), Indices.all(), Indices.at(-3, true)); - - assertEquals(Shape.of(5, 4, 1), slice4.shape()); - assertEquals(2, slice4.getInt(0, 0, 0)); - } - -} diff --git a/ndarray/src/test/java/org/tensorflow/ndarray/IntNdArrayTestBase.java b/ndarray/src/test/java/org/tensorflow/ndarray/IntNdArrayTestBase.java deleted file mode 100644 index 1a3c7cb1a12..00000000000 --- a/ndarray/src/test/java/org/tensorflow/ndarray/IntNdArrayTestBase.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - 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 - - 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 static org.junit.jupiter.api.Assertions.assertEquals; - -import org.junit.jupiter.api.Test; - -public abstract class IntNdArrayTestBase extends NdArrayTestBase { - - @Override - protected abstract IntNdArray allocate(Shape shape); - - @Override - protected Integer valueOf(Long val) { - return val.intValue(); - } - - @Test - public void iteratePrimitiveElements() { - IntNdArray matrix3d = allocate(Shape.of(5, 4, 5)); - - matrix3d.scalars().forEachIndexed((coords, scalar) -> - scalar.setInt((int)coords[2]) - ); - - assertEquals(0, matrix3d.getInt(0, 0, 0)); - assertEquals(1, matrix3d.getInt(0, 0, 1)); - assertEquals(4, matrix3d.getInt(0, 0, 4)); - assertEquals(2, matrix3d.getInt(0, 1, 2)); - - matrix3d.elements(1).forEach(vector -> - vector.set(NdArrays.vectorOf(5, 6, 7, 8, 9)) - ); - - assertEquals(5, matrix3d.getInt(0, 0, 0)); - assertEquals(6, matrix3d.getInt(0, 0, 1)); - assertEquals(9, matrix3d.getInt(0, 0, 4)); - assertEquals(7, matrix3d.getInt(0, 1, 2)); - } -} diff --git a/ndarray/src/test/java/org/tensorflow/ndarray/LongNdArrayTestBase.java b/ndarray/src/test/java/org/tensorflow/ndarray/LongNdArrayTestBase.java deleted file mode 100644 index b91c19d6557..00000000000 --- a/ndarray/src/test/java/org/tensorflow/ndarray/LongNdArrayTestBase.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - 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 - - 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 static org.junit.jupiter.api.Assertions.assertEquals; - -import org.junit.jupiter.api.Test; - -public abstract class LongNdArrayTestBase extends NdArrayTestBase { - - @Override - protected abstract LongNdArray allocate(Shape shape); - - @Override - protected Long valueOf(Long val) { - return val; - } - - @Test - public void iteratePrimitiveElements() { - LongNdArray matrix3d = allocate(Shape.of(5, 4, 5)); - - matrix3d.scalars().forEachIndexed((coords, scalar) -> - scalar.setLong(coords[2]) - ); - - assertEquals(0, matrix3d.getLong(0, 0, 0)); - assertEquals(1, matrix3d.getLong(0, 0, 1)); - assertEquals(4, matrix3d.getLong(0, 0, 4)); - assertEquals(2, matrix3d.getLong(0, 1, 2)); - - matrix3d.elements(1).forEach(vector -> - vector.set(NdArrays.vectorOf(5L, 6L, 7L, 8L, 9L)) - ); - - assertEquals(5, matrix3d.getLong(0, 0, 0)); - assertEquals(6, matrix3d.getLong(0, 0, 1)); - assertEquals(9, matrix3d.getLong(0, 0, 4)); - assertEquals(7, matrix3d.getLong(0, 1, 2)); - } -} diff --git a/ndarray/src/test/java/org/tensorflow/ndarray/NdArrayTestBase.java b/ndarray/src/test/java/org/tensorflow/ndarray/NdArrayTestBase.java deleted file mode 100644 index 26ac533daa8..00000000000 --- a/ndarray/src/test/java/org/tensorflow/ndarray/NdArrayTestBase.java +++ /dev/null @@ -1,338 +0,0 @@ -/* - 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 - - 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 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.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.sliceTo; - -import java.nio.BufferOverflowException; -import java.nio.BufferUnderflowException; -import org.junit.jupiter.api.Test; -import org.tensorflow.ndarray.buffer.DataBuffer; - -public abstract class NdArrayTestBase { - - protected abstract NdArray allocate(Shape shape); - - protected abstract DataBuffer allocateBuffer(long size); - - protected abstract T valueOf(Long val); - - protected T zeroOrNull() { - return valueOf(0L); - } - - @Test - public void shapeAndSizes() { - Shape scalarShape = Shape.scalar(); - NdArray scalar = allocate(scalarShape); - assertEquals(scalarShape, scalar.shape()); - assertEquals(0, scalar.rank()); - assertEquals(scalarShape, Shape.of()); - - Shape vectorShape = Shape.of(10); - NdArray vector = allocate(vectorShape); - assertEquals(vectorShape, vector.shape()); - assertEquals(1, vector.rank()); - } - - @Test - public void setAndGetValues() { - NdArray matrix = allocate(Shape.of(5, 4)); - assertEquals(zeroOrNull(), matrix.getObject(3, 3)); - - matrix.setObject(valueOf(10L), 3, 3); - assertEquals(valueOf(10L), matrix.getObject(3, 3)); - try { - matrix.setObject(valueOf(10L), 3, 4); - fail(); - } catch (IndexOutOfBoundsException e) { - // as expected - } - try { - matrix.setObject(valueOf(10L), -1, 3); - fail(); - } catch (IndexOutOfBoundsException e) { - // as expected - } - try { - matrix.getObject(3); - fail(); - } catch (IllegalRankException e) { - // as expected - } - try { - matrix.setObject(valueOf(10L), 3); - fail(); - } catch (IllegalRankException e) { - // as expected - } - - NdArray matrix2 = allocate(Shape.of(3, 2)) - .set(vectorOfObjects(valueOf(1L), valueOf(2L)), 0) - .set(vectorOfObjects(valueOf(3L), valueOf(4L)), 1) - .setObject(valueOf(5L), 2, 0) - .setObject(valueOf(6L), 2, 1); - - assertEquals(valueOf(1L), matrix2.getObject(0, 0)); - assertEquals(valueOf(2L), matrix2.getObject(0, 1)); - assertEquals(valueOf(3L), matrix2.getObject(1, 0)); - assertEquals(valueOf(4L), matrix2.getObject(1, 1)); - assertEquals(valueOf(5L), matrix2.getObject(2, 0)); - assertEquals(valueOf(6L), matrix2.getObject(2, 1)); - } - - @Test - public void iterateElements() { - NdArray matrix3d = allocate(Shape.of(5, 4, 5)); - - matrix3d.scalars().forEachIndexed((coords, scalar) -> { - scalar.setObject(valueOf(coords[2])); - }); - - assertEquals(valueOf(0L), matrix3d.getObject(0, 0, 0)); - assertEquals(valueOf(1L), matrix3d.getObject(0, 0, 1)); - assertEquals(valueOf(4L), matrix3d.getObject(0, 0, 4)); - assertEquals(valueOf(2L), matrix3d.getObject(0, 1, 2)); - - matrix3d.elements(1).forEach(vector -> { - vector.set(vectorOfObjects(valueOf(5L), valueOf(6L), valueOf(7L), valueOf(8L), valueOf(9L))); - }); - - assertEquals(valueOf(5L), matrix3d.getObject(0, 0, 0)); - assertEquals(valueOf(6L), matrix3d.getObject(0, 0, 1)); - assertEquals(valueOf(9L), matrix3d.getObject(0, 0, 4)); - assertEquals(valueOf(7L), matrix3d.getObject(0, 1, 2)); - - 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)); - - for (NdArray vector : matrix.elements(0)) { - assertEquals(1L, vector.shape().numDimensions()) ; - assertEquals(5L, vector.shape().size(0)); - - for (NdArray scalar : vector.scalars()) { - assertEquals(0L, scalar.shape().numDimensions()) ; - scalar.setObject(valueOf(value++)); - try { - scalar.elements(0); - fail(); - } catch (IllegalArgumentException e) { - // as expected - } - } - } - } - assertEquals(valueOf(0L), matrix3d.getObject(0, 0, 0)); - assertEquals(valueOf(5L), matrix3d.getObject(0, 1, 0)); - assertEquals(valueOf(9L), matrix3d.getObject(0, 1, 4)); - assertEquals(valueOf(20L), matrix3d.getObject(1, 0, 0)); - assertEquals(valueOf(25L), matrix3d.getObject(1, 1, 0)); - assertEquals(valueOf(99L), matrix3d.getObject(4, 3, 4)); - } - - @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); - matrix3d.setObject(val101, 1, 0, 1); - - // Vector (1,0,*) - NdArray vector10X = matrix3d.get(1, 0); - assertEquals(Shape.of(5), vector10X.shape()); - assertEquals(val100, vector10X.getObject(0)); - assertEquals(val101, vector10X.getObject(1)); - - T val102 = valueOf(102L); - vector10X.setObject(val102, 2); - assertEquals(val102, vector10X.getObject(2)); - assertEquals(val102, matrix3d.getObject(1, 0, 2)); - - // Vector (*,0,0) - NdArray vectorX00 = matrix3d.slice(all(), at(0), at(0)); - assertEquals(Shape.of(5), vectorX00.shape()); - assertEquals(val100, vectorX00.getObject(1)); - T val200 = valueOf(200L); - vectorX00.setObject(val200, 2); - assertEquals(val200, vectorX00.getObject(2)); - assertEquals(val200, matrix3d.getObject(2, 0, 0)); - - // Vector (1,0,[2,0]) - NdArray vector10_20 = matrix3d.slice(at(1), at(0), seq(2, 0)); - assertEquals(vector10_20.shape(), Shape.of(2)); - assertEquals(val102, vector10_20.getObject(0)); - assertEquals(val100, vector10_20.getObject(1)); - - // Vector (1,0,[even]) - NdArray vector10_even = matrix3d.slice(at(1), at(0), even()); - assertEquals(vector10_even.shape(), Shape.of(3)); - assertEquals(val100, vector10_even.getObject(0)); - assertEquals(val102, vector10_even.getObject(1)); - - // Vector ([odd]) from vector (1,0,[even]) - NdArray vector10_even_odd = vector10_even.slice(odd()); - assertEquals(vector10_even_odd.shape(), Shape.of(1)); - assertEquals(val102, vector10_even_odd.getObject(0)); - - // Vector (1,0,[flip]) - NdArray vector10_flip = matrix3d.slice(at(1), at(0), flip()); - assertEquals(vector10_flip.shape(), Shape.of(5)); - assertEquals(val100, vector10_flip.getObject(4)); - assertEquals(val101, vector10_flip.getObject(3)); - - // Vector (1,0,[from 1]) from vector (1,0,*) - NdArray vector10_1toX = vector10X.slice(sliceFrom(1)); - assertEquals(vector10_1toX.shape(), Shape.of(4)); - assertEquals(val101, vector10_1toX.getObject(0)); - assertEquals(val102, vector10_1toX.getObject(1)); - - // Vector (1,0,[to 1]) from vector (1,0,*) - NdArray vector10_Xto1 = vector10X.slice(sliceTo(2)); - assertEquals(vector10_Xto1.shape(), Shape.of(2)); - assertEquals(val100, vector10_Xto1.getObject(0)); - assertEquals(val101, vector10_Xto1.getObject(1)); - - // Vector (1,0,[1 to 3]) - NdArray vector10_1to3 = matrix3d.slice(at(1), at(0), range(1, 3)); - assertEquals(vector10_1to3.shape(), Shape.of(2)); - assertEquals(val101, vector10_1to3.getObject(0)); - assertEquals(val102, vector10_1to3.getObject(1)); - - // Scalar (1,0,0) from vector (1,0,*) - NdArray scalar100 = vector10X.get(0); - assertEquals(Shape.of(), scalar100.shape()); - assertEquals(val100, scalar100.getObject()); - - // Slice scalar (1,0,z) - LongNdArray z = NdArrays.scalarOf(2L); - NdArray scalar102 = matrix3d.slice(at(1), at(0), at(z)); - assertEquals(scalar102.shape(), Shape.of()); - assertEquals(val102, scalar102.getObject()); - - // Slicing the 3D matrix so we only keep the first element of the second dimension - NdArray matrix_X0Z = matrix3d.slice(all(), at(0)); - assertEquals(2, matrix_X0Z.rank()); - assertEquals(Shape.of(5, 5), matrix_X0Z.shape()); - assertEquals(val100, matrix_X0Z.getObject(1, 0)); - assertEquals(val101, matrix_X0Z.getObject(1, 1)); - assertEquals(val200, matrix_X0Z.getObject(2, 0)); - } - - @Test - public void writeAndReadWithBuffers() { - DataBuffer buffer = allocateBuffer(15L); - for (long val = 0L; val < buffer.size(); ++val) { - buffer.setObject(valueOf(val), val); - } - NdArray matrix = allocate(Shape.of(3, 5)); - matrix.write(buffer); - assertEquals(valueOf(0L), matrix.getObject(0, 0)); - assertEquals(valueOf(4L), matrix.getObject(0, 4)); - assertEquals(valueOf(5L), matrix.getObject(1, 0)); - assertEquals(valueOf(10L), matrix.getObject(2, 0)); - assertEquals(valueOf(14L), matrix.getObject(2, 4)); - - matrix.setObject(valueOf(100L), 1, 0); - matrix.read(buffer); - assertEquals(valueOf(0L), buffer.getObject(0)); - assertEquals(valueOf(4L), buffer.getObject(4)); - assertEquals(valueOf(100L), buffer.getObject(5)); - assertEquals(valueOf(10L), buffer.getObject(10)); - assertEquals(valueOf(14L), buffer.getObject(14)); - - try { - matrix.write(buffer.narrow(10)); - fail(); - } catch (BufferUnderflowException e) { - // as expected - } - try { - matrix.read(buffer.narrow(10)); - fail(); - } catch (BufferOverflowException e) { - // as expected - } - } - - @Test - public void ndArrayCopies() { - NdArray matrixA = allocate(Shape.of(3, 5)); - - long value = 0L; - for (NdArray s : matrixA.scalars()) { - s.setObject(valueOf(value++)); - } - NdArray matrixB = allocate(Shape.of(3, 5)).setObject(valueOf(100L), 1, 0); - matrixA.copyTo(matrixB); - assertEquals(valueOf(0L), matrixB.getObject(0, 0)); - assertEquals(valueOf(4L), matrixB.getObject(0, 4)); - assertEquals(valueOf(5L), matrixB.getObject(1, 0)); - assertEquals(valueOf(10L), matrixB.getObject(2, 0)); - assertEquals(valueOf(14L), matrixB.getObject(2, 4)); - - NdArray matrixC = allocate(Shape.of(3, 4)); - try { - matrixA.copyTo(matrixC); - fail(); - } catch (IllegalArgumentException e) { - // as expected - } - } - - @Test - public void equalsAndHashCode() { - NdArray array1 = allocate(Shape.of(2, 2)); - NdArray array2 = allocate(Shape.of(2, 2)); - NdArray array3 = allocate(Shape.of(2, 2)); - NdArray array4 = allocate(Shape.of(1, 2, 2)); - - @SuppressWarnings("unchecked") - T[][][] values = (T[][][])(new Object[][][] { - { { valueOf(0L), valueOf(1L) }, { valueOf(2L), valueOf(0L) } } - }); - - StdArrays.copyTo(values[0], array1); - StdArrays.copyTo(values[0], array2); - StdArrays.copyTo(values[0], array3); - array3.setObject(valueOf(0L), 0, 1); - StdArrays.copyTo(values, array4); - - assertEquals(array1, array2); - assertEquals(array1.hashCode(), array2.hashCode()); - assertNotEquals(array1, array3); - assertNotEquals(array1.hashCode(), array3.hashCode()); - assertNotEquals(array1, array4); - assertNotEquals(array1.hashCode(), array4.hashCode()); - } -} diff --git a/ndarray/src/test/java/org/tensorflow/ndarray/ShapeTest.java b/ndarray/src/test/java/org/tensorflow/ndarray/ShapeTest.java deleted file mode 100644 index d2e3e432a2c..00000000000 --- a/ndarray/src/test/java/org/tensorflow/ndarray/ShapeTest.java +++ /dev/null @@ -1,170 +0,0 @@ -/* -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 - - 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 static org.junit.jupiter.api.Assertions.*; - -public class ShapeTest { - - @Test - 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(100, shape.size()); - assertArrayEquals(new long[] {5, 4, 5}, shape.asArray()); - try { - shape.size(3); - fail(); - } catch (IndexOutOfBoundsException e) { - // as expected - } - assertEquals(5, shape.size(-1)); - assertEquals(4, shape.size(-2)); - assertEquals(5, shape.size(-3)); - try { - shape.size(-4); - fail(); - } catch (IndexOutOfBoundsException e) { - // as expected - } - assertFalse(shape.isUnknown()); - assertFalse(shape.hasUnknownDimension()); - assertFalse(shape.isScalar()); - } - - @Test - public void hashCodeEquals() { - Shape shape1 = Shape.of(5, 4, 5); - Shape shape2 = Shape.of(5, 4, 5); - Shape shape3 = Shape.of(5, 4, 5, 6); - Shape shape4 = Shape.of(5, 4, 1); - - assertEquals(shape1, shape2); - assertEquals(shape1.hashCode(), shape2.hashCode()); - assertNotEquals(shape1, shape3); - assertNotEquals(shape1.hashCode(), shape3.hashCode()); - assertNotEquals(shape1, shape4); - assertNotEquals(shape1.hashCode(), shape4.hashCode()); - - Shape scalar1 = Shape.of(); - Shape scalar2 = Shape.of(); - assertEquals(scalar1, scalar2); - assertNotEquals(scalar1, shape1); - - Shape unknown1 = Shape.of(-1, 4, 5); - Shape unknown2 = Shape.of(-1, 4, 5); - assertNotEquals(unknown1, unknown2); - assertNotEquals(unknown1, shape1); - assertEquals(unknown1, unknown1); - - Shape sizeUnknown1 = Shape.unknown(); - Shape sizeUnknown2 = Shape.unknown(); - assertNotEquals(sizeUnknown1, sizeUnknown2); - assertEquals(sizeUnknown1, sizeUnknown1); - } - - @Test - public void testShapeModification() { - Shape one = Shape.of(2, 4, 6, 8); - assertEquals(one.head(), Shape.of(2)); - assertEquals(one.tail(), Shape.of(4, 6, 8)); - - Shape two = Shape.of(5); - assertEquals(two.head(), two); - assertEquals(two.tail(), Shape.of()); - - try { - Shape.of().head(); - fail(); - } catch (IndexOutOfBoundsException e) { - // as expected - } - - assertEquals(Shape.of().tail(), Shape.of()); - - Shape three = Shape.of(2, 4, 6); - assertEquals(three.prepend(5), Shape.of(5, 2, 4, 6)); - - assertEquals(Shape.of(5, 2, 4, 6), two.append(three)); - assertEquals(Shape.of(2, 4, 6, 5), two.prepend(three)); - assertEquals(Shape.of(1, 2, 3, 4), Shape.of(1, 2).append(Shape.of(3, 4))); - assertEquals(Shape.of(1, 2, 3, 4), Shape.of(1, 2, 3).append(4)); - assertEquals(Shape.of(1, 2, 3, 4), Shape.of(1, 2, 3, 4).append(Shape.scalar())); - assertEquals(Shape.of(3, 4, 1, 2), Shape.of(1, 2).prepend(Shape.of(3, 4))); - assertEquals(Shape.of(4, 6), three.takeLast(2)); - assertEquals(Shape.scalar(), three.takeLast(0)); - assertEquals(Shape.of(2, 4), three.take(2)); - assertEquals(Shape.scalar(), three.take(0)); - - try { - Shape.unknown().append(Shape.of(1, 2)); - fail(); - } catch (NullPointerException e) { - // as expected - } - - try { - Shape.unknown().prepend(Shape.of(1, 2)); - fail(); - } catch (NullPointerException e) { - // as expected - } - - // changing the values of the array returned by asArray should not mutate the shape - long[] internalShape = one.asArray(); - assertNotNull(internalShape); - internalShape[0] = 42L; - assertEquals(2L, one.size(0)); - } - - @Test - public void testShapeCompatible() { - Shape a = Shape.unknown(); - Shape b = Shape.of(2, 2); - assertTrue(a.isCompatibleWith(b)); - assertTrue(b.isCompatibleWith(a)); - - a = Shape.of(2, 2); - assertTrue(a.isCompatibleWith(b)); - assertTrue(b.isCompatibleWith(a)); - - a = Shape.of(2, -1); - assertTrue(a.isCompatibleWith(b)); - assertTrue(b.isCompatibleWith(a)); - - a = Shape.of(-1, 2); - assertTrue(a.isCompatibleWith(b)); - assertTrue(b.isCompatibleWith(a)); - - a = Shape.of(-1, -1); - assertTrue(a.isCompatibleWith(b)); - assertTrue(b.isCompatibleWith(a)); - - a = Shape.of(1, 2); - assertFalse(a.isCompatibleWith(b)); - assertFalse(b.isCompatibleWith(a)); - - a = Shape.of(1, 2, 3); - assertFalse(a.isCompatibleWith(b)); - assertFalse(b.isCompatibleWith(a)); - } -} diff --git a/ndarray/src/test/java/org/tensorflow/ndarray/ShortNdArrayTestBase.java b/ndarray/src/test/java/org/tensorflow/ndarray/ShortNdArrayTestBase.java deleted file mode 100644 index f9043fec4f5..00000000000 --- a/ndarray/src/test/java/org/tensorflow/ndarray/ShortNdArrayTestBase.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - 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 - - 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 static org.junit.jupiter.api.Assertions.assertEquals; - -import org.junit.jupiter.api.Test; - -public abstract class ShortNdArrayTestBase extends NdArrayTestBase { - - @Override - protected abstract ShortNdArray allocate(Shape shape); - - @Override - protected Short valueOf(Long val) { - return val.shortValue(); - } - - @Test - public void iteratePrimitiveElements() { - ShortNdArray matrix3d = allocate(Shape.of(5, 4, 5)); - - matrix3d.scalars().forEachIndexed((coords, scalar) -> - scalar.setShort((short)coords[2]) - ); - - assertEquals(0, matrix3d.getShort(0, 0, 0)); - assertEquals(1, matrix3d.getShort(0, 0, 1)); - assertEquals(4, matrix3d.getShort(0, 0, 4)); - assertEquals(2, matrix3d.getShort(0, 1, 2)); - - matrix3d.elements(1).forEach(vector -> - vector.set(NdArrays.vectorOf((short)5, (short)6, (short)7, (short)8, (short)9)) - ); - - assertEquals(5, matrix3d.getShort(0, 0, 0)); - assertEquals(6, matrix3d.getShort(0, 0, 1)); - assertEquals(9, matrix3d.getShort(0, 0, 4)); - assertEquals(7, matrix3d.getShort(0, 1, 2)); - } -} diff --git a/ndarray/src/test/java/org/tensorflow/ndarray/StdArraysTest.java b/ndarray/src/test/java/org/tensorflow/ndarray/StdArraysTest.java deleted file mode 100644 index b7b41564c33..00000000000 --- a/ndarray/src/test/java/org/tensorflow/ndarray/StdArraysTest.java +++ /dev/null @@ -1,211 +0,0 @@ -package org.tensorflow.ndarray; - -import static org.junit.jupiter.api.Assertions.assertArrayEquals; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.fail; - -import org.junit.jupiter.api.Test; - -public class StdArraysTest { - - @Test - public void vectors() { - IntNdArray vector = NdArrays.ofInts(Shape.of(2)); - - StdArrays.copyTo(new int[] {1, 2}, vector); - assertEquals(1, vector.getInt(0)); - assertEquals(2, vector.getInt(1)); - - try { - StdArrays.copyTo(new int[] {1, 2, 3}, vector); - fail(); - } catch (IllegalArgumentException e) { - // as expected - } - try { - StdArrays.copyTo(new int[] {1, 2}, NdArrays.ofInts(Shape.of(4))); - fail(); - } catch (IllegalArgumentException e) { - // as expected - } - try { - StdArrays.copyTo(new int[] {1, 2}, NdArrays.ofInts(Shape.of(2, 2))); - fail(); - } catch (IllegalArgumentException e) { - // as expected - } - - int[] array = StdArrays.array1dCopyOf(vector); - assertEquals(1, array[0]); - assertEquals(2, array[1]); - - array = new int[3]; - StdArrays.copyFrom(vector, array); - assertEquals(1, array[0]); - assertEquals(2, array[1]); - assertEquals(0, array[2]); - - try { - StdArrays.copyFrom(vector, new int[1]); - fail(); - } catch (ArrayIndexOutOfBoundsException e) { - // as expected - } - try { - StdArrays.copyFrom(vector, new int[1][2]); - fail(); - } catch (IllegalArgumentException e) { - // as expected - } - try { - StdArrays.copyFrom(vector, new int[2][2][2]); - fail(); - } catch (IllegalArgumentException e) { - // as expected - } - } - - @Test - public void matrices() { - IntNdArray matrix = NdArrays.ofInts(Shape.of(2, 2)); - - StdArrays.copyTo(new int[][] { - {1, 2}, - {3, 4} - }, matrix); - assertEquals(1, matrix.getInt(0, 0)); - assertEquals(2, matrix.getInt(0, 1)); - assertEquals(3, matrix.getInt(1, 0)); - assertEquals(4, matrix.getInt(1, 1)); - try { - StdArrays.copyTo(new int[][] {{1, 2, 3}, {4, 5, 6}}, matrix); - fail(); - } catch (IllegalArgumentException e) { - // as expected - } - try { - StdArrays.copyTo(new int[][] {{1, 2}, {3, 4}}, NdArrays.ofInts(Shape.of(3, 3))); - fail(); - } catch (IllegalArgumentException e) { - // as expected - } - try { - StdArrays.copyTo(new int[][] {{1, 2}, {3, 4}}, NdArrays.ofInts(Shape.of(2, 2, 1))); - fail(); - } catch (IllegalArgumentException e) { - // as expected - } - - int[][] array = StdArrays.array2dCopyOf(matrix); - assertEquals(1, array[0][0]); - assertEquals(2, array[0][1]); - assertEquals(3, array[1][0]); - assertEquals(4, array[1][1]); - - array = new int[3][3]; - StdArrays.copyFrom(matrix, array); - assertArrayEquals(new int[] { 1, 2, 0 }, array[0]); - assertArrayEquals(new int[] { 3, 4, 0 }, array[1]); - assertArrayEquals(new int[] { 0, 0, 0 }, array[2]); - - try { - StdArrays.copyFrom(matrix, new int[1][2]); - fail(); - } catch (ArrayIndexOutOfBoundsException e) { - // as expected - } - try { - StdArrays.copyFrom(matrix, new int[2][1]); - fail(); - } catch (ArrayIndexOutOfBoundsException e) { - // as expected - } - try { - StdArrays.copyFrom(matrix, new int[2]); - fail(); - } catch (IllegalArgumentException e) { - // as expected - } - try { - StdArrays.copyFrom(matrix, new int[1][2][2]); - fail(); - } catch (IllegalArgumentException e) { - // as expected - } - try { - StdArrays.copyFrom(matrix, new int[2][2][2]); - fail(); - } catch (IllegalArgumentException e) { - // as expected - } - } - - @Test - public void objectMatrix() { - NdArray matrix = StdArrays.ndCopyOf(new String[][] {{"ab", "bc"}, {"cd", "de"}}); - assertEquals(NdArrays.vectorOfObjects("ab", "bc"), matrix.get(0)); - assertEquals(NdArrays.vectorOfObjects("cd", "de"), matrix.get(1)); - - String[][] array = StdArrays.array2dCopyOf(matrix, String.class); - assertEquals("ab", array[0][0]); - assertEquals("bc", array[0][1]); - assertEquals("cd", array[1][0]); - assertEquals("de", array[1][1]); - - array = new String[2][3]; - StdArrays.copyFrom(matrix, array); - assertEquals("ab", array[0][0]); - assertEquals("bc", array[0][1]); - assertNull(array[0][2]); - assertEquals("cd", array[1][0]); - assertEquals("de", array[1][1]); - assertNull(array[1][2]); - } - - @Test - public void cannotInitDenseMatrixWithRaggedArray() { - IntNdArray matrix = NdArrays.ofInts(Shape.of(2, 2)); - try { - StdArrays.copyTo(new int[][]{ - {1, 2}, - {3} - }, matrix); - fail(); - } catch (IllegalArgumentException e) { - // as expected - } - } - - @Test - public void computeShapeDense3DMatrix() { - Shape shape = StdArrays.shapeOf(new int[][][] { - { - {1, 2, 3}, {4, 5, 6} - }, - { - {1, 2, 3}, {4, 5, 6} - } - }); - assertArrayEquals(new long[] {2, 2, 3}, shape.asArray()); - } - - @Test - public void shapeOfRagged3DMatrix() { - Shape shape = StdArrays.shapeOf(new int[][][] { - { - {1, 2, 3}, {4, 5, 6}, {7, 8, 9} - }, - { - {1, 2, 3}, {4, 5, 6} - } - }); - assertArrayEquals(new long[] {2, Shape.UNKNOWN_SIZE, 3}, shape.asArray()); - } - - @Test - public void shapeOfEmptyArray() { - Shape shape = StdArrays.shapeOf(new int[2][2][3]); - assertArrayEquals(new long[] {2, 2, 3}, shape.asArray()); - } -} diff --git a/ndarray/src/test/java/org/tensorflow/ndarray/benchmark/NdArrayBenchmark.java b/ndarray/src/test/java/org/tensorflow/ndarray/benchmark/NdArrayBenchmark.java deleted file mode 100644 index fb7022bc830..00000000000 --- a/ndarray/src/test/java/org/tensorflow/ndarray/benchmark/NdArrayBenchmark.java +++ /dev/null @@ -1,163 +0,0 @@ -/* - 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 - - 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.benchmark; - -import static org.tensorflow.ndarray.index.Indices.all; -import static org.tensorflow.ndarray.index.Indices.at; - -import java.awt.image.BufferedImage; -import java.awt.image.Raster; -import java.io.IOException; -import javax.imageio.ImageIO; -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.BenchmarkMode; -import org.openjdk.jmh.annotations.Fork; -import org.openjdk.jmh.annotations.Measurement; -import org.openjdk.jmh.annotations.Mode; -import org.openjdk.jmh.annotations.Scope; -import org.openjdk.jmh.annotations.Setup; -import org.openjdk.jmh.annotations.State; -import org.openjdk.jmh.annotations.Warmup; -import org.openjdk.jmh.runner.RunnerException; -import org.tensorflow.ndarray.Shape; -import org.tensorflow.ndarray.FloatNdArray; -import org.tensorflow.ndarray.NdArrays; -import org.tensorflow.ndarray.StdArrays; - -@Fork(value = 1, jvmArgs = {"-Xms4G", "-Xmx4G"}) -@BenchmarkMode(Mode.AverageTime) -@Warmup(iterations = 3) -@Measurement(iterations = 5) -@State(Scope.Benchmark) -public class NdArrayBenchmark { - - public static void main(String[] args) throws IOException, RunnerException { - org.openjdk.jmh.Main.main(args); - } - - @Setup - public void setUp() throws IOException { - BufferedImage image = ImageIO.read(getClass().getClassLoader().getResourceAsStream(TEST_IMAGE)); - - int numPixels = image.getWidth() * image.getHeight(); - pixels = NdArrays.ofFloats(Shape.of(numPixels, 3)); - channels = NdArrays.ofFloats(Shape.of(3, numPixels)); - - Raster imageData = image.getData(); - float[] pixel = new float[3]; - for (int y = 0, pixelIdx = 0; y < image.getHeight(); ++y) { - for (int x = 0; x < image.getWidth(); ++x, ++pixelIdx) { - imageData.getPixel(x, y, pixel); - StdArrays.copyTo(pixel, pixels.get(pixelIdx)); - StdArrays.copyTo(pixel, channels.slice(all(), at(pixelIdx))); - } - } - batches = NdArrays.ofFloats(Shape.of(BATCH_SIZE, 3, numPixels)); - firstBatch = batches.get(0); - } - - @Benchmark - @Measurement(batchSize = 2049 * 1537) - public void getElementAtIndex() { - pixels.get(0); - } - - @Benchmark - @Measurement(batchSize = 2049 * 1537) - public void slicing() { - batches.slice(at(0), all(), at(0)); - } - - @Benchmark - public void readingAllPixelsChannelsBySequence() { - pixels.scalars().forEach(pixel -> pixel.getFloat()); - } - - @Benchmark - public void readingAllPixelsChannelsBySequenceSlices() { - pixels.scalars().asSlices().forEach(pixel -> pixel.getFloat()); - } - - @Benchmark - @Measurement(batchSize = 100) - public void readingAllPixelsChannelsByIndex() { - long[] shape = pixels.shape().asArray(); - for (int i = 0; i < shape[0]; ++i) { - for (int j = 0; j < shape[1]; ++j) { - pixels.getFloat(i, j); - } - } - } - - @Benchmark - @Measurement(batchSize = BATCH_SIZE) - public void writeFirstBatchChannels() { - firstBatch.set(channels); - } - - @Benchmark - public void writeAllBatchChannels() { - batches.elements(0).forEach(batch -> - batch.set(channels) - ); - } - - @Benchmark - @Measurement(batchSize = 2049 * 1537) - public void writeOnePixelBySlicing() { - batches.slice(at(0), all(), at(0)).set(pixels.get(0)); - } - - @Benchmark - public void writeAllPixelsBySlicing() { - batches.elements(0).forEach(batch -> - pixels.elements(0).forEachIndexed((coords, pixel) -> - batch.slice(all(), at(coords[0])).set(pixel) - ) - ); - } - - @Benchmark - @Measurement(batchSize = 2049 * 1537) - public void writeOnePixelsByIndex() { - batches - .setFloat(pixels.getFloat(0, 0), 0, 0, 0) - .setFloat(pixels.getFloat(0, 1), 0, 1, 0) - .setFloat(pixels.getFloat(0, 2), 0, 2, 0); - } - - @Benchmark - public void writeAllPixelsByIndex() { - batches.elements(0).forEach(batch -> - pixels.elements(0).forEachIndexed((coords, pixel) -> { - long pixelIndex = coords[0]; - batch - .setFloat(pixel.getFloat(0), 0, pixelIndex) - .setFloat(pixel.getFloat(1), 1, pixelIndex) - .setFloat(pixel.getFloat(2), 2, pixelIndex); - }) - ); - } - - private static final String TEST_IMAGE = "castle.jpg"; - private static final int BATCH_SIZE = 60; - - private FloatNdArray pixels; - private FloatNdArray channels; - private FloatNdArray batches; - private FloatNdArray firstBatch; -} diff --git a/ndarray/src/test/java/org/tensorflow/ndarray/buffer/BooleanDataBufferTestBase.java b/ndarray/src/test/java/org/tensorflow/ndarray/buffer/BooleanDataBufferTestBase.java deleted file mode 100644 index 3f6df8aa1ce..00000000000 --- a/ndarray/src/test/java/org/tensorflow/ndarray/buffer/BooleanDataBufferTestBase.java +++ /dev/null @@ -1,142 +0,0 @@ -/* - 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 - - 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.buffer; - -import static org.junit.jupiter.api.Assertions.assertTrue; -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 java.util.Arrays; -import java.util.BitSet; -import org.junit.jupiter.api.Test; -import org.tensorflow.ndarray.impl.buffer.misc.MiscDataBufferFactory; - -public abstract class BooleanDataBufferTestBase extends DataBufferTestBase { - - @Override - protected abstract BooleanDataBuffer allocate(long size); - - @Override - protected Boolean valueOf(Long val) { - return val != 0; - } - - @Test - public void writeAndReadFromArray() { - BooleanDataBuffer buffer = allocate(10L); - boolean[] values = new boolean[]{true, false, false, true, false}; - - buffer.write(values); - assertTrue(buffer.getObject(0)); - assertFalse(buffer.getObject(1)); - - buffer.offset(5).write(values); - assertTrue(buffer.getObject(5)); - - boolean[] read = new boolean[5]; - buffer.read(read); - assertArrayEquals(values, read); - - buffer.write(values, 2, 3); - assertFalse(buffer.getObject(0)); - assertTrue(buffer.getObject(1)); - assertFalse(buffer.getObject(2)); - - Arrays.fill(read, false); - buffer.read(read, 1, 2); - assertFalse(read[0]); - assertFalse(read[1]); - assertTrue(read[2]); - assertFalse(read[3]); - } - - @Test - public void equalWithBitSetBuffer() { - BitSet bitSet1 = BitSet.valueOf(new byte[] { 0x01, 0x01 }); - BooleanDataBuffer bitSet1Buffer = MiscDataBufferFactory.create(bitSet1, 12, true); - - BitSet bitSet2 = BitSet.valueOf(new byte[] { 0x11, 0x01 }); - BooleanDataBuffer bitSet2Buffer = MiscDataBufferFactory.create(bitSet2, 12, true); - - BooleanDataBuffer buffer = allocate(12) - .setBoolean(true, 0) - .setBoolean(true, 8); - - assertTrue(bitSet1Buffer.equals(buffer)); - assertTrue(buffer.equals(bitSet1Buffer)); - assertEquals(bitSet1Buffer.hashCode(), buffer.hashCode()); - - assertFalse(bitSet2Buffer.equals(buffer)); - assertFalse(buffer.equals(bitSet2Buffer)); - assertNotEquals(bitSet2Buffer.hashCode(), buffer.hashCode()); - } - - @Test - public void equalWithBooleanArrayBuffer() { - boolean[] array1 = new boolean[] { false, false, false, true, true, false }; - BooleanDataBuffer array1Buffer = MiscDataBufferFactory.create(array1, true); - - boolean[] array2 = new boolean[] { false, false, false, true, true, true }; - BooleanDataBuffer array2Buffer = MiscDataBufferFactory.create(array2, true); - - BooleanDataBuffer buffer = allocate(6) - .setBoolean(true, 3) - .setBoolean(true, 4); - - assertTrue(array1Buffer.equals(buffer)); - assertTrue(buffer.equals(array1Buffer)); - assertEquals(array1Buffer.hashCode(), buffer.hashCode()); - - assertFalse(array2Buffer.equals(buffer)); - assertFalse(buffer.equals(array2Buffer)); - assertNotEquals(array2Buffer.hashCode(), buffer.hashCode()); - } - - @Test - public void equalWithBooleanObjectBuffer() { - Boolean[] array1 = new Boolean[] { false, false, false, true, true, false }; - DataBuffer array1Buffer = MiscDataBufferFactory.create(array1, true); - - boolean[] array2 = new boolean[] { false, false, false, true, true, true }; - DataBuffer array2Buffer = MiscDataBufferFactory.create(array2, true); - - BooleanDataBuffer buffer = allocate(6) - .setBoolean(true, 3) - .setBoolean(true, 4); - - assertTrue(array1Buffer.equals(buffer)); - assertTrue(buffer.equals(array1Buffer)); - assertEquals(array1Buffer.hashCode(), buffer.hashCode()); - - assertFalse(array2Buffer.equals(buffer)); - assertFalse(buffer.equals(array2Buffer)); - assertNotEquals(array2Buffer.hashCode(), buffer.hashCode()); - } - - @Test - public void notEqualWithOtherTypes() { - BooleanDataBuffer buffer = allocate(2) - .setBoolean(false, 0) - .setBoolean(true, 1); - ByteDataBuffer byteBuffer = DataBuffers.of((byte)0, (byte)1); - - assertFalse(buffer.equals(byteBuffer)); - assertFalse(byteBuffer.equals(buffer)); - } -} diff --git a/ndarray/src/test/java/org/tensorflow/ndarray/buffer/ByteDataBufferTestBase.java b/ndarray/src/test/java/org/tensorflow/ndarray/buffer/ByteDataBufferTestBase.java deleted file mode 100644 index 777368466f5..00000000000 --- a/ndarray/src/test/java/org/tensorflow/ndarray/buffer/ByteDataBufferTestBase.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - 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 - - 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.buffer; - -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.assertTrue; - -import java.nio.ByteBuffer; -import java.util.Arrays; -import org.junit.jupiter.api.Test; -import org.tensorflow.ndarray.impl.buffer.misc.MiscDataBufferFactory; -import org.tensorflow.ndarray.impl.buffer.nio.NioDataBufferFactory; -import org.tensorflow.ndarray.impl.buffer.raw.RawDataBufferFactory; - -public abstract class ByteDataBufferTestBase extends DataBufferTestBase { - - @Override - protected abstract ByteDataBuffer allocate(long size); - - @Override - protected Byte valueOf(Long val) { - return val.byteValue(); - } - - @Test - public void writeAndReadFromArray() { - ByteDataBuffer buffer = allocate(10L); - byte[] oneToFive = new byte[]{ 1, 2, 3, 4, 5 }; - - buffer.write(oneToFive); - assertEquals(2, buffer.getByte(1)); - - buffer.offset(5).write(oneToFive); - assertEquals(2, buffer.getByte(1)); - assertEquals(2, buffer.getByte(6)); - - byte[] read = new byte[5]; - buffer.read(read); - assertArrayEquals(oneToFive, read); - - buffer.write(oneToFive, 2, 2); - assertEquals(3, buffer.getByte(0)); - assertEquals(4, buffer.getByte(1)); - assertEquals(3, buffer.getByte(2)); - - Arrays.fill(read, valueOf(0L)); - buffer.read(read, 1, 2); - assertEquals(0, read[0]); - assertEquals(3, read[1]); - assertEquals(4, read[2]); - assertEquals(0, read[3]); - } - - @Test - public void equalWithByteNioBuffer() { - ByteDataBuffer nioBuffer1 = NioDataBufferFactory.create(ByteBuffer.wrap(new byte[] { 0x01, 0x10 })); - ByteDataBuffer nioBuffer2 = NioDataBufferFactory.create(ByteBuffer.wrap(new byte[] { 0x01, 0x11 })); - - ByteDataBuffer buffer = allocate(2) - .setByte((byte)0x01, 0) - .setByte((byte)0x10, 1); - - assertTrue(nioBuffer1.equals(buffer)); - assertTrue(buffer.equals(nioBuffer1)); - assertEquals(nioBuffer1.hashCode(), buffer.hashCode()); - - assertFalse(nioBuffer2.equals(buffer)); - assertFalse(buffer.equals(nioBuffer2)); - assertNotEquals(nioBuffer2.hashCode(), buffer.hashCode()); - } - - @Test - public void equalWithByteRawBuffer() { - ByteDataBuffer rawBuffer1 = RawDataBufferFactory.create(new byte[] { 0x01, 0x10 }, true); - ByteDataBuffer rawBuffer2 = RawDataBufferFactory.create(new byte[] { 0x01, 0x11 }, true); - - ByteDataBuffer buffer = allocate(2) - .setByte((byte)0x01, 0) - .setByte((byte)0x10, 1); - - assertTrue(rawBuffer1.equals(buffer)); - assertTrue(buffer.equals(rawBuffer1)); - assertEquals(rawBuffer1.hashCode(), buffer.hashCode()); - - assertFalse(rawBuffer2.equals(buffer)); - assertFalse(buffer.equals(rawBuffer2)); - assertNotEquals(rawBuffer2.hashCode(), buffer.hashCode()); - } - - @Test - public void equalWithByteObjectBuffer() { - DataBuffer objBuffer1 = MiscDataBufferFactory.create(new Byte[] { 0x01, 0x10 }, true); - DataBuffer objBuffer2 = MiscDataBufferFactory.create(new Byte[] { 0x01, 0x11 }, true); - - ByteDataBuffer buffer = allocate(2) - .setByte((byte)0x01, 0) - .setByte((byte)0x10, 1); - - assertTrue(objBuffer1.equals(buffer)); - assertTrue(buffer.equals(objBuffer1)); - assertEquals(objBuffer1.hashCode(), buffer.hashCode()); - - assertFalse(objBuffer2.equals(buffer)); - assertFalse(buffer.equals(objBuffer2)); - assertNotEquals(objBuffer2.hashCode(), buffer.hashCode()); - } - - @Test - public void notEqualWithOtherTypes() { - ByteDataBuffer buffer = allocate(2) - .setByte((byte)1, 0) - .setByte((byte)16, 1); - LongDataBuffer longBuffer = DataBuffers.of(1L, 16L); - - assertFalse(buffer.equals(longBuffer)); - assertFalse(longBuffer.equals(buffer)); - - try { - IntDataBuffer intBuffer = buffer.asInts(); - - assertFalse(buffer.equals(intBuffer)); - assertFalse(intBuffer.equals(buffer)); - - } catch (IllegalStateException e) { - // some byte buffers cannot be converted to ints, ignore the test in that case - } - } -} diff --git a/ndarray/src/test/java/org/tensorflow/ndarray/buffer/DataBufferTestBase.java b/ndarray/src/test/java/org/tensorflow/ndarray/buffer/DataBufferTestBase.java deleted file mode 100644 index 9a023915735..00000000000 --- a/ndarray/src/test/java/org/tensorflow/ndarray/buffer/DataBufferTestBase.java +++ /dev/null @@ -1,293 +0,0 @@ -/* - 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 - - 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.buffer; - -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.assertSame; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; - -import java.nio.BufferOverflowException; -import java.nio.BufferUnderflowException; -import org.junit.jupiter.api.Test; -import org.tensorflow.ndarray.buffer.DataBuffer; -import org.tensorflow.ndarray.buffer.DataBufferWindow; -import org.tensorflow.ndarray.buffer.DataBuffers; - -public abstract class DataBufferTestBase { - - protected final boolean enableLargeBufferTests = System.getProperty("testLargeBuffers") != null; - - protected long maxSize() { - return DataBuffers.MAX_32BITS; - } - - protected abstract DataBuffer allocate(long size); - - protected abstract T valueOf(Long val); - - @Test - public void bufferSize() { - DataBuffer buffer = allocate(10L); - assertEquals(10L, buffer.size()); - - buffer = allocate(0L); - assertEquals(0L, buffer.size()); - - if (enableLargeBufferTests) { - buffer = allocate(maxSize()); - assertEquals(maxSize(), buffer.size()); - } - } - - @Test - public void offsetNarrowAndSlice() { - DataBuffer buffer = allocate(10L).setObject(valueOf(1L), 5); // 0, 0, 0, 0, 0, 1, 0, 0, 0, 0 - assertEquals(10L, buffer.size()); - assertEquals(valueOf(1L), buffer.getObject(5)); - - DataBuffer subBuffer = buffer.slice(2, 6); // 0, 0, 0, 1, 0, 0 - assertEquals(6L, subBuffer.size()); - assertEquals(valueOf(1L), subBuffer.getObject(3)); - - subBuffer = subBuffer.offset(2L); // 0, 1, 0, 0 - assertEquals(4L, subBuffer.size()); - assertEquals(valueOf(1L), subBuffer.getObject(1)); - - subBuffer = subBuffer.narrow(2L); // 0, 1 - assertEquals(2L, subBuffer.size()); - assertEquals(valueOf(1L), subBuffer.getObject(1)); - try { - subBuffer.getObject(2); - fail(); - } catch (IndexOutOfBoundsException e) { - //as expected - } - try { - buffer.slice(2, 12); - fail(); - } catch (IllegalArgumentException e) { - // as expected - } - try { - buffer.slice(-1, 3); - fail(); - } catch (IllegalArgumentException e) { - // as expected - } - try { - buffer.slice(2, -1); - fail(); - } catch (IllegalArgumentException e) { - // as expected - } - try { - buffer.offset(-1L); - fail(); - } catch (IllegalArgumentException e) { - // as expected - } - try { - buffer.offset(11L); - fail(); - } catch (IllegalArgumentException e) { - // as expected - } - try { - buffer.narrow(-1L); - fail(); - } catch (IllegalArgumentException e) { - // as expected - } - try { - buffer.narrow(11L); - fail(); - } catch (IllegalArgumentException e) { - // as expected - } - } - - @Test - public void putAndGet() { - DataBuffer buffer = allocate(10L); - - buffer.setObject(valueOf(5L), 5L); - assertEquals(valueOf(5L), buffer.getObject(5L)); - try { - buffer.setObject(valueOf(10L), 10L); - fail(); - } catch (IndexOutOfBoundsException e) { - // as expected - } - try { - buffer.getObject(10L); - fail(); - } catch (IndexOutOfBoundsException e) { - // as expected - } - try { - buffer.setObject(valueOf(-1L), -1L); - fail(); - } catch (IndexOutOfBoundsException e) { - // as expected - } - try { - buffer.getObject(-1L); - fail(); - } catch (IndexOutOfBoundsException e) { - // as expected - } - } - - @Test - public void copyToBuffer() { - DataBuffer srcBuffer = allocate(25L); - srcBuffer.setObject(valueOf(5L), 5L); - srcBuffer.setObject(valueOf(10L), 10L); - srcBuffer.setObject(valueOf(15L), 15L); - srcBuffer.setObject(valueOf(20L), 20L); - try { - srcBuffer.copyTo(srcBuffer, srcBuffer.size()); - fail(); - } catch (IllegalArgumentException e) { - // as expected - } - DataBuffer dstBuffer = allocate(30L); - srcBuffer.copyTo(dstBuffer, srcBuffer.size()); - assertEquals(valueOf(5L), dstBuffer.getObject(5L)); - try { - srcBuffer.copyTo(dstBuffer, dstBuffer.size()); - fail(); - } catch (BufferUnderflowException e) { - // as expected - } - try { - dstBuffer.copyTo(srcBuffer, dstBuffer.size()); - fail(); - } catch (BufferOverflowException e) { - // as expected - } - } - - @Test - public void createFromVarargs() { - DataBuffer buffer = DataBuffers.ofObjects(valueOf(1L), valueOf(2L), valueOf(3L)); - assertEquals(3, buffer.size()); - assertEquals(valueOf(1L), buffer.getObject(0)); - assertEquals(valueOf(2L), buffer.getObject(1)); - assertEquals(valueOf(3L), buffer.getObject(2)); - } - - @Test - public void equalWithObjectBuffer() { - DataBuffer buffer1 = allocate(2) - .setObject(valueOf(0L), 0) - .setObject(valueOf(1L), 1); - DataBuffer buffer2 = allocate(2) - .setObject(valueOf(0L), 0) - .setObject(valueOf(1L), 1); - DataBuffer buffer3 = allocate(2) - .setObject(valueOf(1L), 0) - .setObject(valueOf(0L), 1); - DataBuffer buffer4 = allocate(1) - .setObject(valueOf(0L), 0); - DataBuffer buffer5 = allocate(3) - .setObject(valueOf(0L), 0) - .setObject(valueOf(1L), 1) - .setObject(valueOf(2L), 2); - - assertTrue(buffer1.equals(buffer2)); - assertTrue(buffer2.equals(buffer1)); - assertEquals(buffer1.hashCode(), buffer1.hashCode()); - assertEquals(buffer1.hashCode(), buffer2.hashCode()); - - assertFalse(buffer3.equals(buffer1)); - assertFalse(buffer1.equals(buffer3)); - assertNotEquals(buffer3.hashCode(), buffer1.hashCode()); - - assertFalse(buffer4.equals(buffer1)); - assertFalse(buffer1.equals(buffer4)); - assertNotEquals(buffer4.hashCode(), buffer1.hashCode()); - - assertFalse(buffer5.equals(buffer1)); - assertFalse(buffer1.equals(buffer5)); - assertNotEquals(buffer5.hashCode(), buffer1.hashCode()); - } - - @Test - public void bufferWindow() { - DataBuffer buffer = allocate(20); - DataBufferWindow> bufferWindow; - try { - bufferWindow = buffer.window(4); - } catch (UnsupportedOperationException e) { - return; // skip test if this buffer does not support windows - } - assertEquals(0, bufferWindow.offset()); - assertEquals(4, bufferWindow.size()); - assertEquals(4, bufferWindow.buffer().size()); - - for (long i = 0; i < buffer.size(); ++i) { - buffer.setObject(valueOf(i), i); - } - assertEquals(valueOf(2L), bufferWindow.buffer().getObject(2)); - DataBuffer windowBuffer = bufferWindow.buffer(); - - bufferWindow.slide(10); - assertEquals(10, bufferWindow.offset()); - assertEquals(4, bufferWindow.size()); - assertEquals(valueOf(12L), bufferWindow.buffer().getObject(2)); - assertSame(windowBuffer, bufferWindow.buffer()); - - bufferWindow.slide(-2); - assertEquals(8, bufferWindow.offset()); - assertEquals(4, bufferWindow.size()); - assertEquals(valueOf(10L), bufferWindow.buffer().getObject(2)); - - bufferWindow.slideTo(16); - assertEquals(16, bufferWindow.offset()); - assertEquals(4, bufferWindow.size()); - assertEquals(valueOf(18L), bufferWindow.buffer().getObject(2)); - - try { - bufferWindow.slide(1); - fail(); - } catch (IndexOutOfBoundsException e) { - // as expected - } - try { - bufferWindow.slide(-17); - fail(); - } catch (IndexOutOfBoundsException e) { - // as expected - } - try { - bufferWindow.slideTo(-1); - fail(); - } catch (IndexOutOfBoundsException e) { - // as expected - } - try { - bufferWindow.slideTo(17); - fail(); - } catch (IndexOutOfBoundsException e) { - // as expected - } - } -} diff --git a/ndarray/src/test/java/org/tensorflow/ndarray/buffer/DoubleDataBufferTestBase.java b/ndarray/src/test/java/org/tensorflow/ndarray/buffer/DoubleDataBufferTestBase.java deleted file mode 100644 index 4dee064968c..00000000000 --- a/ndarray/src/test/java/org/tensorflow/ndarray/buffer/DoubleDataBufferTestBase.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - 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 - - 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.buffer; - -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.assertTrue; - -import java.nio.DoubleBuffer; -import java.util.Arrays; -import org.junit.jupiter.api.Test; -import org.tensorflow.ndarray.impl.buffer.misc.MiscDataBufferFactory; -import org.tensorflow.ndarray.impl.buffer.nio.NioDataBufferFactory; -import org.tensorflow.ndarray.impl.buffer.raw.RawDataBufferFactory; - -public abstract class DoubleDataBufferTestBase extends DataBufferTestBase { - - @Override - protected abstract DoubleDataBuffer allocate(long size); - - @Override - protected Double valueOf(Long val) { - return val.doubleValue(); - } - - @Test - public void writeAndReadFromArray() { - DoubleDataBuffer buffer = allocate(10L); - double[] oneToFive = new double[]{ 1.0, 2.0, 3.0, 4.0, 5.0 }; - - buffer.write(oneToFive); - assertEquals(2.0, buffer.getDouble(1), 0.0); - - buffer.offset(5).write(oneToFive); - assertEquals(2.0, buffer.getDouble(1), 0.0); - assertEquals(2.0, buffer.getDouble(6), 0.0); - - double[] read = new double[5]; - buffer.read(read); - assertArrayEquals(oneToFive, read, 0.0); - - buffer.write(oneToFive, 2, 2); - assertEquals(3.0, buffer.getDouble(0), 0.0); - assertEquals(4.0, buffer.getDouble(1), 0.0); - assertEquals(3.0, buffer.getDouble(2), 0.0); - - Arrays.fill(read, valueOf(0L)); - buffer.read(read, 1, 2); - assertEquals(0.0, read[0], 0.0); - assertEquals(3.0, read[1], 0.0); - assertEquals(4.0, read[2], 0.0); - assertEquals(0.0, read[3], 0.0); - } - - @Test - public void equalWithDoubleNioBuffer() { - DoubleDataBuffer nioBuffer1 = NioDataBufferFactory.create(DoubleBuffer.wrap(new double[] { 1.0, 16.0 })); - DoubleDataBuffer nioBuffer2 = NioDataBufferFactory.create(DoubleBuffer.wrap(new double[] { 1.0, 25.0 })); - - DoubleDataBuffer buffer = allocate(2) - .setDouble(1.0, 0) - .setDouble(16.0, 1); - - assertTrue(nioBuffer1.equals(buffer)); - assertTrue(buffer.equals(nioBuffer1)); - assertEquals(nioBuffer1.hashCode(), buffer.hashCode()); - - assertFalse(nioBuffer2.equals(buffer)); - assertFalse(buffer.equals(nioBuffer2)); - assertNotEquals(nioBuffer2.hashCode(), buffer.hashCode()); - } - - @Test - public void equalWithDoubleRawBuffer() { - DoubleDataBuffer rawBuffer1 = RawDataBufferFactory.create(new double[] { 1.0, 16.0 }, true); - DoubleDataBuffer rawBuffer2 = RawDataBufferFactory.create(new double[] { 1.0, 25.0 }, true); - - DoubleDataBuffer buffer = allocate(2) - .setDouble(1.0, 0) - .setDouble(16.0, 1); - - assertTrue(rawBuffer1.equals(buffer)); - assertTrue(buffer.equals(rawBuffer1)); - assertEquals(rawBuffer1.hashCode(), buffer.hashCode()); - - assertFalse(rawBuffer2.equals(buffer)); - assertFalse(buffer.equals(rawBuffer2)); - assertNotEquals(rawBuffer2.hashCode(), buffer.hashCode()); - } - - @Test - public void equalWithDoubleObjectBuffer() { - DataBuffer objBuffer1 = MiscDataBufferFactory.create(new Double[] { 1.0, 16.0 }, true); - DataBuffer objBuffer2 = MiscDataBufferFactory.create(new Double[] { 1.0, 25.0 }, true); - - DoubleDataBuffer buffer = allocate(2) - .setDouble(1.0, 0) - .setDouble(16.0, 1); - - assertTrue(objBuffer1.equals(buffer)); - assertTrue(buffer.equals(objBuffer1)); - assertEquals(objBuffer1.hashCode(), buffer.hashCode()); - - assertFalse(objBuffer2.equals(buffer)); - assertFalse(buffer.equals(objBuffer2)); - assertNotEquals(objBuffer2.hashCode(), buffer.hashCode()); - } - - @Test - public void notEqualWithOtherTypes() { - DoubleDataBuffer buffer = allocate(2) - .setDouble(1.0, 0) - .setDouble(16.0, 1); - FloatDataBuffer floatBuffer = DataBuffers.of(1.0f, 16.0f); - - assertFalse(buffer.equals(floatBuffer)); - assertFalse(floatBuffer.equals(buffer)); - } -} diff --git a/ndarray/src/test/java/org/tensorflow/ndarray/buffer/FloatDataBufferTestBase.java b/ndarray/src/test/java/org/tensorflow/ndarray/buffer/FloatDataBufferTestBase.java deleted file mode 100644 index 49c4f15b808..00000000000 --- a/ndarray/src/test/java/org/tensorflow/ndarray/buffer/FloatDataBufferTestBase.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - 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 - - 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.buffer; - -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.assertTrue; - -import java.nio.FloatBuffer; -import java.util.Arrays; -import org.junit.jupiter.api.Test; -import org.tensorflow.ndarray.impl.buffer.misc.MiscDataBufferFactory; -import org.tensorflow.ndarray.impl.buffer.nio.NioDataBufferFactory; -import org.tensorflow.ndarray.impl.buffer.raw.RawDataBufferFactory; - -public abstract class FloatDataBufferTestBase extends DataBufferTestBase { - - @Override - protected abstract FloatDataBuffer allocate(long size); - - @Override - protected Float valueOf(Long val) { - return val.floatValue(); - } - - @Test - public void writeAndReadFromArray() { - FloatDataBuffer buffer = allocate(10L); - float[] oneToFive = new float[]{ 1.0f, 2.0f, 3.0f, 4.0f, 5.0f }; - - buffer.write(oneToFive); - assertEquals(2.0f, buffer.getFloat(1), 0.0f); - - buffer.offset(5).write(oneToFive); - assertEquals(2.0f, buffer.getFloat(1), 0.0f); - assertEquals(2.0f, buffer.getFloat(6), 0.0f); - - float[] read = new float[5]; - buffer.read(read); - assertArrayEquals(oneToFive, read, 0.0f); - - buffer.write(oneToFive, 2, 2); - assertEquals(3.0f, buffer.getFloat(0), 0.0f); - assertEquals(4.0f, buffer.getFloat(1), 0.0f); - assertEquals(3.0f, buffer.getFloat(2), 0.0f); - - Arrays.fill(read, valueOf(0L)); - buffer.read(read, 1, 2); - assertEquals(0.0f, read[0], 0.0f); - assertEquals(3.0f, read[1], 0.0f); - assertEquals(4.0f, read[2], 0.0f); - assertEquals(0.0f, read[3], 0.0f); - } - - @Test - public void equalWithFloatNioBuffer() { - FloatDataBuffer nioBuffer1 = NioDataBufferFactory.create(FloatBuffer.wrap(new float[] { 1.0f, 16.0f })); - FloatDataBuffer nioBuffer2 = NioDataBufferFactory.create(FloatBuffer.wrap(new float[] { 1.0f, 25.0f })); - - FloatDataBuffer buffer = allocate(2) - .setFloat(1.0f, 0) - .setFloat(16.0f, 1); - - assertTrue(nioBuffer1.equals(buffer)); - assertTrue(buffer.equals(nioBuffer1)); - assertEquals(nioBuffer1.hashCode(), buffer.hashCode()); - - assertFalse(nioBuffer2.equals(buffer)); - assertFalse(buffer.equals(nioBuffer2)); - assertNotEquals(nioBuffer2.hashCode(), buffer.hashCode()); - } - - @Test - public void equalWithFloatRawBuffer() { - FloatDataBuffer rawBuffer1 = RawDataBufferFactory.create(new float[] { 1.0f, 16.0f }, true); - FloatDataBuffer rawBuffer2 = RawDataBufferFactory.create(new float[] { 1.0f, 25.0f }, true); - - FloatDataBuffer buffer = allocate(2) - .setFloat(1.0f, 0) - .setFloat(16.0f, 1); - - assertTrue(rawBuffer1.equals(buffer)); - assertTrue(buffer.equals(rawBuffer1)); - assertEquals(rawBuffer1.hashCode(), buffer.hashCode()); - - assertFalse(rawBuffer2.equals(buffer)); - assertFalse(buffer.equals(rawBuffer2)); - assertNotEquals(rawBuffer2.hashCode(), buffer.hashCode()); - } - - @Test - public void equalWithFloatObjectBuffer() { - DataBuffer objBuffer1 = MiscDataBufferFactory.create(new Float[] { 1.0f, 16.0f }, true); - DataBuffer objBuffer2 = MiscDataBufferFactory.create(new Float[] { 1.0f, 25.0f }, true); - - FloatDataBuffer buffer = allocate(2) - .setFloat(1.0f, 0) - .setFloat(16.0f, 1); - - assertTrue(objBuffer1.equals(buffer)); - assertTrue(buffer.equals(objBuffer1)); - assertEquals(objBuffer1.hashCode(), buffer.hashCode()); - - assertFalse(objBuffer2.equals(buffer)); - assertFalse(buffer.equals(objBuffer2)); - assertNotEquals(objBuffer2.hashCode(), buffer.hashCode()); - } - - @Test - public void notEqualWithOtherTypes() { - FloatDataBuffer buffer = allocate(2) - .setFloat(1.0f, 0) - .setFloat(16.0f, 1); - DoubleDataBuffer doubleBuffer = DataBuffers.of(1.0, 16.0); - - assertFalse(buffer.equals(doubleBuffer)); - assertFalse(doubleBuffer.equals(buffer)); - } -} diff --git a/ndarray/src/test/java/org/tensorflow/ndarray/buffer/IntDataBufferTestBase.java b/ndarray/src/test/java/org/tensorflow/ndarray/buffer/IntDataBufferTestBase.java deleted file mode 100644 index f3642e88ef8..00000000000 --- a/ndarray/src/test/java/org/tensorflow/ndarray/buffer/IntDataBufferTestBase.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - 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 - - 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.buffer; - -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.assertTrue; - -import java.nio.IntBuffer; -import java.util.Arrays; -import org.junit.jupiter.api.Test; -import org.tensorflow.ndarray.impl.buffer.misc.MiscDataBufferFactory; -import org.tensorflow.ndarray.impl.buffer.nio.NioDataBufferFactory; -import org.tensorflow.ndarray.impl.buffer.raw.RawDataBufferFactory; - -public abstract class IntDataBufferTestBase extends DataBufferTestBase { - - @Override - protected abstract IntDataBuffer allocate(long size); - - @Override - protected Integer valueOf(Long val) { - return val.intValue(); - } - - @Test - public void writeAndReadFromArray() { - IntDataBuffer buffer = allocate(10L); - int[] oneToFive = new int[]{ 1, 2, 3, 4, 5 }; - - buffer.write(oneToFive); - assertEquals(2, buffer.getInt(1)); - - buffer.offset(5).write(oneToFive); - assertEquals(2, buffer.getInt(1)); - assertEquals(2, buffer.getInt(6)); - - int[] read = new int[5]; - buffer.read(read); - assertArrayEquals(oneToFive, read); - - buffer.write(oneToFive, 2, 2); - assertEquals(3, buffer.getInt(0)); - assertEquals(4, buffer.getInt(1)); - assertEquals(3, buffer.getInt(2)); - - Arrays.fill(read, valueOf(0L)); - buffer.read(read, 1, 2); - assertEquals(0, read[0]); - assertEquals(3, read[1]); - assertEquals(4, read[2]); - assertEquals(0, read[3]); - } - - @Test - public void equalWithIntNioBuffer() { - IntDataBuffer nioBuffer1 = NioDataBufferFactory.create(IntBuffer.wrap(new int[] { 1, 16 })); - IntDataBuffer nioBuffer2 = NioDataBufferFactory.create(IntBuffer.wrap(new int[] { 1, 25 })); - - IntDataBuffer buffer = allocate(2) - .setInt(1, 0) - .setInt(16, 1); - - assertTrue(nioBuffer1.equals(buffer)); - assertTrue(buffer.equals(nioBuffer1)); - assertEquals(nioBuffer1.hashCode(), buffer.hashCode()); - - assertFalse(nioBuffer2.equals(buffer)); - assertFalse(buffer.equals(nioBuffer2)); - assertNotEquals(nioBuffer2.hashCode(), buffer.hashCode()); - } - - @Test - public void equalWithIntRawBuffer() { - IntDataBuffer rawBuffer1 = RawDataBufferFactory.create(new int[] { 1, 16 }, true); - IntDataBuffer rawBuffer2 = RawDataBufferFactory.create(new int[] { 1, 25 }, true); - - IntDataBuffer buffer = allocate(2) - .setInt(1, 0) - .setInt(16, 1); - - assertTrue(rawBuffer1.equals(buffer)); - assertTrue(buffer.equals(rawBuffer1)); - assertEquals(rawBuffer1.hashCode(), buffer.hashCode()); - - assertFalse(rawBuffer2.equals(buffer)); - assertFalse(buffer.equals(rawBuffer2)); - assertNotEquals(rawBuffer2.hashCode(), buffer.hashCode()); - } - - @Test - public void equalWithIntObjectBuffer() { - DataBuffer objBuffer1 = MiscDataBufferFactory.create(new Integer[] { 1, 16 }, true); - DataBuffer objBuffer2 = MiscDataBufferFactory.create(new Integer[] { 1, 25 }, true); - - IntDataBuffer buffer = allocate(2) - .setInt(1, 0) - .setInt(16, 1); - - assertTrue(objBuffer1.equals(buffer)); - assertTrue(buffer.equals(objBuffer1)); - assertEquals(objBuffer1.hashCode(), buffer.hashCode()); - - assertFalse(objBuffer2.equals(buffer)); - assertFalse(buffer.equals(objBuffer2)); - assertNotEquals(objBuffer2.hashCode(), buffer.hashCode()); - } - - @Test - public void notEqualWithOtherTypes() { - IntDataBuffer buffer = allocate(2) - .setInt(1, 0) - .setInt(16, 1); - LongDataBuffer longBuffer = DataBuffers.of(1L, 16L); - - assertFalse(buffer.equals(longBuffer)); - assertFalse(longBuffer.equals(buffer)); - } -} diff --git a/ndarray/src/test/java/org/tensorflow/ndarray/buffer/LongDataBufferTestBase.java b/ndarray/src/test/java/org/tensorflow/ndarray/buffer/LongDataBufferTestBase.java deleted file mode 100644 index e0d8b1b4539..00000000000 --- a/ndarray/src/test/java/org/tensorflow/ndarray/buffer/LongDataBufferTestBase.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - 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 - - 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.buffer; - -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.assertTrue; - -import java.nio.LongBuffer; -import java.util.Arrays; -import org.junit.jupiter.api.Test; -import org.tensorflow.ndarray.impl.buffer.misc.MiscDataBufferFactory; -import org.tensorflow.ndarray.impl.buffer.nio.NioDataBufferFactory; -import org.tensorflow.ndarray.impl.buffer.raw.RawDataBufferFactory; - -public abstract class LongDataBufferTestBase extends DataBufferTestBase { - - @Override - protected abstract LongDataBuffer allocate(long size); - - @Override - protected Long valueOf(Long val) { - return val; - } - - @Test - public void writeAndReadFromArray() { - LongDataBuffer buffer = allocate(10L); - long[] oneToFive = new long[]{ 1L, 2L, 3L, 4L, 5L }; - - buffer.write(oneToFive); - assertEquals(2, buffer.getLong(1)); - - buffer.offset(5).write(oneToFive); - assertEquals(2L, buffer.getLong(1)); - assertEquals(2L, buffer.getLong(6)); - - long[] read = new long[5]; - buffer.read(read); - assertArrayEquals(oneToFive, read); - - buffer.write(oneToFive, 2, 2); - assertEquals(3L, buffer.getLong(0)); - assertEquals(4L, buffer.getLong(1)); - assertEquals(3L, buffer.getLong(2)); - - Arrays.fill(read, valueOf(0L)); - buffer.read(read, 1, 2); - assertEquals(0L, read[0]); - assertEquals(3L, read[1]); - assertEquals(4L, read[2]); - assertEquals(0L, read[3]); - } - - @Test - public void equalWithLongNioBuffer() { - LongDataBuffer nioBuffer1 = NioDataBufferFactory.create(LongBuffer.wrap(new long[] { 1, 16 })); - LongDataBuffer nioBuffer2 = NioDataBufferFactory.create(LongBuffer.wrap(new long[] { 1, 25 })); - - LongDataBuffer buffer = allocate(2) - .setLong(1, 0) - .setLong(16, 1); - - assertTrue(nioBuffer1.equals(buffer)); - assertTrue(buffer.equals(nioBuffer1)); - assertEquals(nioBuffer1.hashCode(), buffer.hashCode()); - - assertFalse(nioBuffer2.equals(buffer)); - assertFalse(buffer.equals(nioBuffer2)); - assertNotEquals(nioBuffer2.hashCode(), buffer.hashCode()); - } - - @Test - public void equalWithLongRawBuffer() { - LongDataBuffer rawBuffer1 = RawDataBufferFactory.create(new long[] { 1, 16 }, true); - LongDataBuffer rawBuffer2 = RawDataBufferFactory.create(new long[] { 1, 25 }, true); - - LongDataBuffer buffer = allocate(2) - .setLong(1, 0) - .setLong(16, 1); - - assertTrue(rawBuffer1.equals(buffer)); - assertTrue(buffer.equals(rawBuffer1)); - assertEquals(rawBuffer1.hashCode(), buffer.hashCode()); - - assertFalse(rawBuffer2.equals(buffer)); - assertFalse(buffer.equals(rawBuffer2)); - assertNotEquals(rawBuffer2.hashCode(), buffer.hashCode()); - } - - @Test - public void equalWithLongObjectBuffer() { - DataBuffer objBuffer1 = MiscDataBufferFactory.create(new Long[] { 1L, 16L }, true); - DataBuffer objBuffer2 = MiscDataBufferFactory.create(new Long[] { 1L, 25L }, true); - - LongDataBuffer buffer = allocate(2) - .setLong(1, 0) - .setLong(16, 1); - - assertTrue(objBuffer1.equals(buffer)); - assertTrue(buffer.equals(objBuffer1)); - assertEquals(objBuffer1.hashCode(), buffer.hashCode()); - - assertFalse(objBuffer2.equals(buffer)); - assertFalse(buffer.equals(objBuffer2)); - assertNotEquals(objBuffer2.hashCode(), buffer.hashCode()); - } - - @Test - public void notEqualWithOtherTypes() { - LongDataBuffer buffer = allocate(2) - .setLong(1L, 0) - .setLong(16L, 1); - IntDataBuffer intBuffer = DataBuffers.of(1, 16); - - assertFalse(buffer.equals(intBuffer)); - assertFalse(intBuffer.equals(buffer)); - } -} diff --git a/ndarray/src/test/java/org/tensorflow/ndarray/buffer/ShortDataBufferTestBase.java b/ndarray/src/test/java/org/tensorflow/ndarray/buffer/ShortDataBufferTestBase.java deleted file mode 100644 index f3269e85a8f..00000000000 --- a/ndarray/src/test/java/org/tensorflow/ndarray/buffer/ShortDataBufferTestBase.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - 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 - - 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.buffer; - -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.assertTrue; - -import java.nio.ShortBuffer; -import java.util.Arrays; -import org.junit.jupiter.api.Test; -import org.tensorflow.ndarray.impl.buffer.misc.MiscDataBufferFactory; -import org.tensorflow.ndarray.impl.buffer.nio.NioDataBufferFactory; -import org.tensorflow.ndarray.impl.buffer.raw.RawDataBufferFactory; - -public abstract class ShortDataBufferTestBase extends DataBufferTestBase { - - @Override - protected abstract ShortDataBuffer allocate(long size); - - @Override - protected Short valueOf(Long val) { - return val.shortValue(); - } - - @Test - public void writeAndReadFromArray() { - ShortDataBuffer buffer = allocate(10L); - short[] oneToFive = new short[]{ 1, 2, 3, 4, 5 }; - - buffer.write(oneToFive); - assertEquals(2, buffer.getShort(1)); - - buffer.offset(5).write(oneToFive); - assertEquals(2, buffer.getShort(1), 0); - assertEquals(2, buffer.getShort(6), 0); - - short[] read = new short[5]; - buffer.read(read); - assertArrayEquals(oneToFive, read); - - buffer.write(oneToFive, 2, 2); - assertEquals(3, buffer.getShort(0)); - assertEquals(4, buffer.getShort(1)); - assertEquals(3, buffer.getShort(2)); - - Arrays.fill(read, valueOf(0L)); - buffer.read(read, 1, 2); - assertEquals(0, read[0]); - assertEquals(3, read[1]); - assertEquals(4, read[2]); - assertEquals(0, read[3]); - } - - @Test - public void equalWithShortNioBuffer() { - ShortDataBuffer nioBuffer1 = NioDataBufferFactory.create(ShortBuffer.wrap(new short[] { 1, 16 })); - ShortDataBuffer nioBuffer2 = NioDataBufferFactory.create(ShortBuffer.wrap(new short[] { 1, 25 })); - - ShortDataBuffer buffer = allocate(2) - .setShort((short)1, 0) - .setShort((short)16, 1); - - assertTrue(nioBuffer1.equals(buffer)); - assertTrue(buffer.equals(nioBuffer1)); - assertEquals(nioBuffer1.hashCode(), buffer.hashCode()); - - assertFalse(nioBuffer2.equals(buffer)); - assertFalse(buffer.equals(nioBuffer2)); - assertNotEquals(nioBuffer2.hashCode(), buffer.hashCode()); - } - - @Test - public void equalWithShortRawBuffer() { - ShortDataBuffer rawBuffer1 = RawDataBufferFactory.create(new short[] { 1, 16 }, true); - ShortDataBuffer rawBuffer2 = RawDataBufferFactory.create(new short[] { 1, 25 }, true); - - ShortDataBuffer buffer = allocate(2) - .setShort((short)1, 0) - .setShort((short)16, 1); - - assertTrue(rawBuffer1.equals(buffer)); - assertTrue(buffer.equals(rawBuffer1)); - assertEquals(rawBuffer1.hashCode(), buffer.hashCode()); - - assertFalse(rawBuffer2.equals(buffer)); - assertFalse(buffer.equals(rawBuffer2)); - assertNotEquals(rawBuffer2.hashCode(), buffer.hashCode()); - } - - @Test - public void equalWithShortObjectBuffer() { - DataBuffer objBuffer1 = MiscDataBufferFactory.create(new Short[] { 1, 16 }, true); - DataBuffer objBuffer2 = MiscDataBufferFactory.create(new Short[] { 1, 25 }, true); - - ShortDataBuffer buffer = allocate(2) - .setShort((short)1, 0) - .setShort((short)16, 1); - - assertTrue(objBuffer1.equals(buffer)); - assertTrue(buffer.equals(objBuffer1)); - assertEquals(objBuffer1.hashCode(), buffer.hashCode()); - - assertFalse(objBuffer2.equals(buffer)); - assertFalse(buffer.equals(objBuffer2)); - assertNotEquals(objBuffer2.hashCode(), buffer.hashCode()); - } - - @Test - public void notEqualWithOtherTypes() { - ShortDataBuffer buffer = allocate(2) - .setShort((short)1, 0) - .setShort((short)16, 1); - LongDataBuffer longBuffer = DataBuffers.of(1L, 16L); - - assertFalse(buffer.equals(longBuffer)); - assertFalse(longBuffer.equals(buffer)); - } -} diff --git a/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/adapter/BigIntegerDataBufferAdapterTest.java b/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/adapter/BigIntegerDataBufferAdapterTest.java deleted file mode 100644 index 4bb86fe3f33..00000000000 --- a/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/adapter/BigIntegerDataBufferAdapterTest.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * 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 - * - * 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.buffer.adapter; - -import java.math.BigInteger; -import org.tensorflow.ndarray.buffer.ByteDataBuffer; -import org.tensorflow.ndarray.buffer.DataBuffer; -import org.tensorflow.ndarray.buffer.DataBufferTestBase; -import org.tensorflow.ndarray.buffer.DataBuffers; -import org.tensorflow.ndarray.buffer.layout.DataLayout; - -public class BigIntegerDataBufferAdapterTest extends DataBufferTestBase { - - @Override - protected DataBuffer allocate(long size) { - return LAYOUT.applyTo(DataBuffers.ofBytes(size * LAYOUT.scale())); - } - - @Override - protected long maxSize() { - return super.maxSize() / 3; - } - - @Override - protected BigInteger valueOf(Long val) { - return BigInteger.valueOf(val); - } - - private static DataLayout LAYOUT = new DataLayout() { - - @Override - public void writeObject(ByteDataBuffer buffer, BigInteger value, long index) { - byte[] bytes = value.toByteArray(); - buffer.setByte(bytes.length > 2 ? bytes[2] : 0, index); - buffer.setByte(bytes.length > 1 ? bytes[1] : 0, index + 1); - buffer.setByte(bytes[0], index + 2); - } - - @Override - public BigInteger readObject(ByteDataBuffer buffer, long index) { - byte byte2 = buffer.getByte(index); - byte byte1 = buffer.getByte(index + 1); - byte byte0 = buffer.getByte(index + 2); - return new BigInteger(new byte[] { byte2, byte1, byte0 }); - } - - @Override - public int scale() { - return 3; - } - }; -} diff --git a/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/adapter/BooleanDataBufferAdapterTest.java b/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/adapter/BooleanDataBufferAdapterTest.java deleted file mode 100644 index a15e8f388a8..00000000000 --- a/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/adapter/BooleanDataBufferAdapterTest.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * 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 - * - * 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.buffer.adapter; - -import org.tensorflow.ndarray.buffer.BooleanDataBuffer; -import org.tensorflow.ndarray.buffer.BooleanDataBufferTestBase; -import org.tensorflow.ndarray.buffer.ByteDataBuffer; -import org.tensorflow.ndarray.buffer.DataBuffers; -import org.tensorflow.ndarray.buffer.layout.BooleanDataLayout; - -public class BooleanDataBufferAdapterTest extends BooleanDataBufferTestBase { - - @Override - protected BooleanDataBuffer allocate(long size) { - return LAYOUT.applyTo(DataBuffers.ofBytes(size * LAYOUT.scale())); - } - - private static BooleanDataLayout LAYOUT = new BooleanDataLayout() { - - @Override - public void writeBoolean(ByteDataBuffer buffer, boolean value, long index) { - buffer.setByte((byte)(value ? 1 : 0), index); - } - - @Override - public boolean readBoolean(ByteDataBuffer buffer, long index) { - return buffer.getByte(index) > 0; - } - }; -} diff --git a/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/adapter/ByteDataBufferAdapterTest.java b/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/adapter/ByteDataBufferAdapterTest.java deleted file mode 100644 index 8a6287601f5..00000000000 --- a/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/adapter/ByteDataBufferAdapterTest.java +++ /dev/null @@ -1,27 +0,0 @@ -package org.tensorflow.ndarray.impl.buffer.adapter; - -import org.tensorflow.ndarray.buffer.ByteDataBuffer; -import org.tensorflow.ndarray.buffer.ByteDataBufferTestBase; -import org.tensorflow.ndarray.buffer.DataBuffers; -import org.tensorflow.ndarray.buffer.ShortDataBuffer; -import org.tensorflow.ndarray.buffer.layout.ByteDataLayout; - -public class ByteDataBufferAdapterTest extends ByteDataBufferTestBase { - - public ByteDataBuffer allocate(long size) { - return LAYOUT.applyTo(DataBuffers.ofShorts(size * LAYOUT.scale())); - } - - private static ByteDataLayout LAYOUT = new ByteDataLayout() { - - @Override - public void writeByte(ShortDataBuffer buffer, byte value, long index) { - buffer.setShort(value, index); - } - - @Override - public byte readByte(ShortDataBuffer buffer, long index) { - return (byte)buffer.getShort(index); - } - }; -} diff --git a/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/adapter/DoubleDataBufferAdapterTest.java b/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/adapter/DoubleDataBufferAdapterTest.java deleted file mode 100644 index 8dfee1182b1..00000000000 --- a/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/adapter/DoubleDataBufferAdapterTest.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * 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 - * - * 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.buffer.adapter; - -import org.tensorflow.ndarray.buffer.ByteDataBuffer; -import org.tensorflow.ndarray.buffer.DataBuffers; -import org.tensorflow.ndarray.buffer.DoubleDataBuffer; -import org.tensorflow.ndarray.buffer.DoubleDataBufferTestBase; -import org.tensorflow.ndarray.buffer.layout.DoubleDataLayout; - -public class DoubleDataBufferAdapterTest extends DoubleDataBufferTestBase { - - @Override - protected DoubleDataBuffer allocate(long size) { - return LAYOUT.applyTo(DataBuffers.ofBytes(size * LAYOUT.scale())); - } - - @Override - protected long maxSize() { - return super.maxSize() / 3; - } - - private static DoubleDataLayout LAYOUT = new DoubleDataLayout() { - - @Override - public void writeDouble(ByteDataBuffer buffer, double value, long index) { - long bits = Double.doubleToLongBits(value); - buffer.setByte((byte)((bits >> 56) & 0xFF), index); - buffer.setByte((byte)((bits >> 48) & 0xFF), index + 1); - buffer.setByte((byte)((bits >> 40) & 0xFF), index + 2); - } - - @Override - public double readDouble(ByteDataBuffer buffer, long index) { - long byte7 = buffer.getByte(index); - long byte6 = buffer.getByte(index + 1); - long byte5 = buffer.getByte(index + 2); - return Double.longBitsToDouble(((byte7 & 0xFF) << 56) | ((byte6 & 0xFF) << 48) | ((byte5 & 0xFF) << 40)); - } - - @Override - public int scale() { - return 3; - } - }; -} diff --git a/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/adapter/FloatDataBufferAdapterTest.java b/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/adapter/FloatDataBufferAdapterTest.java deleted file mode 100644 index 82b8ee947dd..00000000000 --- a/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/adapter/FloatDataBufferAdapterTest.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * 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 - * - * 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.buffer.adapter; - -import org.tensorflow.ndarray.buffer.DataBuffers; -import org.tensorflow.ndarray.buffer.FloatDataBuffer; -import org.tensorflow.ndarray.buffer.FloatDataBufferTestBase; -import org.tensorflow.ndarray.buffer.ShortDataBuffer; -import org.tensorflow.ndarray.buffer.layout.FloatDataLayout; - -public class FloatDataBufferAdapterTest extends FloatDataBufferTestBase { - - @Override - public FloatDataBuffer allocate(long size) { - return LAYOUT.applyTo(DataBuffers.ofShorts(size * LAYOUT.scale())); - } - - @Override - protected long maxSize() { - return super.maxSize() / 2; - } - - private static FloatDataLayout LAYOUT = new FloatDataLayout() { - - @Override - public void writeFloat(ShortDataBuffer buffer, float value, long index) { - int bits = Float.floatToIntBits(value); - buffer.setShort((short)(bits >> 16), index); - } - - @Override - public float readFloat(ShortDataBuffer buffer, long index) { - int i = buffer.getShort(index); - return Float.intBitsToFloat(i << 16); - } - }; -} diff --git a/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/adapter/IntDataBufferAdapterTest.java b/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/adapter/IntDataBufferAdapterTest.java deleted file mode 100644 index 9c00f92b00d..00000000000 --- a/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/adapter/IntDataBufferAdapterTest.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * 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 - * - * 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.buffer.adapter; - -import org.tensorflow.ndarray.buffer.DataBuffers; -import org.tensorflow.ndarray.buffer.IntDataBuffer; -import org.tensorflow.ndarray.buffer.IntDataBufferTestBase; -import org.tensorflow.ndarray.buffer.ShortDataBuffer; -import org.tensorflow.ndarray.buffer.layout.IntDataLayout; - -public class IntDataBufferAdapterTest extends IntDataBufferTestBase { - - @Override - protected IntDataBuffer allocate(long size) { - return LAYOUT.applyTo(DataBuffers.ofShorts(size * LAYOUT.scale())); - } - - @Override - protected long maxSize() { - return super.maxSize() / 2; - } - - private static IntDataLayout LAYOUT = new IntDataLayout() { - - @Override - public void writeInt(ShortDataBuffer buffer, int value, long index) { - buffer.setShort((short)(((value & 0x80000000) >> 16) | (value & 0x7FFF)), index); - } - - @Override - public int readInt(ShortDataBuffer buffer, long index) { - int i = buffer.getShort(index); - return ((i & 0x8000) << 16) | ((i & 0x7FFF)); - } - }; -} diff --git a/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/adapter/LongDataBufferAdapterTest.java b/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/adapter/LongDataBufferAdapterTest.java deleted file mode 100644 index 40bc4c55b3e..00000000000 --- a/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/adapter/LongDataBufferAdapterTest.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * 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 - * - * 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.buffer.adapter; - -import org.tensorflow.ndarray.buffer.ByteDataBuffer; -import org.tensorflow.ndarray.buffer.DataBuffers; -import org.tensorflow.ndarray.buffer.LongDataBuffer; -import org.tensorflow.ndarray.buffer.LongDataBufferTestBase; -import org.tensorflow.ndarray.buffer.layout.LongDataLayout; - -public class LongDataBufferAdapterTest extends LongDataBufferTestBase { - - @Override - protected LongDataBuffer allocate(long size) { - return LAYOUT.applyTo(DataBuffers.ofBytes(size * LAYOUT.scale())); - } - - @Override - protected long maxSize() { - return super.maxSize() / 3; - } - - private static LongDataLayout LAYOUT = new LongDataLayout() { - - @Override - public void writeLong(ByteDataBuffer buffer, long value, long index) { - buffer.setByte((byte)(((value >> 56) & 0x80) | ((value >> 16) & 0x7F)), index); - buffer.setByte((byte)((value >> 8) & 0xFF), index + 1); - buffer.setByte((byte)(value & 0xFF), index + 2); - } - - @Override - public long readLong(ByteDataBuffer buffer, long index) { - long msb = buffer.getByte(index); - long midb = buffer.getByte(index + 1); - long lsb = buffer.getByte(index + 2); - return ((msb & 0x80) << 56) | ((msb & 0x7F) << 16) | ((midb & 0xFF) << 8) | (lsb & 0xFF); - } - - @Override - public int scale() { - return 3; - } - }; -} diff --git a/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/adapter/ShortDataBufferAdapterTest.java b/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/adapter/ShortDataBufferAdapterTest.java deleted file mode 100644 index 3c11d3a46ad..00000000000 --- a/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/adapter/ShortDataBufferAdapterTest.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * 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 - * - * 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.buffer.adapter; - -import org.tensorflow.ndarray.buffer.ByteDataBuffer; -import org.tensorflow.ndarray.buffer.DataBuffers; -import org.tensorflow.ndarray.buffer.ShortDataBuffer; -import org.tensorflow.ndarray.buffer.ShortDataBufferTestBase; -import org.tensorflow.ndarray.buffer.layout.ShortDataLayout; - -public class ShortDataBufferAdapterTest extends ShortDataBufferTestBase { - - public ShortDataBuffer allocate(long size) { - return LAYOUT.applyTo(DataBuffers.ofBytes(size * LAYOUT.scale())); - } - - private static ShortDataLayout LAYOUT = new ShortDataLayout() { - - @Override - public void writeShort(ByteDataBuffer buffer, short value, long index) { - buffer.setByte((byte)(((value & 0x8000) >> 8) | (value & 0x7F)), index); - } - - @Override - public short readShort(ByteDataBuffer buffer, long index) { - int b = buffer.getByte(index); - return (short)(((b & 0x80) << 8) | (b & 0x7F)); - } - }; -} diff --git a/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/layout/Bfloat16LayoutTest.java b/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/layout/Bfloat16LayoutTest.java deleted file mode 100644 index 48ddeb1c56e..00000000000 --- a/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/layout/Bfloat16LayoutTest.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2020 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.buffer.layout; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import org.junit.jupiter.api.Test; - -public class Bfloat16LayoutTest { - - @Test - public void testFloat32to16() { - - // Zero and subnormals - assertEquals((short)0x0000, Bfloat16Layout.float32to16(0.0f)); - assertEquals((short)0x8000, Bfloat16Layout.float32to16(-0.0f)); - assertEquals((short)0x0001, Bfloat16Layout.float32to16(1e-40f)); - assertEquals((short)0xC000, Bfloat16Layout.float32to16(-2.0f)); - assertEquals((short)0x0000, Bfloat16Layout.float32to16(4.59e-41f)); - - // Infinite and NaN - assertEquals((short)0x7F80, Bfloat16Layout.float32to16(Float.POSITIVE_INFINITY)); - assertEquals((short)0xFF80, Bfloat16Layout.float32to16(Float.NEGATIVE_INFINITY)); - assertEquals((short)0x7FC0, Bfloat16Layout.float32to16(Float.NaN)); - assertEquals((short)0x7FC0, Bfloat16Layout.float32to16(Float.intBitsToFloat(0xFFFFFFFF))); - - // Normalized - assertEquals((short)0x3F80, Bfloat16Layout.float32to16(1.0f)); - assertEquals((short)0xBF80, Bfloat16Layout.float32to16(-1.0f)); - assertEquals((short)0x42C8, Bfloat16Layout.float32to16(100.0f)); - assertEquals((short)0xC2CA, Bfloat16Layout.float32to16(-101.0f)); - assertEquals((short)0x3F8F, Bfloat16Layout.float32to16(1.1171875f)); - assertEquals((short)0x4800, Bfloat16Layout.float32to16(131072f)); - assertEquals((short)0x7F7F, Bfloat16Layout.float32to16(3.3895314e38f)); - assertEquals((short)0xFF7F, Bfloat16Layout.float32to16(-3.3895314e38f)); - - // Rounding up - assertEquals((short)0x3FCF, Bfloat16Layout.float32to16(1.6191406f)); // 1.6171875 - assertEquals((short)0x4780, Bfloat16Layout.float32to16(65600.0f)); // 65536.0 - } - - @Test - public void testFloat16to32() { - - // Zero and subnormals - assertEquals(0.0f, Bfloat16Layout.float16to32((short)0x0000), 0); - assertEquals(-0.0f, Bfloat16Layout.float16to32((short)0x8000), 0); - assertEquals(9.18355E-41f, Bfloat16Layout.float16to32((short)0x0001), 1e-8f); - assertEquals(-9.403955E-38, Bfloat16Layout.float16to32((short)0x8200), 1e-8f); - - // Infinite and NaN - assertEquals(Float.POSITIVE_INFINITY, Bfloat16Layout.float16to32((short)0x7F80), 0); - assertEquals(Float.NEGATIVE_INFINITY, Bfloat16Layout.float16to32((short)0xFF80), 0); - assertEquals(Float.NaN, Bfloat16Layout.float16to32((short)0x7FC0), 0); - assertEquals(Float.intBitsToFloat(0xFFFFFFFF), Bfloat16Layout.float16to32((short)0x7FC0), 0); - - // Normalized - assertEquals(1.0f, Bfloat16Layout.float16to32((short)0x3F80), 0); - assertEquals(-1.0f, Bfloat16Layout.float16to32((short)0xBF80), 0); - assertEquals(100.0f, Bfloat16Layout.float16to32((short)0x42C8), 0); - assertEquals(-101.0f, Bfloat16Layout.float16to32((short)0xC2CA), 0); - assertEquals(1.1171875f, Bfloat16Layout.float16to32((short)0x3F8F), 0); - assertEquals(131072f, Bfloat16Layout.float16to32((short)0x4800), 0); - assertEquals(3.3895314e38f, Bfloat16Layout.float16to32((short)0x7F7F), 0); - assertEquals(-3.3895314e38f, Bfloat16Layout.float16to32((short)0xFF7F), 0); - assertEquals(1.6171875f, Bfloat16Layout.float16to32((short)0x3FCF), 0); - assertEquals(65536.0, Bfloat16Layout.float16to32((short)0x4780), 0); - } -} diff --git a/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/layout/BoolLayoutTest.java b/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/layout/BoolLayoutTest.java deleted file mode 100644 index 6ba903cadec..00000000000 --- a/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/layout/BoolLayoutTest.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2020 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.buffer.layout; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import org.junit.jupiter.api.Test; - -public class BoolLayoutTest { - - @Test - public void booleanToByteTest() { - assertEquals((byte)1, BoolLayout.booleanToByte(true)); - assertEquals((byte)0, BoolLayout.booleanToByte(false)); - } - - @Test - public void byteToBooleanTest() { - assertTrue(BoolLayout.byteToBoolean((byte)1)); - assertTrue(BoolLayout.byteToBoolean((byte)127)); - assertTrue(BoolLayout.byteToBoolean((byte)-128)); - assertTrue(BoolLayout.byteToBoolean((byte)255)); - assertFalse(BoolLayout.byteToBoolean((byte)0)); - } -} diff --git a/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/layout/Float16LayoutTest.java b/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/layout/Float16LayoutTest.java deleted file mode 100644 index 7bc430ac4ba..00000000000 --- a/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/layout/Float16LayoutTest.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright 2020 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.buffer.layout; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import org.junit.jupiter.api.Test; - -public class Float16LayoutTest { - - @Test - public void testFloat32to16() { - - // Zero and subnormals - assertEquals((short)0x0000, Float16Layout.float32to16(0.0f)); - assertEquals((short)0x8000, Float16Layout.float32to16(-0.0f)); - assertEquals((short)0x0001, Float16Layout.float32to16(6e-8f)); - assertEquals((short)0x8200, Float16Layout.float32to16(-3.052e-5f)); - assertEquals((short)0x0000, Float16Layout.float32to16(6e-9f)); - - // Infinite and NaN - assertEquals((short)0x7C00, Float16Layout.float32to16(Float.POSITIVE_INFINITY)); - assertEquals((short)0xFC00, Float16Layout.float32to16(Float.NEGATIVE_INFINITY)); - assertEquals((short)0x7C00, Float16Layout.float32to16(65520.0f)); - assertEquals((short)0x7C00, Float16Layout.float32to16(165536.0f)); - assertEquals((short)0xFC00, Float16Layout.float32to16(-65520.0f)); - assertEquals((short)0x7E00, Float16Layout.float32to16(Float.NaN)); - assertEquals((short)0x7E00, Float16Layout.float32to16(Float.intBitsToFloat(0xFFFFFFFF))); - - // Normalized - assertEquals((short)0x7BFF, Float16Layout.float32to16(65519.0f)); - assertEquals((short)0x3C00, Float16Layout.float32to16(1.0f)); - assertEquals((short)0xBC00, Float16Layout.float32to16(-1.0f)); - assertEquals((short)0x5640, Float16Layout.float32to16(100.0f)); - assertEquals((short)0xD650, Float16Layout.float32to16(-101.0f)); - assertEquals((short)0x3C7E, Float16Layout.float32to16(1.123f)); - - // Rounding up - assertEquals((short)0x3C7E, Float16Layout.float32to16(1.1235f)); // 1.123 - assertEquals((short)0x3C7F, Float16Layout.float32to16(1.1236f)); // 1.124 - assertEquals((short)0x4000, Float16Layout.float32to16(2.0009f)); // 2.0 - assertEquals((short)0x4001, Float16Layout.float32to16(2.001f)); // 2.002 - assertEquals((short)0x5C00, Float16Layout.float32to16(256.125f)); // 256.0 - assertEquals((short)0x5C01, Float16Layout.float32to16(256.126f)); // 256.3 - assertEquals((short)0x5C01, Float16Layout.float32to16(256.30f)); // 256.3 - assertEquals((short)0x5C01, Float16Layout.float32to16(256.374f)); // 256.3 - assertEquals((short)0x5C02, Float16Layout.float32to16(256.375f)); // 256.5 - assertEquals((short)0x5C02, Float16Layout.float32to16(256.51f)); // 256.5 - } - - @Test - public void testFloat16to32() { - - // Zero and subnormals - assertEquals(0.0f, Float16Layout.float16to32((short)0x0000), 0); - assertEquals(-0.0f, Float16Layout.float16to32((short)0x8000), 0); - assertEquals(6e-8f, Float16Layout.float16to32((short)0x0001), 1e-8f); - assertEquals(-3.052e-5f, Float16Layout.float16to32((short)0x8200), 1e-8f); - - // Infinite and NaN - assertEquals(Float.POSITIVE_INFINITY, Float16Layout.float16to32((short)0x7C00), 0); - assertEquals(Float.NEGATIVE_INFINITY, Float16Layout.float16to32((short)0xFC00), 0); - assertEquals(Float.NaN, Float16Layout.float16to32((short)0x7E00), 0); - assertEquals(Float.intBitsToFloat(0xFFFFFFFF), Float16Layout.float16to32((short)0x7E00), 0); - - // Normalized - assertEquals(1.0f, Float16Layout.float16to32((short)0x3C00), 1e-1f); - assertEquals(-1.0f, Float16Layout.float16to32((short)0xBC00), 1e-1f); - assertEquals(100.0f, Float16Layout.float16to32((short)0x5640), 1e-1f); - assertEquals(-101.0f, Float16Layout.float16to32((short)0xD650), 1e-1f); - assertEquals(1.123f, Float16Layout.float16to32((short)0x3C7E), 1e-3f); - assertEquals(1.123f, Float16Layout.float16to32((short)0x3C7E), 1e-3f); - assertEquals(-62.34f, Float16Layout.float16to32((short)0xD3CB), 1e-2f); - } -} diff --git a/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/misc/ArrayDataBufferTest.java b/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/misc/ArrayDataBufferTest.java deleted file mode 100644 index 1c43a3e3638..00000000000 --- a/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/misc/ArrayDataBufferTest.java +++ /dev/null @@ -1,265 +0,0 @@ -/* - 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 - - 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.buffer.misc; - -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.assertTrue; - -import java.math.BigDecimal; -import org.junit.jupiter.api.Test; -import org.tensorflow.ndarray.buffer.DataBuffer; -import org.tensorflow.ndarray.buffer.DataBufferTestBase; - -public class ArrayDataBufferTest extends DataBufferTestBase { - - @Override - protected DataBuffer allocate(long size) { - return new ArrayDataBuffer<>(new BigDecimal[(int)size], false); - } - - @Override - protected BigDecimal valueOf(Long val) { - return BigDecimal.valueOf(val); - } - - @Test - public void byteArrayBufferEquals() { - DataBuffer buffer1 = new ArrayDataBuffer<>(new byte[][] { { 0x01 }, { 0x03 } }, true); - DataBuffer buffer2 = new ArrayDataBuffer<>(new byte[][] { { 0x01 }, { 0x03 } }, true); - DataBuffer buffer3 = new ArrayDataBuffer<>(new byte[][] { { 0x02 }, { 0x03 } }, true); - DataBuffer buffer4 = new ArrayDataBuffer<>(new byte[][][] { { { 0x01 } }, { { 0x03 } } }, true); - DataBuffer buffer5 = new ArrayDataBuffer<>(new byte[][][] { { { 0x01 } }, { { 0x03 } } }, true); - - assertTrue(buffer1.equals(buffer2)); - assertTrue(buffer2.equals(buffer1)); - assertEquals(buffer1.hashCode(), buffer2.hashCode()); - - assertFalse(buffer1.equals(buffer3)); - assertFalse(buffer3.equals(buffer1)); - assertNotEquals(buffer1.hashCode(), buffer3.hashCode()); - - assertFalse(buffer1.equals(buffer4)); - assertFalse(buffer4.equals(buffer1)); - assertNotEquals(buffer1.hashCode(), buffer4.hashCode()); - - assertTrue(buffer4.equals(buffer5)); - assertTrue(buffer4.equals(buffer5)); - assertEquals(buffer4.hashCode(), buffer5.hashCode()); - } - - @Test - public void intArrayBufferEquals() { - DataBuffer buffer1 = new ArrayDataBuffer<>(new int[][] { { 10 }, { 30 } }, true); - DataBuffer buffer2 = new ArrayDataBuffer<>(new int[][] { { 10 }, { 30 } }, true); - DataBuffer buffer3 = new ArrayDataBuffer<>(new int[][] { { 20 }, { 30 } }, true); - DataBuffer buffer4 = new ArrayDataBuffer<>(new int[][][] { { { 10 } }, { { 30 } } }, true); - DataBuffer buffer5 = new ArrayDataBuffer<>(new int[][][] { { { 10 } }, { { 30 } } }, true); - - assertTrue(buffer1.equals(buffer2)); - assertTrue(buffer2.equals(buffer1)); - assertEquals(buffer1.hashCode(), buffer2.hashCode()); - - assertFalse(buffer1.equals(buffer3)); - assertFalse(buffer3.equals(buffer1)); - assertNotEquals(buffer1.hashCode(), buffer3.hashCode()); - - assertFalse(buffer1.equals(buffer4)); - assertFalse(buffer4.equals(buffer1)); - assertNotEquals(buffer1.hashCode(), buffer4.hashCode()); - - assertTrue(buffer4.equals(buffer5)); - assertTrue(buffer4.equals(buffer5)); - assertEquals(buffer4.hashCode(), buffer5.hashCode()); - } - - @Test - public void shortArrayBufferEquals() { - DataBuffer buffer1 = new ArrayDataBuffer<>(new short[][] { { 10 }, { 30 } }, true); - DataBuffer buffer2 = new ArrayDataBuffer<>(new short[][] { { 10 }, { 30 } }, true); - DataBuffer buffer3 = new ArrayDataBuffer<>(new short[][] { { 20 }, { 30 } }, true); - DataBuffer buffer4 = new ArrayDataBuffer<>(new short[][][] { { { 10 } }, { { 30 } } }, true); - DataBuffer buffer5 = new ArrayDataBuffer<>(new short[][][] { { { 10 } }, { { 30 } } }, true); - - assertTrue(buffer1.equals(buffer2)); - assertTrue(buffer2.equals(buffer1)); - assertEquals(buffer1.hashCode(), buffer2.hashCode()); - - assertFalse(buffer1.equals(buffer3)); - assertFalse(buffer3.equals(buffer1)); - assertNotEquals(buffer1.hashCode(), buffer3.hashCode()); - - assertFalse(buffer1.equals(buffer4)); - assertFalse(buffer4.equals(buffer1)); - assertNotEquals(buffer1.hashCode(), buffer4.hashCode()); - - assertTrue(buffer4.equals(buffer5)); - assertTrue(buffer4.equals(buffer5)); - assertEquals(buffer4.hashCode(), buffer5.hashCode()); - } - - @Test - public void longArrayBufferEquals() { - DataBuffer buffer1 = new ArrayDataBuffer<>(new long[][] { { 10 }, { 30 } }, true); - DataBuffer buffer2 = new ArrayDataBuffer<>(new long[][] { { 10 }, { 30 } }, true); - DataBuffer buffer3 = new ArrayDataBuffer<>(new long[][] { { 20 }, { 30 } }, true); - DataBuffer buffer4 = new ArrayDataBuffer<>(new long[][][] { { { 10 } }, { { 30 } } }, true); - DataBuffer buffer5 = new ArrayDataBuffer<>(new long[][][] { { { 10 } }, { { 30 } } }, true); - - assertTrue(buffer1.equals(buffer2)); - assertTrue(buffer2.equals(buffer1)); - assertEquals(buffer1.hashCode(), buffer2.hashCode()); - - assertFalse(buffer1.equals(buffer3)); - assertFalse(buffer3.equals(buffer1)); - assertNotEquals(buffer1.hashCode(), buffer3.hashCode()); - - assertFalse(buffer1.equals(buffer4)); - assertFalse(buffer4.equals(buffer1)); - assertNotEquals(buffer1.hashCode(), buffer4.hashCode()); - - assertTrue(buffer4.equals(buffer5)); - assertTrue(buffer4.equals(buffer5)); - assertEquals(buffer4.hashCode(), buffer5.hashCode()); - } - - @Test - public void floatArrayBufferEquals() { - DataBuffer buffer1 = new ArrayDataBuffer<>(new float[][] { { 10 }, { 30 } }, true); - DataBuffer buffer2 = new ArrayDataBuffer<>(new float[][] { { 10 }, { 30 } }, true); - DataBuffer buffer3 = new ArrayDataBuffer<>(new float[][] { { 20 }, { 30 } }, true); - DataBuffer buffer4 = new ArrayDataBuffer<>(new float[][][] { { { 10 } }, { { 30 } } }, true); - DataBuffer buffer5 = new ArrayDataBuffer<>(new float[][][] { { { 10 } }, { { 30 } } }, true); - - assertTrue(buffer1.equals(buffer2)); - assertTrue(buffer2.equals(buffer1)); - assertEquals(buffer1.hashCode(), buffer2.hashCode()); - - assertFalse(buffer1.equals(buffer3)); - assertFalse(buffer3.equals(buffer1)); - assertNotEquals(buffer1.hashCode(), buffer3.hashCode()); - - assertFalse(buffer1.equals(buffer4)); - assertFalse(buffer4.equals(buffer1)); - assertNotEquals(buffer1.hashCode(), buffer4.hashCode()); - - assertTrue(buffer4.equals(buffer5)); - assertTrue(buffer4.equals(buffer5)); - assertEquals(buffer4.hashCode(), buffer5.hashCode()); - } - - @Test - public void doubleArrayBufferEquals() { - DataBuffer buffer1 = new ArrayDataBuffer<>(new double[][] { { 10 }, { 30 } }, true); - DataBuffer buffer2 = new ArrayDataBuffer<>(new double[][] { { 10 }, { 30 } }, true); - DataBuffer buffer3 = new ArrayDataBuffer<>(new double[][] { { 20 }, { 30 } }, true); - DataBuffer buffer4 = new ArrayDataBuffer<>(new double[][][] { { { 10 } }, { { 30 } } }, true); - DataBuffer buffer5 = new ArrayDataBuffer<>(new double[][][] { { { 10 } }, { { 30 } } }, true); - - assertTrue(buffer1.equals(buffer2)); - assertTrue(buffer2.equals(buffer1)); - assertEquals(buffer1.hashCode(), buffer2.hashCode()); - - assertFalse(buffer1.equals(buffer3)); - assertFalse(buffer3.equals(buffer1)); - assertNotEquals(buffer1.hashCode(), buffer3.hashCode()); - - assertFalse(buffer1.equals(buffer4)); - assertFalse(buffer4.equals(buffer1)); - assertNotEquals(buffer1.hashCode(), buffer4.hashCode()); - - assertTrue(buffer4.equals(buffer5)); - assertTrue(buffer4.equals(buffer5)); - assertEquals(buffer4.hashCode(), buffer5.hashCode()); - } - - @Test - public void booleanArrayBufferEquals() { - DataBuffer buffer1 = new ArrayDataBuffer<>(new boolean[][] { { true }, { false } }, true); - DataBuffer buffer2 = new ArrayDataBuffer<>(new boolean[][] { { true }, { false} }, true); - DataBuffer buffer3 = new ArrayDataBuffer<>(new boolean[][] { { false }, { false } }, true); - DataBuffer buffer4 = new ArrayDataBuffer<>(new boolean[][][] { { { true } }, { { false } } }, true); - DataBuffer buffer5 = new ArrayDataBuffer<>(new boolean[][][] { { { true } }, { { false } } }, true); - - assertTrue(buffer1.equals(buffer2)); - assertTrue(buffer2.equals(buffer1)); - assertEquals(buffer1.hashCode(), buffer2.hashCode()); - - assertFalse(buffer1.equals(buffer3)); - assertFalse(buffer3.equals(buffer1)); - assertNotEquals(buffer1.hashCode(), buffer3.hashCode()); - - assertFalse(buffer1.equals(buffer4)); - assertFalse(buffer4.equals(buffer1)); - assertNotEquals(buffer1.hashCode(), buffer4.hashCode()); - - assertTrue(buffer4.equals(buffer5)); - assertTrue(buffer4.equals(buffer5)); - assertEquals(buffer4.hashCode(), buffer5.hashCode()); - } - - @Test - public void objectArrayBufferEquals() { - DataBuffer buffer1 = new ArrayDataBuffer<>(new String[][] { { "10" }, { "30" } }, true); - DataBuffer buffer2 = new ArrayDataBuffer<>(new String[][] { { "10" }, { "30" } }, true); - DataBuffer buffer3 = new ArrayDataBuffer<>(new String[][] { { "20" }, { "30" } }, true); - DataBuffer buffer4 = new ArrayDataBuffer<>(new String[][][] { { { "10" } }, { { "30" } } }, true); - DataBuffer buffer5 = new ArrayDataBuffer<>(new String[][][] { { { "10" } }, { { "30" } } }, true); - - assertTrue(buffer1.equals(buffer2)); - assertTrue(buffer2.equals(buffer1)); - assertEquals(buffer1.hashCode(), buffer2.hashCode()); - - assertFalse(buffer1.equals(buffer3)); - assertFalse(buffer3.equals(buffer1)); - assertNotEquals(buffer1.hashCode(), buffer3.hashCode()); - - assertFalse(buffer1.equals(buffer4)); - assertFalse(buffer4.equals(buffer1)); - assertNotEquals(buffer1.hashCode(), buffer4.hashCode()); - - assertTrue(buffer4.equals(buffer5)); - assertTrue(buffer4.equals(buffer5)); - assertEquals(buffer4.hashCode(), buffer5.hashCode()); - } - - @Test - public void nullableObjectArrayBufferEquals() { - DataBuffer buffer1 = new ArrayDataBuffer<>(new String[][] { null, { "30" } }, true); - DataBuffer buffer2 = new ArrayDataBuffer<>(new String[][] { null, { "30" } }, true); - DataBuffer buffer3 = new ArrayDataBuffer<>(new String[][] { { "20" }, { "30" } }, true); - DataBuffer buffer4 = new ArrayDataBuffer<>(new String[][][] { { { "10" } }, null }, true); - DataBuffer buffer5 = new ArrayDataBuffer<>(new String[][][] { { { "10" } }, null }, true); - - assertTrue(buffer1.equals(buffer2)); - assertTrue(buffer2.equals(buffer1)); - assertEquals(buffer1.hashCode(), buffer2.hashCode()); - - assertFalse(buffer1.equals(buffer3)); - assertFalse(buffer3.equals(buffer1)); - assertNotEquals(buffer1.hashCode(), buffer3.hashCode()); - - assertFalse(buffer1.equals(buffer4)); - assertFalse(buffer4.equals(buffer1)); - assertNotEquals(buffer1.hashCode(), buffer4.hashCode()); - - assertTrue(buffer4.equals(buffer5)); - assertTrue(buffer4.equals(buffer5)); - assertEquals(buffer4.hashCode(), buffer5.hashCode()); - } -} diff --git a/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/misc/BitSetDataBufferTest.java b/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/misc/BitSetDataBufferTest.java deleted file mode 100644 index ec5c513869a..00000000000 --- a/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/misc/BitSetDataBufferTest.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - 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 - - 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.buffer.misc; - -import java.util.BitSet; -import org.tensorflow.ndarray.buffer.BooleanDataBuffer; -import org.tensorflow.ndarray.buffer.BooleanDataBufferTestBase; - -public class BitSetDataBufferTest extends BooleanDataBufferTestBase { - - @Override - protected BooleanDataBuffer allocate(long size) { - return new BitSetDataBuffer(new BitSet((int)size), size, false); - } - - @Override - protected Boolean valueOf(Long val) { - return val != 0; - } -} diff --git a/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/misc/StringArrayDataBufferTest.java b/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/misc/StringArrayDataBufferTest.java deleted file mode 100644 index 3e9c3c0cdbf..00000000000 --- a/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/misc/StringArrayDataBufferTest.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - 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 - - 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.buffer.misc; - -import org.tensorflow.ndarray.buffer.DataBuffer; -import org.tensorflow.ndarray.buffer.DataBufferTestBase; - -public class StringArrayDataBufferTest extends DataBufferTestBase { - - @Override - protected DataBuffer allocate(long size) { - return new ArrayDataBuffer<>(new String[(int)size], false); - } - - @Override - protected String valueOf(Long val) { - return val.toString(); - } -} diff --git a/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/nio/ByteNioDataBufferTest.java b/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/nio/ByteNioDataBufferTest.java deleted file mode 100644 index 28ff5a6c104..00000000000 --- a/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/nio/ByteNioDataBufferTest.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - 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 - - 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.buffer.nio; - -import java.nio.ByteBuffer; -import org.tensorflow.ndarray.buffer.ByteDataBuffer; -import org.tensorflow.ndarray.buffer.ByteDataBufferTestBase; - -public class ByteNioDataBufferTest extends ByteDataBufferTestBase { - - @Override - protected ByteDataBuffer allocate(long size) { - return new ByteNioDataBuffer(ByteBuffer.allocate((int)size)); - } -} diff --git a/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/nio/DoubleNioDataBufferTest.java b/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/nio/DoubleNioDataBufferTest.java deleted file mode 100644 index 7a4d39dce94..00000000000 --- a/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/nio/DoubleNioDataBufferTest.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - 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 - - 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.buffer.nio; - -import java.nio.DoubleBuffer; -import org.tensorflow.ndarray.buffer.DoubleDataBuffer; -import org.tensorflow.ndarray.buffer.DoubleDataBufferTestBase; - -public class DoubleNioDataBufferTest extends DoubleDataBufferTestBase { - - @Override - protected DoubleDataBuffer allocate(long size) { - return new DoubleNioDataBuffer(DoubleBuffer.allocate((int)size)); - } -} diff --git a/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/nio/FloatNioDataBufferTest.java b/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/nio/FloatNioDataBufferTest.java deleted file mode 100644 index 08089e76ad8..00000000000 --- a/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/nio/FloatNioDataBufferTest.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - 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 - - 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.buffer.nio; - -import java.nio.FloatBuffer; -import org.tensorflow.ndarray.buffer.FloatDataBuffer; -import org.tensorflow.ndarray.buffer.FloatDataBufferTestBase; - -public class FloatNioDataBufferTest extends FloatDataBufferTestBase { - - @Override - protected FloatDataBuffer allocate(long size) { - return new FloatNioDataBuffer(FloatBuffer.allocate((int)size)); - } -} diff --git a/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/nio/IntNioDataBufferTest.java b/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/nio/IntNioDataBufferTest.java deleted file mode 100644 index 00a993e42ed..00000000000 --- a/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/nio/IntNioDataBufferTest.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - 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 - - 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.buffer.nio; - -import java.nio.IntBuffer; -import org.tensorflow.ndarray.buffer.IntDataBuffer; -import org.tensorflow.ndarray.buffer.IntDataBufferTestBase; - -public class IntNioDataBufferTest extends IntDataBufferTestBase { - - @Override - protected IntDataBuffer allocate(long size) { - return new IntNioDataBuffer(IntBuffer.allocate((int)size)); - } -} diff --git a/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/nio/LongNioDataBufferTest.java b/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/nio/LongNioDataBufferTest.java deleted file mode 100644 index 5922d2b922c..00000000000 --- a/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/nio/LongNioDataBufferTest.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - 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 - - 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.buffer.nio; - -import java.nio.LongBuffer; -import org.tensorflow.ndarray.buffer.LongDataBuffer; -import org.tensorflow.ndarray.buffer.LongDataBufferTestBase; - -public class LongNioDataBufferTest extends LongDataBufferTestBase { - - @Override - protected LongDataBuffer allocate(long size) { - return new LongNioDataBuffer(LongBuffer.allocate((int)size)); - } -} diff --git a/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/nio/ShortNioDataBufferTest.java b/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/nio/ShortNioDataBufferTest.java deleted file mode 100644 index c76191fbcf1..00000000000 --- a/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/nio/ShortNioDataBufferTest.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - 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 - - 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.buffer.nio; - -import java.nio.ShortBuffer; -import org.tensorflow.ndarray.buffer.ShortDataBuffer; -import org.tensorflow.ndarray.buffer.ShortDataBufferTestBase; - -public class ShortNioDataBufferTest extends ShortDataBufferTestBase { - - @Override - protected ShortDataBuffer allocate(long size) { - return new ShortNioDataBuffer(ShortBuffer.allocate((int)size)); - } -} diff --git a/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/raw/BooleanRawDataBufferTest.java b/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/raw/BooleanRawDataBufferTest.java deleted file mode 100644 index 1f09d76055d..00000000000 --- a/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/raw/BooleanRawDataBufferTest.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - 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 - - 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.buffer.raw; - -import org.tensorflow.ndarray.buffer.BooleanDataBuffer; -import org.tensorflow.ndarray.buffer.BooleanDataBufferTestBase; - -public class BooleanRawDataBufferTest extends BooleanDataBufferTestBase { - - @Override - protected BooleanDataBuffer allocate(long size) { - return new BooleanRawDataBuffer(UnsafeMemoryHandle.fromArray(new boolean[(int)size], (int)size), false); - } -} diff --git a/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/raw/ByteRawDataBufferTest.java b/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/raw/ByteRawDataBufferTest.java deleted file mode 100644 index 4a415aff49f..00000000000 --- a/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/raw/ByteRawDataBufferTest.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - 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 - - 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.buffer.raw; - -import org.tensorflow.ndarray.buffer.ByteDataBuffer; -import org.tensorflow.ndarray.buffer.ByteDataBufferTestBase; - -public class ByteRawDataBufferTest extends ByteDataBufferTestBase { - - @Override - protected ByteDataBuffer allocate(long size) { - return new ByteRawDataBuffer(UnsafeMemoryHandle.fromArray(new byte[(int)size], (int)size), false); - } -} diff --git a/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/raw/DoubleRawDataBufferTest.java b/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/raw/DoubleRawDataBufferTest.java deleted file mode 100644 index df845092dd1..00000000000 --- a/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/raw/DoubleRawDataBufferTest.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - 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 - - 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.buffer.raw; - -import org.tensorflow.ndarray.buffer.DoubleDataBuffer; -import org.tensorflow.ndarray.buffer.DoubleDataBufferTestBase; - -public class DoubleRawDataBufferTest extends DoubleDataBufferTestBase { - - @Override - protected DoubleDataBuffer allocate(long size) { - return new DoubleRawDataBuffer(UnsafeMemoryHandle.fromArray(new double[(int)size], (int)size), false); - } -} diff --git a/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/raw/FloatRawDataBufferTest.java b/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/raw/FloatRawDataBufferTest.java deleted file mode 100644 index bc453d79f37..00000000000 --- a/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/raw/FloatRawDataBufferTest.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - 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 - - 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.buffer.raw; - -import org.tensorflow.ndarray.buffer.FloatDataBuffer; -import org.tensorflow.ndarray.buffer.FloatDataBufferTestBase; - -public class FloatRawDataBufferTest extends FloatDataBufferTestBase { - - @Override - protected FloatDataBuffer allocate(long size) { - return new FloatRawDataBuffer(UnsafeMemoryHandle.fromArray(new float[(int)size], (int)size), false); - } -} diff --git a/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/raw/IntRawDataBufferTest.java b/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/raw/IntRawDataBufferTest.java deleted file mode 100644 index 1142f19131d..00000000000 --- a/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/raw/IntRawDataBufferTest.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - 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 - - 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.buffer.raw; - -import org.tensorflow.ndarray.buffer.IntDataBuffer; -import org.tensorflow.ndarray.buffer.IntDataBufferTestBase; - -public class IntRawDataBufferTest extends IntDataBufferTestBase { - - @Override - protected IntDataBuffer allocate(long size) { - return new IntRawDataBuffer(UnsafeMemoryHandle.fromArray(new int[(int)size], (int)size), false); - } -} diff --git a/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/raw/LongRawDataBufferTest.java b/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/raw/LongRawDataBufferTest.java deleted file mode 100644 index af86d64a414..00000000000 --- a/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/raw/LongRawDataBufferTest.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - 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 - - 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.buffer.raw; - -import org.tensorflow.ndarray.buffer.LongDataBuffer; -import org.tensorflow.ndarray.buffer.LongDataBufferTestBase; - -public class LongRawDataBufferTest extends LongDataBufferTestBase { - - @Override - protected LongDataBuffer allocate(long size) { - return new LongRawDataBuffer(UnsafeMemoryHandle.fromArray(new long[(int)size], (int)size), false); - } -} diff --git a/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/raw/ShortRawDataBufferTest.java b/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/raw/ShortRawDataBufferTest.java deleted file mode 100644 index 1ce1f25391b..00000000000 --- a/ndarray/src/test/java/org/tensorflow/ndarray/impl/buffer/raw/ShortRawDataBufferTest.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - 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 - - 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.buffer.raw; - -import org.tensorflow.ndarray.buffer.ShortDataBuffer; -import org.tensorflow.ndarray.buffer.ShortDataBufferTestBase; - -public class ShortRawDataBufferTest extends ShortDataBufferTestBase { - - @Override - protected ShortDataBuffer allocate(long size) { - return new ShortRawDataBuffer(UnsafeMemoryHandle.fromArray(new short[(int)size], (int)size), false); - } -} 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 deleted file mode 100644 index 36540104eb7..00000000000 --- a/ndarray/src/test/java/org/tensorflow/ndarray/impl/dense/BooleanDenseNdArrayTest.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - 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 - - 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.dense; - -import org.tensorflow.ndarray.Shape; -import org.tensorflow.ndarray.buffer.DataBuffer; -import org.tensorflow.ndarray.buffer.DataBuffers; -import org.tensorflow.ndarray.BooleanNdArray; -import org.tensorflow.ndarray.BooleanNdArrayTestBase; -import org.tensorflow.ndarray.NdArrays; - -public class BooleanDenseNdArrayTest extends BooleanNdArrayTestBase { - - @Override protected BooleanNdArray allocate(Shape shape) { - return NdArrays.ofBooleans(shape); - } - - @Override protected DataBuffer allocateBuffer(long size) { - return DataBuffers.ofBooleans(size); - } -} diff --git a/ndarray/src/test/java/org/tensorflow/ndarray/impl/dense/ByteDenseNdArrayTest.java b/ndarray/src/test/java/org/tensorflow/ndarray/impl/dense/ByteDenseNdArrayTest.java deleted file mode 100644 index 2e5d1939bc3..00000000000 --- a/ndarray/src/test/java/org/tensorflow/ndarray/impl/dense/ByteDenseNdArrayTest.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - 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 - - 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.dense; - -import org.tensorflow.ndarray.Shape; -import org.tensorflow.ndarray.buffer.DataBuffer; -import org.tensorflow.ndarray.buffer.DataBuffers; -import org.tensorflow.ndarray.ByteNdArray; -import org.tensorflow.ndarray.ByteNdArrayTestBase; -import org.tensorflow.ndarray.NdArrays; - -public class ByteDenseNdArrayTest extends ByteNdArrayTestBase { - - @Override protected ByteNdArray allocate(Shape shape) { - return NdArrays.ofBytes(shape); - } - - @Override protected DataBuffer allocateBuffer(long size) { - return DataBuffers.ofBytes(size); - } -} diff --git a/ndarray/src/test/java/org/tensorflow/ndarray/impl/dense/DenseNdArrayTest.java b/ndarray/src/test/java/org/tensorflow/ndarray/impl/dense/DenseNdArrayTest.java deleted file mode 100644 index 375f7643875..00000000000 --- a/ndarray/src/test/java/org/tensorflow/ndarray/impl/dense/DenseNdArrayTest.java +++ /dev/null @@ -1,51 +0,0 @@ -package org.tensorflow.ndarray.impl.dense; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import org.junit.jupiter.api.Test; -import org.tensorflow.ndarray.Shape; -import org.tensorflow.ndarray.IntNdArray; -import org.tensorflow.ndarray.NdArrays; -import org.tensorflow.ndarray.StdArrays; -import org.tensorflow.ndarray.index.Indices; - -public class DenseNdArrayTest { - - @Test - public void arrayEquals() { - IntNdArray array = NdArrays.ofInts(Shape.of(2, 2)) - .set(NdArrays.vectorOf(1, 2), 0) - .set(NdArrays.vectorOf(3, 4), 1); - - assertTrue(array.equals(StdArrays.ndCopyOf(new int[][] {{1, 2}, {3, 4}}))); - assertTrue(array.equals(StdArrays.ndCopyOf(new Integer[][] {{1, 2}, {3, 4}}))); - assertFalse(array.equals(NdArrays.vectorOf(1, 2, 3, 4))); - assertFalse(array.equals(StdArrays.ndCopyOf(new int[][] {{3, 4}, {1, 2}}))); - assertFalse(array.equals(StdArrays.ndCopyOf(new long[][] {{1L, 2L}, {3L, 4L}}))); - } - - @Test - public void equalsAndHashCodeOnSlices() { - IntNdArray vector1 = NdArrays.vectorOf(3, 4); - IntNdArray vector2 = NdArrays.vectorOf(1, 2, 3, 4); - IntNdArray matrix1 = StdArrays.ndCopyOf(new int[][] {{1, 2}, {3, 4}}); - IntNdArray matrix2 = StdArrays.ndCopyOf(new int[][] {{1, 0, 2, 0}, {3, 0, 4, 0}}); - IntNdArray matrix3d1 = StdArrays.ndCopyOf(new int[][][] { - {{1, 2}, {3, 4}}, - {{5, 6}, {7, 8}} - }); - IntNdArray matrix3d2 = StdArrays.ndCopyOf(new int[][][] { - {{1, 2}, {4, 5}}, - {{3, 4}, {6, 7}} - }); - - assertTrue(vector1.equals(vector2.slice(Indices.sliceFrom(2)))); - assertTrue(vector1.equals(matrix1.get(1))); - assertTrue(vector1.equals(matrix2.get(1).slice(Indices.even()))); - assertTrue(matrix1.equals(matrix2.slice(Indices.all(), Indices.even()))); - assertTrue(matrix3d1.get(0).equals(matrix1)); - assertFalse(matrix3d1.get(0).equals(vector2)); - assertTrue(matrix1.equals(matrix3d2.slice(Indices.all(), Indices.at(0)))); - } -} 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 deleted file mode 100644 index 9810a744c50..00000000000 --- a/ndarray/src/test/java/org/tensorflow/ndarray/impl/dense/DoubleDenseNdArrayTest.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - 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 - - 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.dense; - -import org.tensorflow.ndarray.Shape; -import org.tensorflow.ndarray.buffer.DataBuffer; -import org.tensorflow.ndarray.buffer.DataBuffers; -import org.tensorflow.ndarray.DoubleNdArray; -import org.tensorflow.ndarray.DoubleNdArrayTestBase; -import org.tensorflow.ndarray.NdArrays; - -public class DoubleDenseNdArrayTest extends DoubleNdArrayTestBase { - - @Override protected DoubleNdArray allocate(Shape shape) { - return NdArrays.ofDoubles(shape); - } - - @Override protected DataBuffer allocateBuffer(long size) { - return DataBuffers.ofDoubles(size); - } -} 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 deleted file mode 100644 index efee2bf2cb8..00000000000 --- a/ndarray/src/test/java/org/tensorflow/ndarray/impl/dense/FloatDenseNdArrayTest.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - 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 - - 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.dense; - -import org.tensorflow.ndarray.Shape; -import org.tensorflow.ndarray.buffer.DataBuffer; -import org.tensorflow.ndarray.buffer.DataBuffers; -import org.tensorflow.ndarray.FloatNdArray; -import org.tensorflow.ndarray.FloatNdArrayTestBase; -import org.tensorflow.ndarray.NdArrays; - -public class FloatDenseNdArrayTest extends FloatNdArrayTestBase { - - @Override protected FloatNdArray allocate(Shape shape) { - return NdArrays.ofFloats(shape); - } - - @Override protected DataBuffer allocateBuffer(long size) { - return DataBuffers.ofFloats(size); - } -} diff --git a/ndarray/src/test/java/org/tensorflow/ndarray/impl/dense/IntDenseNdArrayTest.java b/ndarray/src/test/java/org/tensorflow/ndarray/impl/dense/IntDenseNdArrayTest.java deleted file mode 100644 index 712f6f44333..00000000000 --- a/ndarray/src/test/java/org/tensorflow/ndarray/impl/dense/IntDenseNdArrayTest.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - 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 - - 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.dense; - -import org.tensorflow.ndarray.Shape; -import org.tensorflow.ndarray.buffer.DataBuffer; -import org.tensorflow.ndarray.buffer.DataBuffers; -import org.tensorflow.ndarray.IntNdArray; -import org.tensorflow.ndarray.IntNdArrayTestBase; -import org.tensorflow.ndarray.NdArrays; - -public class IntDenseNdArrayTest extends IntNdArrayTestBase { - - @Override protected IntNdArray allocate(Shape shape) { - return NdArrays.ofInts(shape); - } - - @Override protected DataBuffer allocateBuffer(long size) { - return DataBuffers.ofInts(size); - } -} diff --git a/ndarray/src/test/java/org/tensorflow/ndarray/impl/dense/LongDenseNdArrayTest.java b/ndarray/src/test/java/org/tensorflow/ndarray/impl/dense/LongDenseNdArrayTest.java deleted file mode 100644 index 346e3845080..00000000000 --- a/ndarray/src/test/java/org/tensorflow/ndarray/impl/dense/LongDenseNdArrayTest.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - 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 - - 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.dense; - -import org.tensorflow.ndarray.Shape; -import org.tensorflow.ndarray.buffer.DataBuffer; -import org.tensorflow.ndarray.buffer.DataBuffers; -import org.tensorflow.ndarray.LongNdArray; -import org.tensorflow.ndarray.LongNdArrayTestBase; -import org.tensorflow.ndarray.NdArrays; - -public class LongDenseNdArrayTest extends LongNdArrayTestBase { - - @Override protected LongNdArray allocate(Shape shape) { - return NdArrays.ofLongs(shape); - } - - @Override protected DataBuffer allocateBuffer(long size) { - return DataBuffers.ofLongs(size); - } -} diff --git a/ndarray/src/test/java/org/tensorflow/ndarray/impl/dense/ShortDenseNdArrayTest.java b/ndarray/src/test/java/org/tensorflow/ndarray/impl/dense/ShortDenseNdArrayTest.java deleted file mode 100644 index 6f845c7c65d..00000000000 --- a/ndarray/src/test/java/org/tensorflow/ndarray/impl/dense/ShortDenseNdArrayTest.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - 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 - - 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.dense; - -import org.tensorflow.ndarray.Shape; -import org.tensorflow.ndarray.buffer.DataBuffer; -import org.tensorflow.ndarray.buffer.DataBuffers; -import org.tensorflow.ndarray.NdArrays; -import org.tensorflow.ndarray.ShortNdArray; -import org.tensorflow.ndarray.ShortNdArrayTestBase; - -public class ShortDenseNdArrayTest extends ShortNdArrayTestBase { - - @Override protected ShortNdArray allocate(Shape shape) { - return NdArrays.ofShorts(shape); - } - - @Override protected DataBuffer allocateBuffer(long size) { - return DataBuffers.ofShorts(size); - } -} diff --git a/ndarray/src/test/java/org/tensorflow/ndarray/impl/dense/StringDenseNdArrayTest.java b/ndarray/src/test/java/org/tensorflow/ndarray/impl/dense/StringDenseNdArrayTest.java deleted file mode 100644 index 5afc1420ab2..00000000000 --- a/ndarray/src/test/java/org/tensorflow/ndarray/impl/dense/StringDenseNdArrayTest.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - 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 - - 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.dense; - -import org.tensorflow.ndarray.Shape; -import org.tensorflow.ndarray.buffer.DataBuffer; -import org.tensorflow.ndarray.buffer.DataBuffers; -import org.tensorflow.ndarray.NdArray; -import org.tensorflow.ndarray.NdArrayTestBase; -import org.tensorflow.ndarray.NdArrays; - -public class StringDenseNdArrayTest extends NdArrayTestBase { - - @Override protected NdArray allocate(Shape shape) { - return NdArrays.ofObjects(String.class, shape); - } - - @Override protected DataBuffer allocateBuffer(long size) { - return DataBuffers.ofObjects(String.class, size); - } - - @Override protected String valueOf(Long val) { - return val.toString(); - } - - protected String zeroOrNull() { - return null; - } -} diff --git a/ndarray/src/test/java/org/tensorflow/ndarray/impl/sequence/ElementSequenceTest.java b/ndarray/src/test/java/org/tensorflow/ndarray/impl/sequence/ElementSequenceTest.java deleted file mode 100644 index bad78404e9b..00000000000 --- a/ndarray/src/test/java/org/tensorflow/ndarray/impl/sequence/ElementSequenceTest.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * 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 - * - * 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.sequence; - -import static org.junit.jupiter.api.Assertions.assertArrayEquals; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.fail; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import org.junit.jupiter.api.Test; -import org.tensorflow.ndarray.Shape; -import org.tensorflow.ndarray.buffer.DataBufferWindow; -import org.tensorflow.ndarray.buffer.DataBuffers; -import org.tensorflow.ndarray.buffer.IntDataBuffer; -import org.tensorflow.ndarray.IntNdArray; -import org.tensorflow.ndarray.NdArraySequence; -import org.tensorflow.ndarray.NdArrays; -import org.tensorflow.ndarray.impl.AbstractNdArray; - -public class ElementSequenceTest { - - @Test - public void iterateVectorsWithIndex() { - IntNdArray array = NdArrays.ofInts(Shape.of(2, 3, 2)); - - NdArraySequence sequence = new SlicingElementSequence( - (AbstractNdArray)array, 1); - List coords = new ArrayList<>((int)array.shape().size()); - sequence.forEachIndexed((c, e) -> coords.add(Arrays.copyOf(c, c.length))); - - assertEquals(6, coords.size()); - assertArrayEquals(new long[] {0, 0}, coords.get(0)); - assertArrayEquals(new long[] {0, 1}, coords.get(1)); - assertArrayEquals(new long[] {0, 2}, coords.get(2)); - assertArrayEquals(new long[] {1, 0}, coords.get(3)); - assertArrayEquals(new long[] {1, 1}, coords.get(4)); - assertArrayEquals(new long[] {1, 2}, coords.get(5)); - } - - @Test - public void iterateScalarsWithIndex() { - IntNdArray array = NdArrays.ofInts(Shape.of(2, 3, 2)); - - NdArraySequence cursor = new SlicingElementSequence( - (AbstractNdArray)array, 2); - List coords = new ArrayList<>((int)array.shape().size()); - cursor.forEachIndexed((c, e) -> coords.add(Arrays.copyOf(c, c.length))); - - assertEquals(12, coords.size()); - assertArrayEquals(new long[] {0, 0, 0}, coords.get(0)); - assertArrayEquals(new long[] {0, 0, 1}, coords.get(1)); - assertArrayEquals(new long[] {0, 1, 0}, coords.get(2)); - assertArrayEquals(new long[] {0, 1, 1}, coords.get(3)); - assertArrayEquals(new long[] {0, 2, 0}, coords.get(4)); - assertArrayEquals(new long[] {0, 2, 1}, coords.get(5)); - assertArrayEquals(new long[] {1, 0, 0}, coords.get(6)); - assertArrayEquals(new long[] {1, 0, 1}, coords.get(7)); - assertArrayEquals(new long[] {1, 1, 0}, coords.get(8)); - assertArrayEquals(new long[] {1, 1, 1}, coords.get(9)); - assertArrayEquals(new long[] {1, 2, 0}, coords.get(10)); - assertArrayEquals(new long[] {1, 2, 1}, coords.get(11)); - } - - @Test - public void slicingElementSequenceReturnsUniqueInstances() { - IntNdArray array = NdArrays.ofInts(Shape.of(2, 3, 2)); - NdArraySequence sequence = new SlicingElementSequence( - (AbstractNdArray) array, 1); - List elements = new ArrayList<>(); - sequence.forEach(e -> { - elements.forEach(tmp -> { - if (tmp == e) { - fail(); - } - }); - elements.add(e); - }); - } - - @Test - public void fastElementSequenceReturnsSameInstance() { - IntNdArray array = NdArrays.ofInts(Shape.of(2, 3, 2)); - IntNdArray element = array.get(0); - NdArraySequence sequence = new FastElementSequence( - (AbstractNdArray) array, 1, element, mockDataBufferWindow(2)); - sequence.forEach(e -> { - if (e != element) { - fail(); - } - }); - } - - private DataBufferWindow mockDataBufferWindow(long size) { - return new DataBufferWindow() { - - @Override - public long offset() { - return offset; - } - - @Override - public long size() { - return size; - } - - @Override - public DataBufferWindow slideTo(long index) { - offset = index; - return this; - } - - @Override - public DataBufferWindow slide(long step) { - offset += step; - return this; - } - - @Override - public IntDataBuffer buffer() { - return buffer; - } - - private long offset; - private final long size = 2; - private final IntDataBuffer buffer = DataBuffers.ofInts(2); - }; - } -} diff --git a/ndarray/src/test/resources/COPYRIGHT.txt b/ndarray/src/test/resources/COPYRIGHT.txt deleted file mode 100644 index 5e7bd50bb48..00000000000 --- a/ndarray/src/test/resources/COPYRIGHT.txt +++ /dev/null @@ -1 +0,0 @@ -All images in this folder and its subfolders are free of any copyright. \ No newline at end of file diff --git a/ndarray/src/test/resources/castle.jpg b/ndarray/src/test/resources/castle.jpg deleted file mode 100644 index c5b07b4bc2a47a14c91184211a08fda45a2d79d9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 436164 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%0D*hK^mNe+a-^+ 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

9xXu2LpQ4sa5`7w#gVi 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%0A=!>Nz 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

eQu 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�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ļ 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 diff --git a/pom.xml b/pom.xml index 6260e1f58c3..728f39fcb3a 100644 --- a/pom.xml +++ b/pom.xml @@ -29,7 +29,6 @@ - ndarray tensorflow-core tensorflow-framework @@ -234,7 +233,7 @@ - TensorFlowers + SIG JVM TensorFlow http://www.tensorflow.org diff --git a/tensorflow-core/tensorflow-core-api/pom.xml b/tensorflow-core/tensorflow-core-api/pom.xml index ecbb3cca67c..124f0ab9906 100644 --- a/tensorflow-core/tensorflow-core-api/pom.xml +++ b/tensorflow-core/tensorflow-core-api/pom.xml @@ -20,6 +20,8 @@ ${native.build.skip} ${native.build.skip} org.tensorflow.core.api + 0.3.1 + 1.0.1 @@ -43,17 +45,17 @@ org.tensorflow ndarray - ${project.version} + ${ndarray.version} - - org.junit.jupiter - junit-jupiter-api - test - - - org.junit.jupiter - junit-jupiter-engine - test + + org.junit.jupiter + junit-jupiter-api + test + + + org.junit.jupiter + junit-jupiter-engine + test org.openjdk.jmh @@ -63,7 +65,7 @@ com.google.truth truth - 1.0.1 + ${truth.version} test