Updated data generation code
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,6 +1,6 @@
|
||||
.venv
|
||||
.env
|
||||
__pycaches__
|
||||
__pycache__
|
||||
datasets/
|
||||
temp.*
|
||||
test.*
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
19
config_.py
19
config_.py
@@ -9,7 +9,7 @@ class NoiseType(Enum):
|
||||
|
||||
class ModelConfig:
|
||||
num_agents: int
|
||||
embedding_dim: int = 16
|
||||
embedding_dim: int = 64
|
||||
input_dim: int = 1
|
||||
output_dim: int = 1
|
||||
|
||||
@@ -19,15 +19,12 @@ class TrainConfig:
|
||||
"""Configuration for the training process."""
|
||||
learning_rate: float = 1e-3
|
||||
epochs: int = 100
|
||||
batch_size: int = 32
|
||||
verbose: bool = False # Set to True to see epoch loss during training
|
||||
log: bool = True
|
||||
log_epoch_interval: int = 10
|
||||
noise_type: NoiseType = NoiseType.NONE
|
||||
noise_level: float = 0.01
|
||||
|
||||
# --- New parameters for this script ---
|
||||
batch_size: int = 4096 * 4
|
||||
verbose: bool = False # See loss during training
|
||||
log: bool = True # Logs the attention every log interval
|
||||
log_epoch_interval: int = 10 # How often you want to log intervals
|
||||
noise_type: NoiseType = NoiseType.NONE # What kind of noise you want
|
||||
noise_level: float = 0.00 # Stddev for Normal, half-width for Uniform
|
||||
data_directory:str
|
||||
# Threshold for converting attention scores to a binary graph
|
||||
f1_threshold: float = -0.5
|
||||
f1_threshold: float = -0.5 # Threshold for binary classification
|
||||
|
@@ -8,7 +8,9 @@ import networkx as nx
|
||||
import networkx.algorithms.community as nx_comm
|
||||
from tqdm import tqdm
|
||||
import sims
|
||||
from dotenv import load_dotenv
|
||||
|
||||
load_dotenv()
|
||||
|
||||
|
||||
def generate_connected_graph(rng: np.random.Generator, num_agents: int, graph_type: str) -> tuple[nx.Graph, str, np.ndarray]:
|
||||
@@ -21,35 +23,34 @@ def generate_connected_graph(rng: np.random.Generator, num_agents: int, graph_ty
|
||||
"""
|
||||
|
||||
G = None
|
||||
|
||||
|
||||
if graph_type == "erdos_renyi":
|
||||
|
||||
# p = 2.0 * np.log(num_agents) / num_agents
|
||||
p = 0.5
|
||||
G = nx.erdos_renyi_graph(num_agents, p, seed=rng)
|
||||
|
||||
elif graph_type == "watts_strogatz":
|
||||
k = 2
|
||||
p = 0.1
|
||||
k = 4
|
||||
p = 0.5
|
||||
G = nx.watts_strogatz_graph(num_agents, k, p, seed=rng)
|
||||
|
||||
elif graph_type == "barabasi_albert":
|
||||
m = 2
|
||||
if m >= num_agents: m = max(1, num_agents - 1)
|
||||
G = nx.barabasi_albert_graph(num_agents, m, seed=rng)
|
||||
G = nx.barabasi_albert_graph(num_agents, 2, seed=rng)
|
||||
|
||||
elif graph_type == "powerlaw_cluster":
|
||||
m = 3 # Number of random edges to add for each new node
|
||||
p = 0.1 # Probability of adding a triangle after adding a random edge
|
||||
if m >= num_agents: m = max(1, num_agents - 1)
|
||||
m = 2
|
||||
p = 0.5
|
||||
G = nx.powerlaw_cluster_graph(num_agents, m, p, seed=rng)
|
||||
|
||||
# Add self-loops, as they are often assumed in consensus algorithms
|
||||
G.add_edges_from([(i, i) for i in range(num_agents)])
|
||||
adj_matrix = nx.to_numpy_array(G, dtype=np.float32)
|
||||
|
||||
return G, graph_type, adj_matrix
|
||||
graph_matrix = nx.adjacency_matrix(G).todense()
|
||||
|
||||
for i in range(num_agents):
|
||||
graph_matrix[i,i]=1
|
||||
|
||||
G = nx.Graph(graph_matrix)
|
||||
|
||||
return G, graph_type, graph_matrix
|
||||
|
||||
|
||||
def calculate_graph_metrics(G: nx.Graph) -> dict:
|
||||
"""
|
||||
@@ -91,30 +92,30 @@ def calculate_graph_metrics(G: nx.Graph) -> dict:
|
||||
|
||||
# --- Spectral & Community Metrics (Potentially Slow) ---
|
||||
# These are also limited to smaller graphs.
|
||||
if 1 < num_nodes < 500:
|
||||
# Eigenvalues of the Laplacian matrix
|
||||
try:
|
||||
laplacian_eigenvalues = sorted(nx.laplacian_spectrum(G))
|
||||
metrics["laplacian_eigenvalues"] = laplacian_eigenvalues
|
||||
# The second-smallest eigenvalue of the Laplacian matrix
|
||||
metrics["algebraic_connectivity"] = laplacian_eigenvalues[1]
|
||||
except Exception:
|
||||
metrics["laplacian_eigenvalues"] = None
|
||||
metrics["algebraic_connectivity"] = None
|
||||
# if 1 < num_nodes < 500:
|
||||
# # Eigenvalues of the Laplacian matrix
|
||||
# try:
|
||||
# laplacian_eigenvalues = sorted(nx.laplacian_spectrum(G))
|
||||
# metrics["laplacian_eigenvalues"] = laplacian_eigenvalues
|
||||
# # The second-smallest eigenvalue of the Laplacian matrix
|
||||
# metrics["algebraic_connectivity"] = laplacian_eigenvalues[1]
|
||||
# except Exception:
|
||||
# metrics["laplacian_eigenvalues"] = None
|
||||
# metrics["algebraic_connectivity"] = None
|
||||
|
||||
# Modularity using the Louvain community detection algorithm
|
||||
if num_edges > 0:
|
||||
try:
|
||||
communities = nx_comm.louvain_communities(G, seed=123)
|
||||
metrics["modularity"] = nx_comm.modularity(G, communities)
|
||||
except Exception:
|
||||
metrics["modularity"] = None # Algorithm may fail on some graphs
|
||||
else:
|
||||
metrics["modularity"] = None
|
||||
else:
|
||||
metrics["laplacian_eigenvalues"] = None
|
||||
metrics["algebraic_connectivity"] = None
|
||||
metrics["modularity"] = None
|
||||
# # Modularity using the Louvain community detection algorithm
|
||||
# if num_edges > 0:
|
||||
# try:
|
||||
# communities = nx_comm.louvain_communities(G, seed=123)
|
||||
# metrics["modularity"] = nx_comm.modularity(G, communities)
|
||||
# except Exception:
|
||||
# metrics["modularity"] = None # Algorithm may fail on some graphs
|
||||
# else:
|
||||
# metrics["modularity"] = None
|
||||
# else:
|
||||
# metrics["laplacian_eigenvalues"] = None
|
||||
# metrics["algebraic_connectivity"] = None
|
||||
# metrics["modularity"] = None
|
||||
|
||||
return metrics
|
||||
|
||||
@@ -132,9 +133,9 @@ def main():
|
||||
# --- Configuration ---
|
||||
GRAPH_GEN_ALGOS = ["erdos_renyi", "barabasi_albert", "powerlaw_cluster", "watts_strogatz"]
|
||||
AGENT_COUNTS = range(5, 51, 5)# 5, 10, ..., 50 agents
|
||||
GRAPHS_PER_AGENT_COUNT = 100
|
||||
GRAPHS_PER_AGENT_COUNT = 80
|
||||
GRAPHS_PER_GRAPH_ALGO = GRAPHS_PER_AGENT_COUNT // len(GRAPH_GEN_ALGOS)
|
||||
SIMS_PER_GRAPH = 100
|
||||
SIMS_PER_GRAPH = 400
|
||||
OUTPUT_DIR = "datasets/consensus_dataset"
|
||||
|
||||
# --- Setup ---
|
||||
|
@@ -82,9 +82,9 @@ def main():
|
||||
GRAPH_GEN_ALGOS = ["erdos_renyi", "barabasi_albert", "powerlaw_cluster", "watts_strogatz"]
|
||||
AGENT_COUNTS = range(5, 51, 5) # 5, 10, ..., 50 agents
|
||||
# AGENT_COUNTS = [50]
|
||||
GRAPHS_PER_AGENT_COUNT = 100
|
||||
GRAPHS_PER_AGENT_COUNT = 80
|
||||
GRAPHS_PER_GRAPH_ALGO = GRAPHS_PER_AGENT_COUNT // len(GRAPH_GEN_ALGOS)
|
||||
SIMS_PER_GRAPH = 100
|
||||
SIMS_PER_GRAPH = 400
|
||||
OUTPUT_DIR = "datasets/kuramoto_dataset"
|
||||
|
||||
# --- Setup ---
|
||||
|
3
model.py
3
model.py
@@ -46,7 +46,7 @@ def init_fn(key: jax.Array, config: ModelConfig):
|
||||
|
||||
def forward(params: dict, input_timesteps: jax.Array, config: ModelConfig):
|
||||
"""
|
||||
Model's forward function. Takes in the parameters and inptu timesteps, returns predictions
|
||||
Model's forward function. Takes in the parameters and input timesteps, returns predictions
|
||||
"""
|
||||
batch_size, num_agents, _ = input_timesteps.shape
|
||||
|
||||
@@ -119,6 +119,7 @@ def train_model(config: ModelConfig, inputs: jax.Array, targets: jax.Array,
|
||||
params, opt_state, loss_val = update_step(params, opt_state, x, y, config)
|
||||
running_loss += loss_val
|
||||
|
||||
|
||||
epoch_loss = running_loss / num_batches
|
||||
loss_history[f"epoch_{epoch}"].append(epoch_loss)
|
||||
|
||||
|
@@ -10,7 +10,7 @@ from train_and_eval import calculate_f1_score
|
||||
from sklearn.metrics import f1_score
|
||||
|
||||
if len(sys.argv) < 2:
|
||||
data_dir = "datasets/consensus_dataset"
|
||||
data_dir = "datasets/kuramoto_dataset"
|
||||
else:
|
||||
data_dir = "datasets/" + sys.argv[1]
|
||||
|
||||
@@ -22,53 +22,59 @@ for folder in tqdm(os.listdir(data_dir)):
|
||||
|
||||
folder_path = os.path.join(data_dir, folder)
|
||||
|
||||
# Load model config from summary json
|
||||
with open(os.path.join(folder_path, "results/NoiseType.NONE", "summary_results.json"), "r") as f:
|
||||
summary_results = json.load(f)
|
||||
for noise_level in os.listdir(os.path.join(folder_path, "results/NoiseType.NONE")):
|
||||
|
||||
|
||||
for i, graph in enumerate(os.listdir(folder_path)):
|
||||
|
||||
# train_summary_results
|
||||
summ_results = summary_results[i-1]
|
||||
# Load model config from summary json
|
||||
with open(os.path.join(folder_path, "results/NoiseType.NONE", noise_level, "summary_results.json"), "r") as f:
|
||||
summary_results = json.load(f)
|
||||
|
||||
if graph == "results": # ignore the result folder
|
||||
|
||||
for i, graph in enumerate(os.listdir(folder_path)):
|
||||
|
||||
# train_summary_results
|
||||
summ_results = summary_results[i-1]
|
||||
|
||||
if graph == "results": # ignore the result folder
|
||||
continue
|
||||
|
||||
graph_path = os.path.join(folder_path, graph)
|
||||
|
||||
# Load run data
|
||||
with open(os.path.join(folder_path, graph), "r") as f:
|
||||
run_data = json.load(f)
|
||||
|
||||
true_graph = np.array(run_data["adjacency_matrix"])
|
||||
|
||||
learned_graph = np.array(summ_results["raw_attention"])
|
||||
|
||||
predicted_graph = (learned_graph > THRESHOLD).astype(int)
|
||||
|
||||
true_flat = true_graph.flatten()
|
||||
pred_flat = predicted_graph.flatten()
|
||||
|
||||
calc_f1_score = f1_score(true_flat, pred_flat)
|
||||
|
||||
|
||||
datapoints[num_agents] = datapoints.get(num_agents, [])
|
||||
datapoints[num_agents].append(calc_f1_score)
|
||||
|
||||
|
||||
for key in datapoints.keys():
|
||||
try:
|
||||
datapoints[key] = sum(datapoints[key])/len(datapoints[key])
|
||||
except:
|
||||
continue
|
||||
|
||||
graph_path = os.path.join(folder_path, graph)
|
||||
|
||||
# Load run data
|
||||
with open(os.path.join(folder_path, graph), "r") as f:
|
||||
run_data = json.load(f)
|
||||
x = []
|
||||
y = []
|
||||
|
||||
true_graph = np.array(run_data["adjacency_matrix"])
|
||||
|
||||
learned_graph = np.array(summ_results["raw_attention"])
|
||||
for item in datapoints.items():
|
||||
x.append(item[0])
|
||||
y.append(item[1])
|
||||
|
||||
predicted_graph = (learned_graph > THRESHOLD).astype(int)
|
||||
|
||||
true_flat = true_graph.flatten()
|
||||
pred_flat = predicted_graph.flatten()
|
||||
|
||||
calc_f1_score = f1_score(true_flat, pred_flat)
|
||||
|
||||
|
||||
datapoints[num_agents] = datapoints.get(num_agents, [])
|
||||
datapoints[num_agents].append(calc_f1_score)
|
||||
|
||||
|
||||
for key in datapoints.keys():
|
||||
datapoints[key] = sum(datapoints[key])/len(datapoints[key])
|
||||
|
||||
|
||||
x = []
|
||||
y = []
|
||||
|
||||
for item in datapoints.items():
|
||||
x.append(item[0])
|
||||
y.append(item[1])
|
||||
|
||||
plt.plot(x, y)
|
||||
plt.show()
|
||||
plt.plot(x, y)
|
||||
plt.show()
|
||||
|
||||
|
@@ -12,6 +12,8 @@ dependencies = [
|
||||
"matplotlib>=3.10.3",
|
||||
"networkx>=3.5",
|
||||
"optax>=0.2.5",
|
||||
"pyqt6>=6.9.1",
|
||||
"python-dotenv>=1.1.1",
|
||||
"scikit-learn>=1.7.1",
|
||||
"seaborn>=0.13.2",
|
||||
"tqdm>=4.67.1",
|
||||
|
521
test.ipynb
521
test.ipynb
File diff suppressed because one or more lines are too long
@@ -51,7 +51,7 @@ def prepare_data_for_model(key: jax.Array, trajectories: np.ndarray, train_confi
|
||||
all_targets = all_targets[all_indices]
|
||||
|
||||
if train_config.noise_type != NoiseType.NONE and train_config.noise_level > 0:
|
||||
noise_shape = full_dataset_inputs.shape
|
||||
noise_shape = all_inputs.shape
|
||||
if train_config.noise_type == NoiseType.NORMAL:
|
||||
noise = jax.random.normal(key, noise_shape) * train_config.noise_level
|
||||
elif train_config.noise_type == NoiseType.UNIFORM:
|
||||
@@ -60,18 +60,18 @@ def prepare_data_for_model(key: jax.Array, trajectories: np.ndarray, train_confi
|
||||
minval=-train_config.noise_level,
|
||||
maxval=train_config.noise_level
|
||||
)
|
||||
full_dataset_inputs += np.array(noise) # Add noise to inputs
|
||||
all_inputs += np.array(noise) # Add noise to inputs
|
||||
|
||||
|
||||
full_dataset_inputs = np.expand_dims(all_inputs, axis=-1)
|
||||
all_inputs = np.expand_dims(all_inputs, axis=-1)
|
||||
full_dataset_targets = np.expand_dims(all_targets, axis=-1)
|
||||
|
||||
# Create batches
|
||||
num_samples = full_dataset_inputs.shape[0]
|
||||
num_samples = all_inputs.shape[0]
|
||||
num_batches = num_samples // batch_size
|
||||
|
||||
# Truncate to full batches
|
||||
truncated_inputs = full_dataset_inputs[:num_batches * batch_size]
|
||||
truncated_inputs = all_inputs[:num_batches * batch_size]
|
||||
truncated_targets = full_dataset_targets[:num_batches * batch_size]
|
||||
|
||||
# Reshape into batches
|
||||
@@ -81,6 +81,40 @@ def prepare_data_for_model(key: jax.Array, trajectories: np.ndarray, train_confi
|
||||
|
||||
return batched_inputs, batched_targets
|
||||
|
||||
def f1_score_np(y_true: np.ndarray, y_pred: np.ndarray) -> float:
|
||||
"""
|
||||
Compute the F1 score between two numpy arrays.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
y_true : np.ndarray
|
||||
Ground truth (correct) labels.
|
||||
y_pred : np.ndarray
|
||||
Predicted labels.
|
||||
|
||||
Returns
|
||||
-------
|
||||
float
|
||||
The F1 score.
|
||||
"""
|
||||
# Ensure binary arrays (0 or 1)
|
||||
y_true = np.asarray(y_true).astype(int)
|
||||
y_pred = np.asarray(y_pred).astype(int)
|
||||
|
||||
# Compute True Positives, False Positives, and False Negatives
|
||||
tp = np.sum((y_true == 1) & (y_pred == 1))
|
||||
fp = np.sum((y_true == 0) & (y_pred == 1))
|
||||
fn = np.sum((y_true == 1) & (y_pred == 0))
|
||||
|
||||
# Precision and Recall
|
||||
precision = tp / (tp + fp) if (tp + fp) > 0 else 0.0
|
||||
recall = tp / (tp + fn) if (tp + fn) > 0 else 0.0
|
||||
|
||||
# F1 Score
|
||||
if precision + recall == 0:
|
||||
return 0.0
|
||||
return 2 * (precision * recall) / (precision + recall)
|
||||
|
||||
def calculate_f1_score(
|
||||
params: dict,
|
||||
model_config: ModelConfig,
|
||||
@@ -111,7 +145,7 @@ def calculate_f1_score(
|
||||
true_flat = true_graph.flatten()
|
||||
pred_flat = predicted_graph.flatten()
|
||||
|
||||
return f1_score(true_flat, pred_flat)
|
||||
return f1_score_np(true_flat, pred_flat)
|
||||
|
||||
def main():
|
||||
"""Main script to run the training and evaluation pipeline."""
|
||||
@@ -125,7 +159,7 @@ def main():
|
||||
print(f"Please run the data generation script for '{train_config.data_directory}' first.")
|
||||
return
|
||||
|
||||
print(f"🚀 Starting training pipeline for '{train_config.data_directory}' data.")
|
||||
print(f"Starting training pipeline for '{train_config.data_directory}' data.")
|
||||
|
||||
# Get sorted list of agent directories
|
||||
agent_dirs = sorted(
|
||||
@@ -146,7 +180,8 @@ def main():
|
||||
os.makedirs(results_dir, exist_ok=True)
|
||||
|
||||
subdir = str(train_config.noise_type)
|
||||
sub_results_dir = os.path.join(results_dir, subdir)
|
||||
subsubdir = str(train_config.noise_level)
|
||||
sub_results_dir = os.path.join(results_dir, subdir, subsubdir)
|
||||
os.makedirs(sub_results_dir, exist_ok=True)
|
||||
|
||||
print(f"\nProcessing {len(graph_files)} graphs for {agent_dir_name}...")
|
||||
|
61
uv.lock
generated
61
uv.lock
generated
@@ -415,6 +415,8 @@ dependencies = [
|
||||
{ name = "matplotlib" },
|
||||
{ name = "networkx" },
|
||||
{ name = "optax" },
|
||||
{ name = "pyqt6" },
|
||||
{ name = "python-dotenv" },
|
||||
{ name = "scikit-learn" },
|
||||
{ name = "seaborn" },
|
||||
{ name = "tqdm" },
|
||||
@@ -429,6 +431,8 @@ requires-dist = [
|
||||
{ name = "matplotlib", specifier = ">=3.10.3" },
|
||||
{ name = "networkx", specifier = ">=3.5" },
|
||||
{ name = "optax", specifier = ">=0.2.5" },
|
||||
{ name = "pyqt6", specifier = ">=6.9.1" },
|
||||
{ name = "python-dotenv", specifier = ">=1.1.1" },
|
||||
{ name = "scikit-learn", specifier = ">=1.7.1" },
|
||||
{ name = "seaborn", specifier = ">=0.13.2" },
|
||||
{ name = "tqdm", specifier = ">=4.67.1" },
|
||||
@@ -1678,6 +1682,54 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/05/e7/df2285f3d08fee213f2d041540fa4fc9ca6c2d44cf36d3a035bf2a8d2bcc/pyparsing-3.2.3-py3-none-any.whl", hash = "sha256:a749938e02d6fd0b59b356ca504a24982314bb090c383e3cf201c95ef7e2bfcf", size = 111120, upload-time = "2025-03-25T05:01:24.908Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyqt6"
|
||||
version = "6.9.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "pyqt6-qt6" },
|
||||
{ name = "pyqt6-sip" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/32/1b/567f46eb43ca961efd38d7a0b73efb70d7342854f075fd919179fdb2a571/pyqt6-6.9.1.tar.gz", hash = "sha256:50642be03fb40f1c2111a09a1f5a0f79813e039c15e78267e6faaf8a96c1c3a6", size = 1067230, upload-time = "2025-06-06T08:49:30.307Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/18/c4/fc2a69cf3df09b213185ef5a677c3940cd20e7855d29e40061a685b9c6ee/pyqt6-6.9.1-cp39-abi3-macosx_10_14_universal2.whl", hash = "sha256:33c23d28f6608747ecc8bfd04c8795f61631af9db4fb1e6c2a7523ec4cc916d9", size = 59770566, upload-time = "2025-06-06T08:48:20.331Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d5/78/92f3c46440a83ebe22ae614bd6792e7b052bcb58ff128f677f5662015184/pyqt6-6.9.1-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:37884df27f774e2e1c0c96fa41e817a222329b80ffc6241725b0dc8c110acb35", size = 37804959, upload-time = "2025-06-06T08:48:39.587Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5a/5e/e77fa2761d809cd08d724f44af01a4b6ceb0ff9648e43173187b0e4fac4e/pyqt6-6.9.1-cp39-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:055870b703c1a49ca621f8a89e2ec4d848e6c739d39367eb9687af3b056d9aa3", size = 40414608, upload-time = "2025-06-06T08:49:00.26Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c4/09/69cf80456b6a985e06dd24ed0c2d3451e43567bf2807a5f3a86ef7a74a2e/pyqt6-6.9.1-cp39-abi3-win_amd64.whl", hash = "sha256:15b95bd273bb6288b070ed7a9503d5ff377aa4882dd6d175f07cad28cdb21da0", size = 25717996, upload-time = "2025-06-06T08:49:13.208Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/52/b3/0839d8fd18b86362a4de384740f2f6b6885b5d06fda7720f8a335425e316/pyqt6-6.9.1-cp39-abi3-win_arm64.whl", hash = "sha256:08792c72d130a02e3248a120f0b9bbb4bf4319095f92865bc5b365b00518f53d", size = 25212132, upload-time = "2025-06-06T08:49:27.41Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyqt6-qt6"
|
||||
version = "6.9.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/51/40/04f652e714f85ba6b0c24f4ead860f2c5769f9e64737f415524d792d5914/pyqt6_qt6-6.9.1-py3-none-macosx_10_14_x86_64.whl", hash = "sha256:3854c7f83ee4e8c2d91e23ab88b77f90e2ca7ace34fe72f634a446959f2b4d4a", size = 66236777, upload-time = "2025-06-03T14:53:17.684Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/57/31/e4fa40568a59953ce5cf9a5adfbd1be4a806dafd94e39072d3cc0bed5468/pyqt6_qt6-6.9.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:123e4aeb037c099bb4696a3ea8edcb1d9d62cedd0b2b950556b26024c97f3293", size = 60551574, upload-time = "2025-06-03T14:53:48.42Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/aa/8d/7c8073cbbefe9c103ec8add70f29ffee1db95a3755b429b9f47cd6afa41b/pyqt6_qt6-6.9.1-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:cc5bd193ebd2d1a3ec66e1eee65bf532d762c239459bce1ecebf56177243e89b", size = 82000130, upload-time = "2025-06-03T14:54:26.585Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1e/60/a4ab932028b0c15c0501cb52eb1e7f24f4ce2e4c78d46c7cce58a375a88c/pyqt6_qt6-6.9.1-py3-none-manylinux_2_39_aarch64.whl", hash = "sha256:b065af7243d1d450a49470a8185301196a18b1d41085d3ef476eb55bbb225083", size = 80463127, upload-time = "2025-06-03T14:55:03.272Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e7/85/552710819019a96d39d924071324a474aec54b31c410d7de8ebb398adcc1/pyqt6_qt6-6.9.1-py3-none-win_amd64.whl", hash = "sha256:f9e54c424bc921ecb76792a75d123e4ecfc26b00b0c57dae526f41f1d57951d3", size = 73778423, upload-time = "2025-06-03T14:55:39.756Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/16/b4/70f6b18a4913f2326dcf7acb15c12cc0b91cb3932c2ba3b5728811f22acd/pyqt6_qt6-6.9.1-py3-none-win_arm64.whl", hash = "sha256:432caaedf5570bc8a9b7c75bc6af6a26bf88589536472eca73417ac019f59d41", size = 49617924, upload-time = "2025-06-03T14:57:13.038Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyqt6-sip"
|
||||
version = "13.10.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/2f/4a/96daf6c2e4f689faae9bd8cebb52754e76522c58a6af9b5ec86a2e8ec8b4/pyqt6_sip-13.10.2.tar.gz", hash = "sha256:464ad156bf526500ce6bd05cac7a82280af6309974d816739b4a9a627156fafe", size = 92548, upload-time = "2025-05-23T12:26:49.901Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/22/5b/1240017e0d59575289ba52b58fd7f95e7ddf0ed2ede95f3f7e2dc845d337/pyqt6_sip-13.10.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:83e6a56d3e715f748557460600ec342cbd77af89ec89c4f2a68b185fa14ea46c", size = 112199, upload-time = "2025-05-23T12:26:32.503Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/51/11/1fc3bae02a12a3ac8354aa579b56206286e8b5ca9586677b1058c81c2f74/pyqt6_sip-13.10.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ccf197f8fa410e076936bee28ad9abadb450931d5be5625446fd20e0d8b27a6", size = 322757, upload-time = "2025-05-23T12:26:33.752Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/21/40/de9491213f480a27199690616959a17a0f234962b86aa1dd4ca2584e922d/pyqt6_sip-13.10.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:37af463dcce39285e686d49523d376994d8a2508b9acccb7616c4b117c9c4ed7", size = 304251, upload-time = "2025-05-23T12:26:35.66Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/02/21/cc80e03f1052408c62c341e9fe9b81454c94184f4bd8a95d29d2ec86df92/pyqt6_sip-13.10.2-cp312-cp312-win_amd64.whl", hash = "sha256:c7b34a495b92790c70eae690d9e816b53d3b625b45eeed6ae2c0fe24075a237e", size = 53519, upload-time = "2025-05-23T12:26:36.797Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/77/cf/53bd0863252b260a502659cb3124d9c9fe38047df9360e529b437b4ac890/pyqt6_sip-13.10.2-cp312-cp312-win_arm64.whl", hash = "sha256:c80cc059d772c632f5319632f183e7578cd0976b9498682833035b18a3483e92", size = 45349, upload-time = "2025-05-23T12:26:37.729Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a1/1e/979ea64c98ca26979d8ce11e9a36579e17d22a71f51d7366d6eec3c82c13/pyqt6_sip-13.10.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8b5d06a0eac36038fa8734657d99b5fe92263ae7a0cd0a67be6acfe220a063e1", size = 112227, upload-time = "2025-05-23T12:26:38.758Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d9/21/84c230048e3bfef4a9209d16e56dcd2ae10590d03a31556ae8b5f1dcc724/pyqt6_sip-13.10.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad376a6078da37b049fdf9d6637d71b52727e65c4496a80b753ddc8d27526aca", size = 322920, upload-time = "2025-05-23T12:26:39.856Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b0/1e/c6a28a142f14e735088534cc92951c3f48cccd77cdd4f3b10d7996be420f/pyqt6_sip-13.10.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:3dde8024d055f496eba7d44061c5a1ba4eb72fc95e5a9d7a0dbc908317e0888b", size = 303833, upload-time = "2025-05-23T12:26:41.075Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/89/63/e5adf350c1c3123d4865c013f164c5265512fa79f09ad464fb2fdf9f9e61/pyqt6_sip-13.10.2-cp313-cp313-win_amd64.whl", hash = "sha256:0b097eb58b4df936c4a2a88a2f367c8bb5c20ff049a45a7917ad75d698e3b277", size = 53527, upload-time = "2025-05-23T12:26:42.625Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/58/74/2df4195306d050fbf4963fb5636108a66e5afa6dc05fd9e81e51ec96c384/pyqt6_sip-13.10.2-cp313-cp313-win_arm64.whl", hash = "sha256:cc6a1dfdf324efaac6e7b890a608385205e652845c62130de919fd73a6326244", size = 45373, upload-time = "2025-05-23T12:26:43.536Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "python-dateutil"
|
||||
version = "2.9.0.post0"
|
||||
@@ -1690,6 +1742,15 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "python-dotenv"
|
||||
version = "1.1.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/f6/b0/4bc07ccd3572a2f9df7e6782f52b0c6c90dcbb803ac4a167702d7d0dfe1e/python_dotenv-1.1.1.tar.gz", hash = "sha256:a8a6399716257f45be6a007360200409fce5cda2661e3dec71d23dc15f6189ab", size = 41978, upload-time = "2025-06-24T04:21:07.341Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/5f/ed/539768cf28c661b5b068d66d96a2f155c4971a5d55684a514c1a0e0dec2f/python_dotenv-1.1.1-py3-none-any.whl", hash = "sha256:31f23644fe2602f88ff55e1f5c79ba497e01224ee7737937930c448e4d0e24dc", size = 20556, upload-time = "2025-06-24T04:21:06.073Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "python-json-logger"
|
||||
version = "3.3.0"
|
||||
|
Reference in New Issue
Block a user