TEST(Matrix, InitializerList) { MatrixQ A = {{1, 2, 3}, {4, 5, 6}}; EXPECT_EQ(2, A(0, 1)); EXPECT_EQ(2, A.height()); EXPECT_EQ(3, A.width()); }
TEST(Matrix, ColSwap) { MatrixQ A = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}; MatrixQ B = {{2, 1, 3}, {5, 4, 6}, {8, 7, 9}}; EXPECT_EQ(B, A.col_swap(0, 1)); MatrixQ C = A; EXPECT_EQ(C, A.col_swap(0, 0)); }
TEST(Matrix, RowSwap) { MatrixQ A = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}; MatrixQ B = {{1, 2, 3}, {7, 8, 9}, {4, 5, 6}}; EXPECT_EQ(B, A.row_swap(1, 2)); MatrixQ C = A; EXPECT_EQ(C, A.row_swap(2, 2)); }
GroupWithMorphisms compute_image(const std::size_t p, const MatrixQ& f, const AbelianGroup& X, const AbelianGroup& Y) { MatrixQRefList to_X; MatrixQList from_X = {MatrixQ::identity(f.width())}; GroupWithMorphisms K = compute_kernel(p, f, X, Y, to_X, ref(from_X)); MatrixQList to_X_2 = {MatrixQ::identity(f.width())}; MatrixQList from_X_2 = {f}; GroupWithMorphisms img = compute_cokernel(p, f, Y, ref(to_X_2), ref(from_X_2)); return img; }
bool morphism_equal(std::size_t p, const MatrixQ& f, const MatrixQ& g, const AbelianGroup& Y) { for (std::size_t i = 0; i < f.height(); i++) { for (std::size_t j = 0; j < f.width(); j++) { if (f(i, j) != g(i, j)) { if (i >= Y.tor_rank() || static_cast<std::size_t>(p_val_q(p, f(i, j) - g(i, j))) < Y(i)) { return false; } } } } return true; }
GroupWithMorphisms compute_cokernel(const std::size_t p, const MatrixQ& f, const AbelianGroup& Y, const MatrixQRefList& to_Y_ref, const MatrixQRefList& from_Y_ref) { MatrixQ f_rel_Y(f.height(), f.width() + Y.tor_rank()); f_rel_Y(0, 0, f.height(), f.width()) = f; f_rel_Y(0, f.width(), Y.tor_rank(), Y.tor_rank()) = Y.torsion_matrix(p); MatrixQList to_Y_copy = deref(to_Y_ref); MatrixQList from_Y_copy = deref(from_Y_ref); MatrixQRefList to_X; MatrixQRefList from_X; MatrixQRefList to_Y_copy_ref = ref(to_Y_copy); MatrixQRefList from_Y_copy_ref = ref(from_Y_copy); smith_reduce_p(p, f_rel_Y, to_X, from_X, to_Y_copy_ref, from_Y_copy_ref); std::size_t rank_diff = 0; std::size_t torsion_rank = 0; for (std::size_t i = 0; i < std::min(f_rel_Y.height(), f_rel_Y.width()); ++i) { if (f_rel_Y(i, i) == 1) ++rank_diff; else if (f_rel_Y(i, i) != 0) ++torsion_rank; else break; } GroupWithMorphisms C(f_rel_Y.height() - rank_diff - torsion_rank, torsion_rank); for (std::size_t i = rank_diff; i < rank_diff + torsion_rank; ++i) C.group(i - rank_diff) = static_cast<std::size_t>(p_val_q(p, f_rel_Y(i, i))); for (MatrixQ& g_to_Y : to_Y_copy) C.maps_to.emplace_back( g_to_Y(rank_diff, 0, g_to_Y.height() - rank_diff, g_to_Y.width())); for (MatrixQ& g_from_Y : from_Y_copy) C.maps_from.emplace_back(g_from_Y(0, rank_diff, g_from_Y.height(), g_from_Y.width() - rank_diff)); return C; }
// lifts a map from f:F -> Y over the map map: X -> Y. We only need relations // for Y. // Remark 1: does NOT catch if such a lift doesn't exist! // Remark 2: If the map X -> Y is injective, then for a map A -> Y the lift F_A // -> X of the map // F_A -> Y induces a well-defined map A -> X. // In general, the problem whether there IS such a lift, and how to // compute it, // will involve additional work. MatrixQ lift_from_free(const mod_t p, const MatrixQ& f, const MatrixQ& map, const AbelianGroup& Y) { MatrixQ rel_y_map(map.height(), Y.tor_rank() + map.width()); rel_y_map(Y.free_rank(), 0, Y.tor_rank(), Y.tor_rank()) = Y.torsion_matrix(p); rel_y_map(0, Y.tor_rank(), map.height(), map.width()) = map; MatrixQ proj(map.width(), Y.tor_rank() + map.width()); proj(0, Y.tor_rank(), map.width(), map.width()) = MatrixQ::identity(map.width()); MatrixQ f_copy(f); MatrixQRefList to_X_dummy; MatrixQRefList from_Y_dummy; MatrixQRefList from_X_ref; MatrixQRefList to_Y_ref; from_X_ref.emplace_back(proj); to_Y_ref.emplace_back(f_copy); smith_reduce_p(p, rel_y_map, to_X_dummy, from_X_ref, to_Y_ref, from_Y_dummy); MatrixQ lift(rel_y_map.width(), f.width()); dim_t d_max = 0; while (d_max < rel_y_map.height() && d_max < rel_y_map.width() && rel_y_map(d_max, d_max) != 0) { d_max++; } for (dim_t i = 0; i < d_max; i++) { for (dim_t j = 0; j < f.width(); j++) { lift(i, j) = f_copy(i, j) / rel_y_map(i, i); } } return proj * lift; }
JNIEXPORT void JNICALL Java_matrixqhelperlib_JNIMatrixQHelper_multiplyByVectorInC (JNIEnv *env, jclass myself, jlong handle, jdouble timeScale, jdoubleArray inputVector, jdoubleArray outputVector) { MatrixQ* value = reinterpret_cast<MatrixQ*>(handle); int inputLength = env->GetArrayLength(inputVector); int outputLength = env->GetArrayLength(outputVector); if (inputLength != outputLength) { std::cout<<"The sizes of the passed in vectors were not equal."<<std::endl; abort(); } JDoubleConverter convertedInputVector(env,inputVector); Eigen::Map<Eigen::VectorXd> mappedInputVector(convertedInputVector,inputLength); JDoubleConverter convertedOutputVector(env,outputVector); Eigen::Map<Eigen::VectorXd> mappedOutputVector(convertedOutputVector,inputLength); mappedOutputVector = value->multiplyByVector(timeScale,mappedInputVector); }
GroupWithMorphisms compute_image(const mod_t p, const MatrixQ& f, const AbelianGroup& X, const AbelianGroup& Y) { MatrixQRefList to_X_dummy; MatrixQList from_X = {MatrixQ::identity(f.width())}; GroupWithMorphisms K = compute_kernel(p, f, X, Y, to_X_dummy, ref(from_X)); MatrixQList to_X_2 = {MatrixQ::identity(X.rank())}; MatrixQList from_X_2 = {f,MatrixQ::identity(X.rank())};//hacky, since id:X->X doesn't vanish on K. //but due to the implementation, the columns of this will contain //representatives in X for the generators of img. GroupWithMorphisms img = compute_cokernel(p, K.maps_from[0], X, ref(to_X_2), ref(from_X_2)); return img; }
GroupWithMorphisms compute_kernel(const std::size_t p, const MatrixQ& f, const AbelianGroup& X, const AbelianGroup& Y, const MatrixQRefList& to_X_ref, const MatrixQRefList& from_X_ref) { MatrixQ f_rel_Y(f.height(), f.width() + Y.tor_rank()); f_rel_Y(0, 0, f.height(), f.width()) = f; f_rel_Y(0, f.width(), Y.tor_rank(), Y.tor_rank()) = Y.torsion_matrix(p); MatrixQ rel_x_lift(f.width() + Y.tor_rank(), X.tor_rank()); rel_x_lift(0, 0, X.tor_rank(), X.tor_rank()) = X.torsion_matrix(p); // rel_x_lift(f.width(), 0, Y.tor_rank(), X.tor_rank()) = -lift of f\circ // rel_x over rel_Y. // Can be computed by multiplying the columns of f with the orders of X, // and dividing the rows by the orders of Y. for (std::size_t i = 0; i < Y.tor_rank(); ++i) { for (std::size_t j = 0; j < X.tor_rank(); ++j) { long order_diff = static_cast<long>(X(j) - Y(i)); rel_x_lift(f.width() + i, j) = -f(i, j) * p_pow_q(p, order_diff); } } // build to_X_rel_Y, from_X_rel_Y. MatrixQList to_X_rel_Y; for (MatrixQ& g_to_X : to_X_ref) { to_X_rel_Y.emplace_back(f.width() + Y.tor_rank(), g_to_X.width()); to_X_rel_Y.back()(0, 0, f.width(), g_to_X.width()) = g_to_X; MatrixQ fg = f * g_to_X; for (std::size_t i = 0; i < Y.tor_rank(); ++i) { for (std::size_t j = 0; j < g_to_X.width(); ++j) { to_X_rel_Y.back()(f.width() + i, j) = -fg(i, j) / p_pow_z(p, Y(i)); } } } MatrixQList from_X_rel_Y; for (MatrixQ& g_from_X : from_X_ref) { from_X_rel_Y.emplace_back(g_from_X.height(), f.width() + Y.tor_rank()); from_X_rel_Y.back()(0, 0, g_from_X.height(), f.width()) = g_from_X; } MatrixQRefList to_X_rel_Y_ref = ref(to_X_rel_Y); MatrixQRefList from_X_rel_Y_ref = ref(from_X_rel_Y); to_X_rel_Y_ref.emplace_back(rel_x_lift); MatrixQRefList to_Y; MatrixQRefList from_Y; smith_reduce_p(p, f_rel_Y, to_X_rel_Y_ref, from_X_rel_Y_ref, to_Y, from_Y); std::size_t rank_diff; for (rank_diff = 0; rank_diff < std::min(f_rel_Y.height(), f_rel_Y.width()); ++rank_diff) { if (f_rel_Y(rank_diff, rank_diff) == 0) break; } // next, restrict attention to the entries corresponding to zero columns of // f_rel_Y: // for rel_x_lift as well as the entries of to_X_rel_Y, take the submatrices // formed // by the corresponding rows. For from_X_rel_Y, take the submatrices formed // by // the corresponding columns. MatrixQ rel_K = rel_x_lift(rank_diff, 0, rel_x_lift.height() - rank_diff, rel_x_lift.width()); MatrixQList to_free_K; for (MatrixQ& g_to_X_rel_Y : to_X_rel_Y) { to_free_K.emplace_back(g_to_X_rel_Y( rank_diff, 0, g_to_X_rel_Y.height() - rank_diff, g_to_X_rel_Y.width())); } MatrixQList from_free_K; for (MatrixQ& g_from_X_rel_Y : from_X_rel_Y) { from_free_K.emplace_back( g_from_X_rel_Y(0, rank_diff, g_from_X_rel_Y.height(), g_from_X_rel_Y.width() - rank_diff)); } MatrixQRefList to_free_K_ref = ref(to_free_K); MatrixQRefList from_free_K_ref = ref(from_free_K); AbelianGroup free_K(rel_K.height(), 0); // then, compute the cokernel of the new rel_x_lift with the respective // to_Y, from_Y. return compute_cokernel(p, rel_K, free_K, to_free_K_ref, from_free_K_ref); }
bool morphism_zero(std::size_t p, const MatrixQ& f, const AbelianGroup& Y) { MatrixQ g(f.height(), f.width()); return morphism_equal(p, f, g, Y); }
TEST(Matrix, ColMul) { MatrixQ A = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}; MatrixQ B = {{1_mpq / 2, 2, 3}, {2, 5, 6}, {7 / 2_mpq, 8, 9}}; EXPECT_EQ(B, A.col_mul(0, 1 / 2_mpq)); }
TEST(Matrix, ColAdd) { MatrixQ A = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}; MatrixQ B = {{-1, 2, 3}, {-1, 5, 6}, {-1, 8, 9}}; EXPECT_EQ(B, A.col_add(1, 0, -1_mpq)); }
TEST(Matrix, RowMul) { MatrixQ A = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}; MatrixQ B = {{1_mpq / 2, 1_mpq, 3_mpq / 2}, {4, 5, 6}, {7, 8, 9}}; EXPECT_EQ(B, A.row_mul(0, 1 / 2_mpq)); }
TEST(Matrix, RowAdd) { MatrixQ A = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}; MatrixQ B = {{1, 2, 3}, {4, 5, 6}, {9, 12, 15}}; EXPECT_EQ(B, A.row_add(0, 2, 2_mpq)); }