Skip to content
Snippets Groups Projects
Select Git revision
  • 1681b21dcfa528b26fabe4425cfe038a1cf56de0
  • main default protected
2 results

jsonparse.py

Blame
  • 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);
    }