From cbe7f12b39aae6a21f6a6e65b88fea15ee7362e4 Mon Sep 17 00:00:00 2001 From: pravalikavis Date: Fri, 1 May 2020 20:49:39 +0530 Subject: [PATCH 01/10] generic implementation for dynamic programming --- pydatastructs/__init__.py | 1 + pydatastructs/optimal_grouping.py | 247 ++++++++++++++++++++++++++++++ 2 files changed, 248 insertions(+) create mode 100644 pydatastructs/optimal_grouping.py diff --git a/pydatastructs/__init__.py b/pydatastructs/__init__.py index ca6c0c75a..0df98e4c8 100644 --- a/pydatastructs/__init__.py +++ b/pydatastructs/__init__.py @@ -3,3 +3,4 @@ from .miscellaneous_data_structures import * from .utils import * from .graphs import * +from .optimal_grouping import optimal_grouping \ No newline at end of file diff --git a/pydatastructs/optimal_grouping.py b/pydatastructs/optimal_grouping.py new file mode 100644 index 000000000..3f3774e8e --- /dev/null +++ b/pydatastructs/optimal_grouping.py @@ -0,0 +1,247 @@ +from types import * + + +def get_value(matrix, lookup_index): + """ + gets a value + """ + return matrix[lookup_index[0]][lookup_index[1]] + + +def set_value(matrix, lookup_index, value): + """ + sets a value + """ + matrix[lookup_index[0]][lookup_index[1]] = value + + +def compare(maximize, value, compareWith=None): + """ + compares a value with another. if compareWith is None then value is compared with Infinity or -Infinity + parameters + [maximize] if True then the function returns true if value is greater than compareWith and vice versa + """ + if compareWith == None: + if maximize: + compareWith = float('-inf') + else: + compareWith = float('inf') + if maximize: + return value > compareWith + return value < compareWith + + +def initialize_arrays(maximize, rows, columns): + """ + returns a 2-d array of rows*columns size filled with either Infinity or -Infinity + parameters: + [maximize] + if 'True' fills with -Infinity and vice versa + [rows] + expects a number + [columns] + expects a number + """ + value = float('inf') + if maximize: + value = float('-inf') + return [[value for a in range(0, columns+1)] for a in range(0, rows+1)] + + +def optimal_grouping_rec(object_arr, cost_storage, solution_matrix, maximize_prob, min_compare_len, lookup_index, get_lookup_fn, cost_fn): + """ + Helper function for optimal_grouping function + """ + + # gets the present value at the present index + present_value = get_value(cost_storage, lookup_index) + # return the present value if it is not infinity + if compare(maximize_prob, present_value): + return present_value + + # get the start and end indices where end index depends on the min_compare_len + start_index = lookup_index[0] + end_index = lookup_index[1]+1-(min_compare_len-1) + + if start_index == end_index or start_index > end_index: + cost = cost_fn(object_arr, lookup_index, start_index) + if compare(maximize_prob, cost, present_value): + set_value(cost_storage, lookup_index, cost) + set_value(solution_matrix, lookup_index, start_index) + present_value = cost + + for i in range(start_index, end_index): + + # get indices for left recursion tree + left_rec_indices = get_lookup_fn('before', lookup_index, i) + test_lookup_function(left_rec_indices,lookup_index) + + cost = optimal_grouping_rec(object_arr, cost_storage, solution_matrix, maximize_prob, + min_compare_len, left_rec_indices, get_lookup_fn, cost_fn) + + # get indices for right recursion tree + right_rec_indices = get_lookup_fn('after', lookup_index, i) + test_lookup_function(right_rec_indices,lookup_index) + + cost = cost+optimal_grouping_rec(object_arr, cost_storage, solution_matrix, maximize_prob, + min_compare_len, right_rec_indices, get_lookup_fn, cost_fn) + + + # get cost for present partition + cost = cost+cost_fn(object_arr, lookup_index, i) + + # update the values if this is the best solution until now + if compare(maximize_prob, cost, present_value): + set_value(cost_storage, lookup_index, cost) + set_value(solution_matrix, lookup_index, i) + present_value = cost + + return present_value + +def test_lookup_function(lookup_index,input_index): + if lookup_index is None or type(lookup_index) is not ListType: + raise TypeError( + 'Check lookup_function: returning wrong type should return an array of start and end index') + + if lookup_index.__len__ < 2 or type(lookup_index[0]) is not IntType or type(lookup_index[1]) is not IntType : + raise ValueError( + 'Check lookup_function:lookup index should at least have 2 integer items, first specifying the start and second specifying the last indices') + + if input_index == lookup_index: + raise RuntimeError( + 'Check lookup_function:verify get_lookup_fn giving same output as input which will lead to infinite loop') + + +def optimal_grouping(process_objects, maximize_prob, min_compare_len, lookup_index, get_lookup_fn, cost_fn): + """ + Description: Optimal Grouping groups given set of objects using the given cost function + + Parameters: + object_arr + accepts array of objects on which the algorithm is supposed to run + maximize_prob + pass True if the algorithm should find maximum value of the cost function otherwise pass False + min_compare_len + a positive number decides to which level of gap the algorithm can maintain while iterating from start to end, + for example-> if minimun length is 2 then it can only iterate if endIndex=startIndex+2 + lookup_index + format-->[start_index,endIndex] algorithm runs from start to end + get_lookup_fn + should return next range of indices + sample -> get_lookup_fn(position, rangeIndices, currentIndex) + position is either 'before' or 'after' + rangeIndices is the present range of index like [start_index,endIndex] + cost_fn + should return the cost + sample -> cost_fn(object_arr,rangeIndices,currentIndex) + + + **Usage examples : + + 1.OPTIMAL BINARY SEARCH TREE + + from binarytree import Node + n = 5 + p = [None, Node(0.15), Node(0.10), Node(0.05), Node(0.10), Node(0.20)] + q = [Node(0.05), Node(0.10), Node(0.05), Node(0.05), Node(0.05), Node(0.10)] + + + def lookup(position, endIndex, middle): + if position is 'before': + return [endIndex[0], middle-1] + else: + return [middle+1, endIndex[1]] + + + def cost(obj, endIndex, middle): + + if(endIndex[1] lookup_index[1]: + raise ValueError( + 'lookup index should at least have 2 integer items, first specifying the start and second specifying the last indices') + + if get_lookup_fn is None or type(get_lookup_fn) is not FunctionType: + raise TypeError( + 'get_lookup_fn cannot be none and should be a function with 3 arguments') + + test_result = get_lookup_fn('before', lookup_index, lookup_index[0]) + if test_result == lookup_index: + raise RuntimeError( + 'verify get_lookup_fn giving same output as input which may lead to infinite loop') + test_result = get_lookup_fn('after', lookup_index, lookup_index[0]) + if test_result == lookup_index: + raise RuntimeError( + 'verify get_lookup_fn giving same output as input which may lead to infinite loop') + + if cost_fn is None or type(cost_fn) is not FunctionType: + raise TypeError( + 'cost_fn cannot be none and should be a function with 3 arguments') + + test_result = cost_fn(process_objects, lookup_index, lookup_index[0]) + if test_result is None or (type(test_result) is not IntType and type(test_result) is not FloatType and type(test_result) is not LongType): + raise RuntimeError( + 'output for cost function should be any type of number') + + # end of edge cases + + length = lookup_index[1]-lookup_index[0]+1 + + # for storing the computed values (helper array) + cost_storage = initialize_arrays(maximize_prob, length+1, length+1) + # for storing the solutions + solution_matrix = initialize_arrays(maximize_prob, length+1, length+1) + + optimal_grouping_rec(process_objects, cost_storage, solution_matrix, maximize_prob, + min_compare_len, lookup_index, get_lookup_fn, cost_fn) + return solution_matrix \ No newline at end of file From 8b58019073e35cde62c8d07797382fa72dfb18c7 Mon Sep 17 00:00:00 2001 From: pravalikavis Date: Fri, 1 May 2020 22:39:19 +0530 Subject: [PATCH 02/10] type annotation for arguments --- pydatastructs/optimal_grouping.py | 56 +++++++++++++++++-------------- 1 file changed, 31 insertions(+), 25 deletions(-) diff --git a/pydatastructs/optimal_grouping.py b/pydatastructs/optimal_grouping.py index 3f3774e8e..4c66395bf 100644 --- a/pydatastructs/optimal_grouping.py +++ b/pydatastructs/optimal_grouping.py @@ -1,21 +1,22 @@ from types import * +from typing import List -def get_value(matrix, lookup_index): +def get_value(matrix, lookup_index: List[int]): """ gets a value """ return matrix[lookup_index[0]][lookup_index[1]] -def set_value(matrix, lookup_index, value): +def set_value(matrix, lookup_index: List[int], value): """ sets a value """ matrix[lookup_index[0]][lookup_index[1]] = value -def compare(maximize, value, compareWith=None): +def compare(maximize: bool, value, compareWith=None): """ compares a value with another. if compareWith is None then value is compared with Infinity or -Infinity parameters @@ -31,7 +32,7 @@ def compare(maximize, value, compareWith=None): return value < compareWith -def initialize_arrays(maximize, rows, columns): +def initialize_arrays(maximize: bool, rows: int, columns: int): """ returns a 2-d array of rows*columns size filled with either Infinity or -Infinity parameters: @@ -48,7 +49,7 @@ def initialize_arrays(maximize, rows, columns): return [[value for a in range(0, columns+1)] for a in range(0, rows+1)] -def optimal_grouping_rec(object_arr, cost_storage, solution_matrix, maximize_prob, min_compare_len, lookup_index, get_lookup_fn, cost_fn): +def optimal_grouping_rec(object_arr, cost_storage: List[List[int]], solution_matrix: List[List[int]], maximize_prob: bool, min_compare_len: int, lookup_index: List[int], get_lookup_fn, cost_fn): """ Helper function for optimal_grouping function """ @@ -74,19 +75,18 @@ def optimal_grouping_rec(object_arr, cost_storage, solution_matrix, maximize_pro # get indices for left recursion tree left_rec_indices = get_lookup_fn('before', lookup_index, i) - test_lookup_function(left_rec_indices,lookup_index) + test_lookup_function(left_rec_indices, lookup_index) cost = optimal_grouping_rec(object_arr, cost_storage, solution_matrix, maximize_prob, min_compare_len, left_rec_indices, get_lookup_fn, cost_fn) # get indices for right recursion tree right_rec_indices = get_lookup_fn('after', lookup_index, i) - test_lookup_function(right_rec_indices,lookup_index) + test_lookup_function(right_rec_indices, lookup_index) cost = cost+optimal_grouping_rec(object_arr, cost_storage, solution_matrix, maximize_prob, min_compare_len, right_rec_indices, get_lookup_fn, cost_fn) - # get cost for present partition cost = cost+cost_fn(object_arr, lookup_index, i) @@ -98,12 +98,13 @@ def optimal_grouping_rec(object_arr, cost_storage, solution_matrix, maximize_pro return present_value -def test_lookup_function(lookup_index,input_index): - if lookup_index is None or type(lookup_index) is not ListType: + +def test_lookup_function(lookup_index: List[int], input_index: List[int]): + if lookup_index is None: raise TypeError( 'Check lookup_function: returning wrong type should return an array of start and end index') - - if lookup_index.__len__ < 2 or type(lookup_index[0]) is not IntType or type(lookup_index[1]) is not IntType : + + if lookup_index.__len__() < 2: raise ValueError( 'Check lookup_function:lookup index should at least have 2 integer items, first specifying the start and second specifying the last indices') @@ -112,7 +113,7 @@ def test_lookup_function(lookup_index,input_index): 'Check lookup_function:verify get_lookup_fn giving same output as input which will lead to infinite loop') -def optimal_grouping(process_objects, maximize_prob, min_compare_len, lookup_index, get_lookup_fn, cost_fn): +def optimal_grouping(process_objects, maximize_prob: bool, min_compare_len: int, lookup_index: List[int], get_lookup_fn, cost_fn): """ Description: Optimal Grouping groups given set of objects using the given cost function @@ -167,9 +168,9 @@ def cost(obj, endIndex, middle): print(optimal_grouping({'p': p, 'q': q}, False, 1, [1, n], lookup, cost)) - - - + + + 2.MATRIX CHAIN MULTIPLICATION def cost(matrix, endIndex, middle): @@ -193,21 +194,21 @@ def lookup(position, endIndex, middle): if process_objects is None: raise TypeError('process_objects cannot be none') - if maximize_prob is None or type(maximize_prob) is not BooleanType: + if maximize_prob is None: raise TypeError( - 'maximize_prob cannot be none and should be of type boolean') + 'maximize_prob cannot be none') - if min_compare_len is None or type(min_compare_len) is not IntType: + if min_compare_len is None: raise TypeError( - 'min_compare_len cannot be none and should be of type int') + 'min_compare_len cannot be none') if min_compare_len < 1: raise ValueError( 'min_compare_len should be a positive integer') - if lookup_index is None or type(lookup_index) is not ListType: + if lookup_index is None: raise TypeError( - 'lookup_index cannot be none and should be of type list') - if lookup_index.__len__ < 2 or type(lookup_index[0]) is not IntType or type(lookup_index[1]) is not IntType or lookup_index[0] > lookup_index[1]: + 'lookup_index cannot be none') + if lookup_index.__len__() < 2 or lookup_index[0] > lookup_index[1]: raise ValueError( 'lookup index should at least have 2 integer items, first specifying the start and second specifying the last indices') @@ -229,9 +230,14 @@ def lookup(position, endIndex, middle): 'cost_fn cannot be none and should be a function with 3 arguments') test_result = cost_fn(process_objects, lookup_index, lookup_index[0]) - if test_result is None or (type(test_result) is not IntType and type(test_result) is not FloatType and type(test_result) is not LongType): - raise RuntimeError( + try: + int(test_result) + except Exception: + raise TypeError( 'output for cost function should be any type of number') + if test_result is None: + raise RuntimeError( + 'output for cost function should be any type of number and cannot be None') # end of edge cases From eb4cdc7471d4c54204042c51e72c1253aa18c396 Mon Sep 17 00:00:00 2001 From: pravalikavis Date: Fri, 1 May 2020 22:43:14 +0530 Subject: [PATCH 03/10] moved file to linear data structures --- pydatastructs/__init__.py | 1 - pydatastructs/{ => linear_data_structures}/optimal_grouping.py | 0 2 files changed, 1 deletion(-) rename pydatastructs/{ => linear_data_structures}/optimal_grouping.py (100%) diff --git a/pydatastructs/__init__.py b/pydatastructs/__init__.py index 0df98e4c8..ca6c0c75a 100644 --- a/pydatastructs/__init__.py +++ b/pydatastructs/__init__.py @@ -3,4 +3,3 @@ from .miscellaneous_data_structures import * from .utils import * from .graphs import * -from .optimal_grouping import optimal_grouping \ No newline at end of file diff --git a/pydatastructs/optimal_grouping.py b/pydatastructs/linear_data_structures/optimal_grouping.py similarity index 100% rename from pydatastructs/optimal_grouping.py rename to pydatastructs/linear_data_structures/optimal_grouping.py From cc4244ac571abce6fba7b1999e6831a4b9d3f6e2 Mon Sep 17 00:00:00 2001 From: Pravalika Date: Sat, 9 May 2020 14:30:18 +0530 Subject: [PATCH 04/10] new line at the end of the file --- pydatastructs/linear_data_structures/optimal_grouping.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pydatastructs/linear_data_structures/optimal_grouping.py b/pydatastructs/linear_data_structures/optimal_grouping.py index 4c66395bf..f1459612d 100644 --- a/pydatastructs/linear_data_structures/optimal_grouping.py +++ b/pydatastructs/linear_data_structures/optimal_grouping.py @@ -250,4 +250,4 @@ def lookup(position, endIndex, middle): optimal_grouping_rec(process_objects, cost_storage, solution_matrix, maximize_prob, min_compare_len, lookup_index, get_lookup_fn, cost_fn) - return solution_matrix \ No newline at end of file + return solution_matrix From 740f2a9bc5a88f9649b7ff7ac7d90ad1d7052d6e Mon Sep 17 00:00:00 2001 From: pravalikavis Date: Sat, 16 May 2020 16:52:23 +0530 Subject: [PATCH 05/10] moved optimal grouping to algorithms --- .../linear_data_structures/algorithms.py | 247 ++++++++++++++++- .../optimal_grouping.py | 253 ------------------ 2 files changed, 246 insertions(+), 254 deletions(-) delete mode 100644 pydatastructs/linear_data_structures/optimal_grouping.py diff --git a/pydatastructs/linear_data_structures/algorithms.py b/pydatastructs/linear_data_structures/algorithms.py index f5eb2358d..bcb7c5677 100644 --- a/pydatastructs/linear_data_structures/algorithms.py +++ b/pydatastructs/linear_data_structures/algorithms.py @@ -3,13 +3,16 @@ from pydatastructs.utils.misc_util import _check_type, _comp from concurrent.futures import ThreadPoolExecutor from math import log, floor +from types import * +from typing import List __all__ = [ 'merge_sort_parallel', 'brick_sort', 'brick_sort_parallel', 'heapsort', - 'matrix_multiply_parallel' + 'matrix_multiply_parallel', + 'optimal_grouping' ] def _merge(array, sl, el, sr, er, end, comp): @@ -360,3 +363,245 @@ def matrix_multiply_parallel(matrix_1, matrix_2, num_threads): i, j).result() return C + + + + + + +def _get_value_opt_group(matrix, lookup_index: List[int]): + """ + gets a value + """ + return matrix[lookup_index[0]][lookup_index[1]] + + +def _set_value_opt_group(matrix, lookup_index: List[int], value): + """ + sets a value + """ + matrix[lookup_index[0]][lookup_index[1]] = value + + +def _compare_opt_group(maximize: bool, value, compareWith=None): + """ + compares a value with another. if compareWith is None then value is compared with Infinity or -Infinity + parameters + [maximize] if True then the function returns true if value is greater than compareWith and vice versa + """ + if compareWith == None: + if maximize: + compareWith = float('-inf') + else: + compareWith = float('inf') + if maximize: + return value > compareWith + return value < compareWith + + +def _initialize_arrays_opt_group(maximize: bool, rows: int, columns: int): + """ + returns a 2-d array of rows*columns size filled with either Infinity or -Infinity + parameters: + [maximize] + if 'True' fills with -Infinity and vice versa + [rows] + expects a number + [columns] + expects a number + """ + value = float('inf') + if maximize: + value = float('-inf') + return [[value for a in range(0, columns+1)] for a in range(0, rows+1)] + + +def _optimal_grouping_rec(object_arr, cost_storage: List[List[int]], solution_matrix: List[List[int]], maximize_prob: bool, min_compare_len: int, lookup_index: List[int], get_lookup_fn, cost_fn): + """ + Helper function for optimal_grouping function + """ + + # gets the present value at the present index + present_value = _get_value_opt_group(cost_storage, lookup_index) + # return the present value if it is not infinity + if _compare_opt_group(maximize_prob, present_value): + return present_value + + # get the start and end indices where end index depends on the min_compare_len + start_index = lookup_index[0] + end_index = lookup_index[1]+1-(min_compare_len-1) + + if start_index == end_index or start_index > end_index: + cost = cost_fn(object_arr, lookup_index, start_index) + if _compare_opt_group(maximize_prob, cost, present_value): + _set_value_opt_group(cost_storage, lookup_index, cost) + _set_value_opt_group(solution_matrix, lookup_index, start_index) + present_value = cost + + for i in range(start_index, end_index): + + # get indices for left recursion tree + left_rec_indices = get_lookup_fn('before', lookup_index, i) + _test_lookup_function(left_rec_indices, lookup_index) + + cost = _optimal_grouping_rec(object_arr, cost_storage, solution_matrix, maximize_prob, + min_compare_len, left_rec_indices, get_lookup_fn, cost_fn) + + # get indices for right recursion tree + right_rec_indices = get_lookup_fn('after', lookup_index, i) + _test_lookup_function(right_rec_indices, lookup_index) + + cost = cost+_optimal_grouping_rec(object_arr, cost_storage, solution_matrix, maximize_prob, + min_compare_len, right_rec_indices, get_lookup_fn, cost_fn) + + # get cost for present partition + cost = cost+cost_fn(object_arr, lookup_index, i) + + # update the values if this is the best solution until now + if _compare_opt_group(maximize_prob, cost, present_value): + _set_value_opt_group(cost_storage, lookup_index, cost) + _set_value_opt_group(solution_matrix, lookup_index, i) + present_value = cost + + return present_value + + +def _test_lookup_function(lookup_index: List[int], input_index: List[int]): + if lookup_index is None: + raise TypeError( + 'Check lookup_function: returning wrong type should return an array of start and end index') + + if lookup_index.__len__() < 2: + raise ValueError( + 'Check lookup_function:lookup index should at least have 2 integer items, first specifying the start and second specifying the last indices') + + if input_index == lookup_index: + raise RuntimeError( + 'Check lookup_function:verify get_lookup_fn giving same output as input which will lead to infinite loop') + + +def optimal_grouping(process_objects, maximize_prob: bool, min_compare_len: int, lookup_index: List[int], get_lookup_fn, cost_fn): + """ + Description: Optimal Grouping groups given set of objects using the given cost function + + Parameters: + process_objects + accepts array of objects on which the algorithm is supposed to run + maximize_prob + pass True if the algorithm should find maximum value of the cost function otherwise pass False + min_compare_len + a positive number decides to which level of gap the algorithm can maintain while iterating from start to end, + for example-> if minimun length is 2 then it can only iterate if endIndex=startIndex+2 + lookup_index + format-->[start_index,endIndex] algorithm runs from start to end + get_lookup_fn + should return next range of indices + sample -> get_lookup_fn(position, rangeIndices, currentIndex) + position is either 'before' or 'after' + rangeIndices is the present range of index like [start_index,endIndex] + cost_fn + should return the cost + sample -> cost_fn(process_objects,rangeIndices,currentIndex) + + + **Usage examples : + + 1.OPTIMAL BINARY SEARCH TREE + + from binarytree import Node + n = 5 + p = [None, Node(0.15), Node(0.10), Node(0.05), Node(0.10), Node(0.20)] + q = [Node(0.05), Node(0.10), Node(0.05), Node(0.05), Node(0.05), Node(0.10)] + + + def lookup(position, endIndex, middle): + if position is 'before': + return [endIndex[0], middle-1] + else: + return [middle+1, endIndex[1]] + + + def cost(obj, endIndex, middle): + + if(endIndex[1] lookup_index[1]: + raise ValueError( + 'lookup index should at least have 2 integer items, first specifying the start and second specifying the last indices') + + if get_lookup_fn is None or type(get_lookup_fn) is not FunctionType: + raise TypeError( + 'get_lookup_fn cannot be none and should be a function with 3 arguments') + + test_result = get_lookup_fn('before', lookup_index, lookup_index[0]) + if test_result == lookup_index: + raise RuntimeError( + 'verify get_lookup_fn giving same output as input which may lead to infinite loop') + test_result = get_lookup_fn('after', lookup_index, lookup_index[0]) + if test_result == lookup_index: + raise RuntimeError( + 'verify get_lookup_fn giving same output as input which may lead to infinite loop') + + if cost_fn is None or type(cost_fn) is not FunctionType: + raise TypeError( + 'cost_fn cannot be none and should be a function with 3 arguments') + + test_result = cost_fn(process_objects, lookup_index, lookup_index[0]) + try: + int(test_result) + except Exception: + raise TypeError( + 'output for cost function should be any type of number') + if test_result is None: + raise RuntimeError( + 'output for cost function should be any type of number and cannot be None') + + # end of edge cases + + length = lookup_index[1]-lookup_index[0]+1 + + # for storing the computed values (helper array) + cost_storage = _initialize_arrays_opt_group(maximize_prob, length+1, length+1) + # for storing the solutions + solution_matrix = _initialize_arrays_opt_group(maximize_prob, length+1, length+1) + + _optimal_grouping_rec(process_objects, cost_storage, solution_matrix, maximize_prob, + min_compare_len, lookup_index, get_lookup_fn, cost_fn) + return solution_matrix \ No newline at end of file diff --git a/pydatastructs/linear_data_structures/optimal_grouping.py b/pydatastructs/linear_data_structures/optimal_grouping.py deleted file mode 100644 index 4c66395bf..000000000 --- a/pydatastructs/linear_data_structures/optimal_grouping.py +++ /dev/null @@ -1,253 +0,0 @@ -from types import * -from typing import List - - -def get_value(matrix, lookup_index: List[int]): - """ - gets a value - """ - return matrix[lookup_index[0]][lookup_index[1]] - - -def set_value(matrix, lookup_index: List[int], value): - """ - sets a value - """ - matrix[lookup_index[0]][lookup_index[1]] = value - - -def compare(maximize: bool, value, compareWith=None): - """ - compares a value with another. if compareWith is None then value is compared with Infinity or -Infinity - parameters - [maximize] if True then the function returns true if value is greater than compareWith and vice versa - """ - if compareWith == None: - if maximize: - compareWith = float('-inf') - else: - compareWith = float('inf') - if maximize: - return value > compareWith - return value < compareWith - - -def initialize_arrays(maximize: bool, rows: int, columns: int): - """ - returns a 2-d array of rows*columns size filled with either Infinity or -Infinity - parameters: - [maximize] - if 'True' fills with -Infinity and vice versa - [rows] - expects a number - [columns] - expects a number - """ - value = float('inf') - if maximize: - value = float('-inf') - return [[value for a in range(0, columns+1)] for a in range(0, rows+1)] - - -def optimal_grouping_rec(object_arr, cost_storage: List[List[int]], solution_matrix: List[List[int]], maximize_prob: bool, min_compare_len: int, lookup_index: List[int], get_lookup_fn, cost_fn): - """ - Helper function for optimal_grouping function - """ - - # gets the present value at the present index - present_value = get_value(cost_storage, lookup_index) - # return the present value if it is not infinity - if compare(maximize_prob, present_value): - return present_value - - # get the start and end indices where end index depends on the min_compare_len - start_index = lookup_index[0] - end_index = lookup_index[1]+1-(min_compare_len-1) - - if start_index == end_index or start_index > end_index: - cost = cost_fn(object_arr, lookup_index, start_index) - if compare(maximize_prob, cost, present_value): - set_value(cost_storage, lookup_index, cost) - set_value(solution_matrix, lookup_index, start_index) - present_value = cost - - for i in range(start_index, end_index): - - # get indices for left recursion tree - left_rec_indices = get_lookup_fn('before', lookup_index, i) - test_lookup_function(left_rec_indices, lookup_index) - - cost = optimal_grouping_rec(object_arr, cost_storage, solution_matrix, maximize_prob, - min_compare_len, left_rec_indices, get_lookup_fn, cost_fn) - - # get indices for right recursion tree - right_rec_indices = get_lookup_fn('after', lookup_index, i) - test_lookup_function(right_rec_indices, lookup_index) - - cost = cost+optimal_grouping_rec(object_arr, cost_storage, solution_matrix, maximize_prob, - min_compare_len, right_rec_indices, get_lookup_fn, cost_fn) - - # get cost for present partition - cost = cost+cost_fn(object_arr, lookup_index, i) - - # update the values if this is the best solution until now - if compare(maximize_prob, cost, present_value): - set_value(cost_storage, lookup_index, cost) - set_value(solution_matrix, lookup_index, i) - present_value = cost - - return present_value - - -def test_lookup_function(lookup_index: List[int], input_index: List[int]): - if lookup_index is None: - raise TypeError( - 'Check lookup_function: returning wrong type should return an array of start and end index') - - if lookup_index.__len__() < 2: - raise ValueError( - 'Check lookup_function:lookup index should at least have 2 integer items, first specifying the start and second specifying the last indices') - - if input_index == lookup_index: - raise RuntimeError( - 'Check lookup_function:verify get_lookup_fn giving same output as input which will lead to infinite loop') - - -def optimal_grouping(process_objects, maximize_prob: bool, min_compare_len: int, lookup_index: List[int], get_lookup_fn, cost_fn): - """ - Description: Optimal Grouping groups given set of objects using the given cost function - - Parameters: - object_arr - accepts array of objects on which the algorithm is supposed to run - maximize_prob - pass True if the algorithm should find maximum value of the cost function otherwise pass False - min_compare_len - a positive number decides to which level of gap the algorithm can maintain while iterating from start to end, - for example-> if minimun length is 2 then it can only iterate if endIndex=startIndex+2 - lookup_index - format-->[start_index,endIndex] algorithm runs from start to end - get_lookup_fn - should return next range of indices - sample -> get_lookup_fn(position, rangeIndices, currentIndex) - position is either 'before' or 'after' - rangeIndices is the present range of index like [start_index,endIndex] - cost_fn - should return the cost - sample -> cost_fn(object_arr,rangeIndices,currentIndex) - - - **Usage examples : - - 1.OPTIMAL BINARY SEARCH TREE - - from binarytree import Node - n = 5 - p = [None, Node(0.15), Node(0.10), Node(0.05), Node(0.10), Node(0.20)] - q = [Node(0.05), Node(0.10), Node(0.05), Node(0.05), Node(0.05), Node(0.10)] - - - def lookup(position, endIndex, middle): - if position is 'before': - return [endIndex[0], middle-1] - else: - return [middle+1, endIndex[1]] - - - def cost(obj, endIndex, middle): - - if(endIndex[1] lookup_index[1]: - raise ValueError( - 'lookup index should at least have 2 integer items, first specifying the start and second specifying the last indices') - - if get_lookup_fn is None or type(get_lookup_fn) is not FunctionType: - raise TypeError( - 'get_lookup_fn cannot be none and should be a function with 3 arguments') - - test_result = get_lookup_fn('before', lookup_index, lookup_index[0]) - if test_result == lookup_index: - raise RuntimeError( - 'verify get_lookup_fn giving same output as input which may lead to infinite loop') - test_result = get_lookup_fn('after', lookup_index, lookup_index[0]) - if test_result == lookup_index: - raise RuntimeError( - 'verify get_lookup_fn giving same output as input which may lead to infinite loop') - - if cost_fn is None or type(cost_fn) is not FunctionType: - raise TypeError( - 'cost_fn cannot be none and should be a function with 3 arguments') - - test_result = cost_fn(process_objects, lookup_index, lookup_index[0]) - try: - int(test_result) - except Exception: - raise TypeError( - 'output for cost function should be any type of number') - if test_result is None: - raise RuntimeError( - 'output for cost function should be any type of number and cannot be None') - - # end of edge cases - - length = lookup_index[1]-lookup_index[0]+1 - - # for storing the computed values (helper array) - cost_storage = initialize_arrays(maximize_prob, length+1, length+1) - # for storing the solutions - solution_matrix = initialize_arrays(maximize_prob, length+1, length+1) - - optimal_grouping_rec(process_objects, cost_storage, solution_matrix, maximize_prob, - min_compare_len, lookup_index, get_lookup_fn, cost_fn) - return solution_matrix \ No newline at end of file From 03e5c3fb665a1a9cf77a01114d59e73fd8e0fa06 Mon Sep 17 00:00:00 2001 From: pravalikavis Date: Sat, 16 May 2020 17:28:54 +0530 Subject: [PATCH 06/10] eof --- pydatastructs/linear_data_structures/algorithms.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pydatastructs/linear_data_structures/algorithms.py b/pydatastructs/linear_data_structures/algorithms.py index bcb7c5677..bf22e2cbb 100644 --- a/pydatastructs/linear_data_structures/algorithms.py +++ b/pydatastructs/linear_data_structures/algorithms.py @@ -604,4 +604,5 @@ def lookup(position, endIndex, middle): _optimal_grouping_rec(process_objects, cost_storage, solution_matrix, maximize_prob, min_compare_len, lookup_index, get_lookup_fn, cost_fn) - return solution_matrix \ No newline at end of file + return solution_matrix + \ No newline at end of file From 6299dfa494318e34b140fff7d32ae6dc0e187e26 Mon Sep 17 00:00:00 2001 From: pravalikavis Date: Wed, 27 May 2020 23:59:45 +0530 Subject: [PATCH 07/10] test cases for optimal grouping --- .../tests/test_algorithms.py | 45 +++++++++++++++++-- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/pydatastructs/linear_data_structures/tests/test_algorithms.py b/pydatastructs/linear_data_structures/tests/test_algorithms.py index 1fc24e8ea..13fd78466 100644 --- a/pydatastructs/linear_data_structures/tests/test_algorithms.py +++ b/pydatastructs/linear_data_structures/tests/test_algorithms.py @@ -1,10 +1,11 @@ from pydatastructs import ( merge_sort_parallel, DynamicOneDimensionalArray, OneDimensionalArray, brick_sort, brick_sort_parallel, - heapsort, matrix_multiply_parallel) + heapsort, matrix_multiply_parallel, optimal_grouping) from pydatastructs.utils.raises_util import raises import random + def _test_common_sort(sort, *args, **kwargs): random.seed(1000) @@ -41,25 +42,32 @@ def _test_common_sort(sort, *args, **kwargs): sort(arr, *args, **kwargs, start=2, end=5) assert arr._data == expected_arr + def test_merge_sort_parallel(): _test_common_sort(merge_sort_parallel, num_threads=5) + def test_brick_sort(): _test_common_sort(brick_sort) + def test_brick_sort_parallel(): _test_common_sort(brick_sort_parallel, num_threads=3) + def test_heapsort(): _test_common_sort(heapsort) + def test_matrix_multiply_parallel(): ODA = OneDimensionalArray expected_result = [[3, 3, 3], [1, 2, 1], [2, 2, 2]] - I = ODA(ODA, [ODA(int, [1, 1, 0]), ODA(int, [0, 1, 0]), ODA(int, [0, 0, 1])]) - J = ODA(ODA, [ODA(int, [2, 1, 2]), ODA(int, [1, 2, 1]), ODA(int, [2, 2, 2])]) + I = ODA(ODA, [ODA(int, [1, 1, 0]), ODA( + int, [0, 1, 0]), ODA(int, [0, 0, 1])]) + J = ODA(ODA, [ODA(int, [2, 1, 2]), ODA( + int, [1, 2, 1]), ODA(int, [2, 2, 2])]) output = matrix_multiply_parallel(I, J, num_threads=5) assert expected_result == output @@ -70,9 +78,38 @@ def test_matrix_multiply_parallel(): I = [[1, 1, 0, 1], [0, 1, 0, 1], [0, 0, 1, 1]] J = [[2, 1, 2], [1, 2, 1], [2, 2, 2]] - assert raises(ValueError, lambda: matrix_multiply_parallel(I, J, num_threads=5)) + assert raises(ValueError, lambda: matrix_multiply_parallel( + I, J, num_threads=5)) I = [[1, 1, 0], [0, 1, 0], [0, 0, 1]] J = [[2, 1, 2], [1, 2, 1], [2, 2, 2]] output = matrix_multiply_parallel(I, J, num_threads=1) assert expected_result == output + + +def test_optimal_grouping(): + #test case1: + def cost(matrix, endIndex, middle): + + if endIndex[0] == endIndex[1]: + return 0 + return matrix[endIndex[0]-1]*matrix[middle]*matrix[endIndex[1]] + + def lookup(position, endIndex, middle): + if position is 'before': + return [endIndex[0], middle] + else: + return [middle+1, endIndex[1]] + expected_result = [[float('inf'), float('inf'), float('inf'), float('inf'), float('inf'), float('inf'), float('inf'), float('inf')], + [float('inf'), 1, 1, 1, 3, 3, 3, float('inf')], + [float('inf'), float('inf'), 2, 2, 3, 3, 3, float('inf')], + [float('inf'), float('inf'), float('inf'), 3, 3, 3, 3, float('inf')], + [float('inf'), float('inf'), float('inf'), float('inf'), 4, 4, 5, float('inf')], + [float('inf'), float('inf'), float('inf'), float('inf'), float('inf'), 5, 5, float('inf')], + [float('inf'), float('inf'), float('inf'), float('inf'), float('inf'), float('inf'), 6, float('inf')], + [float('inf'), float('inf'), float('inf'), float('inf'), float('inf'), float('inf'), float('inf'), float('inf')]] + assert expected_result == optimal_grouping([30, 35, 15, 5, 10, 20, 25], False, 2, [1, 6], lookup, cost) + + #test case2: + expected_result = [[0, float('inf'), float('inf')], [float('inf'), float('inf'), float('inf')], [float('inf'), float('inf'), float('inf')]] + assert expected_result == optimal_grouping([], False, 2, [0,0], lookup, cost) From 57129c503f7d3f9549c44ae7255cbebff258f764 Mon Sep 17 00:00:00 2001 From: pravalikavis Date: Thu, 28 May 2020 00:01:41 +0530 Subject: [PATCH 08/10] removed spaces and type errors --- .../linear_data_structures/__init__.py | 3 +- .../linear_data_structures/algorithms.py | 128 +++++------------- 2 files changed, 35 insertions(+), 96 deletions(-) diff --git a/pydatastructs/linear_data_structures/__init__.py b/pydatastructs/linear_data_structures/__init__.py index 602dc6df4..8ba1c68d5 100644 --- a/pydatastructs/linear_data_structures/__init__.py +++ b/pydatastructs/linear_data_structures/__init__.py @@ -26,6 +26,7 @@ brick_sort, brick_sort_parallel, heapsort, - matrix_multiply_parallel + matrix_multiply_parallel, + optimal_grouping ) __all__.extend(algorithms.__all__) diff --git a/pydatastructs/linear_data_structures/algorithms.py b/pydatastructs/linear_data_structures/algorithms.py index bf22e2cbb..7db2a2838 100644 --- a/pydatastructs/linear_data_structures/algorithms.py +++ b/pydatastructs/linear_data_structures/algorithms.py @@ -3,7 +3,6 @@ from pydatastructs.utils.misc_util import _check_type, _comp from concurrent.futures import ThreadPoolExecutor from math import log, floor -from types import * from typing import List __all__ = [ @@ -349,8 +348,8 @@ def matrix_multiply_parallel(matrix_1, matrix_2, num_threads): row_matrix_2, col_matrix_2 = len(matrix_2), len(matrix_2[0]) if col_matrix_1 != row_matrix_2: - raise ValueError("Matrix size mismatch: %s * %s"%( - (row_matrix_1, col_matrix_1), (row_matrix_2, col_matrix_2))) + raise ValueError("Matrix size mismatch: %s * %s" % ( + (row_matrix_1, col_matrix_1), (row_matrix_2, col_matrix_2))) C = [[None for i in range(col_matrix_1)] for j in range(row_matrix_2)] @@ -364,32 +363,13 @@ def matrix_multiply_parallel(matrix_1, matrix_2, num_threads): return C - - - - - -def _get_value_opt_group(matrix, lookup_index: List[int]): - """ - gets a value - """ - return matrix[lookup_index[0]][lookup_index[1]] - - -def _set_value_opt_group(matrix, lookup_index: List[int], value): - """ - sets a value - """ - matrix[lookup_index[0]][lookup_index[1]] = value - - -def _compare_opt_group(maximize: bool, value, compareWith=None): +def _compare_opt_group(maximize, value, compareWith=None): """ compares a value with another. if compareWith is None then value is compared with Infinity or -Infinity parameters [maximize] if True then the function returns true if value is greater than compareWith and vice versa """ - if compareWith == None: + if compareWith is None: if maximize: compareWith = float('-inf') else: @@ -398,15 +378,14 @@ def _compare_opt_group(maximize: bool, value, compareWith=None): return value > compareWith return value < compareWith - -def _initialize_arrays_opt_group(maximize: bool, rows: int, columns: int): +def _initialize_arrays_opt_group(maximize, rows, columns): """ returns a 2-d array of rows*columns size filled with either Infinity or -Infinity parameters: [maximize] if 'True' fills with -Infinity and vice versa [rows] - expects a number + expects a number [columns] expects a number """ @@ -415,14 +394,13 @@ def _initialize_arrays_opt_group(maximize: bool, rows: int, columns: int): value = float('-inf') return [[value for a in range(0, columns+1)] for a in range(0, rows+1)] - -def _optimal_grouping_rec(object_arr, cost_storage: List[List[int]], solution_matrix: List[List[int]], maximize_prob: bool, min_compare_len: int, lookup_index: List[int], get_lookup_fn, cost_fn): +def _optimal_grouping_rec(object_arr, cost_storage, solution_matrix, maximize_prob, min_compare_len, lookup_index, get_lookup_fn, cost_fn): """ Helper function for optimal_grouping function """ # gets the present value at the present index - present_value = _get_value_opt_group(cost_storage, lookup_index) + present_value = cost_storage[lookup_index[0]][lookup_index[1]] # return the present value if it is not infinity if _compare_opt_group(maximize_prob, present_value): return present_value @@ -431,80 +409,67 @@ def _optimal_grouping_rec(object_arr, cost_storage: List[List[int]], solution_ma start_index = lookup_index[0] end_index = lookup_index[1]+1-(min_compare_len-1) - if start_index == end_index or start_index > end_index: + if start_index is end_index or start_index > end_index: cost = cost_fn(object_arr, lookup_index, start_index) if _compare_opt_group(maximize_prob, cost, present_value): - _set_value_opt_group(cost_storage, lookup_index, cost) - _set_value_opt_group(solution_matrix, lookup_index, start_index) + cost_storage[lookup_index[0]][lookup_index[1]] = cost + solution_matrix[lookup_index[0]][lookup_index[1]] = start_index present_value = cost for i in range(start_index, end_index): # get indices for left recursion tree left_rec_indices = get_lookup_fn('before', lookup_index, i) - _test_lookup_function(left_rec_indices, lookup_index) cost = _optimal_grouping_rec(object_arr, cost_storage, solution_matrix, maximize_prob, - min_compare_len, left_rec_indices, get_lookup_fn, cost_fn) + min_compare_len, left_rec_indices, get_lookup_fn, cost_fn) # get indices for right recursion tree right_rec_indices = get_lookup_fn('after', lookup_index, i) - _test_lookup_function(right_rec_indices, lookup_index) cost = cost+_optimal_grouping_rec(object_arr, cost_storage, solution_matrix, maximize_prob, - min_compare_len, right_rec_indices, get_lookup_fn, cost_fn) + min_compare_len, right_rec_indices, get_lookup_fn, cost_fn) # get cost for present partition cost = cost+cost_fn(object_arr, lookup_index, i) # update the values if this is the best solution until now if _compare_opt_group(maximize_prob, cost, present_value): - _set_value_opt_group(cost_storage, lookup_index, cost) - _set_value_opt_group(solution_matrix, lookup_index, i) + cost_storage[lookup_index[0]][lookup_index[1]] = cost + solution_matrix[lookup_index[0]][lookup_index[1]] = i present_value = cost return present_value - -def _test_lookup_function(lookup_index: List[int], input_index: List[int]): - if lookup_index is None: - raise TypeError( - 'Check lookup_function: returning wrong type should return an array of start and end index') - - if lookup_index.__len__() < 2: - raise ValueError( - 'Check lookup_function:lookup index should at least have 2 integer items, first specifying the start and second specifying the last indices') - - if input_index == lookup_index: - raise RuntimeError( - 'Check lookup_function:verify get_lookup_fn giving same output as input which will lead to infinite loop') - - -def optimal_grouping(process_objects, maximize_prob: bool, min_compare_len: int, lookup_index: List[int], get_lookup_fn, cost_fn): +def optimal_grouping(process_objects, maximize_prob, min_compare_len, lookup_index, get_lookup_fn, cost_fn): """ - Description: Optimal Grouping groups given set of objects using the given cost function + Description + =========== + Optimal Grouping groups given set of objects using the given cost function - Parameters: + Parameters + ========== process_objects accepts array of objects on which the algorithm is supposed to run - maximize_prob + maximize_prob pass True if the algorithm should find maximum value of the cost function otherwise pass False - min_compare_len + min_compare_len a positive number decides to which level of gap the algorithm can maintain while iterating from start to end, for example-> if minimun length is 2 then it can only iterate if endIndex=startIndex+2 - lookup_index + lookup_index format-->[start_index,endIndex] algorithm runs from start to end get_lookup_fn should return next range of indices sample -> get_lookup_fn(position, rangeIndices, currentIndex) - position is either 'before' or 'after' + position is either 'before' or 'after' rangeIndices is the present range of index like [start_index,endIndex] cost_fn - should return the cost + should return the cost sample -> cost_fn(process_objects,rangeIndices,currentIndex) - **Usage examples : + Usage examples + ============== 1.OPTIMAL BINARY SEARCH TREE @@ -565,44 +530,17 @@ def lookup(position, endIndex, middle): if lookup_index.__len__() < 2 or lookup_index[0] > lookup_index[1]: raise ValueError( 'lookup index should at least have 2 integer items, first specifying the start and second specifying the last indices') - - if get_lookup_fn is None or type(get_lookup_fn) is not FunctionType: - raise TypeError( - 'get_lookup_fn cannot be none and should be a function with 3 arguments') - - test_result = get_lookup_fn('before', lookup_index, lookup_index[0]) - if test_result == lookup_index: - raise RuntimeError( - 'verify get_lookup_fn giving same output as input which may lead to infinite loop') - test_result = get_lookup_fn('after', lookup_index, lookup_index[0]) - if test_result == lookup_index: - raise RuntimeError( - 'verify get_lookup_fn giving same output as input which may lead to infinite loop') - - if cost_fn is None or type(cost_fn) is not FunctionType: - raise TypeError( - 'cost_fn cannot be none and should be a function with 3 arguments') - - test_result = cost_fn(process_objects, lookup_index, lookup_index[0]) - try: - int(test_result) - except Exception: - raise TypeError( - 'output for cost function should be any type of number') - if test_result is None: - raise RuntimeError( - 'output for cost function should be any type of number and cannot be None') - # end of edge cases length = lookup_index[1]-lookup_index[0]+1 # for storing the computed values (helper array) - cost_storage = _initialize_arrays_opt_group(maximize_prob, length+1, length+1) + cost_storage = _initialize_arrays_opt_group( + maximize_prob, length+1, length+1) # for storing the solutions - solution_matrix = _initialize_arrays_opt_group(maximize_prob, length+1, length+1) + solution_matrix = _initialize_arrays_opt_group( + maximize_prob, length+1, length+1) _optimal_grouping_rec(process_objects, cost_storage, solution_matrix, maximize_prob, - min_compare_len, lookup_index, get_lookup_fn, cost_fn) + min_compare_len, lookup_index, get_lookup_fn, cost_fn) return solution_matrix - \ No newline at end of file From 20badc9ac86d7019ed6f8b4d59193b717c5b6a36 Mon Sep 17 00:00:00 2001 From: pravalikavis Date: Thu, 28 May 2020 00:13:13 +0530 Subject: [PATCH 09/10] reverting format code --- .../tests/test_algorithms.py | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/pydatastructs/linear_data_structures/tests/test_algorithms.py b/pydatastructs/linear_data_structures/tests/test_algorithms.py index 13fd78466..bf0a6debb 100644 --- a/pydatastructs/linear_data_structures/tests/test_algorithms.py +++ b/pydatastructs/linear_data_structures/tests/test_algorithms.py @@ -5,7 +5,6 @@ from pydatastructs.utils.raises_util import raises import random - def _test_common_sort(sort, *args, **kwargs): random.seed(1000) @@ -42,32 +41,25 @@ def _test_common_sort(sort, *args, **kwargs): sort(arr, *args, **kwargs, start=2, end=5) assert arr._data == expected_arr - def test_merge_sort_parallel(): _test_common_sort(merge_sort_parallel, num_threads=5) - def test_brick_sort(): _test_common_sort(brick_sort) - def test_brick_sort_parallel(): _test_common_sort(brick_sort_parallel, num_threads=3) - def test_heapsort(): _test_common_sort(heapsort) - def test_matrix_multiply_parallel(): ODA = OneDimensionalArray expected_result = [[3, 3, 3], [1, 2, 1], [2, 2, 2]] - I = ODA(ODA, [ODA(int, [1, 1, 0]), ODA( - int, [0, 1, 0]), ODA(int, [0, 0, 1])]) - J = ODA(ODA, [ODA(int, [2, 1, 2]), ODA( - int, [1, 2, 1]), ODA(int, [2, 2, 2])]) + I = ODA(ODA, [ODA(int, [1, 1, 0]), ODA(int, [0, 1, 0]), ODA(int, [0, 0, 1])]) + J = ODA(ODA, [ODA(int, [2, 1, 2]), ODA(int, [1, 2, 1]), ODA(int, [2, 2, 2])]) output = matrix_multiply_parallel(I, J, num_threads=5) assert expected_result == output @@ -78,15 +70,13 @@ def test_matrix_multiply_parallel(): I = [[1, 1, 0, 1], [0, 1, 0, 1], [0, 0, 1, 1]] J = [[2, 1, 2], [1, 2, 1], [2, 2, 2]] - assert raises(ValueError, lambda: matrix_multiply_parallel( - I, J, num_threads=5)) + assert raises(ValueError, lambda: matrix_multiply_parallel(I, J, num_threads=5)) I = [[1, 1, 0], [0, 1, 0], [0, 0, 1]] J = [[2, 1, 2], [1, 2, 1], [2, 2, 2]] output = matrix_multiply_parallel(I, J, num_threads=1) assert expected_result == output - def test_optimal_grouping(): #test case1: def cost(matrix, endIndex, middle): From ed858a31b4e2de57a9f2cf7881b0c60b8c87bdfc Mon Sep 17 00:00:00 2001 From: pravalikavis Date: Thu, 28 May 2020 22:18:52 +0530 Subject: [PATCH 10/10] removed space --- pydatastructs/linear_data_structures/algorithms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pydatastructs/linear_data_structures/algorithms.py b/pydatastructs/linear_data_structures/algorithms.py index 7db2a2838..0bf9b6aa8 100644 --- a/pydatastructs/linear_data_structures/algorithms.py +++ b/pydatastructs/linear_data_structures/algorithms.py @@ -349,7 +349,7 @@ def matrix_multiply_parallel(matrix_1, matrix_2, num_threads): if col_matrix_1 != row_matrix_2: raise ValueError("Matrix size mismatch: %s * %s" % ( - (row_matrix_1, col_matrix_1), (row_matrix_2, col_matrix_2))) + (row_matrix_1, col_matrix_1), (row_matrix_2, col_matrix_2))) C = [[None for i in range(col_matrix_1)] for j in range(row_matrix_2)]