Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@
__pycache__/
venv
test.py
!lcs_experiment_results.csv
!lcs_complexity_summary.csv
17 changes: 13 additions & 4 deletions complexity/generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,19 @@ def generate(self, size: int = 1) -> int:

#TODO:add the string geneation logic
class StringGenerator(DataGenerator):
def __init__(self,alphabit=['A','B','C']):
pass
def generate(self, size: int = 1) -> int:
pass
def __init__(self, alphabet=None, string_length: int = 5):
if alphabet is None:
alphabet = ['A', 'B', 'C']
self.alphabet = alphabet
self.string_length = string_length

def generate(self, size: int = 1) -> str:
return "".join(random.choices(self.alphabet, k=size))

def generate_pair(self, len1: int, len2: int) -> tuple[str, str]:
first_string = "".join(random.choices(self.alphabet, k=len1))
second_string = "".join(random.choices(self.alphabet, k=len2))
return first_string, second_string

class GraphGenerator(DataGenerator):
def __init__(self, directed: bool = False, weighted: bool = True):
Expand Down
9 changes: 8 additions & 1 deletion complexity/profiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@ def profile(self, func, *args, **kwargs):
"memory": mem_after - mem_before,
}

logs.update(result._asdict())
if hasattr(result, '_asdict'):
logs.update(result._asdict())
elif isinstance(result, dict):
logs.update(result)
elif isinstance(result, (list, tuple)):
logs['result'] = result
else:
logs['result'] = str(result)

return logs
110 changes: 110 additions & 0 deletions examples/common_string.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,114 @@
#TODO: put the code here
import pandas as pd
from tqdm import tqdm
import numpy as np
import sys
from pathlib import Path

# Add the parent directory to the system path
sys.path.append(str(Path(__file__).parent.parent))

from complexity.generator import StringGenerator
from complexity.profiler import TimeAndSpaceProfiler
from complexity.analyser import ComplexityAnalyzer

def compute_lcs_recursive(str1, str2):
if not str1 or not str2:
return ""
if str1[-1] == str2[-1]:
return compute_lcs_recursive(str1[:-1], str2[:-1]) + str1[-1]
option1 = compute_lcs_recursive(str1[:-1], str2)
option2 = compute_lcs_recursive(str1, str2[:-1])
return option1 if len(option1) > len(option2) else option2

def compute_lcs_memoized(str1, str2, cache=None):
if cache is None:
cache = {}

key = (len(str1), len(str2))
if key in cache:
return cache[key]

if not str1 or not str2:
result = ""
elif str1[-1] == str2[-1]:
result = compute_lcs_memoized(str1[:-1], str2[:-1], cache) + str1[-1]
else:
option1 = compute_lcs_memoized(str1[:-1], str2, cache)
option2 = compute_lcs_memoized(str1, str2[:-1], cache)
result = option1 if len(option1) > len(option2) else option2

cache[key] = result
return result

def compute_lcs_dynamic(str1, str2):
rows, cols = len(str1), len(str2)
dp_table = [["" for _ in range(cols + 1)] for _ in range(rows + 1)]

for i in range(1, rows + 1):
for j in range(1, cols + 1):
if str1[i - 1] == str2[j - 1]:
dp_table[i][j] = dp_table[i - 1][j - 1] + str1[i - 1]
else:
dp_table[i][j] = dp_table[i - 1][j] if len(dp_table[i - 1][j]) > len(dp_table[i][j - 1]) else dp_table[i][j - 1]

return dp_table[rows][cols]

# Initialize components
profiler = TimeAndSpaceProfiler()
generator = StringGenerator(alphabet=['A', 'B', 'C', 'D'], string_length=20)

# Prepare string pairs for experiments
lengths = [3, 5, 6, 8, 10, 13, 15, 20]
pair_list = [(generator.generate(size), generator.generate(size)) for size in lengths]

# Store results
experiment_data = []
print("Executing LCS algorithms...")

with tqdm(total=len(pair_list) * 3, desc="Processing pairs") as progress_bar:
for str_a, str_b in pair_list:
for method, implementation in zip(
["recursive", "memoized", "dynamic"],
[compute_lcs_recursive, lambda x, y: compute_lcs_memoized(x, y), compute_lcs_dynamic]
):
metrics = profiler.profile(implementation, str_a, str_b)
experiment_data.append({
'algorithm': method,
'string1': str_a,
'string2': str_b,
**metrics
})
progress_bar.update(1)

print("\nEvaluating complexity...")

