// 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; }