def sample_space(boundaries, constraints, sample_size, compress_sample=False, compress_region=False, silent=True, debug: bool = False, parallel=10): """ Samples the space in **sample_size** samples in each dimension and saves if the point is in respective interval. Args: constraints (list of strings): list of constraints sample_size (int): number of samples in dimension boundaries (list of intervals): subspace to sample, False for default region of space compress_sample (bool): if True, only a conjunction of the values (prop in the interval) is used for a sample compress_region (bool): if True, only qualitative information (safe, unsafe, neither) about region is returned silent (bool): if silent printed output is set to minimum debug (bool): if True extensive print will be used parallel (bool): flag to run this in parallel mode """ start_time = time() global glob_params global glob_debug global glob_compress global glob_constraints if compress_region is True: compress_sample = True if debug: silent = False if parallel > 1: pool_size = parallel else: pool_size = multiprocessing.cpu_count() - 1 ## Convert z3 functions for index, constraint in enumerate(constraints): if is_this_z3_function(constraint): constraints[index] = translate_z3_function(constraint) parameter_values = [] if debug: print("params", glob_params) print("region", boundaries) print("sample_size", sample_size) ## Create list of parameter values to sample for index in range(len(glob_params)): parameter_values.append(np.linspace(boundaries[index][0], boundaries[index][1], sample_size, endpoint=True)) sampling = create_matrix(sample_size, len(glob_params)) parameter_values = cartesian_product(*parameter_values) if not silent: print("sampling here") print("sample_size", sample_size) print("params", glob_params) print("parameter_values", parameter_values) if debug: print("sampling", sampling) del sampling glob_debug = debug glob_compress = compress_sample glob_constraints = constraints print(colored(f"Sampling initialisation took {round(time() - start_time, 4)} seconds", "yellow")) if not silent else None ## ACTUAL SAMPLING if parallel: ## Parallel sampling with multiprocessing.Pool(pool_size) as p: sat_list = list(p.map(check_sample, parameter_values)) ## TODO check how to alter progress when using Pool if compress_region: if True in sat_list: if False in sat_list: return "neither" else: return "safe" else: return "unsafe" else: return sat_list
def heatmap(function, region, sampling_sizes, posttitle="", where=False, parameters=False, verbose=False): """ Creates 2D heatmap plot of sampled points of given function. Args: function (string): function to be analysed region (list of intervals): boundaries of parameter space to be sampled sampling_sizes (list of ints): tuple of sample size of respective parameter posttitle (string): A string to be put after the title where (tuple/list): output matplotlib sources to output created figure parameters (list): list of parameters verbose (bool): will input maximum information Example: heatmap("p+q",[[0,1],[3,4]],[5,5]) """ ## Convert z3 function if is_this_z3_function(function): function = translate_z3_function(function) if not parameters: parameters = sorted(list(find_param(function))) # print(parameters) if len(parameters) > 2: raise Exception( f"Number of parameters of given function is bigger than 2!") # f = lambda locals()[parameters[0]),locals()[parameters[1]): fun sampling_sizes[0] = sampling_sizes[0] + 1 if len(parameters) == 1: arr = np.zeros((sampling_sizes[0], 3)) ii = -1 for i in np.linspace(region[0][0], region[0][1], sampling_sizes[0], endpoint=True): ii += 1 # print("ii: ",ii) locals()[parameters[0]] = i arr[ii, 0] = round(i, 8) arr[ii, 2] = eval(function) # print(arr) heatmap_data = pd.DataFrame(arr, columns=[parameters[0], "", "E"]) heatmap_data = heatmap_data.pivot(parameters[0], "", "E") vmin = min(arr[:, 2]) vmax = max(arr[:, 2]) sqaure = True else: sampling_sizes[1] = sampling_sizes[1] + 1 arr = np.zeros((sampling_sizes[0] * sampling_sizes[1], 3)) ii = -1 jj = -1 for i in np.linspace(region[0][0], region[0][1], sampling_sizes[0], endpoint=True): ii += 1 # print("ii: ",ii) locals()[parameters[0]] = i for j in np.linspace(region[1][0], region[1][1], sampling_sizes[1], endpoint=True): jj += 1 # print("jj: ",jj) locals()[parameters[1]] = j arr[jj, 0] = round(i, 2) arr[jj, 1] = round(j, 2) arr[jj, 2] = eval(function) # print(arr) heatmap_data = pd.DataFrame( arr, columns=[parameters[0], parameters[1], "E"]) heatmap_data = heatmap_data.pivot(parameters[0], parameters[1], "E") vmin = min(arr[:, 2]) vmax = max(arr[:, 2]) sqaure = False if where: f, ax = plt.subplots() ax = sns.heatmap(heatmap_data, vmin=vmin, vmax=vmax, annot=verbose, square=sqaure) title = f"Heatmap \n{posttitle}" ax.set_title(wraper.wrap(title)) ax.invert_yaxis() return f else: ax = sns.heatmap(heatmap_data, vmin=vmin, vmax=vmax, annot=verbose, square=sqaure) title = f"Heatmap of the parameter space \n function: {function}" ax.set_title(wraper.wrap(title)) ax.invert_yaxis() plt.show()
def sample_list_funs(functions, sample_size, parameters=False, intervals=False, silent: bool = False, debug: bool = False): """ Returns a list of function values for sampled parametrisations. Args: functions: (list of functions) to be sampled sample_size (int): sample size in each parameter parameters (list of strings): parameter names (used for faster eval) intervals (list of pairs of numbers): intervals of parameters silent (bool): if silent command line output is set to minimum debug (bool): if debug extensive output is provided Returns: (array): [function index, [parameter values], function value] """ arr = [] if debug: print("Inside of sample_n_visualise.sample_list_funs()") print("List_fun: ", functions) print("Intervals: ", intervals) for index, polynomial in enumerate(functions): if is_this_z3_function(polynomial): functions[index] = translate_z3_function(polynomial) if debug: print("Polynomial: ", polynomial) if parameters: fun_parameters = parameters else: fun_parameters = set() fun_parameters.update(find_param(polynomial, debug)) ## THIS THING IS WORKING ONLY FOR THE CASE STUDY # if len(parameters) < N: # parameters.update(find_param(polynomial, debug)) if debug: print("Parameters: ", fun_parameters) fun_parameters = sorted(list(fun_parameters)) if debug: print("Sorted parameters: ", fun_parameters) parameter_values = get_param_values(fun_parameters, sample_size, intervals=intervals, debug=debug) for parameter_value in parameter_values: if debug: print("Parameter_value: ", parameter_value) a = [functions.index(polynomial)] for param_index in range(len(fun_parameters)): a.append(parameter_value[param_index]) if debug: print("Parameter[param]: ", fun_parameters[param_index]) print("Parameter_value[param]: ", parameter_value[param_index]) locals()[fun_parameters[param_index]] = float( parameter_value[param_index]) # print("polynomial", polynomial) value = eval(polynomial) if debug: print("Eval ", polynomial, value) a.append(value) arr.append(a) return arr
def eval_and_show(functions, parameter_value, parameters=False, data=False, data_intervals=False, cumulative=False, debug: bool = False, where=False): """ Creates bar plot of evaluation of given functions for given point in parameter space. Args: functions (list of strings): list of rational functions parameter_value: (list of numbers) array of param values parameters (list of strings): parameter names (used for faster eval) data (list of floats): Data comparison next to respective function data_intervals (list of Intervals): intervals obtained from the data to check if the function are within the intervals cumulative (bool): if True cdf instead of pdf is visualised debug (bool): if debug extensive output is provided where (tuple or list): output matplotlib sources to output created figure """ ## Convert z3 functions for index, function in enumerate(functions): if is_this_z3_function(function): functions[index] = translate_z3_function(function) if not parameters: parameters = set() for function in functions: parameters.update(find_param(function, debug)) parameters = sorted(list(parameters)) if debug: print("Parameters: ", parameters) if data: ## Check the sizes of data and functions if len(data) != len(functions): raise Exception( f"Number of data points, {len(data)}, is not equal to number of functions, {len(functions)}." ) title = "Rational functions and data \n Parameter values:" else: title = "Rational functions \n Parameter values:" function_values = [] add = 0 for param in range(len(parameters)): if debug: print("Parameters[param]", parameters[param]) print("Parameter_value[param]", parameter_value[param]) globals()[parameters[param]] = parameter_value[param] title = f"{title} {parameters[param]}={parameter_value[param]}," title = title[:-1] if data_intervals: ## Check the sizes of data and functions if len(data_intervals) != len(functions): raise Exception( f"Number of data intervals, {len(data_intervals)}, is not equal to number of functions, {len(functions)}." ) ## Uniformize the intervals data_intervals = to_sympy_intervals(data_intervals) title = f"{title}\n Function values: " for function in functions: expression = eval(function) if debug: print(function) print("Eval ", function, expression) if cumulative: ## Add sum of all values add = add + expression function_values.append(add) title = f"{title} {add} , " del expression else: function_values.append(expression) title = f"{title} {expression} ," title = title[:-2] if data: # data_to_str = str(data).replace(" ", "\u00A0") does not work title = f"{title}\n Data values: {str(data)[1:-1]}" if cumulative: for index in range(1, len(data)): data[index] = data[index] + data[index - 1] distance = 0 for index in range(len(data)): try: distance = distance + (eval(functions[index]) - data[index])**2 except IndexError as error: raise Exception( f"Unable to show the intervals on the plot. Number of data point ({len(data)}) is not equal to number of functions ({len(functions)})." ) title = f"{title}\n L2 Distance: {distance}" if where: fig = where[0] ax = where[1] plt.autoscale() ax.autoscale() else: fig, ax = plt.subplots() width = 0.2 ax.set_ylabel(f'{("Value", "Cumulative value")[cumulative]}') if data: ax.set_xlabel( 'Rational function indices (blue bars), Data point indices (red bars)' ) # if data_intervals: # functions_inside_of_intervals = [] # for index in range(len(data)): # try: # if function_values[index] in data_intervals[index]: # functions_inside_of_intervals.append(True) # else: # functions_inside_of_intervals.append(False) # except IndexError as error: # raise Exception(f"Unable to show the intervals on the plot. Number of data intervals ({len(data_intervals)}) is not equal to number of functions ({len(functions)}).") # # # functions_inside_of_intervals_to_str = str(functions_inside_of_intervals).replace(" ", "\u00A0") - does not work # title = f"{title} \n Function value within the respective interval: {functions_inside_of_intervals}" else: ax.set_xlabel('Rational function indices') ax.xaxis.set_major_locator(FixedLocator(range(1, len(function_values) + 1))) ax.bar(range(1, len(function_values) + 1), function_values, width, color='b', label="function") if data: if data_intervals: # np.array(list(map(lambda x: np.array([x.start, x.end]), data_intervals))) # wrong shape (N,2) instead of (2,N) errors = [[], []] for index, item in enumerate(data_intervals): errors[0].append(float(abs(data[index] - item.start))) errors[1].append(float(abs(data[index] - item.end))) ax.bar(list(map(lambda x: x + width, range(1, len(data) + 1))), data, width, yerr=errors, color='r', capsize=10, label="data") title = f"{title}\n Data intervals visualised as error bars." else: ax.bar(list(map(lambda x: x + width, range(1, len(data) + 1))), data, width, color='r', label="data") ax.set_title(wraper.wrap(title)) # fig.legend() if debug: print("Len(fun_list): ", len(functions)) print("values:", function_values) print("range:", range(1, len(function_values) + 1)) print("title", title) if where: return fig, ax else: plt.show() return function_values
def sample_region(region, params, constraints, sample_size, boundaries=False, compress=False, silent=True, save=False, debug=False, progress=False, quantitative=False, parallel=True, save_memory=False, stop_on_unknown=False): """ Samples the space in **sample_size** samples in each dimension and saves if the point is in respective interval. Args: region: (Rectangle): region to be sampled params: (list of strings): parameters constraints (list of strings): list of constraints sample_size (int): number of samples in dimension boundaries (list of intervals): subspace to sample, False for default region of space compress (bool): if True, only a conjunction of the values (prop in the interval) is used silent (bool): if silent printed output is set to minimum debug (bool): if True extensive print will be used save (bool): if True output is pickled debug (bool): if True extensive print will be used progress (Tkinter element): progress bar quantitative (bool): if True return how far is the point from satisfying / not satisfying the constraints parallel (bool): flag to run this in parallel mode save_memory (bool): if True saves only sat samples """ start_time = time() global glob_space global glob_debug global glob_compress global glob_constraints ## TODO this can be optimised using check_sample without using space glob_space = RefinedSpace(region, params) assert isinstance(region, Iterable) if debug: silent = False if parallel is True: pool_size = multiprocessing.cpu_count() - 1 elif parallel > 1: pool_size = min(parallel, multiprocessing.cpu_count() - 1) elif parallel == 1: pool_size = 1 ## Convert z3 functions for index, constraint in enumerate(constraints): if is_this_z3_function(constraint): constraints[index] = translate_z3_function(constraint) ## Convert constraints for quantitative sampling if quantitative: constraints = copy(constraints) constraints = list(map(normalise_constraint, constraints)) ## Split constraints into two pairs ((left, mid)(mid, right)) or ((left, right), None) constraints = split_constraints(constraints) parameter_values = [] if debug: print("space.params", params) print("space.region", region) print("sample_size", sample_size) ## Create list of parameter values to sample for index in range(len(params)): if boundaries: parameter_values.append(np.linspace(boundaries[index][0], boundaries[index][1], sample_size, endpoint=True)) else: parameter_values.append(np.linspace(region[index][0], region[index][1], sample_size, endpoint=True)) sampling = create_matrix(sample_size, len(params)) parameter_values = cartesian_product(*parameter_values) if not silent: print("sampling here") print("sample_size", sample_size) print("params", params) print("parameter_values", parameter_values) if debug: print("sampling", sampling) del sampling glob_debug = debug glob_compress = compress glob_constraints = constraints print(colored(f"Sampling initialisation took {round(time() - start_time, 4)} seconds", "yellow")) if not silent else None ## ACTUAL SAMPLING if parallel and not quantitative: ## Parallel sampling if stop_on_unknown: ## TODO implement this, very good idea on paper raise NotImplementedError("this optimisation is not implemented so far, please use option stop_on_unknown=False") current = None with multiprocessing.Pool(pool_size) as p: check_samplee = partial(check_sample, silent=silent) results = [p.apply_async(check_samplee, item).get() for item in parameter_values] print(results) return results # for item in parameter_values: # print("item", item) # result = p.apply_async(check_sample, item) # print("result, ", result) # if current is None: # if result is True: # current = "safe" # else: # current = "unsafe" # elif current == "safe": # if result is False: # current = "neither" # p.terminate() # elif current == "unsafe": # if result is True: # current = "neither" # p.terminate() # return current else: with multiprocessing.Pool(pool_size) as p: sat_list = list(p.map(check_sample, parameter_values)) elif parallel and quantitative: ## Parallel quantitative sampling with multiprocessing.Pool(pool_size) as p: dist_list = list(p.map(sample_sat_degree, parameter_values)) elif not quantitative: ## Sequential sampling sat_list, unsat_list = [], [] for index, item in enumerate(parameter_values): spam = check_sample(item, save_memory, silent=silent) if spam is True: sat_list.append(item) elif spam is False: unsat_list.append(item) else: pass # warnings.filterwarnings("ignore") # raise Warning(f"Checking {parameter_values} resulted in unexpected value: {spam}") if progress: progress(index / len(parameter_values)) else: ## Sequential quantitative sampling dist_map = {} for index, item in enumerate(parameter_values): dist_map[item] = sample_sat_degree(item) if progress: progress(index / len(parameter_values)) ## Setting flag to not visualise sat if no unsat and vice versa print(colored(f"Sampling took {round(time()-start_time, 4)} seconds", "yellow")) if not silent else None if parallel: if quantitative: return dist_list else: return sat_list else: if quantitative: return dist_map else: return sat_list, unsat_list
def optimize(functions: [list], params: [list], param_intervals: [list], data_point: [list], weights=False, debug=False): """ Search for parameter values minimizing the distance of function to data. Args: functions (list): of functions to be optimized params (list): of functions parameters param_intervals (list): of intervals of functions parameters data_point (list): of values of functions to be optimized weights (list): of weights to multiply the respective distance with debug (bool): if True extensive print will be used Returns: (list): [point of parameter space with the least distance, values of functions in the point, the distance between the data and functions values] """ assert len(functions) == len(data_point) assert len(params) == len(param_intervals) if weights: assert len(weights) == len(data_point) ## Convert z3 functions for index, function in enumerate(functions): if is_this_z3_function(function): functions[index] = translate_z3_function(function) ## Get the average value of parameter intervals x0 = np.array(list(map(lambda lst: (lst[0] + lst[1]) / 2, param_intervals))) globals()["functions"] = functions # print("globals()[functions]", globals()["functions"]) globals()["params"] = params # print("globals()[params]", globals()["params"]) globals()["data_point"] = data_point # print("globals()[data_point]", globals()["data_point"]) bounds = [[], []] for interval in param_intervals: bounds[0].append(interval[0]) bounds[1].append(interval[1]) # print("bounds", bounds) if debug: verbose = 2 else: verbose = 0 if weights: res = scipy.optimize.least_squares(weighted_dist, x0, bounds=bounds, args=[weights], verbose=verbose) else: res = scipy.optimize.least_squares(dist, x0, bounds=bounds, verbose=verbose) # print(res.x) ## VALUES OF PARAMS, VALUES OF FUNCTIONS, DISTANCE # print("point", list(res.x)) ## function_values = res.fun ## for index, item in enumerate(function_values): ## function_values[index] = function_values[index] + data_point[index] if debug: print(res) print("params", params) ## !!! THIS IS NOT UPDATED RESULT # spam = [] # for item in params: # spam.append(globals()[item]) # print("param values", spam) print("param values", list(res.x)) print("function values", list(map(eval, functions))) print("distance values", list(res.fun)) print("total distance according to scipy - not transformed", res.cost) ## !!! THIS IS NOT UPDATED RESULT # print("L1 distance", sum([abs(x - y) for x, y in zip(list(map(eval, functions)), data_point)])) # print("L2 distance", (sum([(x - y)**2 for x, y in zip(list(map(eval, functions)), data_point)]))**(1/2)) for index, value in enumerate(params): globals()[str(value)] = res.x[index] print( "L1 distance", sum([ abs(x - y) for x, y in zip(list(map(eval, functions)), data_point) ])) print("L2 distance", (sum([ (x - y)**2 for x, y in zip(list(map(eval, functions)), data_point) ]))**(1 / 2)) return list(res.x), list(map(eval, functions)), (2 * res.cost)**(1 / 2)