| // This should work on LLVM 7, 8, 9, however in CI the version of clang installed on Ubuntu 18.04 cannot load | |
| // a clang plugin properly without segfaulting on exit. This is fine on Ubuntu 20.04 or later LLVM versions... | |
| // RUN: if [ %llvmver -lt 18 ]; then %clang++ -fno-exceptions -ffast-math -mllvm -enable-load-pre=0 -std=c++11 -O1 %s -S -emit-llvm -o - %loadClangEnzyme -mllvm -enzyme-auto-sparsity=1 | %lli - ; fi | |
| // RUN: if [ %llvmver -lt 18 ]; then %clang++ -fno-exceptions -ffast-math -mllvm -enable-load-pre=0 -std=c++11 -O2 %s -S -emit-llvm -o - %loadClangEnzyme -mllvm -enzyme-auto-sparsity=1 | %lli - ; fi | |
| // RUN: if [ %llvmver -lt 18 ]; then %clang++ -fno-exceptions -ffast-math -mllvm -enable-load-pre=0 -std=c++11 -O3 %s -S -emit-llvm -o - %loadClangEnzyme -mllvm -enzyme-auto-sparsity=1 | %lli - ; fi | |
| // TODO: if [ %llvmver -ge 12 ]; then %clang++ -fno-exceptions -ffast-math -mllvm -enable-load-pre=0 -std=c++11 -O1 %s -S -emit-llvm -o - %newLoadClangEnzyme -mllvm -enzyme-auto-sparsity=1 -S | %lli - ; fi | |
| // TODO: if [ %llvmver -ge 12 ]; then %clang++ -fno-exceptions -ffast-math -mllvm -enable-load-pre=0 -std=c++11 -O2 %s -S -emit-llvm -o - %newLoadClangEnzyme -mllvm -enzyme-auto-sparsity=1 -S | %lli - ; fi | |
| // TODO: if [ %llvmver -ge 12 ]; then %clang++ -fno-exceptions -ffast-math -mllvm -enable-load-pre=0 -std=c++11 -O3 %s -S -emit-llvm -o - %newLoadClangEnzyme -mllvm -enzyme-auto-sparsity=1 -S | %lli - ; fi | |
| #include <vector> | |
| #include <assert.h> | |
| #include <stdio.h> | |
| #include <stdint.h> | |
| #include <math.h> | |
| #include <string.h> | |
| #include <time.h> | |
| #include "matrix.h" | |
| template<typename T> | |
| __attribute__((always_inline)) | |
| static T face_load(unsigned long long offset, T* x, const int* faces) { | |
| offset /= sizeof(T); | |
| return x[faces[offset]]; | |
| } | |
| template<typename T> | |
| __attribute__((always_inline)) | |
| static void face_store(unsigned long long offset, T* x, const int* faces) { | |
| assert(0 && "store is not legal"); | |
| } | |
| template<typename T> | |
| __attribute__((always_inline)) | |
| static T area_load(unsigned long long offset, T* pos0, const int* faces) { | |
| offset /= sizeof(T); | |
| int idx = offset / 9; | |
| int inc = offset % 9; | |
| int i = faces[3*idx]; | |
| int j = faces[3*idx+1]; | |
| int k = faces[3*idx+2]; | |
| /// pos_data[0:3] -> pos[3*faces[i]:3*faces[i]+3] | |
| /// pos_data[3:6] -> pos[3*faces[j]:3*faces[j]+3] | |
| /// pos_data[6:9] -> pos[3*faces[k]:3*faces[k]+3] | |
| if (inc < 3) { | |
| return pos0[3*i+inc]; | |
| } else if (inc < 6) { | |
| return pos0[3*j+inc-3]; | |
| } else { | |
| return pos0[3*k+inc-6]; | |
| } | |
| } | |
| template<typename T> | |
| __attribute__((always_inline)) | |
| static void area_store(unsigned long long offset, T* pos0, const int* faces) { | |
| assert(0 && "store is not legal"); | |
| } | |
| template<typename T> | |
| __attribute__((always_inline)) | |
| static T eigenstuffM(const T *__restrict__ pos0, size_t n, const int *__restrict__ faces, const T *__restrict__ x) { | |
| T sum = 0; | |
| __builtin_assume(n != 0); | |
| for (size_t idx=0; idx<n; idx++) { | |
| int i = faces[3*idx]; | |
| int j = faces[3*idx+1]; | |
| int k = faces[3*idx+2]; | |
| /* | |
| T xi = x[i]; | |
| T xj = x[j]; | |
| T xk = x[k]; | |
| */ | |
| T xi = x[3 * idx]; /// x[i] -> real_x[faces[i]] | |
| T xj = x[3 * idx + 1]; | |
| T xk = x[3 * idx + 2]; | |
| const T* pos_data = &pos0[9 * idx]; | |
| /// | |
| /// pos_data[0:3] -> pos[3*faces[i]:3*faces[i]+3] | |
| /// pos_data[3:6] -> pos[3*faces[j]:3*faces[j]+3] | |
| /// pos_data[6:9] -> pos[3*faces[k]:3*faces[k]+3] | |
| T tri_area = area(&pos_data[0], &pos_data[3], &pos_data[6]); | |
| sum += (xi * xi + xj * xj + xk * xk) * (1.0 / 3.0) * tri_area; // barycentric mass lumping | |
| } | |
| return sum; | |
| } | |
| // Calculate total energy for all faces in 3D | |
| template<typename T> | |
| __attribute__((always_inline)) | |
| static T eigenstuffL(const T *__restrict__ x, size_t num_faces, const int *__restrict__ faces, const T *__restrict__ pos0) { | |
| T sum = 0; | |
| __builtin_assume(num_faces != 0); | |
| for (size_t idx=0; idx<num_faces; idx++) { | |
| int i = faces[3*idx]; | |
| int j = faces[3*idx+1]; | |
| int k = faces[3*idx+2]; | |
| T X[2][3] = { | |
| { pos0[3*j+0] - pos0[3*i+0], | |
| pos0[3*j+1] - pos0[3*i+1], | |
| pos0[3*j+2] - pos0[3*i+2]}, | |
| {pos0[3*k+0] - pos0[3*i+0], pos0[3*k+1] - pos0[3*i+1], pos0[3*k+2] - pos0[3*i+2]} | |
| }; | |
| T pInvX[3][2]; | |
| pseudo_inverse(pInvX, X); | |
| T diffs[] = {x[j] - x[i], x[k] - x[i]}; | |
| T g[3]; | |
| #pragma clang loop unroll(full) | |
| for (int i = 0; i < 3; ++i) { | |
| T sum = 0.0f; | |
| #pragma clang loop unroll(full) | |
| for (int j = 0; j < 2; ++j) { | |
| sum += pInvX[i][j] * diffs[j]; | |
| } | |
| g[i] = sum; | |
| } | |
| sum += dot_product<T, 3>(g, g) * area(&pos0[3*i], &pos0[3*j], &pos0[3*k]); | |
| } | |
| return sum; | |
| } | |
| template<typename T> | |
| __attribute__((always_inline)) | |
| static void gradient_ip(const T *__restrict__ pos0, const size_t num_faces, const int* faces, const T *__restrict__ x, T *__restrict__ out) | |
| { | |
| __enzyme_autodiff<void>((void *)eigenstuffM<T>, | |
| enzyme_const, pos0, | |
| enzyme_const, num_faces, | |
| enzyme_const, faces, | |
| enzyme_dup, x, out); | |
| } | |
| template<typename T> | |
| __attribute__((noinline)) | |
| std::vector<Triple<T>> hessian(const T*__restrict__ pos0, size_t num_faces, const int* faces, const T*__restrict__ x, size_t x_pts) | |
| { | |
| float* x2 = __enzyme_post_sparse_todense<float*>(face_load<float>, face_store<float>, x, faces); | |
| /* | |
| float* x3 = (float*)malloc(sizeof(float)*9*num_faces); | |
| for (size_t idx=0; idx<num_faces; idx++) { | |
| int i = faces[3*idx]; | |
| int j = faces[3*idx+1]; | |
| int k = faces[3*idx+2]; | |
| x3[idx * 9 + 0] = pos[3*i+0]; | |
| x3[idx * 9 + 1] = pos[3*i+1]; | |
| x3[idx * 9 + 2] = pos[3*i+2]; | |
| x3[idx * 9 + 3] = pos[3*j+0]; | |
| x3[idx * 9 + 4] = pos[3*j+1]; | |
| x3[idx * 9 + 5] = pos[3*j+2]; | |
| x3[idx * 9 + 6] = pos[3*k+0]; | |
| x3[idx * 9 + 7] = pos[3*k+1]; | |
| x3[idx * 9 + 8] = pos[3*k+2]; | |
| } | |
| */ | |
| float* pos02 = __enzyme_post_sparse_todense<float*>(area_load<float>, area_store<float>, pos0, faces); | |
| std::vector<Triple<T>> hess; | |
| __builtin_assume(x_pts != 0); | |
| for (size_t i=0; i<3*x_pts; i++) | |
| __enzyme_fwddiff<void>((void *)gradient_ip<T>, | |
| enzyme_const, pos02, | |
| enzyme_const, num_faces, | |
| enzyme_const, faces, | |
| enzyme_dup, x2, __enzyme_todense<T*>(ident_load<T>, ident_store<T>, i), | |
| enzyme_dupnoneed, nullptr, __enzyme_todense<T*>(sparse_load<T>, sparse_store<T>, i, &hess)); | |
| return hess; | |
| } | |
| int main(int argc, char** argv) { | |
| size_t x_pts = 8; | |
| if (argc >= 2) { | |
| x_pts = atoi(argv[1]); | |
| } | |
| // TODO generate data for more inputs | |
| assert(x_pts == 8); | |
| const float x[] = {0.0, 1.0, 0.0}; | |
| const size_t num_faces = 1; | |
| const int faces[] = {0, 1, 2}; | |
| const float pos0[] = {1.0, 2.0, 3.0, 4.0, 3.0, 2.0, 3.0, 1.0, 3.0}; | |
| // Call eigenstuffM_simple | |
| struct timeval start, end; | |
| gettimeofday(&start, NULL); | |
| const float resultM = eigenstuffM(pos0, num_faces, faces, x); | |
| gettimeofday(&end, NULL); | |
| printf("Result for eigenstuffM_simple: %f, runtime:%f\n", resultM, tdiff(&start, &end)); | |
| // Call eigenstuffL_simple | |
| gettimeofday(&start, NULL); | |
| const float resultL = eigenstuffL(pos0, num_faces, faces, x); | |
| gettimeofday(&end, NULL); | |
| printf("Result for eigenstuffL_simple: %f, runtime:%f\n", resultL, tdiff(&start, &end)); | |
| float dx[sizeof(x)/sizeof(x[0])]; | |
| for (size_t i=0; i<sizeof(dx)/sizeof(x[0]); i++) | |
| dx[i] = 0; | |
| gradient_ip(pos0, num_faces, faces, x, dx); | |
| if (x_pts < 30) { | |
| for (size_t i=0; i<sizeof(dx)/sizeof(dx[0]); i++) | |
| printf("eigenstuffM grad_vert[%zu]=%f\n", i, dx[i]); | |
| } | |
| gettimeofday(&start, NULL); | |
| auto hess_x = hessian(pos0, num_faces, faces, x, x_pts); | |
| gettimeofday(&end, NULL); | |
| printf("Number of elements %ld\n", hess_x.size()); | |
| printf("Runtime %0.6f\n", tdiff(&start, &end)); | |
| if (x_pts <= 8) | |
| for (auto &hess : hess_x) { | |
| printf("i=%lu, j=%lu, val=%f\n", hess.row, hess.col, hess.val); | |
| } | |
| return 0; | |
| } | |