Skip to content

Commit 6824ec0

Browse files
authored
Add files via upload
1 parent 68f2b73 commit 6824ec0

File tree

3 files changed

+175
-0
lines changed

3 files changed

+175
-0
lines changed

css.py

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
import re
2+
import pandas as pd
3+
import networkx as nx
4+
import matplotlib.pyplot as plt
5+
import argparse
6+
7+
def main(file_path, verbose=False): # Add verbose as a parameter with a default value
8+
try:
9+
df = pd.read_excel(file_path)
10+
except FileNotFoundError:
11+
print(f"Error: The file '{file_path}' was not found. Please ensure it exists in the specified directory.")
12+
exit()
13+
except ValueError as e:
14+
print(f"Error: Unable to read the file. Make sure it's a valid Excel file. Details: {e}")
15+
exit()
16+
17+
# Check if the DataFrame is empty
18+
if df.empty:
19+
print("Error: The provided Excel file is empty.")
20+
exit()
21+
22+
if df.isnull().all().all():
23+
print("Error: The Excel file contains no valid data.")
24+
exit()
25+
26+
# Ensure columns have valid names
27+
if df.columns.duplicated().any():
28+
print("Error: The Excel file contains duplicate transaction names in columns.")
29+
exit()
30+
31+
if any(col == '' for col in df.columns):
32+
print("Error: One or more column names in the Excel file are empty.")
33+
exit()
34+
35+
index = 0
36+
temp = 0
37+
38+
# transaction : other transactions
39+
other = dict()
40+
transactions = [trnsc_name for trnsc_name in df]
41+
n_transactions = len(transactions)
42+
43+
for transaction in transactions:
44+
other[transaction] = []
45+
46+
for transaction in transactions:
47+
for othr in range(n_transactions - 1):
48+
other[transaction].append(transactions[((temp + 1) % n_transactions)])
49+
temp += 1
50+
index += 1
51+
temp = index
52+
53+
# Serial schedule corresponding to each transaction
54+
column_wise_tnrcs = {}
55+
for l in range(0, n_transactions):
56+
column_wise_tnrcs[transactions[l]] = []
57+
58+
temp = 0
59+
for l in range(0, n_transactions):
60+
column_wise_tnrcs[transactions[l]].extend(list(filter(lambda x: x != '', df[transactions[temp]].dropna().tolist())))
61+
temp += 1
62+
63+
# Nodes of dependency graph
64+
nodes = []
65+
# Pairs of transactions having conflict
66+
conflicting_nodes = []
67+
conflicting_pairs = {'r': ['w'],
68+
'w': ['w', 'r']}
69+
70+
# Finding conflicting pairs
71+
for index, row in df.iterrows():
72+
for col_name, operation in row.items():
73+
if pd.notna(operation):
74+
match = re.match(r'([rw])\((\w+)\)', operation)
75+
if not match:
76+
print(f"Error: Invalid operation format '{operation}' in column '{col_name}'. Expected 'r(var)' or 'w(var)'.")
77+
exit()
78+
op = match.group(1) # operation
79+
var = match.group(2) # variable
80+
if op == 'r':
81+
conflict_rw = conflicting_pairs[op][0] + '(' + var + ')'
82+
if verbose: # Use the verbose flag to control output
83+
print(f"Transaction {col_name}'s conflicting pair for operation {operation} is {conflict_rw}")
84+
for other_transactions in range(n_transactions - 1):
85+
if conflict_rw in column_wise_tnrcs[other[col_name][other_transactions]]:
86+
nodes.append(col_name)
87+
conflicting_nodes.append(tuple([col_name, other[col_name][other_transactions]]))
88+
column_wise_tnrcs[col_name].remove(operation)
89+
else:
90+
conflict_ww = conflicting_pairs[op][0] + '(' + var + ')'
91+
conflict_wr = conflicting_pairs[op][1] + '(' + var + ')'
92+
if verbose: # Use the verbose flag to control output
93+
print(f"Transaction {col_name}'s conflicting pair for operation {operation} is {conflict_ww} and {conflict_wr}")
94+
95+
for other_transactions in range(n_transactions - 1):
96+
if conflict_ww in column_wise_tnrcs[other[col_name][other_transactions]]:
97+
nodes.append(col_name)
98+
conflicting_nodes.append(tuple([col_name, other[col_name][other_transactions]]))
99+
column_wise_tnrcs[col_name].remove(operation)
100+
101+
for other_transactions in range(n_transactions - 1):
102+
if conflict_wr in column_wise_tnrcs[other[col_name][other_transactions]]:
103+
nodes.append(col_name)
104+
conflicting_nodes.append(tuple([col_name, other[col_name][other_transactions]]))
105+
106+
# Building dependency graph
107+
G = nx.DiGraph()
108+
G.add_nodes_from(nodes)
109+
G.add_edges_from(conflicting_nodes)
110+
111+
# Detect if the graph has cycles
112+
try:
113+
cycles = list(nx.simple_cycles(G))
114+
if cycles:
115+
print("Detected cycles:", cycles)
116+
print("Therefore, the given schedule is not conflict serializable.")
117+
else:
118+
print("No cycles detected. The given schedule is conflict serializable.")
119+
try:
120+
topological_order = list(nx.topological_sort(G))
121+
print("Order of serializability:", topological_order)
122+
except nx.NetworkXUnfeasible:
123+
print("Error: Topological sorting failed despite no cycles detected.")
124+
except Exception as e:
125+
print(f"Unexpected error during cycle detection or topological sorting: {e}")
126+
127+
# Draw the graph
128+
if not G.nodes or not G.edges:
129+
print("Warning: The dependency graph is empty; nothing to plot.")
130+
else:
131+
pos = nx.spring_layout(G) # positions for all nodes
132+
nx.draw(G, pos, with_labels=True, node_size=3000, node_color='lightblue', font_size=12, font_weight='bold', arrowsize=20)
133+
134+
# Add text on the plot to indicate if a cycle exists
135+
cycle_text = "Cycle Exists, NOT CSS" if cycles else "No Cycle, CSS"
136+
text_color = 'red' if cycles else 'green'
137+
138+
# Determine the center of the graph to place the text dynamically
139+
x_center = sum(p[0] for p in pos.values()) / len(pos)
140+
y_center = sum(p[1] for p in pos.values()) / len(pos)
141+
142+
plt.text(x_center, y_center - 0.1, cycle_text, horizontalalignment='center',
143+
fontsize=14, color=text_color, bbox=dict(facecolor='white', alpha=0.7))
144+
145+
plt.title('Dependency Graph')
146+
plt.show()
147+
148+
if __name__ == "__main__":
149+
# Set up argument parsing
150+
parser = argparse.ArgumentParser(description="Check if a schedule is conflict serializable.")
151+
parser.add_argument("file", type=str, help="Path to the Excel file containing the schedule.")
152+
parser.add_argument("--verbose", action="store_true", help="Enable verbose output for debugging.") # Add verbose flag
153+
154+
# Parse arguments
155+
args = parser.parse_args()
156+
157+
# Call the main function with the file path and verbose flag
158+
main(args.file, args.verbose)

requirements.txt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
contourpy==1.3.1
2+
cycler==0.12.1
3+
et_xmlfile==2.0.0
4+
fonttools==4.55.8
5+
kiwisolver==1.4.8
6+
matplotlib==3.10.0
7+
networkx==3.4.2
8+
numpy==2.2.2
9+
openpyxl==3.1.5
10+
packaging==24.2
11+
pandas==2.2.3
12+
pillow==11.1.0
13+
pyparsing==3.2.1
14+
python-dateutil==2.9.0.post0
15+
pytz==2025.1
16+
six==1.17.0
17+
tzdata==2025.1

schedule.xlsx

7.66 KB
Binary file not shown.

0 commit comments

Comments
 (0)