From de998f0ec0aa4734f614ba7798b20324f71bc800 Mon Sep 17 00:00:00 2001 From: Julian Gethmann Date: Mon, 27 Feb 2017 14:15:22 +0100 Subject: [PATCH 1/2] Fix Python 3 incompatibilities * Make kafe run with Python 3 (tested with Python 3.5.2 only) * No idioms translated, just the basics * Tests run with Python 2 and Python 3 --- .gitignore | 2 + CHANGELOG | 7 ++ install.sh | 30 +++--- kafe/__init__.py | 26 ++--- kafe/config.py | 17 +-- kafe/dataset.py | 30 +++--- kafe/dataset_tools.py | 2 +- kafe/file_tools.py | 32 +++--- kafe/fit.py | 227 +++++++++++++++++++--------------------- kafe/function_tools.py | 43 ++++++-- kafe/iminuit_wrapper.py | 14 +-- kafe/latex_tools.py | 2 +- kafe/minuit.py | 16 +-- kafe/plot.py | 28 ++--- uninstall.sh | 16 +-- 15 files changed, 257 insertions(+), 235 deletions(-) diff --git a/.gitignore b/.gitignore index c4601e0..c144c81 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,8 @@ *.log #.kafe *.egg-info +*.cache +*.eggs ########## # Images # diff --git a/CHANGELOG b/CHANGELOG index bb13d42..0e99682 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,6 +2,12 @@ master ====== % [TODO] add unit test for input file parser +v1.2.0 +====== + + % [FIX] make code Python 3 compatible. + Imports solved with try: Python2 variant; except ImportError: Python3 variant + v1.1.0 ====== @@ -18,6 +24,7 @@ v1.1.0 v1.0.1 ====== + % [BUG] in fit.py (color name "darmagenta" -> "darkmagenta") % [FIX] for matplotlib vers. 1.5.x : bbox parameters in plot.py % [NOTE] set_color_cycle in fit.py is deprecated in matplotlib 1.5, diff --git a/install.sh b/install.sh index 3e571b2..32fa274 100755 --- a/install.sh +++ b/install.sh @@ -2,7 +2,7 @@ # Install script for kafe using pip -function check_python_2 () { +function check_python_2_3 () { echo "Checking Python version for executable '$1'..." if ! [[ `which $1` ]]; then echo "Python executable '$1' not found in PATH!" @@ -13,15 +13,15 @@ function check_python_2 () { echo "Python executable '$1' in Python 2: ok" return 0 elif [[ PYTHON_VERSION -eq 3 ]]; then - echo "Python executable '$1' is Python 3: not supported" - return 1 + echo "Python executable '$1' in Python 3: ok" + return 0 else echo "Python executable '$1' not found in PATH!" return 1 fi } -function check_pip_python_2 () { +function check_pip_python_2_3 () { echo "Checking Python version for pip executable '$1'..." if ! [[ `which $1` ]]; then echo "Pip executable '$1' not found in PATH!" @@ -32,8 +32,8 @@ function check_pip_python_2 () { echo "Pip executable '$1' uses Python 2: ok" return 0 elif [[ PIP_PYTHON_VERSION -eq 3 ]]; then - echo "Pip executable '$1' uses Python 3: not supported" - return 1 + echo "Pip executable '$1' uses Python 3: ok" + return 0 else echo "Pip executable '$1' not found in PATH!" return 1 @@ -43,26 +43,26 @@ function check_pip_python_2 () { ## -- main -- ## # pip executables to try out -PIP_EXEC_LIST="pip pip2 pip2.7" +PIP_EXEC_LIST="pip pip2 pip2.7 pip3 pip3.1 pip3.2 pip3.3 pip3.4 pip3.5 pip3.6 pip3.7" PY_EXEC="python" -check_python_2 $PY_EXEC -found_python_2=$? +check_python_2_3 $PY_EXEC +found_python_2_3=$? -if [[ found_python_2 -eq 0 ]]; then +if [[ found_python_2_3 -eq 0 ]]; then for pip_exec in $PIP_EXEC_LIST; do - check_pip_python_2 $pip_exec - found_pip_python_2=$? - if [[ found_pip_python_2 -eq 0 ]]; then + check_pip_python_2_3 $pip_exec + found_pip_python_2_3=$? + if [[ found_pip_python_2_3 -eq 0 ]]; then break fi done - if [[ found_pip_python_2 -ne 0 ]]; then + if [[ found_pip_python_2_3 -ne 0 ]]; then echo "None of the executables '$PIP_EXEC_LIST' seem to be valid. Aborting." exit 1 fi else - echo "Python 2 executable not found on system. Aborting." + echo "Python 2 or Python 3 executable not found on system. Aborting." exit 1 fi diff --git a/kafe/__init__.py b/kafe/__init__.py index 9360aaf..30a76a1 100644 --- a/kafe/__init__.py +++ b/kafe/__init__.py @@ -15,29 +15,29 @@ Fitting with **kafe** in a nutshell goes like this: 1) create a `Dataset` object from your measurement data - + >>> my_d = kafe.Dataset(data=[[0., 1., 2.], [1.23, 3.45, 5.62]]) - + 2) add errors (uncertainties) to your `Dataset` - + >>> my_d.add_error_source('y', 'simple', 0.5) # y errors, all +/- 0.5 - + 3) import a model function from `kafe.function_library` (or define one yourself) - + >>> from kafe.function_library import linear_2par - + 4) create a `Fit` object from your `Dataset` and your model function - + >>> my_f = kafe.Fit(my_d, linear_2par) - + 5) do the fit - + >>> my_f.do_fit() - + 6) *(optional)* if you want to see a plot of the result, use the `Plot` object - + >>> my_p = kafe.Plot(my_f) >>> my_p.plot_all() >>> my_p.show() @@ -50,7 +50,7 @@ """ # Import config variables -import config +from . import config # Import version info from . import _version_info @@ -88,7 +88,7 @@ import matplotlib try: matplotlib.use(config.G_MATPLOTLIB_BACKEND) -except ValueError, e: +except ValueError as e: # matplotlib does not provide requested backend _logger.error("matplotlib error: %s" % (e,)) _logger.warning("Failed to load requested backend '%s' for matplotlib. " diff --git a/kafe/config.py b/kafe/config.py index 0c119f8..8671b83 100644 --- a/kafe/config.py +++ b/kafe/config.py @@ -8,7 +8,10 @@ import sys import os -import ConfigParser +try: + import ConfigParser +except ImportError: + import configparser as ConfigParser import kafe # import main logger for kafe @@ -80,7 +83,7 @@ def create_config_file(config_type, force=False): "from '%s' to '%s'." \ % (kafe_default_config_file, _file)) else: - raise Exception("Cannot create config file at '%s': file exists" \ + raise Exception("Cannot create config file at '%s': file exists" % (_file,)) @@ -101,11 +104,11 @@ def create_config_file(config_type, force=False): # Raise Error is no default config file found if not os.path.exists(os.path.join(*kafe_configs['system'])): - raise IOError, "No default config file for kafe was " \ - "found on the system but there should " \ - "be one at '%s'. Please check your " \ - "installation. " \ - % (os.path.join(*kafe_configs['system']),) + raise IOError("No default config file for kafe was " + "found on the system but there should " + "be one at '%s'. Please check your " + "installation. " + % (os.path.join(*kafe_configs['system']),)) # Load all found config files into the ConfigParser kafe_config_file = None diff --git a/kafe/dataset.py b/kafe/dataset.py index 795b485..af7b064 100644 --- a/kafe/dataset.py +++ b/kafe/dataset.py @@ -15,13 +15,11 @@ # DS 150610: add ErrorSource object # --------------------------------------------- -from string import join, split - import numpy as np from scipy.linalg import LinAlgError import os -from numeric_tools import cov_to_cor, cor_to_cov, extract_statistical_errors, \ +from .numeric_tools import cov_to_cor, cor_to_cov, extract_statistical_errors, \ zero_pad_lower_triangle, make_symmetric_lower NUMBER_OF_AXES = 2 @@ -190,8 +188,8 @@ def get_matrix(self, size=None): try: iter(self.error_value) except: - raise ValueError, ("Given error `%r' is not iterable." - % (self.error_value,)) + raise ValueError("Given error `%r' is not iterable." + % (self.error_value,)) else: # use value given _val = np.asarray(self.error_value) # size is implicit @@ -211,7 +209,7 @@ def get_matrix(self, size=None): return _mat else: - raise ValueError, "Unknown error type `%s'" % (self.error_type,) + raise ValueError("Unknown error type `%s'" % (self.error_type,)) class Dataset(object): @@ -389,7 +387,7 @@ def set_data(self, data): #data = data pass - for axis in xrange(self.__n_axes): # go through the axes + for axis in range(self.__n_axes): # go through the axes self.set_axis_data(axis, data[axis]) # load data for axis def set_axis_data(self, axis, data): @@ -616,7 +614,7 @@ def calc_cov_mats(self, axis='all'): np.matrix(np.zeros((_size, _size)))] if axis is 'all': - _axes_list = range(self.__n_axes) + _axes_list = list(range(self.__n_axes)) else: _axes_list = [self.get_axis(axis)] @@ -1072,7 +1070,7 @@ def get_formatted(self, format_string=".06e", delimiter='\t'): output_list = [] # go through the axes - for axis in xrange(self.__n_axes): + for axis in range(self.__n_axes): # define a helper list which we will fill out helper_list = [] @@ -1123,7 +1121,7 @@ def get_formatted(self, format_string=".06e", delimiter='\t'): # if there are also correlations (syst errors) if self.__query_has_correlations[axis]: # go through the columns of the correlation matrix - for col in xrange(idx): + for col in range(idx): # append corr. coefficients to the helper list helper_list[-1].append( format(cor_mat[idx, col], format_string) @@ -1136,7 +1134,7 @@ def get_formatted(self, format_string=".06e", delimiter='\t'): tmp_string = '' for row in output_list: for entry in row: - tmp_string += join(entry, delimiter) + '\n' + tmp_string += delimiter.join(entry) + '\n' return tmp_string @@ -1237,7 +1235,7 @@ def read_from_file(self, input_file): tmp_linenumber += 1 # update the line number if '#' in line: - line = split(line, '#')[0] # ignore anything after + line = line.split('#')[0] # ignore anything after # a comment sign (#) if (not line) or (line.isspace()): # if empty line encountered @@ -1293,7 +1291,7 @@ def read_from_file(self, input_file): tmp_reading_data_block = True # get the entries on the line as a list (whitespace-delimited) - tmp_fields = split(line) + tmp_fields = line.split() # if there is only one entry, # we know it's just the measurement data @@ -1314,7 +1312,7 @@ def read_from_file(self, input_file): if tmp_has_syst_errors: # other fields are correlation coefficients # (add 1.0 on main diagonal) - tmp_cormat.append(map(float, tmp_fields[2:]) + [1.0]) + tmp_cormat.append(list(map(float, tmp_fields[2:]) + [1.0])) # if there are not enough entries # for a valid correlation matrix @@ -1358,11 +1356,11 @@ def read_from_file(self, input_file): self.set_cov_mat(tmp_axis, None) # unset cov mat # Turn covariance matrices into ErrorSource objects - for axis in xrange(self.__n_axes): + for axis in range(self.__n_axes): _mat = self.get_cov_mat(axis) # remove existing error model (all error sources) - for err_src_id in xrange(len(self.err_src[axis])): + for err_src_id in range(len(self.err_src[axis])): self.remove_error_source(axis, err_src_id, recompute_cov_mat=False) if _mat is not None: diff --git a/kafe/dataset_tools.py b/kafe/dataset_tools.py index f7383c0..1d035b4 100644 --- a/kafe/dataset_tools.py +++ b/kafe/dataset_tools.py @@ -137,7 +137,7 @@ def build_dataset(xdata, ydata, cov_mats=None, 'yabscor': yabscor, 'yrelcor': yrelcor} # go through the keyword arguments - for key, val in error_keywords.iteritems(): + for key, val in error_keywords.items(): err_spec = key err_val = val diff --git a/kafe/file_tools.py b/kafe/file_tools.py index ad38ba1..99176a2 100644 --- a/kafe/file_tools.py +++ b/kafe/file_tools.py @@ -11,7 +11,6 @@ import numpy as np import os, sys -from string import split, replace from .dataset import Dataset from .dataset_tools import build_dataset from .fit import build_fit @@ -142,7 +141,7 @@ def parse_matrix_file(file_like, delimiter=None): for line in tmp_lines: # go through the lines of the file if '#' in line: # ignore anything after a comment sign (#) - line = split(line, '#')[0] + line = line.split('#')[0] # ignore empty lines if (not line) or (line.isspace()): @@ -150,12 +149,12 @@ def parse_matrix_file(file_like, delimiter=None): # get field contents by splitting lines if delimiter is None: - tmp_fields = split(line) # split line on whitespace + tmp_fields = line.split() else: - tmp_fields = split(line, delimiter) # split line on delimiter + tmp_fields = line.split(delimiter) # turn them into floats - tmp_fields = map(float, tmp_fields) + tmp_fields = list(map(float, tmp_fields)) # append those contents to the right fields result.append(tmp_fields) @@ -228,16 +227,16 @@ def parse_matrix_file(file_like, delimiter=None): if '#' in line: # ignore anything after a comment sign (#) - line = split(line, '#')[0] + line = line.split('#')[0] if (not line) or (line.isspace()): # ignore empty lines continue # get field contents by splitting lines if delimiter is None: - tmp_fields = split(line) # split line on whitespace + tmp_fields = line.split() else: - tmp_fields = split(line, delimiter) # split line on delimiter + tmp_fields = line.split(delimiter) # append those contents to the right fields for idx, field_name in enumerate(field_order_list): @@ -556,7 +555,7 @@ def fitf(x, ...): Here is an example of an input file to calculate the average of four partly correlated measurements (see :ref:`Example 8 `):: - + # Meta data for plotting *TITLE Higgs-mass measurements @@ -581,9 +580,9 @@ def fitf(x, ...): @FitFunction def fitf(x, av=1.0): # fit an average return av - + *FITLABEL Average - + *InitialParameters 120. 1. @@ -705,10 +704,13 @@ def parse_sanitize_fitf_code(code_string): '''Parse and sanitize Python code''' import tokenize import string - from cStringIO import StringIO + try: + from cStringIO import StringIO + except ImportError: + from io import StringIO # backwards compatibility: replace tildes by spaces - code_string = string.replace(code_string, '~', ' ') + code_string = code_string.replace('~', ' ') FORBIDDEN_TOKENS = ['import', 'exec', 'global', 'execfile'] @@ -851,7 +853,7 @@ def parse_sanitize_fitf_code(code_string): [leading_token, float(line[1]), float(line[2])]) else: # expect only floats - tokens[current_key].append(map(float, line.split())) # expect floats + tokens[current_key].append(list(map(float, line.split()))) # expect floats # ---- end parse input file @@ -977,7 +979,7 @@ def parse_sanitize_fitf_code(code_string): prefix = 'from kafe.function_tools import FitFunction, LaTeX, ASCII\n' fullcode = prefix + fitcode # add imports for decorators # execute code and place in global scope - exec fullcode in globals() # note: function name must be 'fitf' + exec(fullcode) in globals() # note: function name must be 'fitf' if '*InitialParameters' in setkeys: fitpars = parval, parerr diff --git a/kafe/fit.py b/kafe/fit.py index 417859f..e9dc5dd 100644 --- a/kafe/fit.py +++ b/kafe/fit.py @@ -49,13 +49,13 @@ # no file is created for quiet=True. # ------------------------------------------------------------------------- - +from __future__ import print_function from .function_tools import FitFunction, outer_product from copy import copy import matplotlib.pyplot as plt import numpy as np -from numeric_tools import cov_to_cor, extract_statistical_errors, MinuitCov_to_cor, cor_to_cov +from .numeric_tools import cov_to_cor, extract_statistical_errors, MinuitCov_to_cor, cor_to_cov from .config import (FORMAT_ERROR_SIGNIFICANT_PLACES, F_SIGNIFICANCE_LEVEL, M_MINIMIZER_TO_USE, log_file, null_file) @@ -129,7 +129,7 @@ def tmp_fit_function(x): return fit_function(x, *parameter_values) # calculate f(x) for all x in xdata - fdata = np.asarray(map(tmp_fit_function, xdata)) + fdata = np.asarray(list(map(tmp_fit_function, xdata))) # calculate residual vector residual = ydata - fdata @@ -138,7 +138,7 @@ def tmp_fit_function(x): # apply constraints, if any if constrain is not None: dchi2 = 0 - for val in constrain.itervalues(): + for val in constrain.values(): dchi2 += val.calculate_chi2_penalty(parameter_values) chi2val += dchi2 return chi2val @@ -336,14 +336,14 @@ def __init__(self, dataset, fit_function, external_fcn=chi2, except ImportError as e: raise ImportError("Minimizer 'root' requested, but could " "not find Python module 'ROOT'.") - from minuit import Minuit + from .minuit import Minuit _minimizer_handle = Minuit elif minimizer_to_use.lower() == "iminuit": - from iminuit_wrapper import IMinuit + from .iminuit_wrapper import IMinuit _minimizer_handle = IMinuit #raise NotImplementedError, "'iminuit' minimizer not yet implemented" else: - raise ValueError, "Unknown minimizer '%s'" % (minimizer_to_use,) + raise ValueError("Unknown minimizer '%s'" % (minimizer_to_use,)) else: # assume class reference is given _minimizer_handle = minimizer_to_use @@ -391,7 +391,8 @@ def __init__(self, dataset, fit_function, external_fcn=chi2, _id += 1 # move existing log to that location - os.rename(_basenamelog, log_file(_basename+'.'+str(_id)+'.log')) + os.rename(_basenamelog, log_file('{bn}.{id}.log'.format( + bn=_basename, id=_id))) self.out_stream = StreamDup([log_file('fit.log'), _basenamelog]) else: @@ -529,7 +530,7 @@ def get_parameter_errors(self, rounding=False): output.append(error) # make sure parameters are in the defined order - _ordering = map(self.parameter_names.index, names) + _ordering = list(map(self.parameter_names.index, names)) _order = dict(zip(output, _ordering)) output.sort(key=_order.get) @@ -561,7 +562,7 @@ def get_parameter_values(self, rounding=False): output.append(value) # make sure parameters are in the defined order - _ordering = map(self.parameter_names.index, names) + _ordering = list(map(self.parameter_names.index, names)) _order = dict(zip(output, _ordering)) output.sort(key=_order.get) @@ -652,7 +653,7 @@ def set_parameters(self, *args, **kwargs): ] else: # if no positional arguments, rely on keywords - for param_name, param_spec in kwargs.iteritems(): + for param_name, param_spec in kwargs.items(): par_id = self._find_parameter(param_name) if par_id is None: raise ValueError("Cannot set parameter. `%s` not " @@ -732,7 +733,7 @@ def release_parameters(self, *parameters_to_release): % (par_id, self.parameter_names[par_id])) else: # go through all parameter IDs - for par_id in xrange(self.number_of_parameters): + for par_id in range(self.number_of_parameters): # Release parameter self.minimizer.release_parameter(par_id) @@ -847,7 +848,7 @@ def get_results(self): ''' return (self.final_parameter_values, self.final_parameter_errors, - self.par_cov_mat, + self.par_cov_mat, self.final_fcn) # Fit Workflow @@ -886,38 +887,36 @@ def do_fit(self, quiet=False, verbose=False): self.out_stream.write_timestamp('Fit performed on') if not quiet: - print >>self.out_stream, "###########" - print >>self.out_stream, "# Dataset #" - print >>self.out_stream, "###########" - print >>self.out_stream, '' - print >>self.out_stream, self.dataset.get_formatted() - - print >>self.out_stream, "################" - print >>self.out_stream, "# Fit function #" - print >>self.out_stream, "################" - print >>self.out_stream, '' - print >>self.out_stream, self.fit_function.get_function_equation( - 'ascii', - 'full' ) - print >>self.out_stream, '' + print("###########", file=self.out_stream,) + print("# Dataset #", file=self.out_stream,) + print("###########", file=self.out_stream,) + print('', file=self.out_stream,) + print(self.dataset.get_formatted(), file=self.out_stream,) + + print("################", file=self.out_stream,) + print("# Fit function #", file=self.out_stream,) + print("################", file=self.out_stream,) + print("", file=self.out_stream,) + print(self.fit_function.get_function_equation('ascii', 'full',), + file=self.out_stream,) + print("", file=self.out_stream) if self.constrain: - print >> self.out_stream, "###############" - print >>self.out_stream, "# Constraints #" - print >>self.out_stream, "###############" - print >>self.out_stream, '' - for i in self.constrain.itervalues(): + print("###############", file=self.out_stream,) + print("# Constraints #", file=self.out_stream,) + print("###############", file=self.out_stream,) + print('' , file=self.out_stream,) + for i in self.constrain.values(): for j, err in enumerate(i.parameter_constrain[1]): if(err): - print >>self.out_stream, "%s: %g +\- %g" % ( + print("%s: %g +\- %g" % ( self.parameter_names[j], i.parameter_constrain[0][j], - err) - + err), file=self.out_stream,) if i.cov_mat_inv is not None: - print >> self.out_stream, "Correlation Matrix: " - print >> self.out_stream, format(cov_to_cor(i.cov_mat_inv.I)) - print >>self.out_stream, '' + print("Correlation Matrix: ", file=self.out_stream,) + print(format(cov_to_cor(i.cov_mat_inv.I)), file=self.out_stream,) + print("", file=self.out_stream) max_x_iterations = 10 @@ -981,16 +980,16 @@ def print_raw_results(self): ''' unformatted print-out of all fit results in ''' - print '\n' - print 'par values', self.final_parameter_values - print 'par errors', self.final_parameter_errors - print 'par. covariance matrix:' - print self.par_cov_mat - print 'MINOS errors', self.minos_errors - print 'contours:' - print self.contours - print 'profiles:' - print self.profiles + print('\n') + print('par values' + str(self.final_parameter_values)) + print('par errors' + str(self.final_parameter_errors)) + print('par. covariance matrix:') + print(self.par_cov_mat) + print('MINOS errors' + str( self.minos_errors)) + print('contours:') + print(self.contours) + print('profiles:') + print(self.profiles) def call_minimizer(self, final_fit=True, verbose=False, quiet=False): @@ -1054,22 +1053,22 @@ def project_x_covariance_matrix(self): def print_rounded_fit_parameters(self): '''prints the fit parameters''' - print >>self.out_stream, "########################" - print >>self.out_stream, "# Final fit parameters #" - print >>self.out_stream, "########################" - print >>self.out_stream, '' + print("########################", file=self.out_stream,) + print("# Final fit parameters #", file=self.out_stream,) + print("########################", file=self.out_stream,) + print('' , file=self.out_stream,) for name, value, error in self.minimizer.get_parameter_info(): tmp_rounded = round_to_significance(value, error, FORMAT_ERROR_SIGNIFICANT_PLACES) if error: - print >>self.out_stream, "%s = %g +- %g" % ( - name, tmp_rounded[0], tmp_rounded[1]) + print("%s = %g +- %g" % ( + name, tmp_rounded[0], tmp_rounded[1]), file=self.out_stream, ) else: - print >>self.out_stream, "%s = %g -fixed-" % ( - name, tmp_rounded[0]) + print("%s = %g -fixed-" % ( + name, tmp_rounded[0]), file=self.out_stream, ) - print >>self.out_stream, '' + print("", file=self.out_stream,) def print_fit_details(self): '''prints some fit goodness details''' @@ -1088,86 +1087,78 @@ def print_fit_details(self): hypothesis_status = 'accepted (sig. %d%s)' \ % (int(F_SIGNIFICANCE_LEVEL*100), '%') - print >>self.out_stream, '###############' - print >>self.out_stream, "# Fit details #" - print >>self.out_stream, "###############" - print >>self.out_stream, '' + print('###############', file=self.out_stream, ) + print("# Fit details #", file=self.out_stream, ) + print("###############", file=self.out_stream, ) + print('' , file=self.out_stream, ) # Print a warning if NDF is zero if not _ndf: - print >>self.out_stream, \ - "# WARNING: Number of degrees of freedom is zero!" - print >>self.out_stream, \ - "# Please review parameterization..." - print >>self.out_stream,'' + print( "# WARNING: Number of degrees of freedom is zero!", file=self.out_stream, ) + print("# Please review parameterization...", file=self.out_stream) + print("", file=self.out_stream) elif _ndf < 0: - print >>self.out_stream, \ - "# WARNING: Number of degrees of freedom is negative!" - print >>self.out_stream, \ - "# Please review parameterization..." - print >>self.out_stream,'' + print("# WARNING: Number of degrees of freedom is negative!", file=self.out_stream) + print("# Please review parameterization...", file=self.out_stream) + print("", file=self.out_stream) if(not self.parabolic_errors): - print >>self.out_stream, 'Attention: use uncertainties from MINOS' - print >>self.out_stream,'' - - print >>self.out_stream, 'USING %s'\ - %(self.minimizer.name) - print >>self.out_stream, 'FCN/ndf %.3g/%d = %.3g'\ - %(self.minimizer.get_fit_info('fcn'), _ndf, - self.minimizer.get_fit_info('fcn')/(_ndf)) - print >>self.out_stream, 'EdM %g' \ - %(self.minimizer.get_fit_info('edm')) - print >>self.out_stream, 'UP %g' \ - %(self.minimizer.get_fit_info('err_def')) - print >>self.out_stream, 'STA ', \ - self.minimizer.get_fit_info('status_code') - print >>self.out_stream, '' - print >>self.out_stream, 'chi2prob', round(chi2prob, 3) - print >>self.out_stream, 'HYPTEST ', hypothesis_status - print >>self.out_stream, '' + print('Attention: use uncertainties from MINOS', file=self.out_stream) + print('', file=self.out_stream) + + print('USING %s' %(self.minimizer.name), file=self.out_stream) + print('FCN/ndf %.3g/%d = %.3g' + % (self.minimizer.get_fit_info('fcn'), _ndf, + self.minimizer.get_fit_info('fcn')/(_ndf)), file=self.out_stream) + print('EdM %g' + %(self.minimizer.get_fit_info('edm')), file=self.out_stream) + print('UP %g' + %(self.minimizer.get_fit_info('err_def')), file=self.out_stream) + print('STA ' + str(self.minimizer.get_fit_info('status_code')) , file=self.out_stream) + print('', file=self.out_stream) + print('chi2prob', round(chi2prob, 3), file=self.out_stream) + print('HYPTEST ' + str(hypothesis_status), file=self.out_stream) + print('', file=self.out_stream) def print_fit_results(self): '''prints fit results''' - print >>self.out_stream, '##############' - print >>self.out_stream, '# Fit result #' - print >>self.out_stream, '##############' - print >>self.out_stream, '' + print('##############', file=self.out_stream) + print('# Fit result #', file=self.out_stream) + print('##############', file=self.out_stream) + print('', file=self.out_stream) par_err = extract_statistical_errors(self.par_cov_mat) par_cor_mat = MinuitCov_to_cor(self.par_cov_mat) - print >>self.out_stream, '# value error ', + print('# value error ', file=self.out_stream, end="") if self.number_of_parameters > 1: - print >>self.out_stream, 'correlations' + print('correlations', file=self.out_stream) else: - print >>self.out_stream, '' + print('', file=self.out_stream) for par_nr, par_val in enumerate(self.final_parameter_values): - print >>self.out_stream, '# '+self.parameter_names[par_nr] - print >>self.out_stream, format(par_val, '.04e')+' ', + print('# '+self.parameter_names[par_nr], file=self.out_stream) + print('{:.04e} '.format(par_val), file=self.out_stream, end="") if par_err[par_nr]: - print >>self.out_stream, format(par_err[par_nr], '.02e')+' ', + print(format(par_err[par_nr], '.02e')+' ', end="", file=self.out_stream) else: - print >>self.out_stream, '-fixed- ', + print('-fixed- ', end="", file=self.out_stream) if par_nr > 0 and par_err[par_nr]: - for i in xrange(par_nr): - print >>self.out_stream, format(par_cor_mat[par_nr, i], - '.3f')+' ', - print >>self.out_stream, '' + for i in range(par_nr): + print('{:.3f} '.format(par_cor_mat[par_nr, i]), end="", file=self.out_stream) + print('', file=self.out_stream) # print MINOS errors if needed if(not self.parabolic_errors): - print >>self.out_stream, '!!! uncertainties from MINOS:' + print( '!!! uncertainties from MINOS:', file=self.out_stream) for par_nr, par_val in enumerate(self.final_parameter_values): - print >>self.out_stream, '# '+self.parameter_names[par_nr] + print( '# '+self.parameter_names[par_nr], file=self.out_stream) if par_err[par_nr]: - print >>self.out_stream, ' '+\ - '+'+format(self.minos_errors[par_nr][0],'.02e')\ - +' '+format(self.minos_errors[par_nr][1],'.02e') + print(' {:.02e} + {:.02e}'.format( + self.minos_errors[par_nr][0], self.minos_errors[par_nr][1]), file=self.out_stream) else: - print >>self.out_stream, '-fixed- ', - print >>self.out_stream, '' - print >>self.out_stream, '' + print('-fixed- ',end="", file=self.out_stream) + print('', file=self.out_stream) + print('', file=self.out_stream) def plot_contour(self, parameter1, parameter2, dchi2=2.3, n_points=100, color='gray', alpha=.1, show=False, @@ -1274,13 +1265,12 @@ def plot_contour(self, parameter1, parameter2, dchi2=2.3, self.contours.append([par1, par2, dc2, xs, ys]) # plot contour lines cl=100*Chi22CL(dc2) # get corresponding confidence level - print >>self.out_stream,\ - 'Contour %.1f %%CL for parameters %d vs. %d with %d points'\ - % (cl, par1, par2, len(xs)) + print('Contour %.1f %%CL for parameters %d vs. %d with %d points' + % (cl, par1, par2, len(xs)), file=self.out_stream) labelstr = "%.1f"%(cl) + r"\% CL" tmp_ax.fill(xs, ys, alpha=alpha, color=color) # as filled area tmp_ax.plot(xs, ys, '--', linewidth=2, label=labelstr) # as line - print >>self.out_stream, '' + print("", file=self.out_stream) self.minimizer.set_err(1.) # set errdef back to default of 1. # plot a legend tmp_leg = tmp_ax.legend(loc='best', fontsize='small') @@ -1338,9 +1328,8 @@ def plot_profile(self, parid, n_points=21, val = _pvals[id] err = _perrs[id] - print >>self.out_stream,\ - 'Profile for parameter %d with %d points'\ - % (id, n_points) + print('Profile for parameter %d with %d points' + % (id, n_points), file=self.out_stream) plt.tight_layout() if axes is None: diff --git a/kafe/function_tools.py b/kafe/function_tools.py index a4b6605..7651993 100644 --- a/kafe/function_tools.py +++ b/kafe/function_tools.py @@ -23,7 +23,7 @@ # get a numerical derivative calculating function from SciPy from scipy.misc import derivative as scipy_der -from latex_tools import ascii_to_latex_math +from .latex_tools import ascii_to_latex_math #GQ from inspect import getsourcelines # import main logger for kafe @@ -93,10 +93,16 @@ def __init__(self, f): # Numeric properties of the function #: The number of parameters - self.number_of_parameters = f.func_code.co_argcount-1 + try: + self.number_of_parameters = f.func_code.co_argcount - 1 + except AttributeError: + self.number_of_parameters = f.__code__.co_argcount - 1 #: The default values of the parameters - self.parameter_defaults = f.func_defaults + try: + self.parameter_defaults = f.func_defaults + except AttributeError: + self.parameter_defaults = f.__defaults__ #: string object holding the source code for the fit-function #GQ self.sourcelines = getsourcelines(f) # (for REALLY explicit docs) @@ -127,11 +133,19 @@ def __init__(self, f): self.parameter_defaults = tuple(defpar) # cast back to tuple #: The names of the parameters - self.parameter_names = f.func_code.co_varnames[ - 1:f.func_code.co_argcount - ] - #: The name given to the independent variable - self.x_name = f.func_code.co_varnames[0] + try: + self.parameter_names = f.func_code.co_varnames[ + 1:f.func_code.co_argcount + ] + #: The name given to the independent variable + self.x_name = f.func_code.co_varnames[0] + except AttributeError: + self.parameter_names = f.__code__.co_varnames[ + 1:f.__code__.co_argcount + ] + #: The name given to the independent variable + self.x_name = f.__code__.co_varnames[0] + #: a math expression (string) representing the function's result self.expression = None @@ -172,7 +186,7 @@ def evaluate(self, x_0, parameter_list): def tempf(x): # define helper function of x only to apply map() return self.f(x, *parameter_list) # use python map to calculate function values at each x - return np.asarray(map(tempf, x_0) ) + return np.asarray(list(map(tempf, x_0) )) def derive_by_x(self, x_0, precision_list, parameter_list): @@ -214,13 +228,20 @@ def derive_by_parameters(self, x_0, precision_spec, parameter_list): iter(precision_spec) except TypeError: # precision_spec is not iterable, create array - precision_spec = np.ones(self.f.func_code.co_argcount - 1) * precision_spec + try: + precision_spec = np.ones(self.f.func_code.co_argcount - 1) * precision_spec + except AttributeError: + precision_spec = np.ones(self.f.__code__.co_argcount - 1) * precision_spec # compile all function arguments into a variables tuple variables_tuple = tuple([x_0] + list(parameter_list)) # go through all arguments except the first one - for derive_by_index in xrange(1, self.f.func_code.co_argcount): + try: + no_of_args = self.f.func_code.co_argcount + except AttributeError: + no_of_args = self.f.__code__.co_argcount + for derive_by_index in range(1, no_of_args): precision = precision_spec[derive_by_index-1] if not precision: precision = 1.e-7 diff --git a/kafe/iminuit_wrapper.py b/kafe/iminuit_wrapper.py index 7ffe53d..cdab19e 100644 --- a/kafe/iminuit_wrapper.py +++ b/kafe/iminuit_wrapper.py @@ -13,7 +13,7 @@ # Changes: # 05-May-15 create module # 08-Oct-16 GQ printout level -1 if "quiet" specified -# suppressed du2() if no printout requested +# suppressed du2() if no printout requested # ---------------------------------------------------------------- # import iminuit as python package @@ -491,7 +491,7 @@ def get_profile(self, parameter, n_points=21): try: par_id = self.parameter_names.index(parameter) except ValueError: - raise ValueError, "No parameter named '%s'" % (parameter,) + raise ValueError("No parameter named '%s'" % (parameter,)) self.out_file.write('\n') # entry in log-file @@ -518,7 +518,7 @@ def get_profile(self, parameter, n_points=21): try: par_id = self.parameter_names.index(parameter) except ValueError: - raise ValueError, "No parameter named '%s'" % (parameter,) + raise ValueError("No parameter named '%s'" % (parameter,)) # save the old stdout stream old_out_stream = os.dup(sys.stdout.fileno()) @@ -555,7 +555,7 @@ def fix_parameter(self, parameter): try: par_id = self.parameter_names.index(parameter) except ValueError: - raise ValueError, "No parameter named '%s'" % (parameter,) + raise ValueError("No parameter named '%s'" % (parameter,)) logger.info("Fixing parameter %d in Minuit" % (par_id,)) @@ -586,7 +586,7 @@ def release_parameter(self, parameter): try: par_id = self.parameter_names.index(parameter) except ValueError: - raise ValueError, "No parameter named '%s'" % (parameter,) + raise ValueError("No parameter named '%s'" % (parameter,)) logger.info("Releasing parameter %d in Minuit" % (par_id,)) @@ -689,7 +689,7 @@ def minos_errors(self, log_print_level=1): old_out_stream = os.dup(sys.stdout.fileno()) os.dup2(self.out_file.fileno(), sys.stdout.fileno()) - self.__iminuit.set_print_level(log_print_level) + self.__iminuit.set_print_level(log_print_level) logger.debug("Running MINOS") _results = self.__iminuit.minos(maxcall=self.max_iterations) @@ -726,7 +726,7 @@ def _fill_in_zeroes_for_fixed(self, submatrix): _mat = submatrix _fparams = self.__iminuit.list_of_fixed_param() - _fparam_ids = map(lambda k: self.parameter_names.index(k), _fparams) + _fparam_ids = [self.parameter_names.index(k) for k in _fparams] for _id in _fparam_ids: _mat = np.insert(np.insert(_mat, _id, 0., axis=0), _id, 0., axis=1) diff --git a/kafe/latex_tools.py b/kafe/latex_tools.py index 94fe488..fd8eeeb 100644 --- a/kafe/latex_tools.py +++ b/kafe/latex_tools.py @@ -36,7 +36,7 @@ def ascii_to_latex_math(str_ascii, monospace=True, ensuremath=True): result = result.replace("\\", "") # remove slashes - for from_ascii, to_latex in L_ESCAPE_FOR_MATH_MODE.iteritems(): + for from_ascii, to_latex in L_ESCAPE_FOR_MATH_MODE.items(): result = result.replace(from_ascii, to_latex) if monospace: diff --git a/kafe/minuit.py b/kafe/minuit.py index 182b1af..77e794c 100644 --- a/kafe/minuit.py +++ b/kafe/minuit.py @@ -15,7 +15,7 @@ # 08-Dec-14 G.Q. added execution of MINOS for final fit # 09-Dec-14 G.Q. added chi2 profiling (function get_profile) # 08-Oct-16 GQ printout level -1 if "quiet" specified -# suppressed du2() if no printout requested +# suppressed du2() if no printout requested # ---------------------------------------------------------------- # ROOT's data types needed to use TMinuit: @@ -145,13 +145,13 @@ def update_parameter_data(self, show_warnings=False): error_code = Long(0) try: # Set up the starting fit parameters in TMinuit - for i in xrange(0, self.number_of_parameters): + for i in range(0, self.number_of_parameters): self.__gMinuit.mnparm(i, self.parameter_names[i], self.current_parameters[i], 0.1 * self.parameter_errors[i], 0, 0, error_code) # use 10% of the par. 1-sigma errors as the initial step size - except AttributeError, e: + except AttributeError as e: if show_warnings: logger.warn("Cannot update Minuit data on the C++ side. " "AttributeError: %s" % (e, )) @@ -267,7 +267,7 @@ def get_parameter_values(self): # retrieve fit parameters p, pe = Double(0), Double(0) - for i in xrange(0, self.number_of_parameters): + for i in range(0, self.number_of_parameters): self.__gMinuit.GetParameter(i, p, pe) # retrieve fitresult result.append(float(p)) @@ -285,7 +285,7 @@ def get_parameter_errors(self): # retrieve fit parameters p, pe = Double(0), Double(0) - for i in xrange(0, self.number_of_parameters): + for i in range(0, self.number_of_parameters): self.__gMinuit.GetParameter(i, p, pe) # retrieve fitresult result.append(float(pe)) @@ -303,7 +303,7 @@ def get_parameter_info(self): # retrieve fit parameters p, pe = Double(0), Double(0) - for i in xrange(0, self.number_of_parameters): + for i in range(0, self.number_of_parameters): self.__gMinuit.GetParameter(i, p, pe) # retrieve fitresult result.append((self.get_parameter_name(i), float(p), float(pe))) @@ -648,7 +648,7 @@ def minos_errors(self, log_print_level=1): old_out_stream = os.dup(sys.stdout.fileno()) os.dup2(self.out_file.fileno(), sys.stdout.fileno()) - self.__gMinuit.SetPrintLevel(log_print_level) + self.__gMinuit.SetPrintLevel(log_print_level) logger.debug("Running MINOS") error_code = Long(0) self.__gMinuit.mnexcm("MINOS", arr('d', [self.max_iterations]), 1, error_code) @@ -666,7 +666,7 @@ def minos_errors(self, log_print_level=1): err=Double(0) # parabolic error gcor=Double(0) # global correlation coefficient - for i in xrange(0, self.number_of_parameters): + for i in range(0, self.number_of_parameters): self.__gMinuit.mnerrs(i, errpos, errneg, err, gcor) output.append([float(errpos),float(errneg),float(err),float(gcor)]) diff --git a/kafe/plot.py b/kafe/plot.py index f1db683..5aeb26c 100644 --- a/kafe/plot.py +++ b/kafe/plot.py @@ -38,7 +38,7 @@ from .config import (G_PADDING_FACTOR_X, G_PADDING_FACTOR_Y, G_PLOT_POINTS, G_FIT_INFOBOX_TITLE) import re # regular expressions -from string import split, join, lower, replace +from string import ascii_lowercase # import main logger for kafe import logging @@ -50,7 +50,7 @@ def label_to_latex(label): Generates a simple LaTeX-formatted label from a plain-text label. This treats isolated characters and words beginning with a backslash as mathematical expressions and surround them with $ signs accordingly. - + Parameters ---------- @@ -58,16 +58,16 @@ def label_to_latex(label): Plain-text string to convert to LaTeX. ''' - tokens = split(label) + tokens = label.split() for token_id, token in enumerate(tokens): if len(token) == 1 or token[0] == '\\': - if lower(token[-1]) not in "abcdefghijklmnopqrstuvwxyz": + if token[-1].lower() not in ascii_lowercase: # surround isolated chars with $ (omit last) tokens[token_id] = '$%s$%s' % (token[:-1], token[-1]) else: # surround isolated chars with $ tokens[token_id] = '$%s$' % (token,) - return join(tokens) + return " ".join(tokens) def pad_span_log(span, pad_coeff=1, additional_pad=None, base=10): @@ -268,8 +268,8 @@ def __init__(self, *fits, **kwargs): if len(fits) == 1: # inherit axis labels from Fit's Dataset #: axis labels - self.axis_labels = map(label_to_latex, - self.fits[0].dataset.axis_labels) + self.axis_labels = list(map(label_to_latex, + self.fits[0].dataset.axis_labels)) # set unit in brackets (if available) for label_id, _ in enumerate(self.axis_labels): @@ -284,8 +284,8 @@ def __init__(self, *fits, **kwargs): self.axis_labels = ('$x$', '$y$') # set default axis names else: # Plot with no Fits -> just set axis names - self.axis_labels = ('$x$', '$y$') # set default axis names - + self.axis_labels = ('$x$', '$y$') # set default axis names + self.init_plots(**kwargs) # initialize the plots def _update_rcParams(self): @@ -341,7 +341,7 @@ def init_plots(self, **kwargs): def set_axis_scale(self, axis, scale_type, **kwargs): ''' Set the scale for an axis. - + Parameters ---------- @@ -504,7 +504,7 @@ def draw_legend(self): def draw_fit_parameters_box(self, plot_spec=0, force_show_uncertainties=False): '''Draw the parameter box to the canvas - + Parameters ---------- @@ -604,7 +604,7 @@ def draw_fit_parameters_box(self, plot_spec=0, # WORKAROUND: replace '\n~\n' with '\n|\n' # (the pipe '|' renders as a dash) - text_content = replace(text_content, '\n~\n', '\n|\n') + text_content = text_content.replace('\n~\n', '\n|\n') self.fitinfobox = self.axes.add_patch( mpl.patches.Rectangle((legend_bbox.xmin, 0.00), @@ -740,8 +740,8 @@ def plot(self, p_id, show_data=True, show_function=True, show_band=True): G_PLOT_POINTS) # apply the current fit function to every point in fxdata => fydata - fydata = np.asarray(map(current_fit.get_current_fit_function(), - fxdata)) + fydata = np.asarray(list(map(current_fit.get_current_fit_function(), + fxdata))) # compute the confidence band around the function ################################################## diff --git a/uninstall.sh b/uninstall.sh index 8cddec2..eb23336 100755 --- a/uninstall.sh +++ b/uninstall.sh @@ -2,7 +2,7 @@ # Uninstall script for kafe using pip -function check_pip_python_2 () { +function check_pip_python_2_3 () { echo "Checking Python version for pip executable '$1'..." if ! [[ `which $1` ]]; then echo "Pip executable '$1' not found in PATH!" @@ -13,8 +13,8 @@ function check_pip_python_2 () { echo "Pip executable '$1' uses Python 2: ok" return 0 elif [[ PIP_PYTHON_VERSION -eq 3 ]]; then - echo "Pip executable '$1' uses Python 3: not supported" - return 1 + echo "Pip executable '$1' uses Python 3: ok" + return 0 else echo "Pip executable '$1' not found in PATH!" return 1 @@ -24,16 +24,16 @@ function check_pip_python_2 () { ## -- main -- ## # pip executables to try out -PIP_EXEC_LIST="pip pip2 pip2.7" +PIP_EXEC_LIST="pip pip2 pip2.7 pip3 pip3.1 pip3.2 pip3.3 pip3.4 pip3.5 pip3.6 pip3.7" for pip_exec in $PIP_EXEC_LIST; do - check_pip_python_2 $pip_exec - found_pip_python_2=$? - if [[ found_pip_python_2 -eq 0 ]]; then + check_pip_python_2_3 $pip_exec + found_pip_python_2_3=$? + if [[ found_pip_python_2_3 -eq 0 ]]; then break fi done -if [[ found_pip_python_2 -ne 0 ]]; then +if [[ found_pip_python_2_3 -ne 0 ]]; then echo "None of the executables '$PIP_EXEC_LIST' seem to be valid. Aborting." exit 1 fi From 39465345f59ab40d82eaecbe9e1ab3adbf6f6f13 Mon Sep 17 00:00:00 2001 From: Julian Gethmann Date: Mon, 27 Feb 2017 15:31:50 +0100 Subject: [PATCH 2/2] Fix wrong versioning * CHANGELOG has no entry for 1.2.0 * Increment the version number to 1.3.0 --- CHANGELOG | 2 +- kafe/_version_info.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 0e99682..2688648 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,7 +2,7 @@ master ====== % [TODO] add unit test for input file parser -v1.2.0 +v1.3.0 ====== % [FIX] make code Python 3 compatible. diff --git a/kafe/_version_info.py b/kafe/_version_info.py index 266b5cb..7a4d619 100644 --- a/kafe/_version_info.py +++ b/kafe/_version_info.py @@ -1,14 +1,14 @@ ''' .. module:: _version_info :platform: Unix - :synopsis: Version 1.2.0 of kafe, release Jun. 2016 + :synopsis: Version 1.3.0 of kafe, release Feb. 2017 .. moduleauthor:: Daniel Savoiu Guenter Quast ''' major = 1 -minor = 2 +minor = 3 revision = 0 def _get_version_tuple():