# Analyze algorithmic complexity
analyzer = ComplexityAnalyzer()
complexity_results = []

for method in ["recursive", "memoized", "dynamic"]:
data_subset = [entry for entry in experiment_data if entry['algorithm'] == method]
input_sizes = [len(entry['string1']) for entry in data_subset]
times_taken = [entry['time'] for entry in data_subset]

best_fit, complexity_model = analyzer.get_best_fit(np.array(input_sizes), np.array(times_taken))
complexity_results.append({
'algorithm': method,
'complexity': best_fit
})

print("\nSaving output files...")

# Save data to CSV
experiment_df = pd.DataFrame(experiment_data)
complexity_df = pd.DataFrame(complexity_results)

experiment_df.to_csv('lcs_experiment_results.csv', index=False)
complexity_df.to_csv('lcs_complexity_summary.csv', index=False)

print("\nOutput saved as 'lcs_experiment_results.csv' and 'lcs_complexity_summary.csv'")


print('Please use the complexity library')

4 changes: 4 additions & 0 deletions lcs_complexity_summary.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
algorithm,complexity
recursive,O(n^3)
memoized,O(1)
dynamic,O(1)
25 changes: 25 additions & 0 deletions lcs_experiment_results.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
algorithm,string1,string2,function,time,memory,result
recursive,CCA,ACB,compute_lcs_recursive,0.20189785957336426,0.0,A
memoized,CCA,ACB,<lambda>,0.20171284675598145,0.0,A
dynamic,CCA,ACB,compute_lcs_dynamic,0.20084071159362793,0.0,A
recursive,DABAB,BDDCD,compute_lcs_recursive,0.2013533115386963,0.0,B
memoized,DABAB,BDDCD,<lambda>,0.20131587982177734,0.0,B
dynamic,DABAB,BDDCD,compute_lcs_dynamic,0.2102956771850586,0.0,B
recursive,BDABBC,CAAADA,compute_lcs_recursive,0.20184087753295898,0.0,DA
memoized,BDABBC,CAAADA,<lambda>,0.20116186141967773,0.0,DA
dynamic,BDABBC,CAAADA,compute_lcs_dynamic,0.20172929763793945,0.0,DA
recursive,CDCDAABC,BAADABAB,compute_lcs_recursive,0.20358586311340332,0.0,DAAB
memoized,CDCDAABC,BAADABAB,<lambda>,0.20259666442871094,0.00390625,DAAB
dynamic,CDCDAABC,BAADABAB,compute_lcs_dynamic,0.20250487327575684,0.0,DAAB
recursive,DDBDBCCADC,BBADACBDDA,compute_lcs_recursive,0.20508527755737305,0.0,BBADC
memoized,DDBDBCCADC,BBADACBDDA,<lambda>,0.20095276832580566,0.0,BBADC
dynamic,DDBDBCCADC,BBADACBDDA,compute_lcs_dynamic,0.20252537727355957,0.0,BBADC
recursive,ABDBDBDBAADAA,DDBCADADADBAC,compute_lcs_recursive,0.2434704303741455,0.0,DDBAADAA
memoized,ABDBDBDBAADAA,DDBCADADADBAC,<lambda>,0.20167183876037598,0.00390625,DDBAADAA
dynamic,ABDBDBDBAADAA,DDBCADADADBAC,compute_lcs_dynamic,0.20750832557678223,0.0,DDBAADAA
recursive,CCDDAAADACAABCC,ABCDCBBCDBDADCB,compute_lcs_recursive,1.4468235969543457,0.0,CCDDADCB
memoized,CCDDAAADACAABCC,ABCDCBBCDBDADCB,<lambda>,0.2023630142211914,0.01171875,CCDDADCB
dynamic,CCDDAAADACAABCC,ABCDCBBCDBDADCB,compute_lcs_dynamic,0.2027440071105957,0.00390625,CCDDADCB
recursive,CDACDBCAACABBCCCBCAD,ABACBCBBCDCADCBACADA,compute_lcs_recursive,7.90017557144165,0.00390625,ABACBBCCCBCAD
memoized,CDACDBCAACABBCCCBCAD,ABACBCBBCDCADCBACADA,<lambda>,0.20429539680480957,0.0078125,ABACBBCCCBCAD
dynamic,CDACDBCAACABBCCCBCAD,ABACBCBBCDCADCBACADA,compute_lcs_dynamic,0.20169329643249512,0.0078125,ABACBBCCCBCAD