def testZero(): """ Check value zero. """ c = Constant(value=0, wl=8) assert(c.FPF == FPF(msb=7, lsb=0)) c = Constant(value=0.0, wl=8) assert (c.FPF == FPF(msb=7, lsb=0)) c = Constant(value="0", wl=8) assert (c.FPF == FPF(msb=7, lsb=0)) c = Constant(value=mpf(0), wl=8) assert (c.FPF == FPF(msb=7, lsb=0)) # with FPF: f = FPF(msb=7, lsb=-1) c = Constant(value=0, fpf=f) assert (c.FPF == f) c = Constant(value=0.0, fpf=f) assert (c.FPF == f) c = Constant(value="0", fpf=f) assert (c.FPF == f) c = Constant(value=mpf(0), fpf=f) assert (c.FPF == f)
def __init__(self, constants, varNames, varFPF, resName, resFPF): """ Build a SoP :param constants: list of constants (Constant objects) :param varNames: list of names (VarName objects) for the variables v_i :param varFPF: list of FPF for the variables v_i :param resName: name (VarName) of the result :param resFPF: FPF of the result """ # store the values self._constants = constants self._varFPF = varFPF self._resFPF = resFPF # each constant should be a Constant if any(not isinstance(c, Constant) for c in constants): raise ValueError( "The constants should be a list of Constant objects (defined in Fixif.FxP)" ) # check the sizes if len({len(constants), len(varNames), len(varFPF)}) != 1: raise ValueError( "The Constnants, the list of names and the list of FPF should have the same size!" ) # compute the FxP format of the products self._productFPF = [ FPF(msb=v.msb + c.FPF.msb, lsb=v.lsb + c.FPF.lsb) for v, c in zip(varFPF, constants) ] # initialize the SoP super(FxPSoP, self).__init__([c.value for c in constants], varNames, resName)
def Sum_service(outputFormat): """Generate Sum of FPF image (or LaTeX)""" try: # get data formats = request.query.sum.split(":") # build each FPF F = [FPF(formatStr=f.replace('_', '.')) for f in formats] resultFPF = FPF(formatStr=request.query.result.replace('_', '.')) except ValueError: return "Invalid Fixed-Point Formats" # TODO: message d'erreur plus explicite ? # process the options options = optionManager(request.query) options.addOptionalOption("colors", colorThemes, "YG") # color theme options.addOptionalOption("width", lambda x: int(x), '500') # used for jpg, png, tiff only options.addOptionalOption("height", lambda x: int(x), '1300') # used for jpg, png, tiff only (default value is very large, to let the user specify width=2000 without being blocked by the height:300) options.addOptionalOption("axis", {"yes": True, "no": False}, "no") # display a vertical axis on bit=0 options.addOptionalOption("sort", ("no", "lsb", "msb"), "no") options.addOptionalOption("hatches", {"yes": True, "no": False}, "no") # display hatches for the bits outside the FPF of the result options.addOptionalOption("xshift", lambda x: float(x), '0') options.addOptionalOption("yshift", lambda x: float(x), '0') # build the LaTeX from the FxP SoP (fake SoP with constant equal to 1 and variable with msb decreased by 1) sop = FxPSoP([Constant(1, wl=2) for _ in F], ["" for _ in F], [FPF(msb=f.msb-1, lsb=f.lsb) for f in F], ["" for _ in F], resultFPF) latexFPF = sop.sumLaTeX(**options.getValues()) # generate the files sumName = "-".join([f.Qnotation() for f in F + [resultFPF]]) if outputFormat != 'tex': # prepare the conversion argument (in the LaTeX class 'standalone') if outputFormat != "pdf": convert = "convert={size=%dx%d,outext=.%s}," % (options['width'], options['height'], outputFormat) else: convert = "" # encompass it into a complete latex file latexStr = template("latex-FPF.tex", options.getValues({"FPF": latexFPF, "format": outputFormat, "convert": convert})) # create the image from the latex return createImageFromLaTeX(sumName + "?" + str(options), latexStr, outputFormat) else: return "\t%Generated from " + Config.baseURL + "Sum.tex?" + request.query_string + "\n" + latexFPF
def aSoP_submit(): """Manage the aSoP data""" # get data constants = request.forms.get('constants').split("\r\n") var_FPF = request.forms.get('var_FPF').split("\r\n") var_wi = request.forms.get('var_wi').split("\r\n") beta_final = request.forms.get('beta_final') # conversion try: beta_final = float(beta_final) except ValueError: return "the beta final is not valid" cons = [] for c in constants: try: c_v, c_w = c.split(',') cons.append(Constant(float(c_v), int(c_w))) except ValueError: return "The constants cannot be converted to float" try: _ = [FPF(formatStr=f) for f in var_FPF] except ValueError: return "Invalid FPF format" var_w = [] # variable wordlength var_i = [] # variable interval for wi in var_wi: try: w, i1, i2 = wi.split(',') var_w.append(int(w)) var_i.append((float(i1), float(i2))) except ValueError: return "Invalid wordlength,inteval format" # now call Benoit's function to do the aSoP... interval_var = [] for w, i in zip(var_w, var_i): interval_var.append(Variable(value_inf=i[0], value_sup=i[1], wl=w)) # beta=w changed in wl=w return simple_cleaned_SoP(cons, interval_var, beta_final)
def iterSomeFPF(N): for _ in range(N): w = randint(2, 30) m = randint(-30, 30) s = choice([True, False]) yield FPF(wl=w, msb=m, signed=s)
def test_construct(): """Unit test for the FPF constructor""" # construct FPF with less than 2 args with pytest.raises(ValueError): FPF(16) with pytest.raises(ValueError): FPF(msb=12) with pytest.raises(ValueError): FPF(lsb=-6) # construct with wrong wl with pytest.raises(ValueError): FPF(wl=-12, msb=6) with pytest.raises(ValueError): FPF(wl=1, msb=6, signed=True) # construct FPF with only wl and (lsb or msb) f = FPF(16, lsb=-12) assert (f.wml() == (16, 3, -12)) f = FPF(16, msb=3) assert (f.wml() == (16, 3, -12)) f = FPF(16, msb=0) assert (f.wml() == (16, 0, -15)) with pytest.raises(ValueError): FPF(16, 12, -5) # construct form string f = FPF(formatStr="Q8.12") assert (f.wml() == (20, 7, -12)) f = FPF(formatStr="sQ4.3") assert (f.wml() == (7, 3, -3)) f = FPF(formatStr="uQ4.3") assert (f.wml() == (7, 3, -3)) f = FPF(formatStr="(8,-12)") assert (f.wml() == (21, 8, -12)) f = FPF(formatStr="u(8,-12)") assert (f.signed is False) assert (f.wml() == (21, 8, -12)) with pytest.raises(ValueError): FPF(formatStr="totoQ6.8") f = FPF(msb=7, lsb=0, signed=True) assert (f.minmax() == (-128, 127)) f = FPF(msb=7, lsb=0, signed=False) assert (f.minmax() == (0, 255))
def test_notation(fpf): assert (FPF(formatStr=fpf.Qnotation()) == fpf) assert (FPF(formatStr=fpf.ParenthesisNotation()) == fpf) assert (FPF(formatStr=repr(fpf)) == fpf) str(fpf)
def mulFPF(F1, F2): """FPF of a multiplication""" return FPF(wl=F1.wl + F2.wl, msb=F1.msb + F2.msb)
def FPF_service(FPForm, outputFormat): """ Service that generates a FPF image (or LaTeX or informations encapsulated in json) from a FPF description Ex: answer to /FPF/uQ3.5.pdf?option1=value1&option2=value2 Process the request, to produce the FPF `FPForm` in the format `outputFormat` with the various options taken from the request The FPF should be given in Q-notation or in Parentheses-notation The outputFormat can be image ('pdf','jpg','png','tiff' or 'eps' (see utilities.imageFormats)), 'tex' or 'json' Possible options: colors: indicates the color theme ('YG' for yellow and green, 'BG' for black and white, etc.) binary_point: (boolean) indicates if the binary point is displayed numeric: (boolean) display numeric values (instead of symbolic) if True notation: use MSB/LSB notation if 'mlsb', or integer/fractional part if 'ifwl' label: indicates where to display the label ('left'/'right'/'above'/'below' or 'no' if the label should not be displayed) intfrac: (boolean) display integer/fractional parts if True power2: (boolean) display power-of-2 if True height: image's height (for jpg, png, tiff only) widht: image's width (for jpg, png, tiff only) bits: binary value to be displayed (nothing if no bit is given) """ # check if the FPF is valid # TODO: Change the name of the generated file: """ Qd on demande une constante sur un grand nombre de bits (j'ai fait 124 bits), on a l'erreur suivante: 127.0.0.1 - - [24/Jul/2017 22:18:52] "GET /Constant/121545@12121?WL=125 HTTP/1.1" 200 22523 cp: cache/Q18_107?numeric=no&power2=no&intfrac=no&bits=01110110101100100100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000&width=500&binary_point=yes¬ation=mlsb&drawMissing=no&label=no&height=1300&y_origin=0&colors=RB.jpg: File name too long Cela est du au fait que le champs bits est trop grand, ce qui donne un nom de fichier stocké en cache trop grand. Une solution pourrait-être de former le nom du fichier autrement : en stockant un hash (genre md5) pour le nom du fichier dès que le nom devient trop grand. On peut aussi, au lieu de stocker tous les bits dans le nom, juste stocker la même chose en décimal (ou en hexa), mais cela ne fait que repousser le problème. À mon avis, la bonne solution est de faire un mélange des deux (on ne met plus "bits=...." dans le nom du fichier, mais value=..., et dès que le nom du fichier dépasse le nombre maximum de caractères pour un fichier (255 sous Mac, 4069 sous Unix, le tout étant défini dans limits.h, cf https://stackoverflow.com/questions/32807560/how-do-i-get-in-python-the-maximum-filesystem-path-length-in-unix) """ FPForm = FPForm.replace('_', '.') # allow '_' (underscore) characters, and translate them in '.' (point) try: F = FPF(formatStr=FPForm) except ValueError: return {'error': "Invalid FPF format"} # process the options options = optionManager(request.query) options.addOptionalOption("colors", colorThemes, "YG") # color theme options.addOptionalOption("binary_point", {"yes": True, "no": False}, "no") # show the binary-point options.addOptionalOption("numeric", {"yes": True, "no": False}, "no") # display numeric values (instead of symbolic) options.addOptionalOption("notation", ("mlsb", "ifwl"), "mlsb") # notation chosen for the display (MSB/LSB or integer/fractional part) options.addOptionalOption("label", ('no', 'left', 'right', 'above', 'below'), 'no') # display the label options.addOptionalOption("intfrac", {"yes": True, "no": False}, "no") # display the integer/fractional part options.addOptionalOption("power2", {"yes": True, "no": False}, "no") # display the power-of-2 options.addOptionalOption("width", lambda x: int(x), '500') # used for jpg, png, tiff only options.addOptionalOption("height", lambda x: int(x), '1300') # used for jpg, png, tiff only (default value is very large, to let the user specify width=2000 without being blocked by the height:300) options.addOptionalOption("bits", lambda x: str(x), "") # value of the bits to be displayed options.addOptionalOption("y_origin", lambda x: float(x), "0") options.addOptionalOption("drawMissing", {"yes": True, "no": False}, "no") # generate LaTeX code for the FPF only latexFPF = F.LaTeX(**options.getValues()) # initializing the hash object m = hashlib.md5() m.update(str(options).encode('UTF-16')) # create and return image (or latex code) if outputFormat != 'tex' and outputFormat != 'json': # prepare the conversion argument (in the LaTeX class 'standalone') if outputFormat != "pdf": convert = "convert={size=%dx%d,outext=.%s}," % (options['width'], options['height'], outputFormat) else: convert = "" # encompass it into a complete latex file latexStr = template("latex-FPF.tex", options.getValues({"FPF": latexFPF, "format": outputFormat, "convert": convert})) # and create the image from the latex return createImageFromLaTeX(F.Qnotation().replace('.', '_') + "?" + m.hexdigest(), latexStr, outputFormat) elif outputFormat == 'json': return {'latex': latexFPF, 'interval': '[%f;%f]' % F.minmax(), 'quantization': '2^%d = %f' % (F.lsb, 2 ** F.lsb)} else: return "\t%Generated from " + Config.baseURL + "FPF/" + FPForm + ".tex?" + request.query_string + "\n" + latexFPF
def Constant_service(constantsInter): """Service that generates all the information for the transformation of a constant It returns a JSON object, containing dictionaries of: the FPF, the integer associated, the errors, etc. of the constants and intervals given in constantsInter. Ex: answer to /Constant/zzzz?option1=xxx&option2=yyy where zzzz is string of constants or a intervals seperated by @ Possible options: FPF: string describing the FPF (Q-notation or parenthesis notation) WL: word-length -> only one of these two options should be given ! signed: (bool) indicates if the constant is represented with a signed constant It returns a json object containing dictionaries related to each constant or interval with the following fields error: (string) indicates if there is an error FPF: (string) the FPF used for the conversion (usefull if the WL was given) FPF_image: (url) the url used for the image of the FPF and for a constant (not for an interval) integer: the associated Fixed-Point integer bits: 2's complement binary of the integer approx: the approximated value error_abs: absolute error error_rel: relative error""" # get the FPF q = request.query if "FPF" in q: try: F = FPF(formatStr=q["FPF"]) WL = None except ValueError: return {"error": "invalid FPF"} # or get the word-length elif "WL" not in q: # return {'error':"At least one option 'FPF' or 'WL' must be given"} WL = 8 F = None else: try: WL = int(q["WL"]) except ValueError: return {"error": " The Word-length must be an integer"} F = None # and get the signedness if "signed" in q: signed = q["signed"] != 'no' else: signed = True returningJson = {} exps = [] # List containing input constants / intervals / their evaluation. # Treating the raw input from client: for i in range(0, len(constantsInter.split("@"))): line = constantsInter.split("@")[i].replace("div", "/") # replacing div with the real division sign line = line.strip() if len(line) > 0: # empty strings shouldn't be treated try: # if it's a normal constant then it should simply be added to exps Constant(value=line, wl=100, signed=signed) exps.append({'exp': line, 'val': line, 'const': True}) except ValueError: # it's either an interval or a mathematical expression that needs to be evaluated if line[0] == '[': # Since the input format is once validated in client side, we can simply use this condition to determine whether it's an interval or not if WL: exps.append({'exp': line, 'val': get_interval_inf(line, WL), 'const': False}) else: exps.append({'exp': line, 'val': get_interval_inf(line, F.wl), 'const': False}) else: # Not an interval but a mathematical expression that needs to be evaluated if WL: exps.append({'exp': line, 'val': evaluate_exp(line, WL), 'const': True}) else: exps.append({'exp': line, 'val': evaluate_exp(line, F.wl), 'const': True}) counter = 0 for expression in exps: constInter = expression['val'] if len(constInter) != 0: inter = reobj_interval.match(constInter) # is it an interval ? # get the constant if expression['const'] and expression['val'] != "NaN": try: C = Constant(value=expression['val'], wl=WL, signed=signed, fpf=F) dico = return_dictionary_constant(C) dico['value'] = expression['exp'] returningJson[counter] = dico except ValueError: Cs = Constant(value=expression['val'], wl=F.wl, signed=signed) dico = return_dictionary_constant(Cs) dico['value'] = expression['exp'] errStr = "Not possible with the asked FPF; suggestion: " + str(Cs.FPF) dico["error"] = errStr returningJson[counter] = dico elif inter: try: val_inf = float(inter.group(1)) val_sup = float(inter.group(2)) # TODO: conversion str->float... faire avec GMP? except ValueError: returningJson[counter] = {'value': expression['exp'], 'error': 'The interval must be of the form [xxx;yyy] where xxx and yyy are litteral'} try: if WL: C = Constant(value=val_sup, wl=WL) if float(Constant(value=val_inf, wl=WL).FPF.msb) > float(C.FPF.msb): C = Constant(value=val_inf, wl=WL) else: C = Constant(value=val_sup, fpf=F) if float(Constant(value=val_inf, fpf=F).FPF.msb) > float(C.FPF.msb): C = Constant(value=val_inf, fpf=F) dico = return_dictionary_constant(C) dico["value"] = expression['exp'] returningJson[counter] = dico except ValueError: # None of the interval values could be represented with the given format Cs = Constant(value=val_sup, wl=F.wl, signed=signed) if float(Constant(value=val_inf, wl=F.wl).FPF.msb) > float(Cs.FPF.msb): Cs = Constant(value=val_inf, wl=F.wl) dico = return_dictionary_constant(Cs) dico["value"] = expression['exp'] errStr = "Not possible with the asked FPF; suggestion: " + str(Cs.FPF) dico["error"] = errStr returningJson[counter] = dico else: if expression["val"] != "NaN": returningJson[counter] = { 'value': expression['exp'], 'error': "The url should contain the constant or the interval (ex '/Constant/12.44' or '/Constant/[-120;10])'" } # General Error else: returningJson[counter] = { 'value': expression['exp'], 'error': "Arithmetic problem" } counter += 1 return returningJson
def test_construct(method): """ Test the Constant constructor """ c = Constant(value=127, wl=8, signed=False, method=method) assert(c.FPF.wml() == (8, 6, -1)) assert(c.mantissa == 254) c = Constant(value=127, wl=8, signed=True, method=method) assert(c.FPF.wml() == (8, 7, 0)) assert(c.mantissa == 127) c = Constant(value=-127, wl=8, signed=True, method=method) assert(c.FPF.wml() == (8, 7, 0)) assert(c.mantissa == -127) c = Constant(value=0.36567, wl=8, signed=True, method=method) assert(c.FPF.wml() == (8, -1, -8)) assert(c.mantissa == 94) assert(c.approx == 94*2**-8) with pytest.raises(ValueError): Constant(value=-12, wl=12, signed=False, method=method) with pytest.raises(ValueError): Constant(value=42, wl=1) # particular cases c = Constant(value=127.78, wl=8, signed=False, method=method) assert(c.FPF.wml() == (8, 7, 0)) assert(c.mantissa == 128) c = Constant(value=-128.1, wl=8, signed=True, method=method) assert(c.FPF.wml() == (8, 7, 0)) assert(c.mantissa == -128) c = Constant(value=127.7, wl=8, signed=True, method=method) assert(c.FPF.wml() == (8, 8, 1)) assert(c.mantissa == 64) c = Constant(value=-128.25, wl=8, signed=True, method=method) assert(c.FPF.wml() == (8, 7, 0)) assert(c.mantissa == -128) c = Constant(value=-128.5, wl=8, signed=True, method=method) assert(c.FPF.wml() == (8, 7, 0)) assert(c.mantissa == -128) c = Constant(value=-128.6, wl=8, signed=True, method=method) assert(c.FPF.wml() == (8, 7, 0)) assert(c.mantissa == -128) c = Constant(value=-129, wl=8, signed=True, method=method) assert(c.FPF.wml() == (8, 7, 0)) # tie to even (otherwise, with ties to away, it should be (8,8,1), and mantissa=-65 assert(c.mantissa == -128) c = Constant(value=-129.01, wl=8, signed=True, method=method) assert(c.FPF.wml() == (8, 8, 1)) assert(c.mantissa == -65) # wrong combination of arguments with pytest.raises(ValueError): Constant(value=12) with pytest.raises(ValueError): Constant(value=12, signed=True) with pytest.raises(ValueError): Constant(value=12, wl=5, fpf=FPF(8, 7, 0)) # construct with a given FPF with pytest.raises(ValueError): Constant(value=258.54, wl=8, fpf=FPF(8, 7, 0)) c = Constant(value=127.1, fpf=FPF(8, 7, 0)) assert(c.FPF.wml() == (8, 7, 0)) with pytest.raises(ValueError): Constant(value=132, fpf=FPF(8, 7, 0, signed=True)) with pytest.raises(ValueError): Constant(value=300, fpf=FPF(8, 7, 0, signed=False)) with pytest.raises(ValueError): Constant(value=0.123, fpf=FPF(8, 7, 0))