diff --git a/src/main/cpp/imgUtils.cpp b/src/main/cpp/imgUtils.cpp new file mode 100644 index 00000000000..35d3ff7c863 --- /dev/null +++ b/src/main/cpp/imgUtils.cpp @@ -0,0 +1,239 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +#include +#include +#include "mkl.h" + +using namespace std; + +void printImage(const double* image, int rows, int cols) { + for (int i = 0; i < rows; i++) { + for (int j = 0; j < cols; j++) { + cout << image[i * cols + j] << " "; + } + cout << endl; + } + cout << "\n"<< endl; +} + +void img_transform(const double* img_in, int orig_w, int orig_h, int out_w, int out_h, double a, double b, double c, double d, + double e, double f, double fill_value, double* img_out) { + double divisor = a * e - b * d; + if (divisor == 0) { + std::cout << "Inverse matrix does not exist! Returning input." << std::endl; + for (int i = 0; i < orig_h; i++) { + for (int j = 0; j < orig_w; j++) { + img_out[i * orig_w + j] = img_in[i * orig_w + j]; + } + } + } else { + // Create the inverted transformation matrix + double T_inv[9]; + T_inv[0] = e / divisor; + T_inv[1] = -b / divisor; + T_inv[2] = (b * f - c * e) / divisor; + T_inv[3] = -d / divisor; + T_inv[4] = a / divisor; + T_inv[5] = (c * d - a * f) / divisor; + T_inv[6] = 0.0; + T_inv[7] = 0.0; + T_inv[8] = 1.0; + + // Create the coordinates of output pixel centers linearized in row-major order + double* coords = new double[2 * out_w * out_h]; + for (int i = 0; i < out_h; i++) { + for (int j = 0; j < out_w; j++) { + coords[2 * (i * out_w + j)] = j + 0.5; + coords[2 * (i * out_w + j) + 1] = i + 0.5; + } + } + + // Perform matrix multiplication to compute sampling pixel indices + double* transformed_coords = new double[2 * out_w * out_h]; + for (int i = 0; i < out_w * out_h; i++) { + double x = coords[2 * i]; + double y = coords[2 * i + 1]; + transformed_coords[2 * i] = std::floor(T_inv[0] * x + T_inv[1] * y + T_inv[2]) + 1; + transformed_coords[2 * i + 1] = std::floor(T_inv[3] * x + T_inv[4] * y + T_inv[5]) + 1; + } + + // Fill output image + for (int i = 0; i < out_h; i++) { + for (int j = 0; j < out_w; j++) { + int inx = static_cast(transformed_coords[2 * (i * out_w + j)]) - 1; + int iny = static_cast(transformed_coords[2 * (i * out_w + j) + 1]) - 1; + if (inx >= 0 && inx < orig_w && iny >= 0 && iny < orig_h) { + img_out[i * out_w + j] = img_in[iny * orig_w + inx]; + } else { + img_out[i * out_w + j] = fill_value; + } + } + } + + delete[] coords; + delete[] transformed_coords; + } +} + +void imageRotate(double* img_in, int rows, int cols, double radians, double fill_value, double* img_out) { + // Translation matrix for moving the origin to the center of the image + double t1_data[] = { + 1, 0, static_cast(-cols)/2, + 0, 1, static_cast(-rows)/2, + 0, 0, 1 + }; + double* t1 = t1_data; + // Translation matrix for moving the origin back to the top left corner + double t2_data[] = { + 1, 0, static_cast(cols)/2, + 0, 1, static_cast(rows)/2, + 0, 0, 1 + }; + double* t2 = t2_data; + // The rotation matrix around the origin + double rot_data[] = { + cos(radians), sin(radians), 0, + -sin(radians), cos(radians), 0, + 0, 0, 1 + }; + double* rot = rot_data; + + // Combined transformation matrix + double m_data1[3*3]; + cblas_dgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, 3, 3, 3, 1.0, t2, 3, rot, 3, 0.0, m_data1, 3); + double m_data2[3*3]; + cblas_dgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, 3, 3, 3, 1.0, m_data1, 3, t1, 3, 0.0, m_data2, 3); + double* m = m_data2; + // Transform image + img_transform(img_in,rows,cols,rows,cols,m[0], m[1], m[2], m[3], m[4], m[5], fill_value, img_out); +} + +double* imageCutout(double* img_in, int rows, int cols, int x, int y, int width, int height, double fill_value) { + // Allocate memory for the output image using MKL + double* img_out = new double[rows * cols]; + + if (width < 1 || height < 1) { + // Invalid width or height, return the input image as it is + cblas_dcopy(rows * cols, img_in, 1, img_out, 1); + } else { + int end_x = x + width - 1; + int end_y = y + height - 1; + + int start_x = std::max(1, x); + int start_y = std::max(1, y); + end_x = std::min(cols, end_x); + end_y = std::min(rows, end_y); + + // Copy the input image to the output image using MKL + cblas_dcopy(rows * cols, img_in, 1, img_out, 1); + + // Fill the cutout region with the fill_value + for (int i = start_y - 1; i < end_y; ++i) { + for (int j = start_x - 1; j < end_x; ++j) { + img_out[i * cols + j] = fill_value; + } + } + } + + return img_out; +} + +double* imageCrop(double* img_in, int orig_w, int orig_h, int w, int h, int x_offset, int y_offset) { + // Allocate memory for the output image + double* img_out = new double[w * h]; + + int start_h = (std::ceil((orig_h - h) / 2)) + y_offset - 1 ; + int end_h = (start_h + h - 1); + int start_w = (std::ceil((orig_w - w) / 2)) + x_offset - 1; + int end_w = (start_w + w - 1); + + // Create a mask to identify the cropped region + double* mask = new double[orig_w * orig_h]; + double* temp_mask = new double[w * h]; + + // Set mask elements to 0 outside the cropped region and 1 inside + std::memset(mask, 0, orig_w * orig_h * sizeof(double)); + for(int i = 0; i < h * w; i++) { + temp_mask[i] = 1; + } + + for (int i = start_h; i <= end_h; ++i) { + for (int j = start_w; j <= end_w; ++j) { + mask[i * orig_w + j] = temp_mask[(i - start_h) * w + (j - start_w)]; + } + } + + // Apply the mask to crop the image + for (int i = 0; i < h; ++i) { + for (int j = 0; j < w; ++j) { + img_out[i * w + j] = img_in[(start_h + i) * orig_w + (start_w + j)] * mask[(start_h + i) * orig_w + (start_w + j)]; + } + } + + // Free memory for the mask + delete[] mask; + delete[] temp_mask; + + return img_out; +} + +void img_translate(double* img_in, double offset_x, double offset_y, + int in_w, int in_h, int out_w, int out_h, double fill_value, double* img_out) { + int w = out_w; + int h = out_h; + offset_x = round(offset_x); + offset_y = round(offset_y); + + int start_x = 0 - static_cast(offset_x); + int start_y = 0 - static_cast(offset_y); + int end_x = std::max(w, out_w) - static_cast(offset_x); + int end_y = std::max(h, out_h) - static_cast(offset_y); + + if (start_x < 0) + start_x = 0; + if (start_y < 0) + start_y = 0; + + if (w < end_x) + end_x = w; + if (h < end_y) + end_y = h; + + if (out_w < end_x + static_cast(offset_x)) + end_x = out_w - static_cast(offset_x); + if (out_h < end_y + static_cast(offset_y)) + end_y = out_h - static_cast(offset_y); + + for (int y = 0; y < out_h; ++y) { + for (int x = 0; x < out_w; ++x) { + img_out[y * out_w + x] = fill_value; + } + } + + if (start_x < end_x && start_y < end_y) { + for (int y = start_y + static_cast(offset_y); y < end_y + static_cast(offset_y); ++y) { + int x_in = (start_x > 0) ? start_x + static_cast(offset_x): start_x; + int y_in = (start_y > 0 ) ? y : y - static_cast(offset_y); + cblas_dcopy(end_x - start_x, &img_in[(x_in) + (y_in) * in_w], + 1, &img_out[y * out_w + start_x + static_cast(offset_x)], 1); + } + } +} + diff --git a/src/main/cpp/imgUtils.h b/src/main/cpp/imgUtils.h new file mode 100644 index 00000000000..001d5ad282c --- /dev/null +++ b/src/main/cpp/imgUtils.h @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +#ifndef IMAGE_UTILS_H +#define IMAGE_UTILS_H +#include + +void printImage(const double* image, int rows, int cols); + +void m_img_transform(const double* img_in, int orig_w, int orig_h, int out_w, int out_h, double a, double b, double c, double d, + double e, double f, double fill_value, double* img_out); + +void imageRotate(double* img_in, int rows, int cols, double radians, double fill_value, double* img_out); + +double* imageCutout(double* img_in, int rows, int cols, int x, int y, int width, int height, double fill_value); + +double* imageCrop(double* img_in, int orig_w, int orig_h, int w, int h, int x_offset, int y_offset); + +void img_translate(double* img_in, double offset_x, double offset_y, + int in_w, int in_h, int out_w, int out_h, double fill_value, double* img_out); + +#endif /* IMAGE_UTILS_H */ diff --git a/src/main/cpp/lib/libhe-Linux-x86_64.so b/src/main/cpp/lib/libhe-Linux-x86_64.so deleted file mode 100644 index 5d55922788b..00000000000 Binary files a/src/main/cpp/lib/libhe-Linux-x86_64.so and /dev/null differ diff --git a/src/main/cpp/lib/libsystemds_mkl-Linux-x86_64.so b/src/main/cpp/lib/libsystemds_mkl-Linux-x86_64.so deleted file mode 100644 index a677b940138..00000000000 Binary files a/src/main/cpp/lib/libsystemds_mkl-Linux-x86_64.so and /dev/null differ diff --git a/src/main/cpp/lib/libsystemds_mkl-Windows-AMD64.dll b/src/main/cpp/lib/libsystemds_mkl-Windows-AMD64.dll deleted file mode 100644 index ca4c24ed2a0..00000000000 Binary files a/src/main/cpp/lib/libsystemds_mkl-Windows-AMD64.dll and /dev/null differ diff --git a/src/main/cpp/lib/libsystemds_openblas-Linux-x86_64.so b/src/main/cpp/lib/libsystemds_openblas-Linux-x86_64.so deleted file mode 100644 index 227443e3248..00000000000 Binary files a/src/main/cpp/lib/libsystemds_openblas-Linux-x86_64.so and /dev/null differ diff --git a/src/main/cpp/lib/libsystemds_openblas-Windows-AMD64.dll b/src/main/cpp/lib/libsystemds_openblas-Windows-AMD64.dll deleted file mode 100644 index 78d7ac119c6..00000000000 Binary files a/src/main/cpp/lib/libsystemds_openblas-Windows-AMD64.dll and /dev/null differ diff --git a/src/main/cpp/lib/libsystemds_spoof_cuda-Linux-x86_64.so b/src/main/cpp/lib/libsystemds_spoof_cuda-Linux-x86_64.so deleted file mode 100644 index 70f0d812753..00000000000 Binary files a/src/main/cpp/lib/libsystemds_spoof_cuda-Linux-x86_64.so and /dev/null differ diff --git a/src/main/cpp/lib/libsystemds_spoof_cuda-Windows-AMD64.dll b/src/main/cpp/lib/libsystemds_spoof_cuda-Windows-AMD64.dll deleted file mode 100644 index b005c1b2c55..00000000000 Binary files a/src/main/cpp/lib/libsystemds_spoof_cuda-Windows-AMD64.dll and /dev/null differ diff --git a/src/main/cpp/systemds.cpp b/src/main/cpp/systemds.cpp index 86ac053ad10..474f5296eab 100644 --- a/src/main/cpp/systemds.cpp +++ b/src/main/cpp/systemds.cpp @@ -27,7 +27,7 @@ #include "libmatrixdnn.h" #include "libmatrixmult.h" #include "systemds.h" - +#include "imgUtils.h" // Results from Matrix-vector/vector-matrix 1M x 1K, dense show that GetDoubleArrayElements creates a copy on OpenJDK. // Logic: @@ -38,8 +38,8 @@ #define GET_DOUBLE_ARRAY(env, input, numThreads) \ ((double*)env->GetPrimitiveArrayCritical(input, NULL)) // ( maxThreads != -1 && ((int)numThreads) == maxThreads ? ((double*)env->GetPrimitiveArrayCritical(input, NULL)) : env->GetDoubleArrayElements(input,NULL) ) - -// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- // From: https://developer.android.com/training/articles/perf-jni.html // 0 // Actual: the array object is un-pinned. @@ -54,9 +54,9 @@ #define RELEASE_ARRAY(env, input, inputPtr, numThreads) \ env->ReleasePrimitiveArrayCritical(input, inputPtr, 0) // ( maxThreads != -1 && ((int)numThreads) == maxThreads ? env->ReleasePrimitiveArrayCritical(input, inputPtr, 0) : env->ReleaseDoubleArrayElements(input, inputPtr, 0) ) - + // ------------------------------------------------------------------- -JNIEXPORT void JNICALL Java_org_apache_sysds_utils_NativeHelper_setMaxNumThreads +/*JNIEXPORT void JNICALL Java_org_apache_sysds_utils_NativeHelper_setMaxNumThreads (JNIEnv *, jclass, jint jmaxThreads) { setNumThreadsForBLAS(jmaxThreads); } @@ -76,7 +76,7 @@ JNIEXPORT jlong JNICALL Java_org_apache_sysds_utils_NativeHelper_dmmdd( RELEASE_INPUT_ARRAY(env, m1, m1Ptr, numThreads); RELEASE_INPUT_ARRAY(env, m2, m2Ptr, numThreads); - RELEASE_ARRAY(env, ret, retPtr, numThreads); + RELEASE_ARRAY(env, ret, retPtr, numThreads); return static_cast(nnz); } @@ -122,33 +122,33 @@ JNIEXPORT jboolean JNICALL Java_org_apache_sysds_utils_NativeHelper_conv2dSparse double* avalsPtr = GET_DOUBLE_ARRAY(env, avals, numThreads); double* filterPtr = GET_DOUBLE_ARRAY(env, filter, numThreads); double* retPtr = GET_DOUBLE_ARRAY(env, ret, numThreads); - - conv2dSparse((int)apos, (int)alen, aixPtr, avalsPtr, filterPtr, retPtr, (int)N, (int)C, (int)H, (int)W, + + conv2dSparse((int)apos, (int)alen, aixPtr, avalsPtr, filterPtr, retPtr, (int)N, (int)C, (int)H, (int)W, (int)K, (int)R, (int)S, (int)stride_h, (int)stride_w, (int)pad_h, (int)pad_w, (int)P, (int)Q, (int)numThreads); - + RELEASE_INPUT_ARRAY(env, avals, avalsPtr, numThreads); RELEASE_INPUT_ARRAY(env, filter, filterPtr, numThreads); env->ReleasePrimitiveArrayCritical(aix, aixPtr, JNI_ABORT); - RELEASE_ARRAY(env, ret, retPtr, numThreads); + RELEASE_ARRAY(env, ret, retPtr, numThreads); return (jboolean) true; } JNIEXPORT jboolean JNICALL Java_org_apache_sysds_utils_NativeHelper_conv2dBackwardFilterSparseDense - (JNIEnv * env, jclass, jint apos, jint alen, jintArray aix, jdoubleArray avals, jdoubleArray dout, + (JNIEnv * env, jclass, jint apos, jint alen, jintArray aix, jdoubleArray avals, jdoubleArray dout, jdoubleArray ret, jint N, jint C, jint H, jint W, jint K, jint R, jint S, jint stride_h, jint stride_w, jint pad_h, jint pad_w, jint P, jint Q, jint numThreads) { int* aixPtr = ((int*)env->GetPrimitiveArrayCritical(aix, NULL)); double* avalsPtr = GET_DOUBLE_ARRAY(env, avals, numThreads); double* doutPtr = GET_DOUBLE_ARRAY(env, dout, numThreads); double* retPtr = GET_DOUBLE_ARRAY(env, ret, numThreads); - - conv2dBackwardFilterSparseDense((int)apos, (int)alen, aixPtr, avalsPtr, doutPtr, retPtr, (int)N, (int)C, (int)H, (int)W, + + conv2dBackwardFilterSparseDense((int)apos, (int)alen, aixPtr, avalsPtr, doutPtr, retPtr, (int)N, (int)C, (int)H, (int)W, (int)K, (int)R, (int)S, (int)stride_h, (int)stride_w, (int)pad_h, (int)pad_w, (int)P, (int)Q, (int)numThreads); - + RELEASE_INPUT_ARRAY(env, avals, avalsPtr, numThreads); RELEASE_INPUT_ARRAY(env, dout, doutPtr, numThreads); env->ReleasePrimitiveArrayCritical(aix, aixPtr, JNI_ABORT); - RELEASE_ARRAY(env, ret, retPtr, numThreads); + RELEASE_ARRAY(env, ret, retPtr, numThreads); return (jboolean) true; } @@ -162,11 +162,11 @@ JNIEXPORT jlong JNICALL Java_org_apache_sysds_utils_NativeHelper_conv2dDense( double* retPtr = GET_DOUBLE_ARRAY(env, ret, numThreads); if(inputPtr == NULL || filterPtr == NULL || retPtr == NULL) return -1; - + size_t nnz = dconv2dBiasAddDense(inputPtr, 0, filterPtr, retPtr, (int) N, (int) C, (int) H, (int) W, (int) K, (int) R, (int) S, (int) stride_h, (int) stride_w, (int) pad_h, (int) pad_w, (int) P, (int) Q, false, (int) numThreads); - + RELEASE_INPUT_ARRAY(env, input, inputPtr, numThreads); RELEASE_INPUT_ARRAY(env, filter, filterPtr, numThreads); RELEASE_ARRAY(env, ret, retPtr, numThreads); @@ -184,11 +184,11 @@ JNIEXPORT jlong JNICALL Java_org_apache_sysds_utils_NativeHelper_dconv2dBiasAddD double* retPtr = GET_DOUBLE_ARRAY(env, ret, numThreads); if(inputPtr == NULL || biasPtr == NULL || filterPtr == NULL || retPtr == NULL) return -1; - + size_t nnz = dconv2dBiasAddDense(inputPtr, biasPtr, filterPtr, retPtr, (int) N, (int) C, (int) H, (int) W, (int) K, (int) R, (int) S, (int) stride_h, (int) stride_w, (int) pad_h, (int) pad_w, (int) P, (int) Q, true, (int) numThreads); - + RELEASE_INPUT_ARRAY(env, input, inputPtr, numThreads); RELEASE_INPUT_ARRAY(env, bias, biasPtr, numThreads); RELEASE_INPUT_ARRAY(env, filter, filterPtr, numThreads); @@ -199,7 +199,7 @@ JNIEXPORT jlong JNICALL Java_org_apache_sysds_utils_NativeHelper_dconv2dBiasAddD JNIEXPORT jlong JNICALL Java_org_apache_sysds_utils_NativeHelper_sconv2dBiasAddDense( JNIEnv* env, jclass, jobject input, jobject bias, jobject filter, jobject ret, jint N, jint C, jint H, jint W, jint K, jint R, jint S, - jint stride_h, jint stride_w, jint pad_h, jint pad_w, jint P, jint Q, jint numThreads) + jint stride_h, jint stride_w, jint pad_h, jint pad_w, jint P, jint Q, jint numThreads) { float* inputPtr = (float*) env->GetDirectBufferAddress(input); float* biasPtr = (float*) env->GetDirectBufferAddress(bias); @@ -207,7 +207,7 @@ JNIEXPORT jlong JNICALL Java_org_apache_sysds_utils_NativeHelper_sconv2dBiasAddD float* retPtr = (float*) env->GetDirectBufferAddress(ret); if(inputPtr == NULL || biasPtr == NULL || filterPtr == NULL || retPtr == NULL) return -1; - + size_t nnz = sconv2dBiasAddDense(inputPtr, biasPtr, filterPtr, retPtr, (int) N, (int) C, (int) H, (int) W, (int) K, (int) R, (int) S, (int) stride_h, (int) stride_w, (int) pad_h, (int) pad_w, (int) P, (int) Q, true, (int) numThreads); @@ -254,4 +254,136 @@ JNIEXPORT jlong JNICALL Java_org_apache_sysds_utils_NativeHelper_conv2dBackwardF RELEASE_INPUT_ARRAY(env, dout, doutPtr, numThreads); RELEASE_ARRAY(env, ret, retPtr, numThreads); return static_cast(nnz); +} +*/ +JNIEXPORT void JNICALL Java_org_apache_sysds_utils_NativeHelper_testNativeBindingWithDgemm + (JNIEnv* env, jclass cls, jchar transa, jchar transb, jint m, jint n, jint k, jdouble alpha, jdoubleArray A, jint lda, jdoubleArray B, jint ldb, jdouble beta, jdoubleArray C, jint ldc) { + + // Obtain native pointers to Java arrays + jdouble* nativeA = env->GetDoubleArrayElements(A, NULL); + jdouble* nativeB = env->GetDoubleArrayElements(B, NULL); + jdouble* nativeC = env->GetDoubleArrayElements(C, NULL); + + // Perform matrix multiplication using Intel MKL library + cblas_dgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, m, n, k, alpha, nativeA, lda, nativeB, ldb, beta, nativeC, ldc); + + // Release the native pointers + env->ReleaseDoubleArrayElements(A, nativeA, 0); + env->ReleaseDoubleArrayElements(B, nativeB, 0); + env->ReleaseDoubleArrayElements(C, nativeC, 0); +} + +JNIEXPORT void JNICALL Java_org_apache_sysds_utils_NativeHelper_imageRotate + (JNIEnv* env, jclass clazz, jdoubleArray img_in, jint rows, jint cols, jdouble radians, jdouble fill_value, jdoubleArray img_out) { + // Get input image data + + jsize num_pixels = env->GetArrayLength(img_in); + jdouble* img_in_data = env->GetDoubleArrayElements(img_in, NULL); + + // Create output image array + jdouble* img_out_data = new jdouble[num_pixels]; + + // Rotate the image + imageRotate(img_in_data, rows, cols, radians, fill_value, img_out_data); + + // Set the output image data + env->SetDoubleArrayRegion(img_out, 0, num_pixels, img_out_data); + + // Clean up + delete[] img_out_data; + env->ReleaseDoubleArrayElements(img_in, img_in_data, JNI_ABORT); +} + +JNIEXPORT jdoubleArray JNICALL Java_org_apache_sysds_utils_NativeHelper_imageCutout + (JNIEnv* env, jclass cls, jdoubleArray img_in, jint rows, jint cols, jint x, jint y, jint width, jint height, jdouble fill_value) { + // Convert the Java 1D array to a double* + jdouble* img_in_arr = env->GetDoubleArrayElements(img_in, nullptr); + + // Call the C++ implementation of the Image Cutout function + jdouble* img_out_arr = imageCutout(img_in_arr, rows, cols, x, y, width, height, fill_value); + + // Convert the double* back to a Java 1D array + jdoubleArray img_out = env->NewDoubleArray(rows * cols); + env->SetDoubleArrayRegion(img_out, 0, rows * cols, img_out_arr); + + // Release the native array reference + env->ReleaseDoubleArrayElements(img_in, img_in_arr, JNI_ABORT); + delete[] img_out_arr; + + return img_out; +} + +JNIEXPORT jdoubleArray JNICALL Java_org_apache_sysds_utils_NativeHelper_cropImage(JNIEnv *env, jclass, + jdoubleArray img_in, jint orig_w, jint orig_h, jint w, jint h, jint x_offset, jint y_offset) { + jsize length = env->GetArrayLength(img_in); + double *img_in_array = env->GetDoubleArrayElements(img_in, 0); + + int start_h = (ceil((orig_h - h) / 2)) + y_offset - 1; + int end_h = (start_h + h - 1); + int start_w = (ceil((orig_w - w) / 2)) + x_offset - 1; + int end_w = (start_w + w - 1); + + jdoubleArray img_out_java; + if (start_h < 0 || end_h >= orig_h || start_w < 0 || end_w >= orig_w) { + img_out_java = env->NewDoubleArray(orig_w * orig_h); + env->SetDoubleArrayRegion(img_out_java, 0, orig_w * orig_h, img_in_array); + + }else { + double *img_out = imageCrop(img_in_array, orig_w, orig_h, w, h, x_offset, y_offset); + + if (img_out == nullptr) { + return nullptr; + } + + img_out_java = env->NewDoubleArray(w * h); + env->SetDoubleArrayRegion(img_out_java, 0, w * h, img_out); + delete[] img_out; + } + + env->ReleaseDoubleArrayElements(img_in, img_in_array, 0); + + return img_out_java; +} + +/*JNIEXPORT jdoubleArray JNICALL Java_org_apache_sysds_utils_NativeHelper_shearImage(JNIEnv *env, jclass, + jdoubleArray img_in, jint width, jint height, jdouble shear_x, jdouble shear_y, jdouble fill_value) { + + // Convert the Java input double array to a C++ double array + jsize length = env->GetArrayLength(img_in); + double *img_in_array = env->GetDoubleArrayElements(img_in, 0); + + // Call the C++ m_img_shear function (assuming it is implemented) + double* img_out = img_transform(img_in_array, width, height, shear_x, shear_y, fill_value); + + // Release the input double array elements + env->ReleaseDoubleArrayElements(img_in, img_in_array, 0); + + if (img_out == nullptr) { + return nullptr; + } + + // Create a new Java double array and copy the result into it + jdoubleArray img_out_java = env->NewDoubleArray(width * height); + env->SetDoubleArrayRegion(img_out_java, 0, width * height, img_out); + + // Free memory for the output image + delete[] img_out; + + return img_out_java; +}*/ + +JNIEXPORT void JNICALL Java_org_apache_sysds_utils_NativeHelper_imgTranslate(JNIEnv *env, jclass cls, + jdoubleArray img_in, jdouble offset_x, jdouble offset_y, + jint in_w, jint in_h, jint out_w, jint out_h, + jdouble fill_value, jdoubleArray img_out) { + // Convert Java arrays to C++ arrays + jdouble* j_img_in = env->GetDoubleArrayElements(img_in, nullptr); + jdouble* j_img_out = env->GetDoubleArrayElements(img_out, nullptr); + + // Call your C++ ImageTranslate function + img_translate(j_img_in, offset_x, offset_y, in_w, in_h, out_w, out_h, fill_value, j_img_out); + + // Release Java arrays + env->ReleaseDoubleArrayElements(img_in, j_img_in, 0); + env->ReleaseDoubleArrayElements(img_out, j_img_out, 0); } \ No newline at end of file diff --git a/src/main/cpp/systemds.h b/src/main/cpp/systemds.h index b4f741f2904..665cc249b37 100644 --- a/src/main/cpp/systemds.h +++ b/src/main/cpp/systemds.h @@ -114,6 +114,56 @@ JNIEXPORT jboolean JNICALL Java_org_apache_sysds_utils_NativeHelper_conv2dSparse JNIEXPORT void JNICALL Java_org_apache_sysds_utils_NativeHelper_setMaxNumThreads (JNIEnv *, jclass, jint); + /* + * Class: org_apache_sysds_utils_NativeHelper + * Method: testNativeBindingWithDgemm + * Signature: (CCIIID[DI[DID[DI)V + */ + +JNIEXPORT void JNICALL Java_org_apache_sysds_utils_NativeHelper_testNativeBindingWithDgemm + (JNIEnv *, jclass, jchar, jchar, jint, jint, jint, jdouble, jdoubleArray, jint, jdoubleArray, jint, jdouble, jdoubleArray, jint); + +/* + * Class: org_apache_sysds_utils_NativeHelper + * Method: imageRotate + * Signature: ([DIIIDD[D)V + */ +JNIEXPORT void JNICALL Java_org_apache_sysds_utils_NativeHelper_imageRotate + (JNIEnv *, jclass, jdoubleArray, jint, jint, jdouble, jdouble, jdoubleArray); + +/* + * Class: org_apache_sysds_utils_NativeHelper + * Method: imageCutout + * Signature: ([DIIIIIDD)[D + */ +JNIEXPORT jdoubleArray JNICALL Java_org_apache_sysds_utils_NativeHelper_imageCutout + (JNIEnv *, jclass, jdoubleArray, jint, jint, jint, jint, jint, jint, jdouble); + +/* + * Class: org_apache_sysds_utils_NativeHelper + * Method: cropImage + * Signature: ([DIIIIII)[D + */ +JNIEXPORT jdoubleArray JNICALL Java_org_apache_sysds_utils_NativeHelper_cropImage(JNIEnv *, jclass, + jdoubleArray, jint, jint, jint, jint, jint, jint); + +/* + * Class: org_apache_sysds_utils_NativeHelper + * Method: shearImage + * Signature: ([DIIIIII)[D + */ +JNIEXPORT jdoubleArray JNICALL Java_org_apache_sysds_utils_NativeHelper_shearImage(JNIEnv *env, jclass, + jdoubleArray img_in, jint width, jint height, jdouble shear_x, jdouble shear_y, jdouble fill_value); + +/* + * Class: org_apache_sysds_utils_NativeHelper + * Method: imgTranslate + * Signature: ([DDBIIIIID[D)V + */ +JNIEXPORT void JNICALL Java_org_apache_sysds_utils_NativeHelper_imgTranslate(JNIEnv *env, jclass cls, + jdoubleArray img_in, jdouble offset_x, jdouble offset_y, + jint in_w, jint in_h, jint out_w, jint out_h, + jdouble fill_value, jdoubleArray img_out); #ifdef __cplusplus } #endif diff --git a/src/main/java/org/apache/sysds/utils/NativeHelper.java b/src/main/java/org/apache/sysds/utils/NativeHelper.java index f673ba4e4e0..ec5a30f8e3e 100644 --- a/src/main/java/org/apache/sysds/utils/NativeHelper.java +++ b/src/main/java/org/apache/sysds/utils/NativeHelper.java @@ -416,4 +416,21 @@ public static native boolean conv2dSparse(int apos, int alen, int[] aix, double[ // different tradeoffs. In current implementation, we always use GetPrimitiveArrayCritical as it has proven to be // fastest. We can revisit this decision later and hence I would not recommend removing this method. private static native void setMaxNumThreads(int numThreads); + + //test interface for native binding + public static native void testNativeBindingWithDgemm(char transa, char transb, int m, int n, int k, double alpha, double[] A, int lda, double[] B, int ldb, double beta, double[] C, int ldc); + + //image rotation + public static native void imageRotate(double[] img_in, int rows, int cols, double radians, double fill_value, double[] img_out); + + //image cutout + public static native double[] imageCutout(double[] img_in, int rows, int cols, int x, int y, int width, int height, double fill_value); + + //image crop + public static native double[] cropImage(double[] img_in, int orig_w, int orig_h, int w, int h, int x_offset, int y_offset); + + //image translate + public static native void imgTranslate(double[] img_in, double offset_x, double offset_y, + int in_w, int in_h, int out_w, int out_h, + double fill_value, double[] img_out); } diff --git a/src/test/java/org/apache/sysds/test/functions/nativ/ImgUtilsTest.java b/src/test/java/org/apache/sysds/test/functions/nativ/ImgUtilsTest.java new file mode 100644 index 00000000000..18b42d6758e --- /dev/null +++ b/src/test/java/org/apache/sysds/test/functions/nativ/ImgUtilsTest.java @@ -0,0 +1,414 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.sysds.test.functions.nativ; + +import org.apache.sysds.utils.NativeHelper; +import org.junit.Ignore; +import org.junit.Test; + +import static org.junit.Assert.assertArrayEquals; + +public class ImgUtilsTest { + + static { + //System.loadLibrary("systemds_mkl-Darwin-x86_64"); + System.loadLibrary("systemds_openblas-Darwin-x86_64"); + } + + @Test + public void testImageRotation90() { + // Input image dimensions + int rows = 3; + int cols = 3; + + // Input image + double[] img_in = { + 1,2,3, + 4,5,6, + 7,8,9 + }; + // Rotation angle in radians + double radians = Math.PI / 2; + // Fill value for the output image + double fill_value = 0.0; + // Expected output image + double[] expected_img_out_90 = { + 3,6,9, + 2,5,8, + 1,4,7 + }; + + double[] expected_img_out_45 = { + 2,3,6, + 1,5,9, + 4,7,8 + }; + + // Create the output image array + double[] img_out = new double[rows * cols]; + NativeHelper.imageRotate(img_in, rows, cols, radians, fill_value, img_out); + assertArrayEquals(expected_img_out_90, img_out, 0.0001); + //rotate by 45 + NativeHelper.imageRotate(img_in, rows, cols, radians/2, fill_value, img_out); + assertArrayEquals(expected_img_out_45, img_out, 0.0001); + } + + @Test + public void testImageRotation45() { + // Input image dimensions + int rows = 6; + int cols = 6; + + // Input image + double[] img_in = { + 1.0, 2.0, 3.0, 4.0,5.0,6.0, + 5.0, 6.0, 7.0, 8.0,9.0,10.0, + 9.0, 10.0, 11.0, 12.0,13.0,14.0, + 13.0, 14.0, 15.0, 16.0,17.0,18.0, + 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, + 21.0,22.0,23.0,24.0,25.0,26.0 + }; + + // Rotation angle in radians + double radians = Math.PI / 4; + + // Fill value for the output image + double fill_value = 0.0; + + // Expected output image + double[] expected_img_out = { + 0, 4, 5, 10 ,14, 0, + 3, 4, 8 ,13 ,18 ,18, + 2, 7, 12 ,16 ,17 ,22, + 5, 10, 15 ,16, 20 ,25, + 9, 13, 14, 19, 24 ,24, + 0, 13, 17 ,22, 23 ,0 + }; + + // Create the output image array + double[] img_out = new double[rows * cols]; + + // Rotate the image + NativeHelper.imageRotate(img_in, rows, cols, radians, fill_value, img_out); + + // Compare the output image with the expected image + assertArrayEquals(expected_img_out, img_out, 0.0001); + } + + @Test + public void testImageRotation180() { + // Input image dimensions + int rows = 4; + int cols = 4; + + // Input image + double[] img_in = { + 1.0, 2.0, 3.0, 4.0, + 5.0, 6.0, 7.0, 8.0, + 9.0, 10.0, 11.0, 12.0, + 13.0, 14.0, 15.0, 16.0 + }; + + // Rotation angle in radians + double radians = Math.PI; + + // Fill value for the output image + double fill_value = 0.0; + + // Expected output image + double[] expected_img_out = { + 16.0, 15.0, 14.0, 13.0, + 12.0, 11.0, 10.0, 9.0, + 8.0, 7.0, 6.0, 5.0, + 4.0, 3.0, 2.0, 1.0 + }; + + // Create the output image array + double[] img_out = new double[rows * cols]; + + // Rotate the image + NativeHelper.imageRotate(img_in, rows, cols, radians, fill_value, img_out); + + // Compare the output image with the expected image + assertArrayEquals(expected_img_out, img_out, 0.0001); + } + + @Test + public void testCutoutImage() { + // Example input 2D matrix + double[] img_in = { + 1.0, 2.0, 3.0, 4.0, + 5.0, 6.0, 7.0, 8.0, + 9.0, 10.0, 11.0, 12.0, + 13.0, 14.0, 15.0, 16.0 + }; + + int rows = 4; + int cols = 4; + int x = 2; + int y = 2; + int width = 3; + int height = 3; + double fill_value = 0.0; + + // Perform image cutout using JNI + double[] img_out = NativeHelper.imageCutout(img_in, rows, cols, x, y, width, height, fill_value); + + // Expected output image after cutout + double[] expectedOutput = { + 1.0, 2.0, 3.0, 4.0, + 5.0, 0.0, 0.0, 0.0, + 9.0, 0.0, 0.0, 0.0, + 13.0, 0.0, 0.0, 0.0 + }; + + // Check if the output image matches the expected output + assertArrayEquals(expectedOutput, img_out,0.0001); + } + @Test + public void testImageCutoutInvalidCutout() { + double[] img_in = { + 1.0, 2.0, 3.0, 4.0, + 5.0, 6.0, 7.0, 8.0, + 9.0, 10.0, 11.0, 12.0, + 13.0, 14.0, 15.0, 16.0 + }; + + int rows = 4; + int cols = 4; + int x = 3; + int y = 3; + int width = -2; + int height = 0; + double fill_value = 0.0; + + double[] expectedOutput = img_in; // Expect no change since the cutout is invalid + + double[] img_out = NativeHelper.imageCutout(img_in, rows, cols, x, y, width, height, fill_value); + assertArrayEquals(expectedOutput, img_out,0.0001); + } + + @Test + public void testImageCutoutNoCutout() { + double[] img_in = { + 1.0, 2.0, 3.0, 4.0, + 5.0, 6.0, 7.0, 8.0, + 9.0, 10.0, 11.0, 12.0, + 13.0, 14.0, 15.0, 16.0 + }; + + int rows = 4; + int cols = 4; + int x = 3; + int y = 3; + int width = 1; + int height = 1; + double fill_value = 0.0; + + double[] expectedOutput = { + 1.0, 2.0, 3.0, 4.0, + 5.0, 6.0, 7.0, 8.0, + 9.0, 10.0, 0.0, 12.0, + 13.0, 14.0, 15.0, 16.0 + }; + + double[] img_out = NativeHelper.imageCutout(img_in, rows, cols, x, y, width, height, fill_value); + assertArrayEquals(expectedOutput, img_out,0.0001); + } + + @Test + public void testImageCropValidCrop() { + double[] img_in = { + 1.0, 2.0, 3.0, 4.0, + 5.0, 6.0, 7.0, 8.0, + 9.0, 10.0, 11.0, 12.0, + 13.0, 14.0, 15.0, 16.0 + }; + + int orig_w = 4; + int orig_h = 4; + int w = 2; + int h = 2; + int x_offset = 1; + int y_offset = 1; + + double[] expectedOutput = { + 6.0, 7.0, + 10.0, 11.0 + }; + + double[] img_out = NativeHelper.cropImage(img_in, orig_w, orig_h, w, h, x_offset, y_offset); + + assertArrayEquals(expectedOutput, img_out,0.0001); } + + @Test + public void testImageCropInvalidCrop() { + double[] img_in = { + 1.0, 2.0, 3.0, 4.0, + 5.0, 6.0, 7.0, 8.0, + 9.0, 10.0, 11.0, 12.0, + 13.0, 14.0, 15.0, 16.0 + }; + + int orig_w = 4; + int orig_h = 4; + int w = 5; + int h = 5; + int x_offset = 1; + int y_offset = 1; + + double[] expectedOutput = img_in; // Expect no change since the crop is invalid + + double[] img_out = NativeHelper.cropImage(img_in, orig_w, orig_h, w, h, x_offset, y_offset); + assertArrayEquals(expectedOutput, img_out,0.0001); + } + + @Test + public void testImgTranslate() { + int in_w = 5; + int in_h = 5; + int out_w = 7; + int out_h = 7; + double fill_value = 0.0; + + double[] img_in = new double[in_w * in_h]; + for (int i = 0; i < in_w * in_h; ++i) { + img_in[i] = i + 1; // Filling input image with sequential values + } + + double[] img_out = new double[out_w * out_h]; + + double offset_x = 1.5; + double offset_y = 1.5; + + NativeHelper.imgTranslate(img_in, offset_x, offset_y, in_w, in_h, out_w, out_h, fill_value, img_out); + + // Expected output based on the given offsets and fill value + double[] expectedOutput = { + 0,0,0,0,0,0,0, + 0,0,0,0,0,0,0, + 0,0,1,2,3,4,5, + 0,0,6,7,8,9,10, + 0,0,11,12,13,14,15, + 0,0,16,17,18,19,20, + 0,0,21,22,23,24,25 + }; + + assertArrayEquals(expectedOutput, img_out, 1e-9); // Compare arrays with a small epsilon + } + + @Test + public void testImgTranslateNegativeOffsets() { + int in_w = 5; + int in_h = 5; + int out_w = 6; + int out_h = 6; + double fill_value = 0.0; + + double[] img_in = new double[in_w * in_h]; + for (int i = 0; i < in_w * in_h; ++i) { + img_in[i] = i + 1; // Filling input image with sequential values + } + + double[] img_out = new double[out_w * out_h]; + + double offset_x = -0.5; // Negative offset in X direction + double offset_y = -0.5; // Negative offset in Y direction + + NativeHelper.imgTranslate(img_in, offset_x, offset_y, in_w, in_h, out_w, out_h, fill_value, img_out); + + // Expected output based on the given offsets and fill value + double[] expectedOutput = { + 1,2,3,4,5,0, + 6,7,8,9,10,0, + 11,12,13,14,15,0, + 16,17,18,19,20,0, + 21,22,23,24,25,0, + 0,0,0,0,0,0, + }; + + assertArrayEquals(expectedOutput, img_out, 1e-9); // Compare arrays with a small epsilon + } + + @Test + public void testImgTranslatePositiveAndNegativeOffsets() { + int in_w = 5; + int in_h = 5; + int out_w = 6; + int out_h = 6; + double fill_value = 0.0; + + double[] img_in = new double[in_w * in_h]; + for (int i = 0; i < in_w * in_h; ++i) { + img_in[i] = i + 1; // Filling input image with sequential values + } + + double[] img_out = new double[out_w * out_h]; + + double offset_x = -0.5; // Negative offset in X direction + double offset_y = 0.5; // Negative offset in Y direction + + NativeHelper.imgTranslate(img_in, offset_x, offset_y, in_w, in_h, out_w, out_h, fill_value, img_out); + + // Expected output based on the given offsets and fill value + double[] expectedOutput = { + 0,0,0,0,0,0, + 1,2,3,4,5,0, + 6,7,8,9,10,0, + 11,12,13,14,15,0, + 16,17,18,19,20,0, + 21,22,23,24,25,0, + }; + + assertArrayEquals(expectedOutput, img_out, 1e-9); // Compare arrays with a small epsilon + } + + @Test + public void testImgTranslatePositiveAndNegativeOffsets2() { + int in_w = 5; + int in_h = 5; + int out_w = 6; + int out_h = 6; + double fill_value = 0.0; + + double[] img_in = new double[in_w * in_h]; + for (int i = 0; i < in_w * in_h; ++i) { + img_in[i] = i + 1; // Filling input image with sequential values + } + + double[] img_out = new double[out_w * out_h]; + + double offset_x = 0.5; // Negative offset in X direction + double offset_y = -0.5; // Negative offset in Y direction + + NativeHelper.imgTranslate(img_in, offset_x, offset_y, in_w, in_h, out_w, out_h, fill_value, img_out); + + // Expected output based on the given offsets and fill value + double[] expectedOutput = { + 0,1,2,3,4,5, + 0,6,7,8,9,10, + 0,11,12,13,14,15, + 0,16,17,18,19,20, + 0,21,22,23,24,25, + 0,0,0,0,0,0, + }; + + assertArrayEquals(expectedOutput, img_out, 1e-9); // Compare arrays with a small epsilon + } +} diff --git a/src/test/java/org/apache/sysds/test/functions/nativ/NativeBindingTestWithDgemm.java b/src/test/java/org/apache/sysds/test/functions/nativ/NativeBindingTestWithDgemm.java new file mode 100644 index 00000000000..6a985dcfca9 --- /dev/null +++ b/src/test/java/org/apache/sysds/test/functions/nativ/NativeBindingTestWithDgemm.java @@ -0,0 +1,140 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.sysds.test.functions.nativ; + +import java.util.Random; +import org.apache.sysds.utils.NativeHelper; +import org.junit.Assert; +import org.junit.Test; + +public class NativeBindingTestWithDgemm { + static { + System.loadLibrary("systemds_openblas-Darwin-x86_64"); + } + // Helper method to flatten a 2D matrix into a 1D array + private double[] flattenMatrix(double[][] matrix) { + int rows = matrix.length; + int columns = matrix[0].length; + double[] flattened = new double[rows * columns]; + + for (int i = 0; i < rows; i++) { + for (int j = 0; j < columns; j++) { + flattened[i * columns + j] = matrix[i][j]; + } + } + + return flattened; + } + + // Helper method to generate a random matrix + private double[][] generateRandomMatrix(int rows, int columns) { + Random random = new Random(); + double[][] matrix = new double[rows][columns]; + + for (int i = 0; i < rows; i++) { + for (int j = 0; j < columns; j++) { + matrix[i][j] = random.nextDouble(); + } + } + + return matrix; + } + + public void testImageRotation() { + // Input image dimensions + int rows = 4; + int cols = 4; + + // Input image + double[] img_in = { + 1, 1, 1, 0, + 0, 3, 1, 2, + 2, 3, 1, 0, + 1, 0, 2, 1 + }; + + // Rotation angle in radians + double radians = Math.PI / 2; + + // Fill value for the output image + double fill_value = 0.0; + + // Expected output image + double[] expected_img_out = { + 0, 2, 0, 1, + 1, 1, 1, 2, + 1, 3, 3, 0, + 1, 0, 2, 1 + }; + + // Create the output image array + double[] img_out = new double[rows * cols]; + + // Rotate the image + NativeHelper.imageRotate(img_in, rows, cols, radians, fill_value, img_out); + + // Compare the output image with the expected image + Assert.assertArrayEquals(expected_img_out, img_out, 0.0001); + } + + // Method to test the dgemm function + public void testDgemm() { + char transa = 'N'; + char transb = 'N'; + int m = 2000; + int n = 100; + int k = 1000; + double alpha = 1.0; + double beta = 0.0; + + // Generate random input matrices A, B, and C + double[][] A = generateRandomMatrix(m, k); + double[][] B = generateRandomMatrix(k, n); + double[][] C = new double[m][n]; + + // Convert matrices to 1D arrays + double[] flatA = flattenMatrix(A); + double[] flatB = flattenMatrix(B); + double[] flatC = flattenMatrix(C); + + // Call the native dgemm method + long startTime = System.currentTimeMillis(); + NativeHelper.testNativeBindingWithDgemm(transa, transb, m, n, k, alpha, flatA, k, flatB, n, beta, flatC, n); + long endTime = System.currentTimeMillis(); + long executionTime = (endTime - startTime); + + + + // Print the result matrix C + /*System.out.println("Result Matrix C:"); + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + System.out.print(flatC[i * n + j] + " "); + } + System.out.println(); + }*/ + System.out.println("Execution time: " + executionTime + " ms"); + } + + public static void main(String[] args) { + new NativeBindingTestWithDgemm().testDgemm(); + new NativeBindingTestWithDgemm().testImageRotation(); + } +}