Select Git revision
jsonparse.py
-
Susanna Weber authoredSusanna Weber authored
weil.cpp 14.68 KiB
/* SPDX-FileCopyrightText: © 2022 Maik Herbers */
/* SPDX-License-Identifier: GPL-3.0-or-later */
#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <exception>
#include <fstream>
#include <iostream>
#include <optional>
#include <sstream>
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <getopt.h>
#include <unistd.h>
#include "common.hpp"
#include "discriminant_form.hpp"
#include "sl2z.hpp"
int
print_help(bool error)
{
const char* s = "Usage:\n"
"\tweil [options] <command> [arguments]\n"
"\n"
"Commands:\n"
" DS-info <file> output some information about the discriminant form associated\n"
" to the gram matrix in `file'\n"
" DS-c-kernel <file> <c> compute `D_c' where `D' is the discriminant form associated\n"
" to the gram matrix in `file'\n"
" DS-c-image <file> <c> compute `D^c' where `D' is the discriminant form associated\n"
" to the gram matrix in `file'\n"
" DS-c-star <file> <c> compute `D^c*' where `D' is the discriminant form associated\n"
" to the gram matrix in `file'\n"
" Lattice-smith-gram-matrix <file> compute the gram matrix of the quadratic form in `file' with\n"
" respect to the smith basis\n"
" DS-get-orbits <gram> <aut> compute the orbits in the discriminant form associated to the gram\n"
" matrix `gram' under the action induced by the automorphisms in `aut'\n"
" SL2Z-split <m> split the matrix `m' in SL2Z into generators\n"
" VV-Theta-Series [options] <file> <prec> compute the vector valued theta series associated to the\n"
" lattice with gram matrix in `file' up to precision `prec'\n"
" Transformation-Matrix <file> <aut> <M> compute the transformation matrix (up to a root of unity and a square root)\n"
" of the action induced by `M' on the vector space spanned by the component\n"
" functions of the vector-valued theta function of the lattice associated to\n"
" the gram matrix in `file' modulo the modulo the automorphisms in `aut'\n"
"\n"
"Options:\n"
" --help, -h show this help message\n"
" --print-style=STYLE use this style to print things: pretty (default), sage, pari, latex\n"
"\n"
"Options specific to VV-Theta-series:\n"
" --with-automorphisms=AUT compute the component functions only for one representative of the orbits\n"
" induced by the automorphisms AUT each. this may significantly reduce the\n"
" required memory at a cost of run time.";
(error ? std::cerr : std::cout) << s << std::endl;
return error ? EXIT_FAILURE : EXIT_SUCCESS;
}
static std::optional<weil::mpz_matrix>
read_square_mpz_matrix(const char* file)
{
std::vector<mpz_class> v;
std::ifstream in{ file };
if (!in.is_open()) {
std::cerr << "failed to open file `" << file << "'" << std::endl;
return std::nullopt;
}
mpz_class n;
while (in >> n) {
v.push_back(n);
}
if (!in.eof()) {
std::cerr << "failed to parse" << std::endl;
return std::nullopt;
}
auto dimension{ static_cast<uint64_t>(std::sqrt(v.size())) };
if (dimension * dimension != v.size()) {
std::cerr << "not a square matrix" << std::endl;
return std::nullopt;
}
weil::mpz_matrix m{ dimension, dimension };
for (uint64_t i = 0; i < dimension; i++) {
for (uint64_t j = 0; j < dimension; j++) {
m(i, j) = v[i * dimension + j];
}
}
return m;
}
int
sl2z_split(int argc, const char** argv, weil::IOFormat style)
{
if (argc != 1) {
return print_help(true);
}
auto m{ read_square_mpz_matrix(argv[0]) };
if (!m) {
return EXIT_FAILURE;
} else if (m->cols() != 2 ||
m->rows() != 2 ||
(*m)(0, 0) * (*m)(1, 1) - (*m)(0, 1) * (*m)(1, 0) != 1_mpz) {
std::cerr << "Not in SL2Z:\n" << *m << std::endl;
return EXIT_FAILURE;
}
// As we already checked above, this will always contain a value
auto split = weil::split_into_generators(*m);
const char* exp_prefix = "";
const char* exp_suffix = "";
const char* sep = "";
switch (style) {
using enum weil::IOFormat;
case Pretty:
exp_prefix = "^(";
exp_suffix = ")";
sep = " ";
break;
case SageMath:
case PARI:
exp_prefix = "^(";
exp_suffix = ")";
sep = " * ";
break;
case LaTeX:
exp_prefix = "^{";
exp_suffix = "}";
sep = " ";
break;
}
bool first{ true };
for (auto&& [r1, r2] : split.value()) {
if (first) {
first = false;
} else {
std::cout << sep;
}
std::cout << ((r1 == weil::SL2ZGenerator::T) ? "T" : "S") << exp_prefix << r2 << exp_suffix;
}
std::cout << std::endl;
return EXIT_SUCCESS;
}
int
ds_c_action_common(int argc, const char** argv, weil::IOFormat style, auto f)
{
if (argc != 2) {
return print_help(true);
}
auto m{ read_square_mpz_matrix(argv[0]) };
if (!m) {
return EXIT_FAILURE;
}
mpz_class c{ argv[1] };
weil::DiscriminantForm D{ *m };
const char* start = "";
const char* stop = "";
const char* sep = "";
switch (style) {
using enum weil::IOFormat;
case Pretty:
case LaTeX:
start = "";
stop = "";
sep = "\n";
break;
case SageMath:
start = "{\n";
stop = "\n}";
sep = ",\n";
break;
case PARI:
start = "[\\\n";
stop = "\\\n]";
sep = ",\\\n";
break;
}
std::cout << start;
bool first{ true };
for (auto&& v : f(D, c)) {
if (first) {
first = false;
} else {
std::cout << sep;
}
weil::format_eigen(std::cout, v, style);
}
std::cout << stop << std::endl;
return EXIT_SUCCESS;
}
static std::optional<std::vector<weil::mpz_matrix>>
read_automorphism_group(const char* file)
{
std::ifstream in{ file };
if (!in.is_open()) {
std::cerr << "failed to open file `" << file << "'" << std::endl;
return std::nullopt;
}
int64_t size;
if (!(in >> size)) {
return std::nullopt;
}
if (size < 0) {
std::cerr << "matrix size must be nonnegative: `" << size << "'" << std::endl;
}
std::vector<weil::mpz_matrix> Aut;
std::string delimiter;
while (in >> delimiter && delimiter == "--") {
weil::mpz_matrix o{ size, size };
for (int64_t i = 0; i < size; i++) {
for (int64_t j = 0; j < size; j++) {
if (!(in >> o(i, j))) {
std::cerr << "failed to read matrix" << std::endl;
return std::nullopt;
}
}
}
Aut.push_back(o);
}
return Aut;
}
int
lattice_smith_gram_matrix(int argc, const char** argv, weil::IOFormat style)
{
if (argc != 1) {
return print_help(true);
}
auto m{ read_square_mpz_matrix(argv[0]) };
if (!m) {
return EXIT_FAILURE;
}
weil::DiscriminantForm D{ *m };
weil::format_eigen(std::cout, D.get_gram_wrt_smith_basis(), style);
std::cout << std::endl;
return EXIT_SUCCESS;
}
std::string
base26(uint64_t n)
{
const char* alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const uint64_t len = strlen(alphabet) - 1;
if (n == 0) {
return { alphabet[0] };
}
std::string s;
while (n > 0) {
s.push_back(alphabet[n % len]);
n /= len;
}
std::ranges::reverse(s);
return s;
}
int
ds_get_orbits(int argc, const char** argv, weil::IOFormat style)
{
if (argc != 2) {
return print_help(true);
}
auto m{ read_square_mpz_matrix(argv[0]) };
if (!m) {
return EXIT_FAILURE;
}
auto Aut{ read_automorphism_group(argv[1]) };
if (!Aut) {
return EXIT_FAILURE;
}
weil::DiscriminantForm D{ *m };
auto [orbits, orbit_mapping] = D.get_orbits(*Aut);
// sort by norm
std::ranges::stable_sort(
orbits,
[&D](const weil::mpz_vector& v, const weil::mpz_vector& w) { return D.q(v) < D.q(w); },
[](const weil::Orbit& o) { return o.representative; });
const char* start = "";
const char* stop = "";
const char* prefix = "";
const char* infix = "";
const char* suffix = "";
const char* sep = "";
switch (style) {
using enum weil::IOFormat;
case Pretty:
start = "representative\tname\tlength\tnorm\torder\n";
stop = "";
prefix = "";
infix = "\t";
suffix = "";
sep = "\n";
break;
case SageMath:
start = "{\n";
stop = "\n}";
prefix = "(";
infix = ", ";
suffix = ")";
sep = ",\n";
break;
case PARI:
start = "[\\\n";
stop = "\\\n]";
prefix = "[";
infix = ", ";
suffix = "]";
sep = ",\\\n";
break;
case LaTeX:
start = "\\begin{tabular}{c c c c c}\n";
stop = "\n\\end{tabular}";
prefix = "$";
infix = "$ & $";
suffix = "$\\\\";
sep = "\n";
break;
}
std::cout << start;
bool first{ true };
uint64_t offset_idx{ 0 };
mpz_class last_offset{ 0_mpz };
for (auto&& o : orbits) {
if (first) {
first = false;
} else {
std::cout << sep;
}
mpz_class offset{ (D.level() * D.q(o.representative)) };
if (last_offset != offset) {
last_offset = offset;
offset_idx = 0;
}
std::cout << prefix;
format_eigen(std::cout, o.representative, style);
std::cout << infix;
if (style == weil::IOFormat::Pretty || style == weil::IOFormat::LaTeX) {
std::cout << offset << base26(offset_idx);
std::cout << infix;
}
std::cout << o.length;
std::cout << infix;
std::cout << D.q(o.representative);
std::cout << infix;
std::cout << D.element_order(o.representative);
std::cout << suffix;
offset_idx++;
}
std::cout << stop << std::endl;
return EXIT_SUCCESS;
}
int
vv_theta_series(int argc, const char** argv, weil::IOFormat style)
{
const struct option long_options[] = {
{ "with-automorphisms", required_argument, nullptr, 0 },
{ nullptr, 0, nullptr, 0 },
};
std::optional<std::vector<weil::mpz_matrix>> Aut;
int opt_idx{ 0 };
int opt{ 0 };
optind = 0;
while (true) {
// `getopt_long' starts at `argv + 1' (as `argv[0]' usually is the program name). Just pretend that it
// starts one earlier. On a parse error, `getopt_long' outputs `argv[0]'. This is acceptable here, as
// `argv[0]' is just the name of this command.
opt = getopt_long(argc + 1, const_cast<char* const*>(argv - 1), "+", long_options, &opt_idx);
if (opt == -1) {
break;
}
switch (opt) {
case 0:
if (strcmp("with-automorphisms", long_options[opt_idx].name) == 0) {
Aut = read_automorphism_group(optarg);
if (!Aut) {
return EXIT_FAILURE;
}
}
break;
case '?':
default: return print_help(true);
}
}
// see above, off by one
optind--;
if (argc - optind != 2) {
return print_help(true);
}
auto m{ read_square_mpz_matrix(argv[optind]) };
if (!m) {
return EXIT_FAILURE;
}
int64_t prec;
std::istringstream prec_stream{ argv[optind + 1] };
if (!(prec_stream >> prec)) {
std::cerr << "not an integer `" << argv[optind + 1] << "'" << std::endl;
return print_help(true);
}
if (prec < 0) {
std::cerr << "precision must be non-negative" << std::endl;
return print_help(true);
}
weil::DiscriminantForm D{ *m };
weil::format_vvthetaseries(std::cout, (Aut ? D.theta_series_direct(prec, *Aut) : D.theta_series(prec)), style);
std::cout << std::endl;
return EXIT_SUCCESS;
}
int
transformation_matrix(int argc, const char** argv, weil::IOFormat style)
{
if (argc != 3) {
return print_help(true);
}
auto m{ read_square_mpz_matrix(argv[0]) };
if (!m) {
return EXIT_FAILURE;
}
weil::DiscriminantForm D{ *m };
auto Aut{ read_automorphism_group(argv[1]) };
if (!Aut) {
return EXIT_FAILURE;
}
auto M{ read_square_mpz_matrix(argv[2]) };
if (!M) {
return EXIT_FAILURE;
} else if (M->cols() != 2 ||
M->rows() != 2 ||
(*M)(0, 0) * (*M)(1, 1) - (*M)(0, 1) * (*M)(1, 0) != 1_mpz) {
std::cerr << "Not in SL2Z:\n" << *M << std::endl;
return EXIT_FAILURE;
}
weil::SL2Z M_sl2z;
M_sl2z << (*M)(0, 0), (*M)(0, 1),
(*M)(1, 0), (*M)(1, 1);
auto trans{ D.transformation_matrix(*Aut, M_sl2z) };
weil::format_eigen(std::cout, trans, style);
std::cout << std::endl;
return EXIT_SUCCESS;
}
int
ds_info(int argc, const char** argv, weil::IOFormat style)
{
// FIXME: merge with DS-get-orbits?
(void)style;
if (argc != 1) {
return print_help(true);
}
auto m{ read_square_mpz_matrix(argv[0]) };
if (!m) {
return EXIT_FAILURE;
}
weil::DiscriminantForm D{ *m };
std::cout << "cardinality: " << D.cardinality << "\n";
std::cout << "level: " << D.level() << "\n";
return EXIT_SUCCESS;
}
int
main(int argc, const char** argv)
{
weil::IOFormat style{ weil::IOFormat::Pretty };
const struct option long_options[] = {
{ "help", no_argument, nullptr, 'h' },
{ "print-style", required_argument, nullptr, 0 },
{ nullptr, 0, nullptr, 0 },
};
int opt_idx{ 0 };
int opt;
while (true) {
// the `+' instructs `getopt_long' not to permute `argv'
opt = getopt_long(argc, const_cast<char* const*>(argv), "+h", long_options, &opt_idx);
if (opt == -1) {
break;
}
switch (opt) {
case 0:
if (strcmp("print-style", long_options[opt_idx].name) == 0) {
if (strcmp("pretty", optarg) == 0) {
style = weil::IOFormat::Pretty;
} else if (strcmp("sage", optarg) == 0) {
style = weil::IOFormat::SageMath;
} else if (strcmp("pari", optarg) == 0) {
style = weil::IOFormat::PARI;
} else if (strcmp("latex", optarg) == 0) {
style = weil::IOFormat::LaTeX;
} else {
std::cerr << "Invalid style `" << optarg << "'" << std::endl;
return print_help(true);
}
}
break;
case 'h': return print_help(false);
case '?':
default: return print_help(true);
}
}
if (!argv[optind]) {
return print_help(true);
}
auto check_option = [argc, argv, style](const char* opt_name, auto func) -> void {
if (strcmp(opt_name, argv[optind]) == 0) {
exit(func(argc - optind - 1, argv + optind + 1, style));
}
};
check_option("DS-info", ds_info);
#define ds_c_action(action_type) \
[]<typename... Ts>(Ts... args) { \
return ds_c_action_common( \
args..., [](const weil::DiscriminantForm& D, const mpz_class& c) { return D.action_type(c); }); \
}
check_option("DS-c-kernel", ds_c_action(c_kernel));
check_option("DS-c-image", ds_c_action(c_image));
check_option("DS-c-star", ds_c_action(c_star));
#undef ds_c_action
check_option("Lattice-smith-gram-matrix", lattice_smith_gram_matrix);
check_option("DS-get-orbits", ds_get_orbits);
check_option("SL2Z-split", sl2z_split);
check_option("VV-Theta-Series", vv_theta_series);
check_option("Transformation-Matrix", transformation_matrix);
return print_help(true);
}