Commit 87f59c70 authored by Charles Bouillaguet's avatar Charles Bouillaguet
Browse files

reasonable driver

parent 232dc683
......@@ -8,6 +8,6 @@ moebius_demo
/monica_packed
/monica_oblivious
/monica_vector
/macaulay
/macaulay_gen
/lanczos
venv
#!/usr/bin/env python3
import subprocess
import sys
from hashlib import sha256
import argparse
import os
import os.path
import tempfile
import mqlogger
import logging
import pathlib
import math
try:
import yaml
except ModuleNotFoundError:
print("This program requires the PyYAML package.")
print("Install it with:")
print(" $ python3 -m pip install pyyaml")
sys.exit(1)
class Parameters:
outer_hybridation = 0
inner_hybridation = 0
degree = 2
linear_variables = 6
#
verbose = True
dry_run = True
def run(args, parameters):
if parameters.verbose:
print(' '.join(args))
if parameters.dry_run:
return
subprocess.run(args, check=True)
def macaulay_step(input_system, output_base, parameters):
args = ['./macaulay_gen', '--in', input_system, '--degree', str(parameters.degree),
'--inner-hybridation', str(parameters.linear_variables), '--out', output_base]
run(args, parameters)
D = {}
D['matrix'] = output_base + '.bin'
D['ilog'] = output_base + '.ilog.bin'
D['jlog'] = output_base + '.jlog.bin'
return D
def linear_algebra_step(Dma, output, parameters):
args = ['./lanczos', '--matrix', Dma['matrix'], '--output', output]
run(args, parameters)
return {'kernel': output}
def reconstruct_step(input_system, Dma, Dla, output, parameters):
args = ['./reconstruct_monomials', '--in', input_system, '--degree', str(parameters.degree),
'--inner-hybridation', str(parameters.linear_variables), '--out', output,
'--ilog', Dma['ilog'], '--jlog', Dma['jlog']]
run(args, parameters)
return {'polynomials': output}
input_system = "examples/random_32_quad.in"
parameters = Parameters()
Dma = macaulay_step(input_system, "matrix", parameters)
Dla = linear_algebra_step(Dma, 'kernel.bin', parameters)
Dre = reconstruct_step(input_system, Dma, Dla, "new_system.in", parameters)
\ No newline at end of file
degree = 3
linear_variables = 7
def __init__(self, **kwds):
for k, v in kwds.items():
setattr(self, k, v)
class Main:
def __init__(self):
self.logger = logging.getLogger()
# add a handler that emits logs to screen
if not self.logger.handlers:
self.logger.addHandler(mqlogger.ScreenHandler(lvl=logging.NOTSET, colour=True))
def ensure_workdir(self, args):
"""
Return the path of an (existing) workdir
"""
logger = self.logger
if args.workdir:
wdir = args.workdir
else:
wdir = tempfile.mkdtemp(prefix="mq.")
logger.info(f"Created temporary directory {wdir}")
#purge_files.append(wdir)
#if os.environ.get("CADO_DEBUG", None):
# self.logger.info("CADO_DEBUG is on, data will be kept in %s" % wdir)
if not os.path.isdir(wdir):
os.makedirs(wdir)
logger.debug(f"Created directory {wdir}")
return pathlib.Path(wdir)
def parse_input_system(self, filename):
"""
Return n, m
"""
n = None
m = 0
with open(filename) as f:
for line in f:
if line.startswith('#'):
continue
if n is None:
n = len(line.split(','))
continue
if line.strip() == '':
continue
m += 1
return n, m
def choose_parameters(self, filename, ninput, minput):
best = None
logger = logging.getLogger('Parameters')
logger.debug(f"Loading parameters database from {filename}")
with open(filename) as f:
logger.debug("Known parameter sets:")
for pset in yaml.safe_load(f):
m = pset['m']
n = pset['n']
logger.debug('* m={}, n={}'.format(m, n))
if minput < m or ninput < n:
continue # inadmissible
if best is None:
best = pset
mbest, nbest = best['m'], best['n']
if m > mbest or (m == mbest and n > nbest):
best = (m, n)
if best is None:
logger.warning("No suitable parameter sets. Auto-choosing fail-safe (bad) parameters")
v = int(math.floor(math.sqrt(2*minput) - 2))
return Parameters(degree=2, linear_variables=v)
mbest, nbest = best['m'], best['n']
if minput == mbest and ninput == nbest:
logger.info('Precise parameters founds')
else:
logger.warning(f'Auto-choosing parameters (no precise match). Using m={mbest}, n={nbest}')
return Parameters(**best)
def run(self, name, cmd):
"""
Execute external command
"""
args = self.args
logger = logging.getLogger(name)
logger.command(' '.join(cmd))
if args.dry_run:
return
process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
with process.stdout:
for line in iter(process.stdout.readline, b''): # b'\n'-separated lines
logger.info(line.decode().strip())
exitcode = process.wait() # 0 means success
if exitcode != 0:
raise ValueError("Something failed")
def macaulay_step(self, filename, output_base):
parameters = self.parameters
cmd = ['./macaulay_gen', '--in', filename, '--degree', str(parameters.degree),
'--inner-hybridation', str(parameters.linear_variables), '--out', str(output_base)]
self.run('macaulay_gen', cmd)
D = {}
D['matrix'] = str(output_base) + '.bin'
D['ilog'] = str(output_base) + '.ilog.bin'
D['jlog'] = str(output_base) + '.jlog.bin'
return D
def linear_algebra_step(self, Dma, output):
parameters = self.parameters
cmd = ['./lanczos', '--matrix', Dma['matrix'], '--output', str(output)]
self.run('lanczos', cmd)
return {'kernel': output}
def reconstruct_step(self, filename, Dma, Dla, output):
parameters = self.parameters
cmd = ['./reconstruct_monomials', '--in', filename, '--degree', str(parameters.degree),
'--inner-hybridation', str(parameters.linear_variables), '--out', str(output),
'--ilog', Dma['ilog'], '--jlog', Dma['jlog'], '--kernel', str(Dla['kernel'])]
self.run('reconstruct', cmd)
return {'polynomials': output}
def go(self):
logger = self.logger
parser = argparse.ArgumentParser()
parser.add_argument("filename", help="file containing the input system")
parser.add_argument("-v", "--verbose", help="increase output verbosity", action="store_true")
parser.add_argument("-n", "--dry-run", help="Don't actually run any command; just print them", action="store_true")
parser.add_argument("--workdir", help="WORKDIR is created if it does not exist. If unspecified, a temporary directory is used")
self.args = parser.parse_args()
args = self.args
# setup workdir
wdir = self.ensure_workdir(args)
self.wdir = wdir
# Add logging to the workdir
logfilename = wdir / "mq.log"
filehandler = mqlogger.FileHandler(filename=logfilename, lvl=logging.DEBUG)
logger.addHandler(filehandler)
logger.info("If this computation gets interrupted, it can be resumed with %s --workdir %s", sys.argv[0], wdir)
# parse input system
n, m = self.parse_input_system(args.filename)
logger.info(f'Input system: {args.filename}')
logger.info(f'{m} polynomials')
logger.info(f'{n} variables')
self.parameters = self.choose_parameters('parameters.yaml', n, m)
params = self.parameters
if params.outer_hybridation:
logger.info(f'Outer hybridation on {params.outer_hybridation} variables')
logger.info(f'{1 << params.outer_hybridation} independent sub-jobs')
logger.info(f'Expanding to degree {params.degree}')
logger.info(f'Targetting {params.linear_variables} linear variables')
e = n - params.outer_hybridation - params.linear_variables
logger.info(f'Enumeration on {e} variables')
Dma = self.macaulay_step(args.filename, wdir / "matrix")
Dla = self.linear_algebra_step(Dma, wdir / 'kernel.bin')
Dre = self.reconstruct_step(args.filename, Dma, Dla, wdir / "new_system.in")
if __name__ == '__main__':
Main().go()
logging.shutdown()
\ No newline at end of file
# shamelessly taken from CADO-NFS (scripts/cadofactor/cadologger.py)
# the original file was mostly written by Alexander Kruppa
import logging
from logging import DEBUG, INFO, WARNING, ERROR, FATAL, CRITICAL
COMMAND = DEBUG + 1
class ANSI(object):
""" Class defining some ANSI control sequences, for example for
changing text colour """
CSI = '\x1b[' # ANSI Control Sequence Introducer. Not the TV show
SGR = 'm' # Set Graphics Rendition code
NORMAL = CSI + '0' + SGR
BLACK = CSI + '30' + SGR
GREY = CSI + '30;1'
RED = CSI + '31' + SGR
BRIGHTRED = CSI + '31;1' + SGR
GREEN = CSI + '32' + SGR
BRIGHTGREEN = CSI + '32;1' + SGR
YELLOW = CSI + '33' + SGR
BRIGHTYELLOW = CSI + '33;1' + SGR
BLUE = CSI + '34' + SGR
BRIGHTBLUE = CSI + '34;1' + SGR
MAGENTA = CSI + '35' + SGR
BRIGHTMAGENTA = CSI + '35;1' + SGR
CYAN = CSI + '36' + SGR
BRIGHTCYAN = CSI + '36;1' + SGR
# There is another grey with code "37" - white without intensity
# Not sure if it is any different from "30;1" aka "bright black"
WHITE = CSI + '37;1' + SGR
class ScreenFormatter(logging.Formatter):
""" Class for formatting logger records for screen output, optionally
with colorized logger level name (like cadofct.pl used to). """
colours = {
INFO : ANSI.BRIGHTGREEN,
WARNING : ANSI.BRIGHTYELLOW,
ERROR : ANSI.BRIGHTRED,
FATAL : ANSI.BRIGHTRED,
COMMAND : ANSI.BRIGHTCYAN
}
# Format string that switches to a different colour (with ANSI code
# specified in the 'colour' key of the log record) for the log level name,
# then back to default text rendition (ANSI code in 'nocolour')
colourformatstr = '%(padding)s%(colour)s%(levelnametitle)s%(nocolour)s:%(name)s: %(message)s'
# Format string that does not use colour changes
nocolourformatstr = '%(padding)s%(levelnametitle)s:%(name)s: %(message)s'
def __init__(self, colour=True):
self.useColour = colour
if colour:
super().__init__(fmt=self.colourformatstr)
else:
super().__init__(fmt=self.nocolourformatstr)
def format(self, record):
# Add attributes to record that our format string expects
if self.useColour:
record.colour = self.colours.get(record.levelno, ANSI.NORMAL)
record.nocolour = ANSI.NORMAL
if record.levelno == COMMAND:
record.levelnametitle = 'Running Command'
else:
record.levelnametitle = record.levelname.title()
if hasattr(record, "indent"):
record.padding = " " * record.indent
else:
record.padding = ""
return super().format(record)
class FileFormatter(logging.Formatter):
""" Class for formatting a log record for writing to a log file. No colours
here, but we add the process ID and a time stamp """
formatstr = 'PID%(process)s %(asctime)s %(levelnametitle)s:%(name)s: %(message)s'
def format(self, record):
record.levelnametitle = record.levelname.title()
return super().format(record)
def __init__(self):
super().__init__(fmt=self.formatstr)
class ScreenHandler(logging.StreamHandler):
def __init__(self, lvl = logging.INFO, colour = True, **kwargs):
super().__init__(**kwargs)
self.setLevel(lvl)
self.setFormatter(ScreenFormatter(colour = colour))
class FileHandler(logging.FileHandler):
def __init__(self, filename, lvl = logging.DEBUG, **kwargs):
super().__init__(filename, **kwargs)
self.setLevel(lvl)
self.setFormatter(FileFormatter())
def addLoggingLevel(levelName, levelNum, methodName=None):
"""
Comprehensively adds a new logging level to the `logging` module and the
currently configured logging class.
`levelName` becomes an attribute of the `logging` module with the value
`levelNum`. `methodName` becomes a convenience method for both `logging`
itself and the class returned by `logging.getLoggerClass()` (usually just
`logging.Logger`). If `methodName` is not specified, `levelName.lower()` is
used.
To avoid accidental clobberings of existing attributes, this method will
raise an `AttributeError` if the level name is already an attribute of the
`logging` module or if the method name is already present
Example
-------
>>> addLoggingLevel('TRACE', logging.DEBUG - 5)
>>> logging.getLogger(__name__).setLevel("TRACE")
>>> logging.getLogger(__name__).trace('that worked')
>>> logging.trace('so did this')
>>> logging.TRACE
5
"""
if not methodName:
methodName = levelName.lower()
if hasattr(logging, levelName):
raise AttributeError('{} already defined in logging module'.format(levelName))
if hasattr(logging, methodName):
raise AttributeError('{} already defined in logging module'.format(methodName))
if hasattr(logging.getLoggerClass(), methodName):
raise AttributeError('{} already defined in logger class'.format(methodName))
# This method was inspired by the answers to Stack Overflow post
# http://stackoverflow.com/q/2183233/2988730, especially
# http://stackoverflow.com/a/13638084/2988730
def logForLevel(self, message, *args, **kwargs):
if self.isEnabledFor(levelNum):
self._log(levelNum, message, args, **kwargs)
def logToRoot(message, *args, **kwargs):
logging.log(levelNum, message, *args, **kwargs)
logging.addLevelName(levelNum, levelName)
setattr(logging, levelName, levelNum)
setattr(logging.getLoggerClass(), methodName, logForLevel)
setattr(logging, methodName, logToRoot)
logging.getLogger().setLevel(logging.NOTSET)
addLoggingLevel('COMMAND', logging.DEBUG + 1)
def init_test_logger():
logger = logging.getLogger()
logger.addHandler(ScreenHandler(lvl = logging.NOTSET))
logger.addHandler(FileHandler(filename = "log", lvl = logging.NOTSET))
if __name__ == '__main__':
init_test_logger()
logger = logging.getLogger()
logger.info("An Info Center!")
logger.command("ls -l")
logger.warning("Beware")
logger.error("All hope abandon", extra={"indent" : 4})
# good for n=m=40
- m: 40
n: 40
degree: 4
linear_variables: 11
# good for n=m=48
- m: 48
n: 48
degree: 4
linear_variables: 12
# good for n=m=64
- m: 64
n: 54
degree: 4
linear_variables: 14
# good for n=m=72 ?????
- m: 72
n: 72
degree: 5
linear_variables: 16
# good for n=m=80
- m: 80
n: 71
degree: 5
linear_variables: 16
options:
linear_algebra:
software: CADO-NFS
################### overdetermined systems
- m: 148
n: 71
degree: 5
linear_variables: 23
- m: 160
n: 80
degree: 5
linear_variables: 24
options:
linear_algebra:
software: CADO-NFS
......@@ -51,7 +51,10 @@ struct input_poly_t *reconstruct(char *filename_ker, char *filename_ilog,
free(jlog);
free(ker);
// TODO: check that bad monomials are absent
/* check that bad monomials are absent */
for (int k = 0; k < (int) size_ker; k++)
if (monomial_is_bad(k, mono) && res[k] != 0)
errx(1, "bad monomial present in linear combination");
/* build the 64 output polynomials */
struct input_poly_t * ans = alloc_array(NB_VEC, sizeof(*ans), "output polynomials");
......@@ -75,19 +78,21 @@ struct input_poly_t *reconstruct(char *filename_ker, char *filename_ilog,
int main(int argc, char **argv)
{
/* process command-line options */
struct option longopts[7] = {
struct option longopts[8] = {
{"in", required_argument, NULL, 'i'},
{"out", required_argument, NULL, 'o'},
{"degree", required_argument, NULL, 'D'},
{"inner-hybridation", required_argument, NULL, 'v'},
{"ilog", required_argument, NULL, 'I'},
{"jlog", required_argument, NULL, 'J'},
{"kernel", required_argument, NULL, 'k'},
{NULL, 0, NULL, 0}
};
char *in_filename = NULL;
char *out_filename = NULL;
char *ilog_filename = NULL;
char *jlog_filename = NULL;
char *kernel_filename = NULL;
int v = -1;
int D = -1;
......@@ -112,10 +117,24 @@ int main(int argc, char **argv)
case 'J':
jlog_filename = optarg;
break;
case 'k':
kernel_filename = optarg;
break;
default:
errx(1, "Unknown option\n");
}
}
/* validate arguments */
if (D < 0)
errx(1, "the --degree argument is mandatory");
if (v < 0)
errx(1, "the --inner-hybridation argument is mandatory");
if (ilog_filename == NULL)
errx(1, "the --ilog argument is mandatory");
if (jlog_filename == NULL)
errx(1, "the --ilog argument is mandatory");
if (kernel_filename == NULL)
errx(1, "the --kernel argument is mandatory");
log(0, "Reading input system\n");
FILE *input_file = fopen(in_filename, "r");
......@@ -126,7 +145,7 @@ int main(int argc, char **argv)
struct monomial_ctx_t *mono = monomial_setup(sys->n, v, D);
struct input_poly_t *input_system = convert_input_system(sys, mono);
struct input_poly_t *new = reconstruct("kernel.bin", ilog_filename, jlog_filename, D, input_system, mono);
struct input_poly_t *new = reconstruct(kernel_filename, ilog_filename, jlog_filename, D, input_system, mono);
FILE *output_file = stdout;
if (out_filename) {
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment