From f65103843115642b311a8eac8aa3fcefb5c43863 Mon Sep 17 00:00:00 2001 From: allierc Date: Thu, 22 Jan 2026 09:39:46 -0500 Subject: [PATCH 01/12] add code option --- GNN_LLM.py | 158 +++++++++++++++++++-- cluster/experiment.sh | 0 cluster/training_cluster.sh | 87 ++++++++++++ config/signal/signal_landscape_Claude.yaml | 2 +- 4 files changed, 235 insertions(+), 12 deletions(-) create mode 100644 cluster/experiment.sh create mode 100644 cluster/training_cluster.sh diff --git a/GNN_LLM.py b/GNN_LLM.py index d96b7cdb..ba860396 100755 --- a/GNN_LLM.py +++ b/GNN_LLM.py @@ -13,6 +13,8 @@ os.makedirs('/scratch/allierc', exist_ok=True) +import sys + from NeuralGraph.config import NeuralGraphConfig from NeuralGraph.generators.graph_data_generator import data_generate from NeuralGraph.models.graph_trainer import data_train, data_test, data_train_INR @@ -21,6 +23,7 @@ from NeuralGraph.models.utils import save_exploration_artifacts from NeuralGraph.utils import set_device, add_pre_folder from NeuralGraph.models.NGP_trainer import data_train_NGP +from NeuralGraph.git_code_tracker import track_code_modifications, is_git_repo, get_modified_code_files from GNN_PlotFigure import data_plot import warnings @@ -55,7 +58,7 @@ task_params[key] = int(value) if value.isdigit() else value else: best_model = '' - task = 'generate_train_test_plot_Claude' # 'train', 'test', 'generate', 'plot', 'train_NGP', 'train_INR', 'Claude' + task = 'generate_train_test_plot_Claude' # 'train', 'test', 'generate', 'plot', 'train_NGP', 'train_INR', 'Claude', 'code' config_list = ['signal_landscape'] task_params = {'iterations': 2048} @@ -137,9 +140,26 @@ print(f"\033[93mpreserving {ucb_file} (resuming from iter {start_iteration})\033[0m") config_list = [llm_task_name] - else: - iteration_range = range(1, 2) + # Track if code was modified by Claude (starts False, set True after Claude modifies code) + code_modified_by_claude = False + # Check if code modifications are enabled (task contains 'code') + code_changes_enabled = 'code' in task + # Check if cluster execution is enabled (task contains 'cluster') + cluster_enabled = 'cluster' in task + if code_changes_enabled: + print("\033[93mCode modifications ENABLED (task contains 'code')\033[0m") + if cluster_enabled: + print("\033[93mCluster execution ENABLED (task contains 'cluster')\033[0m") + else: + print("\033[90mCluster execution disabled (add 'cluster' to task to enable)\033[0m") + else: + print("\033[90mCode modifications disabled (add 'code' to task to enable)\033[0m") + else: + iteration_range = range(1, 2) + code_modified_by_claude = False + code_changes_enabled = False + cluster_enabled = False @@ -303,14 +323,99 @@ data_train_INR(config=config, device=device, total_steps=50000) elif "train" in task: - data_train( - config=config, - erase='Claude' in task, # erase old models when iterating with Claude - best_model=best_model, - style = 'color', - device=device, - log_file=log_file - ) + # For Claude tasks, use subprocess only if code changes enabled AND Claude modified code + if 'Claude' in task and code_changes_enabled and code_modified_by_claude: + print("\033[93mcode modified by Claude - running training in subprocess...\033[0m") + + # Construct subprocess command + train_script = os.path.join(root_dir, 'train_signal_subprocess.py') + config_path = f"{config_root}/{config_file}.yaml" + + # Create log directory and error log paths + log_dir = f"{root_dir}/log/Claude_exploration/{instruction_name}" + os.makedirs(log_dir, exist_ok=True) + error_log_path = f"{log_dir}/training_output_latest.log" + error_details_path = f"{log_dir}/training_error_latest.log" + + train_cmd = [ + sys.executable, # Use same Python interpreter + '-u', # Force unbuffered output for real-time streaming + train_script, + '--config', config_path, + '--device', str(device), + '--log_file', analysis_log_path, + '--config_file', config.config_file, + '--error_log', error_details_path, + '--erase' + ] + + # Run training subprocess and stream output + env = os.environ.copy() + env['PYTHONUNBUFFERED'] = '1' + env['TQDM_DISABLE'] = '1' # Disable tqdm in subprocess (doesn't stream well) + + process = subprocess.Popen( + train_cmd, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + text=True, + bufsize=1, + env=env + ) + + # Capture all output for logging while also streaming to console + output_lines = [] + with open(error_log_path, 'w') as output_file: + for line in process.stdout: + output_file.write(line) + output_file.flush() + output_lines.append(line.rstrip()) + # Filter: skip tqdm-like lines (progress bars) + if '|' in line and '%' in line and 'it/s' in line: + continue + print(line, end='', flush=True) + + process.wait() + + if process.returncode != 0: + print(f"\033[91m\ntraining subprocess failed with code {process.returncode}\033[0m") + print("\033[93mthis may indicate a code modification error.\033[0m\n") + + # Show last 20 lines of output for context + print("\033[93mLast 20 lines of output:\033[0m") + print("-" * 80) + for line in output_lines[-20:]: + print(line) + print("-" * 80) + + # Show paths to log files + print(f"\nFull output logged to: {error_log_path}") + if os.path.exists(error_details_path): + print(f"Error details logged to: {error_details_path}") + try: + with open(error_details_path, 'r') as f: + error_details = f.read() + if error_details.strip(): + print("\n\033[91mDetailed error information:\033[0m") + print(error_details) + except Exception as e: + print(f"Could not read error details: {e}") + + raise RuntimeError(f"training failed at iteration {iteration}") + + print("\033[92mtraining subprocess completed successfully\033[0m") + else: + # No code modifications - run training directly (faster) + if 'Claude' in task and code_changes_enabled: + print("\033[92mno code modifications - running training directly...\033[0m") + data_train( + config=config, + erase='Claude' in task, # erase old models when iterating with Claude + best_model=best_model, + style = 'color', + device=device, + log_file=log_file + ) if "test" in task: @@ -450,6 +555,37 @@ f.write(output_text.strip()) f.write("\n\n") + # Git tracking: commit any code modifications made by Claude (only if code changes enabled) + if code_changes_enabled: + if is_git_repo(root_dir): + print("\n\033[96mchecking for code modifications to commit\033[0m") + git_results = track_code_modifications( + root_dir=root_dir, + iteration=iteration, + analysis_path=analysis_path, + reasoning_path=reasoning_log_path + ) + + if git_results: + for file_path, success, message in git_results: + if success: + print(f"\033[92m✓ Git: {message}\033[0m") + # Set flag so next iteration uses subprocess + code_modified_by_claude = True + else: + print(f"\033[93m⚠ Git: {message}\033[0m") + else: + print("\033[90m No code modifications detected\033[0m") + else: + # Not a git repo - check for code modifications directly + tracked_code_files = ['src/NeuralGraph/models/graph_trainer.py'] + modified_files = get_modified_code_files(root_dir, tracked_code_files) + if modified_files: + code_modified_by_claude = True + print(f"\033[93m Code modified (no git): {modified_files}\033[0m") + if iteration == 1: + print("\033[90m Not a git repository - code modifications will not be version controlled\033[0m") + # save instruction file at first iteration of each block if iter_in_block == 1: dst_instruction = f"{protocol_save_dir}/block_{block_number:03d}.md" diff --git a/cluster/experiment.sh b/cluster/experiment.sh new file mode 100644 index 00000000..e69de29b diff --git a/cluster/training_cluster.sh b/cluster/training_cluster.sh new file mode 100644 index 00000000..416d5e40 --- /dev/null +++ b/cluster/training_cluster.sh @@ -0,0 +1,87 @@ +#!/bin/bash +# +# Cluster training script for GNN_LLM code modifications +# Called via: ssh login1 'bsub -n 8 -gpu "num=1" -q gpu_h100 "Graph/NeuralGraph/cluster/training_cluster.sh"' +# +# This script reads parameters from a job file and runs training on the cluster + +set -e # Exit on error + +# Configuration - adjust these paths for your environment +NEURALGRAPH_DIR="/groups/bhatti/bhattilabb/allierc/Graph/NeuralGraph" +CONDA_ENV="pyg" +RESULTS_DIR="${NEURALGRAPH_DIR}/cluster/results" + +# Job file contains the training parameters (written by GNN_LLM.py before submission) +JOB_FILE="${NEURALGRAPH_DIR}/cluster/current_job.txt" + +# Check job file exists +if [ ! -f "$JOB_FILE" ]; then + echo "Error: Job file not found: $JOB_FILE" + exit 1 +fi + +# Read parameters from job file +CONFIG_PATH=$(grep "^CONFIG_PATH=" "$JOB_FILE" | cut -d'=' -f2-) +DEVICE=$(grep "^DEVICE=" "$JOB_FILE" | cut -d'=' -f2-) +LOG_FILE=$(grep "^LOG_FILE=" "$JOB_FILE" | cut -d'=' -f2-) +CONFIG_FILE=$(grep "^CONFIG_FILE=" "$JOB_FILE" | cut -d'=' -f2-) +ERROR_LOG=$(grep "^ERROR_LOG=" "$JOB_FILE" | cut -d'=' -f2-) +ERASE=$(grep "^ERASE=" "$JOB_FILE" | cut -d'=' -f2-) +ITERATION=$(grep "^ITERATION=" "$JOB_FILE" | cut -d'=' -f2-) + +echo "========================================" +echo "Cluster Training Job - Iteration $ITERATION" +echo "========================================" +echo "Config: $CONFIG_PATH" +echo "Device: $DEVICE" +echo "Log file: $LOG_FILE" +echo "Started at: $(date)" +echo "========================================" + +# Change to NeuralGraph directory +cd "$NEURALGRAPH_DIR" + +# Build command +CMD="python train_signal_subprocess.py --config '$CONFIG_PATH' --device '$DEVICE'" + +if [ -n "$LOG_FILE" ]; then + CMD="$CMD --log_file '$LOG_FILE'" +fi + +if [ -n "$CONFIG_FILE" ]; then + CMD="$CMD --config_file '$CONFIG_FILE'" +fi + +if [ -n "$ERROR_LOG" ]; then + CMD="$CMD --error_log '$ERROR_LOG'" +fi + +if [ "$ERASE" = "true" ]; then + CMD="$CMD --erase" +fi + +echo "Running: $CMD" +echo "" + +# Activate conda and run +source /groups/bhatti/bhattilabb/allierc/miniconda3/etc/profile.d/conda.sh +conda activate "$CONDA_ENV" + +# Run training +eval $CMD +EXIT_CODE=$? + +# Write completion status +DONE_FILE="${NEURALGRAPH_DIR}/cluster/job_done.txt" +echo "ITERATION=$ITERATION" > "$DONE_FILE" +echo "EXIT_CODE=$EXIT_CODE" >> "$DONE_FILE" +echo "COMPLETED_AT=$(date)" >> "$DONE_FILE" + +echo "" +echo "========================================" +echo "Training completed with exit code: $EXIT_CODE" +echo "Finished at: $(date)" +echo "========================================" + +exit $EXIT_CODE diff --git a/config/signal/signal_landscape_Claude.yaml b/config/signal/signal_landscape_Claude.yaml index 34d2b93c..26d062cc 100644 --- a/config/signal/signal_landscape_Claude.yaml +++ b/config/signal/signal_landscape_Claude.yaml @@ -86,7 +86,7 @@ training: fix_cluster_embedding: true learning_rate_W_start: 0.1 learning_rate_start: 1.0e-04 - learning_rate_embedding_start: 1.0e-03 + learning_rate_embedding_start: 2.5e-03 coeff_W_L1: 1.0e-05 coeff_edge_diff: 100 low_rank_factorization: false From 4060643807c456d85da201cca4a9b8c0f9eabbc5 Mon Sep 17 00:00:00 2001 From: cedric Date: Thu, 22 Jan 2026 14:01:02 -0500 Subject: [PATCH 02/12] add cluster submission --- GNN_LLM.py | 228 ++++++++++++++------- cluster/experiment1.sh | 13 +- cluster/training_cluster.sh | 87 -------- config/signal/signal_landscape_Claude.yaml | 88 ++++---- train_signal_subprocess.py | 6 +- 5 files changed, 202 insertions(+), 220 deletions(-) delete mode 100644 cluster/training_cluster.sh diff --git a/GNN_LLM.py b/GNN_LLM.py index ba860396..3b83ad7e 100755 --- a/GNN_LLM.py +++ b/GNN_LLM.py @@ -58,7 +58,7 @@ task_params[key] = int(value) if value.isdigit() else value else: best_model = '' - task = 'generate_train_test_plot_Claude' # 'train', 'test', 'generate', 'plot', 'train_NGP', 'train_INR', 'Claude', 'code' + task = 'generate_train_test_plot_Claude_cluster' # 'train', 'test', 'generate', 'plot', 'train_NGP', 'train_INR', 'Claude', 'code', 'cluster' config_list = ['signal_landscape'] task_params = {'iterations': 2048} @@ -94,6 +94,13 @@ # Only copy and initialize config on fresh start (not when resuming) if start_iteration == 1: + # Erase config from new/processing/done directories + for subdir in ['new', 'processing', 'done']: + cleanup_path = f"{config_root}/{subdir}/{llm_task_name}.yaml" + if os.path.exists(cleanup_path): + os.remove(cleanup_path) + print(f"\033[93mdeleted {cleanup_path}\033[0m") + if os.path.exists(source_config): shutil.copy2(source_config, target_config) print(f"\033[93mcopied {source_config} -> {target_config}\033[0m") @@ -266,7 +273,7 @@ source_config = f"{config_root}/{config_file}.yaml" shutil.copy2(source_config, new_path) submit_time = time.time() - print(f"\033[93mSubmitted to daemon: {new_path}\033[0m") + print(f"\033[93msubmitted to daemon: {new_path}\033[0m") # Wait for job to complete (file appears in done/) print(f"\033[93mWaiting for {config_filename} to be copied into config/done/ ...\033[0m") @@ -323,91 +330,154 @@ data_train_INR(config=config, device=device, total_steps=50000) elif "train" in task: - # For Claude tasks, use subprocess only if code changes enabled AND Claude modified code - if 'Claude' in task and code_changes_enabled and code_modified_by_claude: - print("\033[93mcode modified by Claude - running training in subprocess...\033[0m") + # Training execution: cluster > subprocess (code modified) > direct + use_subprocess = 'Claude' in task and code_changes_enabled and code_modified_by_claude - # Construct subprocess command - train_script = os.path.join(root_dir, 'train_signal_subprocess.py') + if cluster_enabled or use_subprocess: + # Paths for subprocess/cluster execution config_path = f"{config_root}/{config_file}.yaml" - - # Create log directory and error log paths log_dir = f"{root_dir}/log/Claude_exploration/{instruction_name}" os.makedirs(log_dir, exist_ok=True) error_log_path = f"{log_dir}/training_output_latest.log" error_details_path = f"{log_dir}/training_error_latest.log" - train_cmd = [ - sys.executable, # Use same Python interpreter - '-u', # Force unbuffered output for real-time streaming - train_script, - '--config', config_path, - '--device', str(device), - '--log_file', analysis_log_path, - '--config_file', config.config_file, - '--error_log', error_details_path, - '--erase' - ] - - # Run training subprocess and stream output - env = os.environ.copy() - env['PYTHONUNBUFFERED'] = '1' - env['TQDM_DISABLE'] = '1' # Disable tqdm in subprocess (doesn't stream well) - - process = subprocess.Popen( - train_cmd, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - text=True, - bufsize=1, - env=env - ) - - # Capture all output for logging while also streaming to console - output_lines = [] - with open(error_log_path, 'w') as output_file: - for line in process.stdout: - output_file.write(line) - output_file.flush() - output_lines.append(line.rstrip()) - # Filter: skip tqdm-like lines (progress bars) - if '|' in line and '%' in line and 'it/s' in line: - continue - print(line, end='', flush=True) - - process.wait() - - if process.returncode != 0: - print(f"\033[91m\ntraining subprocess failed with code {process.returncode}\033[0m") - print("\033[93mthis may indicate a code modification error.\033[0m\n") - - # Show last 20 lines of output for context - print("\033[93mLast 20 lines of output:\033[0m") - print("-" * 80) - for line in output_lines[-20:]: - print(line) - print("-" * 80) - - # Show paths to log files - print(f"\nFull output logged to: {error_log_path}") - if os.path.exists(error_details_path): - print(f"Error details logged to: {error_details_path}") - try: + if cluster_enabled: + # Submit training job to cluster via SSH + bsub + if use_subprocess: + print("\033[93mcode modified by Claude - submitting training to cluster...\033[0m") + else: + print("\033[93msubmitting training to cluster....\033[0m") + + # Build the python command + train_cmd = f"python train_signal_subprocess.py --config '{config_path}' --device cuda" + train_cmd += f" --log_file '{analysis_log_path}'" + train_cmd += f" --config_file '{config.config_file}'" + train_cmd += f" --error_log '{error_details_path}'" + if 'Claude' in task: + train_cmd += " --erase" + + # Create temporary bash script that activates conda and runs training + cluster_script_path = f"{log_dir}/cluster_train.sh" + # Use absolute path for cluster (home directory on cluster) + cluster_home = "/groups/saalfeld/home/allierc" + cluster_root_dir = f"{cluster_home}/Graph/NeuralGraph" + conda_path = f"{cluster_home}/miniforge3/etc/profile.d/conda.sh" + + # Update paths in train_cmd to use cluster paths + cluster_config_path = config_path.replace(root_dir, cluster_root_dir) + cluster_analysis_log = analysis_log_path.replace(root_dir, cluster_root_dir) + cluster_error_log = error_details_path.replace(root_dir, cluster_root_dir) + + cluster_train_cmd = f"python train_signal_subprocess.py --config '{cluster_config_path}' --device cuda" + cluster_train_cmd += f" --log_file '{cluster_analysis_log}'" + cluster_train_cmd += f" --config_file '{config.config_file}'" + cluster_train_cmd += f" --error_log '{cluster_error_log}'" + if 'Claude' in task: + cluster_train_cmd += " --erase" + + with open(cluster_script_path, 'w') as f: + f.write("#!/bin/bash\n") + f.write(f"cd {cluster_root_dir}\n") + f.write(f"conda run -n neural-graph {cluster_train_cmd}\n") + os.chmod(cluster_script_path, 0o755) + + # Path to script on cluster + cluster_script = cluster_script_path.replace(root_dir, cluster_root_dir) + + # Submit job to cluster via SSH to login1 + # -W 6000 = 100 hours max wall time, -K makes bsub wait for job completion + ssh_cmd = f"ssh login1 \"cd {cluster_root_dir} && bsub -n 8 -gpu 'num=1' -q gpu_h100 -W 6000 -K 'bash {cluster_script}'\"" + + print(f"\033[96msubmitting via SSH: {ssh_cmd}\033[0m") + + result = subprocess.run(ssh_cmd, shell=True, capture_output=True, text=True) + + if result.returncode != 0: + print(f"\033[91mCluster training failed:\033[0m") + print(f"stdout: {result.stdout}") + print(f"stderr: {result.stderr}") + if os.path.exists(error_details_path): with open(error_details_path, 'r') as f: - error_details = f.read() - if error_details.strip(): - print("\n\033[91mDetailed error information:\033[0m") - print(error_details) - except Exception as e: - print(f"Could not read error details: {e}") + print(f.read()) + raise RuntimeError(f"Cluster training failed at iteration {iteration}") - raise RuntimeError(f"training failed at iteration {iteration}") + print(f"\033[92mCluster training completed successfully\033[0m") + print(result.stdout) - print("\033[92mtraining subprocess completed successfully\033[0m") + else: + # Run training locally in subprocess (code was modified) + print("\033[93mcode modified by Claude - running training in subprocess...\033[0m") + + train_script = os.path.join(root_dir, 'train_signal_subprocess.py') + train_cmd = [ + sys.executable, # Use same Python interpreter + '-u', # Force unbuffered output for real-time streaming + train_script, + '--config', config_path, + '--device', str(device), + '--log_file', analysis_log_path, + '--config_file', config.config_file, + '--error_log', error_details_path, + '--erase' + ] + + # Run training subprocess and stream output + env = os.environ.copy() + env['PYTHONUNBUFFERED'] = '1' + env['TQDM_DISABLE'] = '1' # Disable tqdm in subprocess (doesn't stream well) + + process = subprocess.Popen( + train_cmd, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + text=True, + bufsize=1, + env=env + ) + + # Capture all output for logging while also streaming to console + output_lines = [] + with open(error_log_path, 'w') as output_file: + for line in process.stdout: + output_file.write(line) + output_file.flush() + output_lines.append(line.rstrip()) + # Filter: skip tqdm-like lines (progress bars) + if '|' in line and '%' in line and 'it/s' in line: + continue + print(line, end='', flush=True) + + process.wait() + + if process.returncode != 0: + print(f"\033[91m\ntraining subprocess failed with code {process.returncode}\033[0m") + print("\033[93mthis may indicate a code modification error.\033[0m\n") + + # Show last 20 lines of output for context + print("\033[93mLast 20 lines of output:\033[0m") + print("-" * 80) + for line in output_lines[-20:]: + print(line) + print("-" * 80) + + # Show paths to log files + print(f"\nFull output logged to: {error_log_path}") + if os.path.exists(error_details_path): + print(f"Error details logged to: {error_details_path}") + try: + with open(error_details_path, 'r') as f: + error_details = f.read() + if error_details.strip(): + print("\n\033[91mDetailed error information:\033[0m") + print(error_details) + except Exception as e: + print(f"Could not read error details: {e}") + + raise RuntimeError(f"training failed at iteration {iteration}") + + print("\033[92mtraining subprocess completed successfully\033[0m") else: - # No code modifications - run training directly (faster) - if 'Claude' in task and code_changes_enabled: - print("\033[92mno code modifications - running training directly...\033[0m") + # No cluster and no code modifications - run training directly (fastest) data_train( config=config, erase='Claude' in task, # erase old models when iterating with Claude @@ -608,8 +678,8 @@ block_size=n_iter_block) # generate UCB tree visualization from ucb_scores.txt - # For block 0: save every iteration; for block 1+: save only final tree - should_save_tree = (block_number == 0) or is_block_end + # For block 1: save every iteration; for block 2+: save only at block end + should_save_tree = (block_number == 1) or is_block_end if should_save_tree: ucb_tree_path = f"{tree_save_dir}/ucb_tree_iter_{iteration:03d}.png" nodes = parse_ucb_scores(ucb_path) @@ -651,4 +721,4 @@ title=f"UCB Tree - Iter {iteration}", simulation_info=sim_info) -# bsub -n 8 -gpu "num=1" -q gpu_h100 -Is "python GNN_Daemon.py -o generate_train_test_plot signal_landscape_Claude" \ No newline at end of file +# bsub -n 8 -gpu "num=1" -q gpu_h100 -Is -W 6000 "python GNN_Daemon.py -o generate_train_test_plot signal_landscape_Claude" \ No newline at end of file diff --git a/cluster/experiment1.sh b/cluster/experiment1.sh index ff7ef277..3be908e0 100755 --- a/cluster/experiment1.sh +++ b/cluster/experiment1.sh @@ -1,7 +1,12 @@ #!/bin/bash -#cd /the/place/where/everything/happens -conda run -n neural-graph \ - python /somewhere/do_thework.py -echo "Some success message" +cd /groups/saalfeld/home/allierc/Graph/NeuralGraph + +# Activate conda environment +source /groups/saalfeld/home/allierc/miniforge3/etc/profile.d/conda.sh +conda activate neural-graph + +python GNN_LLM.py + +echo "Experiment completed" diff --git a/cluster/training_cluster.sh b/cluster/training_cluster.sh deleted file mode 100644 index 416d5e40..00000000 --- a/cluster/training_cluster.sh +++ /dev/null @@ -1,87 +0,0 @@ -#!/bin/bash -# -# Cluster training script for GNN_LLM code modifications -# Called via: ssh login1 'bsub -n 8 -gpu "num=1" -q gpu_h100 "Graph/NeuralGraph/cluster/training_cluster.sh"' -# -# This script reads parameters from a job file and runs training on the cluster - -set -e # Exit on error - -# Configuration - adjust these paths for your environment -NEURALGRAPH_DIR="/groups/bhatti/bhattilabb/allierc/Graph/NeuralGraph" -CONDA_ENV="pyg" -RESULTS_DIR="${NEURALGRAPH_DIR}/cluster/results" - -# Job file contains the training parameters (written by GNN_LLM.py before submission) -JOB_FILE="${NEURALGRAPH_DIR}/cluster/current_job.txt" - -# Check job file exists -if [ ! -f "$JOB_FILE" ]; then - echo "Error: Job file not found: $JOB_FILE" - exit 1 -fi - -# Read parameters from job file -CONFIG_PATH=$(grep "^CONFIG_PATH=" "$JOB_FILE" | cut -d'=' -f2-) -DEVICE=$(grep "^DEVICE=" "$JOB_FILE" | cut -d'=' -f2-) -LOG_FILE=$(grep "^LOG_FILE=" "$JOB_FILE" | cut -d'=' -f2-) -CONFIG_FILE=$(grep "^CONFIG_FILE=" "$JOB_FILE" | cut -d'=' -f2-) -ERROR_LOG=$(grep "^ERROR_LOG=" "$JOB_FILE" | cut -d'=' -f2-) -ERASE=$(grep "^ERASE=" "$JOB_FILE" | cut -d'=' -f2-) -ITERATION=$(grep "^ITERATION=" "$JOB_FILE" | cut -d'=' -f2-) - -echo "========================================" -echo "Cluster Training Job - Iteration $ITERATION" -echo "========================================" -echo "Config: $CONFIG_PATH" -echo "Device: $DEVICE" -echo "Log file: $LOG_FILE" -echo "Started at: $(date)" -echo "========================================" - -# Change to NeuralGraph directory -cd "$NEURALGRAPH_DIR" - -# Build command -CMD="python train_signal_subprocess.py --config '$CONFIG_PATH' --device '$DEVICE'" - -if [ -n "$LOG_FILE" ]; then - CMD="$CMD --log_file '$LOG_FILE'" -fi - -if [ -n "$CONFIG_FILE" ]; then - CMD="$CMD --config_file '$CONFIG_FILE'" -fi - -if [ -n "$ERROR_LOG" ]; then - CMD="$CMD --error_log '$ERROR_LOG'" -fi - -if [ "$ERASE" = "true" ]; then - CMD="$CMD --erase" -fi - -echo "Running: $CMD" -echo "" - -# Activate conda and run -source /groups/bhatti/bhattilabb/allierc/miniconda3/etc/profile.d/conda.sh -conda activate "$CONDA_ENV" - -# Run training -eval $CMD -EXIT_CODE=$? - -# Write completion status -DONE_FILE="${NEURALGRAPH_DIR}/cluster/job_done.txt" -echo "ITERATION=$ITERATION" > "$DONE_FILE" -echo "EXIT_CODE=$EXIT_CODE" >> "$DONE_FILE" -echo "COMPLETED_AT=$(date)" >> "$DONE_FILE" - -echo "" -echo "========================================" -echo "Training completed with exit code: $EXIT_CODE" -echo "Finished at: $(date)" -echo "========================================" - -exit $EXIT_CODE diff --git a/config/signal/signal_landscape_Claude.yaml b/config/signal/signal_landscape_Claude.yaml index 26d062cc..d1334830 100644 --- a/config/signal/signal_landscape_Claude.yaml +++ b/config/signal/signal_landscape_Claude.yaml @@ -2,51 +2,47 @@ description: designed by Claude dataset: signal_landscape_Claude simulation: connectivity_type: chaotic - connectivity_rank: 20 connectivity_init: - - 0 - - 0.1 - connectivity_filling_factor: 1.0 - Dale_law: false - Dale_law_factor: 0.5 - noise_model_level: 0 + - 0 + - 0.1 + connectivity_filling_factor: 1 params: - - - 1.0 - - 0.0 - - 7.0 - - 0.0 - - 1.0 - - 0.0 - - - 1.0 - - 0.0 - - 7.0 - - 1.0 - - 1.0 - - 0.0 - - - 2.0 - - 0.0 - - 7.0 - - 1.0 - - 1.0 - - 0.0 - - - 2.0 - - 0.0 - - 7.0 - - 2.0 - - 1.0 - - 0.0 + - - 1.0 + - 0.0 + - 7.0 + - 0.0 + - 1.0 + - 0.0 + - - 1.0 + - 0.0 + - 7.0 + - 1.0 + - 1.0 + - 0.0 + - - 2.0 + - 0.0 + - 7.0 + - 1.0 + - 1.0 + - 0.0 + - - 2.0 + - 0.0 + - 7.0 + - 2.0 + - 1.0 + - 0.0 phi: tanh n_neurons: 100 - n_neuron_types: 2 + n_neuron_types: 1 n_frames: 10000 delta_t: 0.01 dpos_init: 0 - boundary: "no" + boundary: 'no' start_frame: -100 graph_model: signal_model_name: PDE_N4 - particle_model_name: "" - mesh_model_name: "" + particle_model_name: '' + mesh_model_name: '' prediction: first_derivative input_size: 3 output_size: 1 @@ -59,35 +55,33 @@ graph_model: update_type: none claude: n_epochs: 1 - data_augmentation_loop: 40 + data_augmentation_loop: 100 n_iter_block: 8 ucb_c: 1.414 plotting: colormap: tab10 arrow_length: 1 xlim: - - -5 - - 5 + - -5 + - 5 ylim: - - -2 - - 2 + - -2 + - 2 training: n_epochs: 1 n_runs: 1 device: auto batch_size: 8 small_init_batch_size: false - seed: 27 - data_augmentation_loop: 40 + seed: 24 + data_augmentation_loop: 100 sparsity: replace_embedding_function sparsity_freq: 4 cluster_method: distance_plot cluster_distance_threshold: 0.1 fix_cluster_embedding: true - learning_rate_W_start: 0.1 - learning_rate_start: 1.0e-04 - learning_rate_embedding_start: 2.5e-03 + learning_rate_W_start: 0.004 + learning_rate_start: 0.0001 + learning_rate_embedding_start: 0.00025 coeff_W_L1: 1.0e-05 coeff_edge_diff: 100 - low_rank_factorization: false - low_rank: 20 diff --git a/train_signal_subprocess.py b/train_signal_subprocess.py index 87d17904..3f7ec941 100644 --- a/train_signal_subprocess.py +++ b/train_signal_subprocess.py @@ -66,9 +66,9 @@ def main(): # Run training - this will reload any modified code data_train( config=config, - erase=args.erase, - best_model=args.best_model, - style='black', + erase='True', + best_model='', + style='color', device=device, log_file=log_file ) From 9112ad58175604f5d489d636afc87798fbc1140e Mon Sep 17 00:00:00 2001 From: cedric Date: Thu, 22 Jan 2026 14:38:43 -0500 Subject: [PATCH 03/12] update notebook_03 remove loop config_file --- GNN_LLM.py | 2 +- config/signal/signal_landscape_Claude.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/GNN_LLM.py b/GNN_LLM.py index 3b83ad7e..c083bb35 100755 --- a/GNN_LLM.py +++ b/GNN_LLM.py @@ -58,7 +58,7 @@ task_params[key] = int(value) if value.isdigit() else value else: best_model = '' - task = 'generate_train_test_plot_Claude_cluster' # 'train', 'test', 'generate', 'plot', 'train_NGP', 'train_INR', 'Claude', 'code', 'cluster' + task = 'generate_train_test_plot_Claude_code' # 'train', 'test', 'generate', 'plot', 'train_NGP', 'train_INR', 'Claude', 'code', 'cluster' config_list = ['signal_landscape'] task_params = {'iterations': 2048} diff --git a/config/signal/signal_landscape_Claude.yaml b/config/signal/signal_landscape_Claude.yaml index d1334830..c032ea15 100644 --- a/config/signal/signal_landscape_Claude.yaml +++ b/config/signal/signal_landscape_Claude.yaml @@ -80,7 +80,7 @@ training: cluster_method: distance_plot cluster_distance_threshold: 0.1 fix_cluster_embedding: true - learning_rate_W_start: 0.004 + learning_rate_W_start: 0.002 learning_rate_start: 0.0001 learning_rate_embedding_start: 0.00025 coeff_W_L1: 1.0e-05 From 9c49412f853c1afa7ec903d15c193b6674fd5bb0 Mon Sep 17 00:00:00 2001 From: cedric Date: Thu, 22 Jan 2026 18:02:15 -0500 Subject: [PATCH 04/12] training started no code modification yet --- config/signal/signal_landscape_Claude.yaml | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/config/signal/signal_landscape_Claude.yaml b/config/signal/signal_landscape_Claude.yaml index c032ea15..be8b2baa 100644 --- a/config/signal/signal_landscape_Claude.yaml +++ b/config/signal/signal_landscape_Claude.yaml @@ -2,10 +2,13 @@ description: designed by Claude dataset: signal_landscape_Claude simulation: connectivity_type: chaotic + connectivity_rank: 20 connectivity_init: - 0 - 0.1 connectivity_filling_factor: 1 + Dale_law: true + Dale_law_factor: 0.5 params: - - 1.0 - 0.0 @@ -80,8 +83,10 @@ training: cluster_method: distance_plot cluster_distance_threshold: 0.1 fix_cluster_embedding: true - learning_rate_W_start: 0.002 - learning_rate_start: 0.0001 - learning_rate_embedding_start: 0.00025 + learning_rate_W_start: 5.0e-03 + learning_rate_start: 5.0e-04 + learning_rate_embedding_start: 2.5e-04 coeff_W_L1: 1.0e-05 coeff_edge_diff: 100 + low_rank_factorization: false + low_rank: 20 From 9f6bbbc9a1098acee14dd985cb9520e2aaa2f572 Mon Sep 17 00:00:00 2001 From: allierc Date: Sat, 24 Jan 2026 23:27:33 -0500 Subject: [PATCH 05/12] add auto-repair code --- GNN_LLM.py | 188 +++++-- config/signal/signal_landscape_Claude.yaml | 13 +- plot_epistemic_timeline.py | 457 +++++++++++++++- plot_landscape_epistemic.py | 600 +++++++++++++++++++++ 4 files changed, 1183 insertions(+), 75 deletions(-) create mode 100644 plot_landscape_epistemic.py diff --git a/GNN_LLM.py b/GNN_LLM.py index c083bb35..0bd6a5ba 100755 --- a/GNN_LLM.py +++ b/GNN_LLM.py @@ -65,7 +65,7 @@ # resume support: start_iteration parameter (default 1) - start_iteration = 1 + start_iteration = 73 n_iterations = task_params.get('iterations', 5) @@ -421,59 +421,147 @@ '--erase' ] - # Run training subprocess and stream output + # Run training subprocess with repair loop env = os.environ.copy() env['PYTHONUNBUFFERED'] = '1' env['TQDM_DISABLE'] = '1' # Disable tqdm in subprocess (doesn't stream well) - process = subprocess.Popen( - train_cmd, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - text=True, - bufsize=1, - env=env - ) - - # Capture all output for logging while also streaming to console - output_lines = [] - with open(error_log_path, 'w') as output_file: - for line in process.stdout: - output_file.write(line) - output_file.flush() - output_lines.append(line.rstrip()) - # Filter: skip tqdm-like lines (progress bars) - if '|' in line and '%' in line and 'it/s' in line: - continue - print(line, end='', flush=True) - - process.wait() - - if process.returncode != 0: - print(f"\033[91m\ntraining subprocess failed with code {process.returncode}\033[0m") - print("\033[93mthis may indicate a code modification error.\033[0m\n") - - # Show last 20 lines of output for context - print("\033[93mLast 20 lines of output:\033[0m") - print("-" * 80) - for line in output_lines[-20:]: - print(line) - print("-" * 80) - - # Show paths to log files - print(f"\nFull output logged to: {error_log_path}") - if os.path.exists(error_details_path): - print(f"Error details logged to: {error_details_path}") - try: - with open(error_details_path, 'r') as f: - error_details = f.read() - if error_details.strip(): - print("\n\033[91mDetailed error information:\033[0m") - print(error_details) - except Exception as e: - print(f"Could not read error details: {e}") - - raise RuntimeError(f"training failed at iteration {iteration}") + # Code files that Claude might modify + code_files = [ + 'src/NeuralGraph/models/graph_trainer.py', + 'src/NeuralGraph/generators/graph_data_generator.py', + ] + + max_repair_attempts = 10 + training_success = False + error_traceback = None + + for repair_attempt in range(max_repair_attempts + 1): + process = subprocess.Popen( + train_cmd, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + text=True, + bufsize=1, + env=env + ) + + # Capture all output for logging while also streaming to console + output_lines = [] + with open(error_log_path, 'w') as output_file: + for line in process.stdout: + output_file.write(line) + output_file.flush() + output_lines.append(line.rstrip()) + # Filter: skip tqdm-like lines (progress bars) + if '|' in line and '%' in line and 'it/s' in line: + continue + print(line, end='', flush=True) + + process.wait() + + if process.returncode == 0: + training_success = True + break + + # Training failed - capture error info + error_traceback = '\n'.join(output_lines[-50:]) # Last 50 lines + + if repair_attempt == 0: + print(f"\033[91m\ntraining subprocess failed with code {process.returncode}\033[0m") + print("\033[93mthis may indicate a code modification error.\033[0m\n") + + # Show last 20 lines of output for context + print("\033[93mLast 20 lines of output:\033[0m") + print("-" * 80) + for line in output_lines[-20:]: + print(line) + print("-" * 80) + + # Show paths to log files + print(f"\nFull output logged to: {error_log_path}") + if os.path.exists(error_details_path): + print(f"Error details logged to: {error_details_path}") + try: + with open(error_details_path, 'r') as f: + error_details = f.read() + if error_details.strip(): + print("\n\033[91mDetailed error information:\033[0m") + print(error_details) + error_traceback = error_details + '\n' + error_traceback + except Exception as e: + print(f"Could not read error details: {e}") + + # Check if code was modified (only attempt repair for code errors) + modified_code = get_modified_code_files(root_dir, code_files) if is_git_repo(root_dir) else [] + + if not modified_code and repair_attempt == 0: + print("\033[93mNo code modifications detected - skipping repair attempts\033[0m") + break + + # Attempt repair only if code was modified + if repair_attempt < max_repair_attempts and modified_code: + print(f"\033[93mRepair attempt {repair_attempt + 1}/{max_repair_attempts}: Asking Claude to fix the code error...\033[0m") + + repair_prompt = f"""TRAINING CRASHED - Please fix the code error. + +Attempt {repair_attempt + 1}/{max_repair_attempts} + +Error traceback: +``` +{error_traceback[-3000:] if error_traceback else 'No traceback available'} +``` + +Modified code files that may contain the bug: +{chr(10).join(f'- {root_dir}/{f}' for f in modified_code)} + +Instructions: +1. Read the error traceback carefully +2. Identify the bug in the modified code +3. Fix the bug using the Edit tool +4. Do NOT make other changes, only fix the crash + +If you cannot fix it, say "CANNOT_FIX" and explain why.""" + + repair_cmd = [ + 'claude', + '-p', repair_prompt, + '--output-format', 'text', + '--max-turns', '10', + '--allowedTools', 'Read', 'Edit' + ] + + repair_result = subprocess.run(repair_cmd, cwd=root_dir, capture_output=True, text=True) + repair_output = repair_result.stdout + + if 'CANNOT_FIX' in repair_output: + print("\033[91mClaude cannot fix the error\033[0m") + break + + print(f"\033[92mRepair attempt {repair_attempt + 1} complete, retrying training...\033[0m") + + # If still failing after all attempts, rollback and skip iteration + if not training_success: + print("\033[91mAll repair attempts failed - rolling back code changes\033[0m") + + # Rollback modified files using git + if is_git_repo(root_dir): + for file_path in code_files: + try: + subprocess.run(['git', 'checkout', 'HEAD', '--', file_path], + cwd=root_dir, capture_output=True, timeout=10) + except: + pass + print("\033[93mRolled back code to last working state\033[0m") + + # Log failed modification to memory + if os.path.exists(memory_path): + with open(memory_path, 'a') as f: + f.write(f"\n### Failed Code Modification (Iter {iteration})\n") + f.write(f"Error: {error_traceback[-500:] if error_traceback else 'Unknown'}\n") + f.write("**DO NOT retry this modification**\n\n") + + continue # Skip to next iteration print("\033[92mtraining subprocess completed successfully\033[0m") else: diff --git a/config/signal/signal_landscape_Claude.yaml b/config/signal/signal_landscape_Claude.yaml index be8b2baa..8be3b36b 100644 --- a/config/signal/signal_landscape_Claude.yaml +++ b/config/signal/signal_landscape_Claude.yaml @@ -6,9 +6,10 @@ simulation: connectivity_init: - 0 - 0.1 - connectivity_filling_factor: 1 - Dale_law: true + connectivity_filling_factor: 0.75 + Dale_law: false Dale_law_factor: 0.5 + noise_model_level: 0 params: - - 1.0 - 0.0 @@ -58,7 +59,7 @@ graph_model: update_type: none claude: n_epochs: 1 - data_augmentation_loop: 100 + data_augmentation_loop: 500 n_iter_block: 8 ucb_c: 1.414 plotting: @@ -77,14 +78,14 @@ training: batch_size: 8 small_init_batch_size: false seed: 24 - data_augmentation_loop: 100 + data_augmentation_loop: 500 sparsity: replace_embedding_function sparsity_freq: 4 cluster_method: distance_plot cluster_distance_threshold: 0.1 fix_cluster_embedding: true - learning_rate_W_start: 5.0e-03 - learning_rate_start: 5.0e-04 + learning_rate_W_start: 1.5e-02 + learning_rate_start: 1.0e-03 learning_rate_embedding_start: 2.5e-04 coeff_W_L1: 1.0e-05 coeff_edge_diff: 100 diff --git a/plot_epistemic_timeline.py b/plot_epistemic_timeline.py index e1357d9a..c8655ea8 100644 --- a/plot_epistemic_timeline.py +++ b/plot_epistemic_timeline.py @@ -21,11 +21,27 @@ 'Meta-reasoning': '#e91e63', # Pink - strategy adaptation 'Regime': '#795548', # Brown - phase identification 'Uncertainty': '#607d8b', # Gray - stochasticity awareness - 'Causal': '#00bcd4', # Cyan - causal chain + 'Causal Chain': '#00bcd4', # Cyan - causal chain 'Predictive': '#8bc34a', # Light green - predictive modeling 'Constraint': '#ff5722', # Deep orange - constraint propagation } +# Definitions for each reasoning mode (for legend) +DEFINITIONS = { + 'Induction': 'observations → pattern', + 'Abduction': 'observation → hypothesis', + 'Deduction': 'hypothesis → prediction', + 'Falsification': 'prediction failed → refine', + 'Analogy': 'cross-regime transfer', + 'Boundary': 'limit-finding', + 'Meta-reasoning': 'strategy adaptation', + 'Regime': 'phase identification', + 'Uncertainty': 'stochasticity awareness', + 'Causal Chain': 'multi-step causation', + 'Predictive': 'quantitative modeling', + 'Constraint': 'parameter relationships', +} + # Events from epistemic_detailed.md # Format: (iteration, mode, significance) events = [ @@ -270,11 +286,11 @@ (203, 'Uncertainty', 'High'), # Same config: 0.244 vs 0.093 # === Causal: 5 instances === - (48, 'Causal', 'High'), # low_rank→eff_rank=6→R²<0.4 - (64, 'Causal', 'High'), # rank≠eff_rank - (96, 'Causal', 'High'), # n_frames→eff_rank→R² - (127, 'Causal', 'Medium'), # eff_rank=10→R²=0.945 - (224, 'Causal', 'High'), # high n_frames→high eff_rank→high R² + (48, 'Causal Chain', 'High'), # low_rank→eff_rank=6→R²<0.4 + (64, 'Causal Chain', 'High'), # rank≠eff_rank + (96, 'Causal Chain', 'High'), # n_frames→eff_rank→R² + (127, 'Causal Chain', 'Medium'), # eff_rank=10→R²=0.945 + (224, 'Causal Chain', 'High'), # high n_frames→high eff_rank→high R² # === Predictive: 8 instances === (96, 'Predictive', 'High'), # Minimum n_frames ≈ 5000 @@ -321,7 +337,7 @@ (40, 'Deduction', 42, 'Falsification', 'leads_to'), # Best result → robustness test (42, 'Falsification', 42, 'Uncertainty', 'triggers'), # Failed test → stochasticity recognized (42, 'Uncertainty', 48, 'Induction', 'leads_to'), # Stochasticity → eff_rank principle - (42, 'Regime', 48, 'Causal', 'triggers'), # eff_rank=6 regime → causal model + (42, 'Regime', 48, 'Causal Chain', 'triggers'), # eff_rank=6 regime → causal model # === Block 4 (rank=50, iters 49-64) === (49, 'Deduction', 49, 'Falsification', 'triggers'), # Prediction failed → reject hypothesis @@ -338,7 +354,7 @@ (81, 'Deduction', 82, 'Falsification', 'triggers'), # Partial → failure on next iter (82, 'Falsification', 91, 'Regime', 'triggers'), # Repeated eff_rank=6 → regime recognition (91, 'Regime', 96, 'Induction', 'leads_to'), # Below threshold recognized → principle - (91, 'Falsification', 96, 'Causal', 'leads_to'), # Factorization fails → causal understanding + (91, 'Falsification', 96, 'Causal Chain', 'leads_to'), # Factorization fails → causal understanding # === Block 7 (7500 frames, iters 97-111) === (98, 'Deduction', 101, 'Boundary', 'triggers'), # Convergence → probe upper bound @@ -402,7 +418,7 @@ (32, 'Induction', 49, 'Analogy', 'triggers'), # Dale_law findings → rank test (32, 'Induction', 112, 'Analogy', 'triggers'), # Block 2 findings → Block 8 retry (48, 'Induction', 65, 'Abduction', 'triggers'), # eff_rank→R² → n_frames hypothesis - (48, 'Causal', 96, 'Causal', 'refines'), # eff_rank ceiling → minimum n_frames + (48, 'Causal Chain', 96, 'Causal Chain', 'refines'), # eff_rank ceiling → minimum n_frames (64, 'Constraint', 144, 'Constraint', 'refines'), # rank≠eff_rank → 3x rule (80, 'Induction', 97, 'Deduction', 'triggers'), # n_frames relationship → intermediate test (96, 'Regime', 112, 'Deduction', 'triggers'), # Data threshold → high data retry @@ -448,7 +464,7 @@ def create_timeline(): # Meta-cognition 'Analogy', 'Meta-reasoning', 'Regime', 'Uncertainty', # Advanced patterns - 'Constraint', 'Predictive', 'Causal', + 'Constraint', 'Predictive', 'Causal Chain', ] mode_to_y = {mode: i for i, mode in enumerate(modes)} @@ -492,20 +508,15 @@ def create_timeline(): ax.set_xlim(0, 260) ax.set_ylim(-0.5, len(modes) + 2.5) ax.set_yticks(range(len(modes))) - ax.set_yticklabels(modes, fontsize=14) + # Y-tick labels with definitions in parenthesis + ytick_labels = [f"{mode} ({DEFINITIONS.get(mode, '')})" for mode in modes] + ax.set_yticklabels(ytick_labels, fontsize=11) ax.set_xlabel('Iteration', fontsize=20) ax.set_ylabel('Reasoning Mode', fontsize=20) ax.tick_params(axis='x', labelsize=12) ax.grid(True, axis='x', alpha=0.3, linestyle='--') - # Legend for modes - legend_patches = [mpatches.Patch(color=COLORS[mode], label=mode) - for mode in modes if mode in COLORS] - legend1 = ax.legend(handles=legend_patches, loc='upper left', bbox_to_anchor=(1.01, 1), - title='Reasoning Modes', fontsize=8) - ax.add_artist(legend1) - # Legend for node sizes from matplotlib.lines import Line2D size_legend = [ @@ -524,8 +535,405 @@ def create_timeline(): plt.close() +def create_streamgraph(): + """ + Create a streamgraph visualization showing reasoning mode activity over iterations. + Streamgraph is a stacked area chart with a wiggle baseline for aesthetic flow. + """ + import numpy as np + from scipy.ndimage import gaussian_filter1d + + # Define modes in order (for stacking) + modes = [ + 'Induction', 'Boundary', 'Abduction', 'Deduction', 'Falsification', + 'Analogy', 'Meta-reasoning', 'Regime', 'Uncertainty', + 'Constraint', 'Predictive', 'Causal Chain', + ] + + # Create iteration bins + max_iter = max(e[0] for e in events) + bin_width = 8 # Aggregate events into bins + n_bins = (max_iter // bin_width) + 1 + iterations = np.arange(0, n_bins * bin_width, bin_width) + + # Count events per mode per bin + data = np.zeros((len(modes), n_bins)) + for iteration, mode, significance in events: + if mode in modes: + mode_idx = modes.index(mode) + bin_idx = iteration // bin_width + if bin_idx < n_bins: + # Weight by significance + weight = {'High': 2.0, 'Medium': 1.0, 'Low': 0.5}.get(significance, 1.0) + data[mode_idx, bin_idx] += weight + + # Smooth the data for aesthetic flow + sigma = 1.0 + data_smooth = np.array([gaussian_filter1d(row, sigma) for row in data]) + + # Compute streamgraph baseline (wiggle/ThemeRiver) + # Baseline offset to minimize weighted wiggle + n_layers = len(modes) + baseline = np.zeros(n_bins) + for i in range(n_layers): + baseline -= (n_layers - i - 0.5) * data_smooth[i] + baseline /= n_layers + + # Stack the layers + y_stack = np.vstack([baseline, baseline + np.cumsum(data_smooth, axis=0)]) + + # Create figure + fig, ax = plt.subplots(figsize=(24, 10)) + + # Plot each layer + for i, mode in enumerate(modes): + color = COLORS.get(mode, '#333333') + ax.fill_between(iterations, y_stack[i], y_stack[i + 1], + color=color, alpha=0.8, label=f"{mode} ({DEFINITIONS.get(mode, '')})", + edgecolor='white', linewidth=0.3) + + # Add block boundaries as vertical lines + for start, end, label, info in blocks: + ax.axvline(x=start, color='gray', linewidth=0.5, linestyle='--', alpha=0.3) + + # Styling + ax.set_xlim(0, max_iter + 5) + ax.set_xlabel('Iteration', fontsize=18) + ax.set_ylabel('Reasoning Activity (weighted)', fontsize=18) + ax.tick_params(axis='both', labelsize=12) + + # Legend with definitions + ax.legend(loc='upper left', bbox_to_anchor=(1.01, 1), fontsize=9, + title='Reasoning Modes', title_fontsize=11) + + ax.set_facecolor('#f8f8f8') + ax.grid(True, axis='x', alpha=0.3, linestyle='--') + + plt.tight_layout() + plt.savefig('signal_chaotic_1_Claude_epistemic_streamgraph.png', dpi=150, bbox_inches='tight', + pad_inches=0.5) + print("Saved: signal_chaotic_1_Claude_epistemic_streamgraph.png") + plt.close() + + +def create_sankey_diagram(): + """ + Create a Sankey diagram showing causal flows between reasoning modes. + Uses Plotly for interactive visualization. + """ + try: + import plotly.graph_objects as go + except ImportError: + print("Plotly not installed. Install with: pip install plotly") + return + + from collections import Counter + + # Count transitions between modes from edges + transitions = Counter() + for from_iter, from_mode, to_iter, to_mode, edge_type in edges: + transitions[(from_mode, to_mode)] += 1 + + # Define modes (nodes) + modes = list(COLORS.keys()) + mode_to_idx = {mode: i for i, mode in enumerate(modes)} + + # Build source, target, value lists for Sankey + source = [] + target = [] + value = [] + link_colors = [] + + for (from_mode, to_mode), count in transitions.items(): + if from_mode in mode_to_idx and to_mode in mode_to_idx: + source.append(mode_to_idx[from_mode]) + target.append(mode_to_idx[to_mode]) + value.append(count) + # Use source mode color with transparency + color = COLORS.get(from_mode, '#888888') + # Convert hex to rgba + r, g, b = int(color[1:3], 16), int(color[3:5], 16), int(color[5:7], 16) + link_colors.append(f'rgba({r},{g},{b},0.4)') + + # Node colors + node_colors = [COLORS.get(mode, '#888888') for mode in modes] + + # Node labels with definitions + node_labels = [f"{mode}
({DEFINITIONS.get(mode, '')})" for mode in modes] + + # Create Sankey diagram + fig = go.Figure(data=[go.Sankey( + node=dict( + pad=15, + thickness=20, + line=dict(color='black', width=0.5), + label=node_labels, + color=node_colors, + ), + link=dict( + source=source, + target=target, + value=value, + color=link_colors, + ) + )]) + + fig.update_layout( + title_text="Epistemic Reasoning Flow: signal_chaotic_1_Claude", + font_size=12, + width=1200, + height=800, + ) + + # Save as HTML (interactive) and PNG (static) + fig.write_html('signal_chaotic_1_Claude_epistemic_sankey.html') + print("Saved: signal_chaotic_1_Claude_epistemic_sankey.html") + + try: + fig.write_image('signal_chaotic_1_Claude_epistemic_sankey.png', scale=2) + print("Saved: signal_chaotic_1_Claude_epistemic_sankey.png") + except Exception as e: + print(f"Could not save PNG (kaleido may be needed): {e}") + + +def create_2x2_panels(): + """ + Create a 2x3 panel figure: + - Top left: Scatterplot (timeline) + - Top middle: Legend with colors and definitions + - Top right: Summary statistics + - Bottom left: Streamgraph + - Bottom right: Sankey Diagram (reference to HTML) + + Only top middle and top right panels have text; others show just colors. + """ + import numpy as np + from scipy.ndimage import gaussian_filter1d + from collections import Counter + from matplotlib.gridspec import GridSpec + + fig = plt.figure(figsize=(28, 16)) + gs = GridSpec(2, 3, figure=fig, width_ratios=[2, 1, 1], height_ratios=[1, 1]) + + # Define modes in order + modes = [ + 'Induction', 'Boundary', 'Abduction', 'Deduction', 'Falsification', + 'Analogy', 'Meta-reasoning', 'Regime', 'Uncertainty', + 'Constraint', 'Predictive', 'Causal Chain', + ] + mode_to_y = {mode: i for i, mode in enumerate(modes)} + + # ============ TOP LEFT: Scatterplot ============ + ax1 = fig.add_subplot(gs[0, 0]) + ax1.set_title('Scatterplot', fontsize=18) + + # Draw block backgrounds + for block_idx, (start, end, label, info) in enumerate(blocks): + color = '#f0f0f0' if block_idx % 2 == 1 else 'white' + ax1.axvspan(start - 0.5, end + 0.5, alpha=1.0, color=color) + + # Plot events + for iteration, mode, significance in events: + if mode not in mode_to_y: + continue + y = mode_to_y[mode] + color = COLORS.get(mode, '#333333') + size = {'High': 150, 'Medium': 80, 'Low': 40}.get(significance, 80) + ax1.scatter(iteration, y, c=color, s=size, alpha=0.9, + edgecolors='black', linewidths=0.3, zorder=3) + + # Minimal styling (no labels) + ax1.set_xlim(0, 260) + ax1.set_ylim(-0.5, len(modes) - 0.5) + ax1.set_yticks([]) + ax1.set_xticks([]) + ax1.grid(True, axis='x', alpha=0.2, linestyle='--') + + # ============ TOP MIDDLE: Legend Panel ============ + ax2 = fig.add_subplot(gs[0, 1]) + ax2.set_title('Legend', fontsize=18) + ax2.axis('off') + + # Create legend entries + y_pos = 0.95 + y_step = 0.07 + + for mode in modes: + color = COLORS.get(mode, '#333333') + definition = DEFINITIONS.get(mode, '') + + # Color circle + ax2.scatter([0.08], [y_pos], c=color, s=250, edgecolors='black', linewidths=1) + # Mode name and definition + ax2.text(0.18, y_pos, f"{mode}", fontsize=11, va='center') + ax2.text(0.18, y_pos - 0.028, f"({definition})", fontsize=9, va='center', color='#666666') + + y_pos -= y_step + + ax2.set_xlim(0, 1) + ax2.set_ylim(0, 1) + + # ============ TOP RIGHT: Summary Panel ============ + ax_summary = fig.add_subplot(gs[0, 2]) + ax_summary.set_title('Summary', fontsize=18) + ax_summary.axis('off') + + # Count statistics from events + mode_counts = Counter(e[1] for e in events) + total_events = len(events) + total_edges = len(edges) + + # Calculate deduction validation rate (from the data) + deduction_validated = 22 # from analysis + deduction_total = 31 + deduction_rate = f"{100 * deduction_validated / deduction_total:.0f}%" + + # Transfer success rate + transfer_success = 9 + transfer_total = 12 + transfer_rate = f"{100 * transfer_success / transfer_total:.0f}%" + + summary_text = f"""Epistemic Analysis + +{total_events} reasoning instances +60 iterations, 8 blocks +10 principles discovered + +Deduction validation: {deduction_rate} +Transfer success: {transfer_rate} + +Mode counts: + Induction: {mode_counts.get('Induction', 0)} + Abduction: {mode_counts.get('Abduction', 0)} + Deduction: {mode_counts.get('Deduction', 0)} + Falsification: {mode_counts.get('Falsification', 0)} + Analogy: {mode_counts.get('Analogy', 0)} + Boundary: {mode_counts.get('Boundary', 0)} + +{total_edges} causal edges +4 key causal chains + +See HTML for interactive Sankey""" + + ax_summary.text(0.05, 0.95, summary_text, fontsize=10, va='top', ha='left', + family='monospace', linespacing=1.4) + ax_summary.set_xlim(0, 1) + ax_summary.set_ylim(0, 1) + + # ============ BOTTOM LEFT: Streamgraph ============ + ax3 = fig.add_subplot(gs[1, 0]) + ax3.set_title('Streamgraph', fontsize=18) + + # Create iteration bins + max_iter = max(e[0] for e in events) + bin_width = 8 + n_bins = (max_iter // bin_width) + 1 + iterations = np.arange(0, n_bins * bin_width, bin_width) + + # Count events per mode per bin + data = np.zeros((len(modes), n_bins)) + for iteration, mode, significance in events: + if mode in modes: + mode_idx = modes.index(mode) + bin_idx = iteration // bin_width + if bin_idx < n_bins: + weight = {'High': 2.0, 'Medium': 1.0, 'Low': 0.5}.get(significance, 1.0) + data[mode_idx, bin_idx] += weight + + # Smooth the data + sigma = 1.0 + data_smooth = np.array([gaussian_filter1d(row, sigma) for row in data]) + + # Compute streamgraph baseline (wiggle) + n_layers = len(modes) + baseline = np.zeros(n_bins) + for i in range(n_layers): + baseline -= (n_layers - i - 0.5) * data_smooth[i] + baseline /= n_layers + + # Stack the layers + y_stack = np.vstack([baseline, baseline + np.cumsum(data_smooth, axis=0)]) + + # Plot each layer (no labels) + for i, mode in enumerate(modes): + color = COLORS.get(mode, '#333333') + ax3.fill_between(iterations, y_stack[i], y_stack[i + 1], + color=color, alpha=0.8, + edgecolor='white', linewidth=0.3) + + # Minimal styling + ax3.set_xlim(0, max_iter + 5) + ax3.set_yticks([]) + ax3.set_xticks([]) + ax3.set_facecolor('#f8f8f8') + + # ============ BOTTOM RIGHT: Sankey-like flow (spans 2 columns) ============ + ax4 = fig.add_subplot(gs[1, 1:]) + ax4.set_title('Sankey Diagram (see HTML for interactive version)', fontsize=18) + + # Count transitions between modes from edges + transitions = Counter() + for from_iter, from_mode, to_iter, to_mode, edge_type in edges: + transitions[(from_mode, to_mode)] += 1 + + # Position modes in a circle + n_modes = len(modes) + angles = np.linspace(0, 2 * np.pi, n_modes, endpoint=False) + radius = 0.35 + center = (0.5, 0.5) + + # Draw nodes + for i, mode in enumerate(modes): + x = center[0] + radius * np.cos(angles[i]) + y = center[1] + radius * np.sin(angles[i]) + color = COLORS.get(mode, '#333333') + ax4.scatter([x], [y], c=color, s=800, edgecolors='black', linewidths=1.5, zorder=5) + + # Draw connections (curved lines) + from matplotlib.path import Path + import matplotlib.patches as mpatches + + for (from_mode, to_mode), count in transitions.items(): + if from_mode in modes and to_mode in modes: + from_idx = modes.index(from_mode) + to_idx = modes.index(to_mode) + + x1 = center[0] + radius * np.cos(angles[from_idx]) + y1 = center[1] + radius * np.sin(angles[from_idx]) + x2 = center[0] + radius * np.cos(angles[to_idx]) + y2 = center[1] + radius * np.sin(angles[to_idx]) + + # Control point for curve (towards center) + cx = center[0] + 0.1 * (x1 + x2 - 2 * center[0]) + cy = center[1] + 0.1 * (y1 + y2 - 2 * center[1]) + + color = COLORS.get(from_mode, '#888888') + alpha = min(0.3 + count * 0.1, 0.8) + linewidth = 1 + count * 0.5 + + # Draw quadratic bezier curve + verts = [(x1, y1), (cx, cy), (x2, y2)] + codes = [Path.MOVETO, Path.CURVE3, Path.CURVE3] + path = Path(verts, codes) + patch = mpatches.PathPatch(path, facecolor='none', edgecolor=color, + alpha=alpha, linewidth=linewidth) + ax4.add_patch(patch) + + ax4.set_xlim(0, 1) + ax4.set_ylim(0, 1) + ax4.set_xticks([]) + ax4.set_yticks([]) + ax4.set_aspect('equal') + + plt.tight_layout() + plt.savefig('signal_chaotic_1_Claude_epistemic_2x2_panels.png', dpi=150, bbox_inches='tight', + pad_inches=0.3) + print("Saved: signal_chaotic_1_Claude_epistemic_2x2_panels.png") + plt.close() + + if __name__ == '__main__': - print("Creating epistemic timeline visualization...") + print("Creating epistemic timeline visualizations...") print(f"Total events: {len(events)}") print(f"Total edges: {len(edges)}") @@ -534,5 +942,16 @@ def create_timeline(): for mode, count in sorted(counts.items()): print(f" {mode}: {count}") + print("\n--- Timeline ---") create_timeline() + + print("\n--- Streamgraph ---") + create_streamgraph() + + print("\n--- Sankey Diagram ---") + create_sankey_diagram() + + print("\n--- 2x2 Panels ---") + create_2x2_panels() + print("\nDone!") diff --git a/plot_landscape_epistemic.py b/plot_landscape_epistemic.py new file mode 100644 index 00000000..9fb51725 --- /dev/null +++ b/plot_landscape_epistemic.py @@ -0,0 +1,600 @@ +#!/usr/bin/env python3 +""" +Visualize epistemic reasoning timeline from signal_landscape_Claude experiment. +67 iterations across 9 blocks. +""" + +import matplotlib +matplotlib.use('Agg') +import matplotlib.pyplot as plt +import numpy as np +from scipy.ndimage import gaussian_filter1d +from collections import Counter +from matplotlib.gridspec import GridSpec +from matplotlib.path import Path +import matplotlib.patches as mpatches + +# Define color scheme for reasoning modes +COLORS = { + 'Induction': '#2ecc71', # Green + 'Abduction': '#9b59b6', # Purple + 'Deduction': '#3498db', # Blue + 'Falsification': '#e74c3c', # Red + 'Analogy': '#f39c12', # Orange + 'Boundary': '#1abc9c', # Teal + 'Meta-reasoning': '#e91e63', # Pink + 'Regime': '#795548', # Brown + 'Uncertainty': '#607d8b', # Gray + 'Causal Chain': '#00bcd4', # Cyan + 'Predictive': '#8bc34a', # Light green + 'Constraint': '#ff5722', # Deep orange +} + +DEFINITIONS = { + 'Induction': 'observations -> pattern', + 'Abduction': 'observation -> hypothesis', + 'Deduction': 'hypothesis -> prediction', + 'Falsification': 'prediction failed -> refine', + 'Analogy': 'cross-regime transfer', + 'Boundary': 'limit-finding', + 'Meta-reasoning': 'strategy adaptation', + 'Regime': 'phase identification', + 'Uncertainty': 'stochasticity awareness', + 'Causal Chain': 'multi-step causation', + 'Predictive': 'quantitative modeling', + 'Constraint': 'parameter relationships', +} + +# Events from signal_landscape_Claude_epistemic_detailed.md +# 60 iterations, 8 blocks +events = [ + # Block 1: Chaotic baseline (iters 1-8) + (4, 'Induction', 'Medium'), # lr_W 5x range + (4, 'Deduction', 'High'), # lr_W boundary prediction + (4, 'Boundary', 'Medium'), # lr_W approaching + (5, 'Boundary', 'Medium'), # L1 probe + (5, 'Meta-reasoning', 'Medium'), # dimension switch + (6, 'Boundary', 'Medium'), # L1 tolerance + (7, 'Deduction', 'Medium'), # L1 extreme test + (7, 'Boundary', 'Medium'), # L1 boundary + (8, 'Induction', 'High'), # Block summary: 100x L1 range + (8, 'Deduction', 'Medium'), # L1=1E-3 test + (8, 'Falsification', 'High'), # L1 boundary not found + + # Block 2: Low-rank (iters 9-16) + (9, 'Abduction', 'High'), # eff_rank hypothesis + (9, 'Regime', 'High'), # Low_rank regime discovered + (10, 'Deduction', 'High'), # Factorization test + (10, 'Falsification', 'High'), # Factorization hurts + (10, 'Abduction', 'Medium'), # Over-constrains model + (11, 'Deduction', 'Medium'), # Higher lr_W + (11, 'Induction', 'Medium'), # lr_W improvement + (12, 'Deduction', 'High'), # lr_W=8E-3 test + (12, 'Falsification', 'High'), # lr_W boundary found + (12, 'Boundary', 'High'), # lr_W upper limit + (12, 'Constraint', 'High'), # low_rank lr_W constraint + (14, 'Meta-reasoning', 'Medium'), # Multi-param violation + (14, 'Falsification', 'Medium'), # L1+aug didn't help + (15, 'Deduction', 'High'), # lr breakthrough + (15, 'Causal Chain', 'High'), # lr mechanism + (15, 'Abduction', 'High'), # MLP lr critical + (16, 'Deduction', 'Medium'), # lr=1E-3 confirmed + (16, 'Induction', 'High'), # Block summary + + # Block 3: Dale's law (iters 17-24) + (17, 'Analogy', 'High'), # Block 2 transfer + (17, 'Deduction', 'High'), # Settings transfer test + (17, 'Abduction', 'High'), # Dale reduces eff_rank + (17, 'Regime', 'High'), # Dale regime + (17, 'Falsification', 'High'), # Dale is easy + (18, 'Deduction', 'High'), # lr_W=8E-3 works + (19, 'Deduction', 'High'), # lr_W=2E-2 works + (19, 'Boundary', 'Medium'), # lr_W probe + (19, 'Induction', 'Medium'), # Dale robust + (20, 'Deduction', 'High'), # lr_W=5E-2 works + (20, 'Boundary', 'Medium'), # lr_W extreme + (21, 'Boundary', 'Medium'), # L1=1E-3 test + (22, 'Deduction', 'High'), # lr_W=1E-1 works + (22, 'Boundary', 'Medium'), # More extreme + (23, 'Boundary', 'Medium'), # lr_W=2E-1 + (24, 'Deduction', 'High'), # lr_W=5E-1 works + (24, 'Boundary', 'High'), # 100x range found + (24, 'Induction', 'High'), # Block summary + + # Block 4: Heterogeneous n_types=2 (iters 25-32) + (25, 'Analogy', 'High'), # Blocks 2-3 transfer + (25, 'Deduction', 'High'), # Transfer test + (27, 'Deduction', 'Medium'), # lr_W=1E-2 works + (27, 'Boundary', 'Medium'), # lr_W probe + (27, 'Induction', 'Medium'), # Heterogeneous robust + (28, 'Deduction', 'High'), # lr_W=5E-2 test + (28, 'Falsification', 'High'), # Embedding boundary + (28, 'Abduction', 'High'), # lr_W starves embedding + (28, 'Boundary', 'High'), # lr_W boundary + (28, 'Causal Chain', 'High'), # Dual-objective conflict + (28, 'Constraint', 'High'), # n_types lr_W constraint + (29, 'Deduction', 'High'), # lr_W=2E-2 restores + (29, 'Boundary', 'Medium'), # Boundary narrowed + (30, 'Abduction', 'Medium'), # L1 hurts embedding + (30, 'Falsification', 'Medium'), # L1 effect + (31, 'Deduction', 'High'), # lr_emb compensation + (31, 'Abduction', 'Medium'), # lr_emb compensates + (31, 'Causal Chain', 'Medium'), # Compensation mechanism + (31, 'Analogy', 'Medium'), # lr_emb insight + (32, 'Deduction', 'High'), # L1=5E-4 with lr_emb + (32, 'Induction', 'High'), # Block summary + + # Block 5: Noise (iters 33-40) + (33, 'Analogy', 'High'), # Block 3-4 transfer + (33, 'Deduction', 'High'), # Noise test + (33, 'Abduction', 'High'), # Noise adds variance + (33, 'Regime', 'High'), # Noise regime + (33, 'Causal Chain', 'High'), # Noise mechanism + (33, 'Predictive', 'High'), # eff_rank prediction + (33, 'Induction', 'High'), # Noise increases eff_rank + (34, 'Deduction', 'High'), # lr_W=1E-2 works + (34, 'Induction', 'Medium'), # Noise tolerates lr_W + (36, 'Deduction', 'High'), # lr_W=5E-2 works + (36, 'Boundary', 'Medium'), # lr_W probe + (38, 'Deduction', 'High'), # lr_W=1E-1 works + (38, 'Boundary', 'Medium'), # More extreme + (39, 'Boundary', 'Medium'), # L1=5E-4 + (39, 'Induction', 'Medium'), # 7/7 converged + (40, 'Deduction', 'High'), # L1=1E-3 works + (40, 'Boundary', 'High'), # No boundary found + (40, 'Induction', 'High'), # Block summary: super easy + (40, 'Regime', 'High'), # Super easy regime + + # Block 6: Low_rank + n_types=2 (iters 41-48) + (41, 'Analogy', 'Medium'), # Block 2+4 transfer + (41, 'Abduction', 'High'), # Compound difficulty + (41, 'Regime', 'High'), # Compound regime + (41, 'Predictive', 'Medium'), # eff_rank prediction + (42, 'Analogy', 'High'), # lr insight transfer + (43, 'Deduction', 'High'), # lr_W=8E-3, lr=1E-3 + (43, 'Induction', 'Medium'), # Both lr tuned + (43, 'Analogy', 'High'), # Combined solution + (44, 'Deduction', 'High'), # lr_W=1E-2 works + (44, 'Boundary', 'Medium'), # lr_W probe + (44, 'Induction', 'Medium'), # lr_W upper higher + (45, 'Deduction', 'High'), # lr_W=2E-2 test + (45, 'Falsification', 'High'), # lr_W boundary + (45, 'Boundary', 'High'), # lr_W upper found + (45, 'Constraint', 'High'), # Compound lr_W constraint + (46, 'Deduction', 'Medium'), # lr_emb probe + (47, 'Falsification', 'High'), # L1=1E-4 fails + (47, 'Abduction', 'High'), # L1 tolerance narrow + (47, 'Boundary', 'High'), # L1 boundary + (47, 'Constraint', 'High'), # L1 constraint + (48, 'Falsification', 'High'), # lr=5E-4 insufficient + (48, 'Constraint', 'High'), # lr requirement + (48, 'Induction', 'High'), # Block summary + + # Block 7: Sparse (iters 49-56) + (49, 'Abduction', 'High'), # Sparse collapse + (49, 'Regime', 'High'), # Sparse regime + (49, 'Causal Chain', 'High'), # Collapse mechanism + (49, 'Predictive', 'High'), # eff_rank<8 prediction + (49, 'Uncertainty', 'Medium'), # eff_rank varies + (50, 'Deduction', 'High'), # lr=1E-3 test + (50, 'Falsification', 'High'), # Training can't fix + (50, 'Abduction', 'High'), # Problem is data + (50, 'Analogy', 'Medium'), # Block 2 insight + (51, 'Deduction', 'High'), # lr_W boost test + (51, 'Falsification', 'High'), # Still fails + (51, 'Meta-reasoning', 'High'), # Recognized futility + (51, 'Induction', 'Medium'), # 3 failures + (52, 'Deduction', 'High'), # Factorization test + (52, 'Falsification', 'High'), # Made worse + (52, 'Abduction', 'Medium'), # Constraints can't fix + (53, 'Meta-reasoning', 'High'), # Strategy exhaustion + (54, 'Falsification', 'High'), # L1=0 worse + (54, 'Abduction', 'Medium'), # Overfits to noise + (54, 'Uncertainty', 'Medium'), # eff_rank dropped + (55, 'Falsification', 'High'), # L1 restore fails + (56, 'Deduction', 'High'), # Scale-up test + (56, 'Falsification', 'High'), # Still fails + (56, 'Induction', 'High'), # Block summary: unrecoverable + + # Block 8: Sparse + Noise (iters 57-64) + (57, 'Analogy', 'High'), # Block 5 noise insight + (57, 'Deduction', 'High'), # Noise rescue test + (57, 'Causal Chain', 'High'), # Rescue mechanism + (57, 'Regime', 'High'), # Rescued regime + (57, 'Predictive', 'High'), # eff_rank=92 + (57, 'Induction', 'High'), # Noise rescues sparse + (57, 'Analogy', 'Medium'), # lr insight applied + (58, 'Falsification', 'Medium'), # lr_W plateau + (59, 'Meta-reasoning', 'Medium'), # Dimension switch + (59, 'Boundary', 'Medium'), # lr_W plateau + (60, 'Falsification', 'Medium'), # L1 plateau + (60, 'Boundary', 'Medium'), # L1 plateau + (60, 'Induction', 'Medium'), # Plateau at R2~0.20 + (60, 'Uncertainty', 'Medium'), # May be fundamental limit + (61, 'Induction', 'High'), # 5 consecutive partial plateau + (62, 'Deduction', 'High'), # Scale-up test + (62, 'Falsification', 'High'), # Scale-up failed + (63, 'Induction', 'High'), # 7 consecutive partial + (64, 'Induction', 'High'), # 8 consecutive partial - definitive + + # Block 9: Intermediate sparsity ff=0.5 (iters 65-67) + (65, 'Analogy', 'High'), # Block 8 failure → ff=0.5 hypothesis + (65, 'Regime', 'High'), # ff=0.5 regime discovered + (65, 'Deduction', 'High'), # eff_rank hypothesis validated + (65, 'Induction', 'High'), # eff_rank=26 vs ff=0.2 eff_rank=6 + (66, 'Deduction', 'High'), # lr_W=1E-2 test + (66, 'Falsification', 'High'), # lr_W hurt test_pearson + (67, 'Deduction', 'High'), # L1=0 test + (67, 'Induction', 'Medium'), # test_pearson recovered but W stuck +] + +# Causal edges from signal_landscape_Claude_epistemic_edges.md +edges = [ + # Block 1 + (4, 'Deduction', 8, 'Induction', 'leads_to'), + (4, 'Boundary', 5, 'Meta-reasoning', 'triggers'), + (7, 'Deduction', 8, 'Falsification', 'leads_to'), + (8, 'Induction', 9, 'Analogy', 'triggers'), + + # Block 2 + (9, 'Abduction', 10, 'Deduction', 'triggers'), + (10, 'Deduction', 10, 'Falsification', 'leads_to'), + (10, 'Falsification', 11, 'Induction', 'refines'), + (11, 'Deduction', 12, 'Deduction', 'leads_to'), + (12, 'Deduction', 12, 'Falsification', 'leads_to'), + (12, 'Falsification', 12, 'Boundary', 'leads_to'), + (12, 'Boundary', 15, 'Deduction', 'triggers'), + (15, 'Deduction', 15, 'Causal Chain', 'leads_to'), + (15, 'Causal Chain', 16, 'Induction', 'leads_to'), + (16, 'Induction', 17, 'Analogy', 'triggers'), + + # Block 3 + (17, 'Analogy', 17, 'Deduction', 'triggers'), + (17, 'Deduction', 17, 'Falsification', 'leads_to'), + (17, 'Abduction', 19, 'Deduction', 'triggers'), + (19, 'Deduction', 24, 'Boundary', 'leads_to'), + (24, 'Boundary', 24, 'Induction', 'leads_to'), + (24, 'Induction', 25, 'Analogy', 'triggers'), + + # Block 4 + (25, 'Analogy', 25, 'Deduction', 'triggers'), + (27, 'Deduction', 28, 'Deduction', 'leads_to'), + (28, 'Deduction', 28, 'Falsification', 'leads_to'), + (28, 'Falsification', 28, 'Abduction', 'triggers'), + (28, 'Abduction', 29, 'Deduction', 'triggers'), + (28, 'Falsification', 28, 'Boundary', 'leads_to'), + (30, 'Abduction', 31, 'Deduction', 'triggers'), + (31, 'Deduction', 31, 'Causal Chain', 'leads_to'), + (32, 'Induction', 33, 'Analogy', 'triggers'), + + # Block 5 + (33, 'Analogy', 33, 'Deduction', 'triggers'), + (33, 'Abduction', 33, 'Causal Chain', 'leads_to'), + (33, 'Regime', 34, 'Deduction', 'triggers'), + (36, 'Deduction', 40, 'Boundary', 'leads_to'), + (40, 'Boundary', 40, 'Induction', 'leads_to'), + (40, 'Induction', 41, 'Analogy', 'triggers'), + + # Block 6 + (41, 'Analogy', 42, 'Analogy', 'triggers'), + (42, 'Analogy', 43, 'Deduction', 'triggers'), + (43, 'Deduction', 44, 'Deduction', 'leads_to'), + (45, 'Deduction', 45, 'Falsification', 'leads_to'), + (45, 'Falsification', 45, 'Constraint', 'leads_to'), + (47, 'Falsification', 47, 'Constraint', 'leads_to'), + (48, 'Induction', 49, 'Analogy', 'triggers'), + + # Block 7 + (49, 'Abduction', 49, 'Causal Chain', 'leads_to'), + (49, 'Causal Chain', 50, 'Deduction', 'triggers'), + (50, 'Deduction', 50, 'Falsification', 'leads_to'), + (50, 'Falsification', 51, 'Meta-reasoning', 'triggers'), + (51, 'Meta-reasoning', 52, 'Deduction', 'leads_to'), + (52, 'Deduction', 52, 'Falsification', 'leads_to'), + (54, 'Falsification', 56, 'Induction', 'refines'), + (56, 'Induction', 57, 'Analogy', 'triggers'), + + # Block 8 + (57, 'Analogy', 57, 'Deduction', 'triggers'), + (57, 'Deduction', 57, 'Causal Chain', 'leads_to'), + (57, 'Regime', 58, 'Deduction', 'triggers'), + (58, 'Deduction', 58, 'Falsification', 'leads_to'), + (58, 'Falsification', 59, 'Meta-reasoning', 'triggers'), + (60, 'Falsification', 60, 'Induction', 'refines'), + (61, 'Induction', 62, 'Deduction', 'leads_to'), + (62, 'Deduction', 62, 'Falsification', 'leads_to'), + (62, 'Falsification', 63, 'Induction', 'refines'), + (63, 'Induction', 64, 'Induction', 'leads_to'), + (64, 'Induction', 65, 'Analogy', 'triggers'), + + # Block 9 + (65, 'Regime', 65, 'Deduction', 'triggers'), + (65, 'Deduction', 66, 'Deduction', 'leads_to'), + (66, 'Deduction', 66, 'Falsification', 'leads_to'), + (66, 'Falsification', 67, 'Deduction', 'triggers'), + + # Cross-block + (33, 'Causal Chain', 57, 'Deduction', 'triggers'), + (64, 'Induction', 65, 'Analogy', 'triggers'), +] + +# Block boundaries +blocks = [ + (1, 8, 'Block 1', {'regime': 'chaotic', 'eff_rank': '34-35'}), + (9, 16, 'Block 2', {'regime': 'low_rank', 'eff_rank': '11'}), + (17, 24, 'Block 3', {'regime': 'Dale', 'eff_rank': '23'}), + (25, 32, 'Block 4', {'regime': 'n_types=2', 'eff_rank': '32-34'}), + (33, 40, 'Block 5', {'regime': 'noise', 'eff_rank': '83'}), + (41, 48, 'Block 6', {'regime': 'compound', 'eff_rank': '9-11'}), + (49, 56, 'Block 7', {'regime': 'sparse', 'eff_rank': '4-6'}), + (57, 64, 'Block 8', {'regime': 'sparse+noise', 'eff_rank': '92'}), + (65, 67, 'Block 9', {'regime': 'ff=0.5', 'eff_rank': '26'}), +] + + +def create_2x2_panels(): + """Create 3-row panel figure for signal_landscape_Claude. + + Layout: + - Row 1: Scatterplot (left), Legend (right) + - Row 2: Streamgraph (left), Summary (right) + - Row 3: Sankey diagram (full width, from Sankey.png) + """ + + fig = plt.figure(figsize=(18, 18), constrained_layout=True) + gs = GridSpec(3, 2, figure=fig, width_ratios=[2.5, 0.8], height_ratios=[1, 1, 1.2]) + + modes = [ + 'Induction', 'Boundary', 'Abduction', 'Deduction', 'Falsification', + 'Analogy', 'Meta-reasoning', 'Regime', 'Uncertainty', + 'Constraint', 'Predictive', 'Causal Chain', + ] + mode_to_y = {mode: i for i, mode in enumerate(modes)} + + # Common x-axis limits for alignment + x_min, x_max = 0, 72 + + # ============ TOP LEFT: Scatterplot ============ + ax1 = fig.add_subplot(gs[0, 0]) + + for block_idx, (start, end, label, info) in enumerate(blocks): + color = '#f0f0f0' if block_idx % 2 == 1 else 'white' + ax1.axvspan(start - 0.5, end + 0.5, alpha=1.0, color=color) + + for iteration, mode, significance in events: + if mode not in mode_to_y: + continue + y = mode_to_y[mode] + color = COLORS.get(mode, '#333333') + size = {'High': 150, 'Medium': 80, 'Low': 40}.get(significance, 80) + ax1.scatter(iteration, y, c=color, s=size, alpha=0.9, + edgecolors='none', zorder=3) + + ax1.set_xlim(x_min, x_max) + ax1.set_ylim(-0.5, len(modes) - 0.5) + ax1.set_yticks([]) + ax1.set_xticks([]) + ax1.grid(True, axis='x', alpha=0.2, linestyle='--') + # Remove box + for spine in ax1.spines.values(): + spine.set_visible(False) + + # ============ TOP RIGHT: Legend Panel ============ + ax2 = fig.add_subplot(gs[0, 1]) + ax2.set_title('Legend', fontsize=16, loc='left') + ax2.axis('off') + + y_pos = 0.95 + y_step = 0.075 + + for mode in modes: + color = COLORS.get(mode, '#333333') + definition = DEFINITIONS.get(mode, '') + ax2.scatter([0.08], [y_pos], c=color, s=200, edgecolors='none') + ax2.text(0.16, y_pos, f"{mode}: {definition}", fontsize=13, va='center') + y_pos -= y_step + + ax2.set_xlim(0, 1) + ax2.set_ylim(0, 1) + + # ============ BOTTOM LEFT: Streamgraph ============ + ax3 = fig.add_subplot(gs[1, 0]) + + max_iter = max(e[0] for e in events) + bin_width = 4 + n_bins = (max_iter // bin_width) + 2 # Extra bin for proper coverage + # Center bins on iteration ranges: bin 0 covers iters 1-4, centered at 2.5 + iterations = np.arange(bin_width / 2, (n_bins + 0.5) * bin_width, bin_width) + + data = np.zeros((len(modes), n_bins)) + for iteration, mode, significance in events: + if mode in modes: + mode_idx = modes.index(mode) + # Bin index: iter 1-4 -> bin 0, iter 5-8 -> bin 1, etc. + bin_idx = (iteration - 1) // bin_width + if 0 <= bin_idx < n_bins: + weight = {'High': 2.0, 'Medium': 1.0, 'Low': 0.5}.get(significance, 1.0) + data[mode_idx, bin_idx] += weight + + sigma = 1.0 + data_smooth = np.array([gaussian_filter1d(row, sigma) for row in data]) + + n_layers = len(modes) + baseline = np.zeros(n_bins) + for i in range(n_layers): + baseline -= (n_layers - i - 0.5) * data_smooth[i] + baseline /= n_layers + + y_stack = np.vstack([baseline, baseline + np.cumsum(data_smooth, axis=0)]) + + for i, mode in enumerate(modes): + color = COLORS.get(mode, '#333333') + ax3.fill_between(iterations, y_stack[i], y_stack[i + 1], + color=color, alpha=0.8, + edgecolor='white', linewidth=0.3) + + # Align x-axis with scatterplot + ax3.set_xlim(x_min, x_max) + ax3.set_yticks([]) + ax3.set_facecolor('#f8f8f8') + # Remove box except bottom x-axis line + ax3.spines['top'].set_visible(False) + ax3.spines['right'].set_visible(False) + ax3.spines['left'].set_visible(False) + + # ============ BOTTOM RIGHT: Summary Panel ============ + ax_summary = fig.add_subplot(gs[1, 1]) + ax_summary.set_title('Summary', fontsize=16, loc='left') + ax_summary.axis('off') + + mode_counts = Counter(e[1] for e in events) + total_events = len(events) + total_edges = len(edges) + + summary_text = f"""signal_landscape_Claude + +{total_events} reasoning instances +67 iterations, 9 blocks +12 principles discovered + +Deduction validation: 69% +Transfer success: 77% + +Mode counts: + Induction: {mode_counts.get('Induction', 0)} + Abduction: {mode_counts.get('Abduction', 0)} + Deduction: {mode_counts.get('Deduction', 0)} + Falsification: {mode_counts.get('Falsification', 0)} + Analogy: {mode_counts.get('Analogy', 0)} + Boundary: {mode_counts.get('Boundary', 0)} + +{total_edges} causal edges +5 key causal chains""" + + ax_summary.text(0.05, 0.95, summary_text, fontsize=13, va='top', ha='left', + linespacing=1.4) + ax_summary.set_xlim(0, 1) + ax_summary.set_ylim(0, 1) + + # ============ ROW 3: Sankey Diagram (first column) ============ + ax_sankey = fig.add_subplot(gs[2, 0]) + ax_sankey.axis('off') + + # Load and display Sankey.png + import os + sankey_path = os.path.join(os.path.dirname(__file__) or '.', 'Sankey.png') + if os.path.exists(sankey_path): + sankey_img = plt.imread(sankey_path) + ax_sankey.imshow(sankey_img, aspect='equal') + else: + ax_sankey.text(0.5, 0.5, 'Sankey.png not found', fontsize=14, + ha='center', va='center', transform=ax_sankey.transAxes) + + # ============ ROW 3: Caption (second column) ============ + ax_caption = fig.add_subplot(gs[2, 1]) + ax_caption.set_title('Claude summary', fontsize=16, loc='left') + ax_caption.axis('off') + + caption_text = ("Epistemic flow of LLM-guided scientific discovery. " + "Deduction acts as the central hub—nearly all reasoning modes " + "converge through it before branching outward. The dominant " + "Deduction→Falsification pathway demonstrates genuine hypothesis " + "testing rather than mere pattern matching: predictions are " + "generated, tested, and refined through iterative loops back to " + "induction and abduction. Late-stage emergence of boundary-finding " + "and meta-reasoning indicate progressive deepening.") + + # Use textwrap for proper formatting + import textwrap + wrapped_text = textwrap.fill(caption_text, width=35) + ax_caption.text(0.0, 0.88, wrapped_text, fontsize=11, va='top', ha='left', + linespacing=1.4) + ax_caption.set_xlim(0, 1) + ax_caption.set_ylim(0, 1) + + plt.savefig('signal_landscape_Claude_epistemic.png', dpi=150, bbox_inches='tight', + pad_inches=0.3) + print("Saved: signal_landscape_Claude_epistemic.png") + plt.close() + + +def create_sankey_html(): + """Create interactive Sankey diagram using Plotly with legend box.""" + try: + import plotly.graph_objects as go + from plotly.subplots import make_subplots + except ImportError: + print("Plotly not installed") + return + + modes = list(COLORS.keys()) + mode_to_idx = {mode: i for i, mode in enumerate(modes)} + + transitions = Counter() + for from_iter, from_mode, to_iter, to_mode, edge_type in edges: + transitions[(from_mode, to_mode)] += 1 + + source = [] + target = [] + value = [] + link_colors = [] + + for (from_mode, to_mode), count in transitions.items(): + if from_mode in mode_to_idx and to_mode in mode_to_idx: + source.append(mode_to_idx[from_mode]) + target.append(mode_to_idx[to_mode]) + value.append(count) + color = COLORS.get(from_mode, '#888888') + r, g, b = int(color[1:3], 16), int(color[3:5], 16), int(color[5:7], 16) + link_colors.append(f'rgba({r},{g},{b},0.4)') + + node_colors = [COLORS.get(mode, '#888888') for mode in modes] + # Use only mode names on nodes + node_labels = modes + + fig = go.Figure(data=[go.Sankey( + textfont=dict(size=11), + node=dict( + pad=20, + thickness=25, + line=dict(color='black', width=0.5), + label=node_labels, + color=node_colors, + ), + link=dict( + source=source, + target=target, + value=value, + color=link_colors, + ), + arrangement='snap', + )]) + + fig.update_layout( + title_text="Epistemic Reasoning Flow: signal_landscape_Claude", + font_size=12, + width=1400, + height=800, + margin=dict(l=50, r=200, t=50, b=50), + ) + + fig.write_html('signal_landscape_Claude_epistemic_sankey.html') + print("Saved: signal_landscape_Claude_epistemic_sankey.html") + + +if __name__ == '__main__': + print("Creating signal_landscape_Claude epistemic visualizations...") + print(f"Total events: {len(events)}") + print(f"Total edges: {len(edges)}") + + counts = Counter(e[1] for e in events) + for mode, count in sorted(counts.items()): + print(f" {mode}: {count}") + + print("\n--- 2x2 Panels ---") + create_2x2_panels() + + print("\n--- Sankey HTML ---") + create_sankey_html() + + print("\nDone!") From fbf265e5d9d577687e5410a67e883b92a1a52b4e Mon Sep 17 00:00:00 2001 From: Cedric Allier Date: Sun, 25 Jan 2026 09:44:42 -0800 Subject: [PATCH 06/12] Add Write tool to allowedTools and reset to iteration 64 - Add Write to subprocess allowedTools for code generation - Reset start_iteration=64 for experiment resumption Co-Authored-By: Claude Opus 4.5 --- GNN_LLM.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/GNN_LLM.py b/GNN_LLM.py index 0bd6a5ba..4156e56d 100755 --- a/GNN_LLM.py +++ b/GNN_LLM.py @@ -58,14 +58,14 @@ task_params[key] = int(value) if value.isdigit() else value else: best_model = '' - task = 'generate_train_test_plot_Claude_code' # 'train', 'test', 'generate', 'plot', 'train_NGP', 'train_INR', 'Claude', 'code', 'cluster' + task = 'generate_train_test_plot_Claude_cluster' # 'train', 'test', 'generate', 'plot', 'train_NGP', 'train_INR', 'Claude', 'code', 'cluster' config_list = ['signal_landscape'] task_params = {'iterations': 2048} # resume support: start_iteration parameter (default 1) - start_iteration = 73 + start_iteration = 64 n_iterations = task_params.get('iterations', 5) @@ -528,7 +528,7 @@ '-p', repair_prompt, '--output-format', 'text', '--max-turns', '10', - '--allowedTools', 'Read', 'Edit' + '--allowedTools', 'Read', 'Edit', 'Write' ] repair_result = subprocess.run(repair_cmd, cwd=root_dir, capture_output=True, text=True) @@ -671,7 +671,7 @@ '--output-format', 'text', '--max-turns', '100', '--allowedTools', - 'Read', 'Edit' + 'Read', 'Edit', 'Write' ] # run with real-time output streaming and token expiry detection From ea39b1b0317e5d4c15b525927999a155145b4988 Mon Sep 17 00:00:00 2001 From: Cedric Allier Date: Sun, 25 Jan 2026 09:44:42 -0800 Subject: [PATCH 07/12] Reset config to iteration 64 parameters - connectivity_filling_factor=0.2, noise_model_level=0.5 - n_neurons=100, coeff_W_L1=0 Co-Authored-By: Claude Opus 4.5 --- config/signal/signal_landscape_Claude.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/config/signal/signal_landscape_Claude.yaml b/config/signal/signal_landscape_Claude.yaml index 8be3b36b..9485339e 100644 --- a/config/signal/signal_landscape_Claude.yaml +++ b/config/signal/signal_landscape_Claude.yaml @@ -6,10 +6,10 @@ simulation: connectivity_init: - 0 - 0.1 - connectivity_filling_factor: 0.75 + connectivity_filling_factor: 0.2 Dale_law: false Dale_law_factor: 0.5 - noise_model_level: 0 + noise_model_level: 0.5 params: - - 1.0 - 0.0 @@ -84,10 +84,10 @@ training: cluster_method: distance_plot cluster_distance_threshold: 0.1 fix_cluster_embedding: true - learning_rate_W_start: 1.5e-02 + learning_rate_W_start: 5.0e-03 learning_rate_start: 1.0e-03 learning_rate_embedding_start: 2.5e-04 - coeff_W_L1: 1.0e-05 + coeff_W_L1: 0 coeff_edge_diff: 100 low_rank_factorization: false low_rank: 20 From 7c510b26a578f8a4c710ac37abdd57b3f4ffcd71 Mon Sep 17 00:00:00 2001 From: cedric Date: Mon, 26 Jan 2026 11:41:31 -0500 Subject: [PATCH 08/12] modify instruction to look at existing config files --- GNN_LLM.py | 2 +- config/signal/signal_fig_2 copy.yaml | 72 +++++++++++++++ config/signal/signal_fig_3.yaml | 96 +++++++++++++++++++ config/signal/signal_fig_supp_10 copy.yaml | 73 +++++++++++++++ config/signal/signal_fig_supp_11.yaml | 75 +++++++++++++++ config/signal/signal_fig_supp_11_bis.yaml | 74 +++++++++++++++ config/signal/signal_fig_supp_12.yaml | 102 +++++++++++++++++++++ config/signal/signal_fig_supp_13.yaml | 78 ++++++++++++++++ config/signal/signal_fig_supp_14.yaml | 78 ++++++++++++++++ config/signal/signal_fig_supp_3.yaml | 74 +++++++++++++++ config/signal/signal_fig_supp_7_1.yaml | 68 ++++++++++++++ config/signal/signal_fig_supp_7_2.yaml | 68 ++++++++++++++ config/signal/signal_fig_supp_7_3.yaml | 68 ++++++++++++++ config/signal/signal_fig_supp_7_4.yaml | 68 ++++++++++++++ config/signal/signal_fig_supp_7_5.yaml | 68 ++++++++++++++ config/signal/signal_fig_supp_8 copy.yaml | 76 +++++++++++++++ config/signal/signal_fig_supp_8_1.yaml | 76 +++++++++++++++ config/signal/signal_fig_supp_8_2.yaml | 76 +++++++++++++++ config/signal/signal_fig_supp_8_3.yaml | 76 +++++++++++++++ config/signal/signal_landscape.yaml | 2 +- config/signal/signal_landscape_Claude.yaml | 68 +++++++------- 21 files changed, 1402 insertions(+), 36 deletions(-) create mode 100644 config/signal/signal_fig_2 copy.yaml create mode 100644 config/signal/signal_fig_3.yaml create mode 100644 config/signal/signal_fig_supp_10 copy.yaml create mode 100644 config/signal/signal_fig_supp_11.yaml create mode 100644 config/signal/signal_fig_supp_11_bis.yaml create mode 100644 config/signal/signal_fig_supp_12.yaml create mode 100644 config/signal/signal_fig_supp_13.yaml create mode 100644 config/signal/signal_fig_supp_14.yaml create mode 100644 config/signal/signal_fig_supp_3.yaml create mode 100644 config/signal/signal_fig_supp_7_1.yaml create mode 100644 config/signal/signal_fig_supp_7_2.yaml create mode 100644 config/signal/signal_fig_supp_7_3.yaml create mode 100644 config/signal/signal_fig_supp_7_4.yaml create mode 100644 config/signal/signal_fig_supp_7_5.yaml create mode 100755 config/signal/signal_fig_supp_8 copy.yaml create mode 100755 config/signal/signal_fig_supp_8_1.yaml create mode 100755 config/signal/signal_fig_supp_8_2.yaml create mode 100755 config/signal/signal_fig_supp_8_3.yaml diff --git a/GNN_LLM.py b/GNN_LLM.py index 4156e56d..337292d6 100755 --- a/GNN_LLM.py +++ b/GNN_LLM.py @@ -65,7 +65,7 @@ # resume support: start_iteration parameter (default 1) - start_iteration = 64 + start_iteration = 81 n_iterations = task_params.get('iterations', 5) diff --git a/config/signal/signal_fig_2 copy.yaml b/config/signal/signal_fig_2 copy.yaml new file mode 100644 index 00000000..1e6df330 --- /dev/null +++ b/config/signal/signal_fig_2 copy.yaml @@ -0,0 +1,72 @@ +description: "Figure 2 baseline - 1000 neurons, 4 types, no noise, no external inputs" +dataset: "signal_fig_2" + +simulation: + connectivity_type: "Lorentz" + connectivity_init: [0, 0.1] + # params: [g, s, c] + # du = -c * u + s * torch.tanh(u) + g * msg + params: + [[10.0, 1.0, 1.0], [10.0, 2.0, 1.0], [10.0, 1.0, 2.0], [10.0, 2.0, 2.0]] + phi: "tanh" + n_neurons: 1000 + n_neuron_types: 4 + n_frames: 100000 + delta_t: 0.01 + dpos_init: 0 + boundary: "no" + start_frame: -1000 + +graph_model: + signal_model_name: "PDE_N2" + particle_model_name: "" + mesh_model_name: "" + prediction: "first_derivative" + + input_size: 1 + output_size: 1 + hidden_dim: 64 + n_layers: 3 + + input_size_update: 3 + n_layers_update: 3 + hidden_dim_update: 64 + + aggr_type: "add" + embedding_dim: 2 + update_type: "none" + +plotting: + colormap: "tab10" + arrow_length: 1 + xlim: [-4, 4] + ylim: [-8, 8] + label_style: "greek" + mlp0_xlim: [-5, 5] + mlp0_ylim: [-8, 8] + mlp1_xlim: [-5, 5] + mlp1_ylim: [-1.1, 1.1] + +training: + n_epochs: 10 + n_runs: 1 + device: "auto" + + batch_size: 8 + small_init_batch_size: False + + seed: 16 + + data_augmentation_loop: 50 + + sparsity: "replace_embedding_function" + sparsity_freq: 4 + cluster_method: "distance_plot" + cluster_distance_threshold: 0.1 + fix_cluster_embedding: True + + learning_rate_W_start: 1.0E-3 + learning_rate_start: 5.0E-4 + learning_rate_embedding_start: 7.50E-4 + + coeff_lin_phi_zero: 1.0 diff --git a/config/signal/signal_fig_3.yaml b/config/signal/signal_fig_3.yaml new file mode 100644 index 00000000..7d2aed2d --- /dev/null +++ b/config/signal/signal_fig_3.yaml @@ -0,0 +1,96 @@ +description: "test neuro-neuron dependent structure matrix modulation noise_model_level: 1 batch_size: 8" +dataset: "signal_fig_3" + +simulation: + connectivity_type: "Lorentz" + connectivity_init: [0, 0.1] + external_input_type: "visual" + external_input_mode: "multiplicative" + # params: [a, b, g, s, w, h] + # a: decay, b: offset, g: gain, s: self-recurrence, w: width, h: threshold MLP1((u-h)/w) + params: + [ + [1.0, 0.0, 10.0, 1.0, 1.0, 0.0], + [1.0, 0.0, 10.0, 2.0, 2.0, 0.0], + [2.0, 0.0, 10.0, 1.0, 4.0, 0.0], + [2.0, 0.0, 10.0, 2.0, 8.0, 0.0], + ] + phi: "tanh" + n_neurons: 2048 + n_input_neurons: 1024 + n_neuron_types: 4 + n_frames: 50000 + delta_t: 0.01 + dpos_init: 0 + boundary: "no" + start_frame: -10000 + node_value_map: "video_bisons_32.tif" + noise_model_level: 1 + +graph_model: + signal_model_name: "PDE_N4" + particle_model_name: "" + mesh_model_name: "" + prediction: "first_derivative" + + input_size: 3 + output_size: 1 + hidden_dim: 64 + n_layers: 5 + + input_size_update: 3 + n_layers_update: 3 + hidden_dim_update: 64 + + input_size_nnr_f: 3 + n_layers_nnr_f: 5 + hidden_dim_nnr_f: 128 + output_size_nnr_f: 1 + outermost_linear_nnr_f: True + omega_f: 30 + + aggr_type: "add" + embedding_dim: 2 + update_type: "none" + +plotting: + colormap: "tab10" + arrow_length: 1 + xlim: [-5, 5] + ylim: [0, 0.3] + label_style: "greek" + +claude: + n_epochs: 5 + data_augmentation_loop: 10 + n_iter_block: 1024 + +training: + n_epochs: 10 + n_runs: 1 + device: "auto" + + batch_size: 1 + small_init_batch_size: True + + data_augmentation_loop: 50 + + learn_external_input: True + + sparsity: "replace_embedding_function" + sparsity_freq: 4 + cluster_method: "distance_plot" + cluster_distance_threshold: 0.1 + fix_cluster_embedding: True + + coeff_W_L1: 1.0E-12 + coeff_edge_diff: 10 + coeff_update_diff: 5 + coeff_lin_phi_zero: 1.0 + + learning_rate_W_start: 2.0E-3 + learning_rate_start: 5.0E-4 + learning_rate_embedding_start: 5.0E-3 + + learning_rate_NNR_f: 1.0E-5 + diff --git a/config/signal/signal_fig_supp_10 copy.yaml b/config/signal/signal_fig_supp_10 copy.yaml new file mode 100644 index 00000000..10f336b3 --- /dev/null +++ b/config/signal/signal_fig_supp_10 copy.yaml @@ -0,0 +1,73 @@ +description: "Figure supp 10 Noise test - 1000 neurons, 4 types, no noise, no external inputs" +dataset: "signal_fig_supp_10" + +simulation: + connectivity_type: "Lorentz" + connectivity_init: [0, 0.1] + # params: [g, s, c] + # du = -c * u + s * torch.tanh(u) + g * msg + params: + [[10.0, 1.0, 1.0], [10.0, 2.0, 1.0], [10.0, 1.0, 2.0], [10.0, 2.0, 2.0]] + phi: "tanh" + n_neurons: 1000 + n_neuron_types: 4 + n_frames: 100000 + delta_t: 0.01 + dpos_init: 0 + boundary: "no" + start_frame: -1000 + noise_model_level: 7.2 + +graph_model: + signal_model_name: "PDE_N2" + particle_model_name: "" + mesh_model_name: "" + prediction: "first_derivative" + + input_size: 1 + output_size: 1 + hidden_dim: 64 + n_layers: 3 + + input_size_update: 3 + n_layers_update: 3 + hidden_dim_update: 64 + + aggr_type: "add" + embedding_dim: 2 + update_type: "none" + +plotting: + colormap: "tab10" + arrow_length: 1 + xlim: [-4, 4] + ylim: [-8, 8] + label_style: "greek" + mlp0_xlim: [-5, 5] + mlp0_ylim: [-8, 8] + mlp1_xlim: [-5, 5] + mlp1_ylim: [-1.1, 1.1] + +training: + n_epochs: 10 + n_runs: 1 + device: "auto" + + batch_size: 1 + small_init_batch_size: False + + seed: 16 + + data_augmentation_loop: 50 + + sparsity: "replace_embedding_function" + sparsity_freq: 4 + cluster_method: "distance_plot" + cluster_distance_threshold: 0.1 + fix_cluster_embedding: True + + learning_rate_W_start: 1.0E-4 + learning_rate_start: 5.0E-5 + learning_rate_embedding_start: 5.0E-4 + + coeff_lin_phi_zero: 1.0 diff --git a/config/signal/signal_fig_supp_11.yaml b/config/signal/signal_fig_supp_11.yaml new file mode 100644 index 00000000..54f3a42b --- /dev/null +++ b/config/signal/signal_fig_supp_11.yaml @@ -0,0 +1,75 @@ +description: "Figure supp 11 - 8000 neurons, 4 types, little noise, no external inputs" +dataset: "signal_fig_supp_11" + +simulation: + connectivity_type: "Lorentz" + connectivity_init: [0, 0.1] + # params: [g, s, c] + # du = -c * u + s * torch.tanh(u) + g * msg + params: + [[10.0, 1.0, 1.0], [10.0, 2.0, 1.0], [10.0, 1.0, 2.0], [10.0, 2.0, 2.0]] + phi: "tanh" + n_neurons: 8000 + n_neuron_types: 4 + n_frames: 100000 + delta_t: 0.01 + dpos_init: 0 + boundary: "no" + start_frame: -1000 + noise_model_level: 1 + +graph_model: + signal_model_name: "PDE_N2" + particle_model_name: "" + mesh_model_name: "" + prediction: "first_derivative" + + input_size: 1 + output_size: 1 + hidden_dim: 64 + n_layers: 3 + + input_size_update: 3 + n_layers_update: 3 + hidden_dim_update: 64 + + aggr_type: "add" + embedding_dim: 2 + update_type: "none" + +plotting: + colormap: "tab10" + arrow_length: 1 + xlim: [-4, 4] + ylim: [-8, 8] + label_style: "greek" + mlp0_xlim: [-5, 5] + mlp0_ylim: [-8, 8] + mlp1_xlim: [-5, 5] + mlp1_ylim: [-1.1, 1.1] + +training: + n_epochs: 10 + n_runs: 1 + device: "auto" + + batch_size: 1 + small_init_batch_size: False + + seed: 16 + + data_augmentation_loop: 15 + + sparsity: "replace_embedding_function" + sparsity_freq: 4 + cluster_method: "distance_plot" + cluster_distance_threshold: 0.1 + fix_cluster_embedding: True + + learning_rate_W_start: 1.0E-3 + learning_rate_start: 5.0E-5 + learning_rate_embedding_start: 5.0E-4 + + + coeff_lin_phi_zero: 1.0 + coeff_edge_diff: 100 diff --git a/config/signal/signal_fig_supp_11_bis.yaml b/config/signal/signal_fig_supp_11_bis.yaml new file mode 100644 index 00000000..212614c7 --- /dev/null +++ b/config/signal/signal_fig_supp_11_bis.yaml @@ -0,0 +1,74 @@ +description: "Figure supp 11 - 8000 neurons, 4 types, little noise, no external inputs" +dataset: "signal_fig_supp_11_bis" + +simulation: + connectivity_type: "Lorentz" + connectivity_init: [0, 0.1] + # params: [g, s, c] + # du = -c * u + s * torch.tanh(u) + g * msg + params: + [[10.0, 1.0, 1.0], [10.0, 2.0, 1.0], [10.0, 1.0, 2.0], [10.0, 2.0, 2.0]] + phi: "tanh" + n_neurons: 8000 + n_neuron_types: 4 + n_frames: 100000 + delta_t: 0.01 + dpos_init: 0 + boundary: "no" + start_frame: -1000 + noise_model_level: 1 + +graph_model: + signal_model_name: "PDE_N2" + particle_model_name: "" + mesh_model_name: "" + prediction: "first_derivative" + + input_size: 1 + output_size: 1 + hidden_dim: 64 + n_layers: 3 + + input_size_update: 3 + n_layers_update: 3 + hidden_dim_update: 64 + + aggr_type: "add" + embedding_dim: 2 + update_type: "none" + +plotting: + colormap: "tab10" + arrow_length: 1 + xlim: [-4, 4] + ylim: [-8, 8] + label_style: "greek" + mlp0_xlim: [-5, 5] + mlp0_ylim: [-8, 8] + mlp1_xlim: [-5, 5] + mlp1_ylim: [-1.1, 1.1] + +training: + n_epochs: 10 + n_runs: 1 + device: "auto" + + batch_size: 1 + small_init_batch_size: False + + seed: 16 + + data_augmentation_loop: 15 + + sparsity: "replace_embedding_function" + sparsity_freq: 4 + cluster_method: "distance_plot" + cluster_distance_threshold: 0.1 + fix_cluster_embedding: True + + learning_rate_W_start: 1.0E-3 + learning_rate_start: 5.0E-5 + learning_rate_embedding_start: 5.0E-4 + + coeff_lin_phi_zero: 1.0 + coeff_edge_diff: 100 diff --git a/config/signal/signal_fig_supp_12.yaml b/config/signal/signal_fig_supp_12.yaml new file mode 100644 index 00000000..3d376aff --- /dev/null +++ b/config/signal/signal_fig_supp_12.yaml @@ -0,0 +1,102 @@ +description: "Figure supplmentary 12 - 1000 neurons, 32 types, no noise, no external inputs" +dataset: "signal_fig_supp_12" + +simulation: + connectivity_type: "Lorentz" + connectivity_init: [0, 0.1] + # params: [g, s, c] + # du = -c * u + s * torch.tanh(u) + g * msg + + params: + [ + [10.0, 1.0, 1.0], + [10.0, 2.0, 1.0], + [10.0, 3.0, 1.0], + [10.0, 4.0, 1.0], + [10.0, 1.0, 2.0], + [10.0, 2.0, 2.0], + [10.0, 3.0, 2.0], + [10.0, 4.0, 2.0], + [10.0, 1.0, 3.0], + [10.0, 2.0, 3.0], + [10.0, 3.0, 3.0], + [10.0, 4.0, 3.0], + [10.0, 1.0, 4.0], + [10.0, 2.0, 4.0], + [10.0, 3.0, 4.0], + [10.0, 4.0, 4.0], + [10.0, 5.0, 1.0], + [10.0, 6.0, 1.0], + [10.0, 7.0, 1.0], + [10.0, 8.0, 1.0], + [10.0, 5.0, 2.0], + [10.0, 6.0, 2.0], + [10.0, 7.0, 2.0], + [10.0, 8.0, 2.0], + [10.0, 5.0, 3.0], + [10.0, 6.0, 3.0], + [10.0, 7.0, 3.0], + [10.0, 8.0, 3.0], + [10.0, 5.0, 4.0], + [10.0, 6.0, 4.0], + [10.0, 7.0, 4.0], + [10.0, 8.0, 4.0], + ] + phi: "tanh" + n_neurons: 1000 + n_neuron_types: 32 + n_frames: 100000 + delta_t: 0.01 + dpos_init: 0 + boundary: "no" + start_frame: -1000 + +graph_model: + signal_model_name: "PDE_N2" + particle_model_name: "" + mesh_model_name: "" + prediction: "first_derivative" + + input_size: 1 + output_size: 1 + hidden_dim: 64 + n_layers: 3 + + input_size_update: 3 + n_layers_update: 3 + hidden_dim_update: 64 + + aggr_type: "add" + embedding_dim: 2 + update_type: "none" + +plotting: + colormap: "tab20" + arrow_length: 1 + xlim: [-5, 5] + ylim: [-8, 8] + label_style: "greek" + +training: + n_epochs: 10 + n_runs: 1 + device: "auto" + + batch_size: 1 + small_init_batch_size: False + + seed: 16 + + data_augmentation_loop: 50 + + sparsity: "replace_embedding_function" + sparsity_freq: 4 + cluster_method: "distance_plot" + cluster_distance_threshold: 0.05 + fix_cluster_embedding: True + + learning_rate_W_start: 1.0E-4 + learning_rate_start: 5.0E-5 + learning_rate_embedding_start: 5.0E-4 + + coeff_lin_phi_zero: 1.0 diff --git a/config/signal/signal_fig_supp_13.yaml b/config/signal/signal_fig_supp_13.yaml new file mode 100644 index 00000000..371c2f07 --- /dev/null +++ b/config/signal/signal_fig_supp_13.yaml @@ -0,0 +1,78 @@ +description: "heterogeneity MLP0 and MLP1 slope" +dataset: "signal_fig_supp_13" + +simulation: + connectivity_type: "Lorentz" + connectivity_init: [0, 0.1] + # params: [a, b, g, s, w, h] + # a: decay, b: offset, g: gain, s: self-recurrence, w: width, h: threshold MLP1((u-h)/w) + params: + [ + [1.0, 0.0, 10.0, 1.0, 1.0, 0.0], + [1.0, 0.0, 10.0, 2.0, 2.0, 0.0], + [2.0, 0.0, 10.0, 1.0, 4.0, 0.0], + [2.0, 0.0, 10.0, 2.0, 8.0, 0.0], + ] + phi: "tanh" + n_neurons: 1000 + n_neuron_types: 4 + n_frames: 100000 + delta_t: 0.01 + dpos_init: 0 + boundary: "no" + start_frame: -1000 + +graph_model: + signal_model_name: "PDE_N4" + particle_model_name: "" + mesh_model_name: "" + prediction: "first_derivative" + + input_size: 3 + output_size: 1 + hidden_dim: 64 + n_layers: 3 + + input_size_update: 3 + n_layers_update: 3 + hidden_dim_update: 64 + + aggr_type: "add" + embedding_dim: 2 + update_type: "none" + +plotting: + colormap: "tab10" + arrow_length: 1 + xlim: [-5, 5] + ylim: [-8, 8] + mlp1_xlim: [-5, 5] + mlp1_ylim: [-1.1, 1.1] + # MLP1 normalization: plateau detection between x=10 and x=20 + norm_method: "max" + norm_x_start: 25.0 + norm_x_stop: 30.0 + +training: + n_epochs: 20 + n_runs: 1 + device: "auto" + + batch_size: 1 + small_init_batch_size: False + + seed: 24 + + data_augmentation_loop: 100 + + sparsity: "replace_embedding_function" + sparsity_freq: 4 + cluster_distance_threshold: 0.2 + cluster_method: "distance_plot" + fix_cluster_embedding: True + + learning_rate_W_start: 1.0E-3 + learning_rate_start: 5.0E-4 + learning_rate_embedding_start: 2.5E-4 + + coeff_edge_diff: 100 diff --git a/config/signal/signal_fig_supp_14.yaml b/config/signal/signal_fig_supp_14.yaml new file mode 100644 index 00000000..5f973cc7 --- /dev/null +++ b/config/signal/signal_fig_supp_14.yaml @@ -0,0 +1,78 @@ +description: "heterogeneity MLP0 and MLP1 slope" +dataset: "signal_fig_supp_14" + +simulation: + connectivity_type: "Lorentz" + connectivity_init: [0, 0.1] + # params: [a, b, g, s, w, h] + # a: decay, b: offset, g: gain, s: self-recurrence, w: width, h: threshold MLP1((u-h)/w) + params: + [ + [1.0, 0.0, 10.0, 1.0, 1.0, 0.0], + [1.0, 0.0, 10.0, 2.0, 2.0, 0.0], + [2.0, 0.0, 10.0, 1.0, 4.0, 0.0], + [2.0, 0.0, 10.0, 2.0, 8.0, 0.0], + ] + phi: "tanh" + n_neurons: 1000 + n_neuron_types: 4 + n_frames: 100000 + delta_t: 0.01 + dpos_init: 0 + boundary: "no" + start_frame: -1000 + +graph_model: + signal_model_name: "PDE_N5" + particle_model_name: "" + mesh_model_name: "" + prediction: "first_derivative" + + input_size: 5 + output_size: 1 + hidden_dim: 64 + n_layers: 3 + + input_size_update: 3 + n_layers_update: 3 + hidden_dim_update: 64 + + aggr_type: "add" + embedding_dim: 2 + update_type: "none" + +plotting: + colormap: "tab10" + arrow_length: 1 + xlim: [-5, 5] + ylim: [-8, 8] + mlp1_xlim: [-20, 20] + mlp1_ylim: [-1.1, 1.1] + # MLP1 normalization: plateau detection between x=10 and x=20 + norm_method: "plateau" + norm_x_start: 10.0 + norm_x_stop: 20.0 + +training: + n_epochs: 20 + n_runs: 1 + device: "auto" + + batch_size: 1 + small_init_batch_size: False + + seed: 24 + + data_augmentation_loop: 100 + + sparsity: "replace_embedding_function" + sparsity_freq: 4 + cluster_distance_threshold: 0.2 + cluster_method: "distance_plot" + fix_cluster_embedding: True + + learning_rate_W_start: 1.0E-3 + learning_rate_start: 5.0E-4 + learning_rate_embedding_start: 2.5E-4 + + coeff_edge_diff: 500 diff --git a/config/signal/signal_fig_supp_3.yaml b/config/signal/signal_fig_supp_3.yaml new file mode 100644 index 00000000..a34a3638 --- /dev/null +++ b/config/signal/signal_fig_supp_3.yaml @@ -0,0 +1,74 @@ +description: "Figure 2 no embedding training - 1000 neurons, 4 types, no noise, no external inputs" +dataset: "signal_fig_supp_3" + +simulation: + connectivity_type: "Lorentz" + connectivity_init: [0, 0.1] + # params: [g, s, c] + # du = -c * u + s * torch.tanh(u) + g * msg + params: + [[10.0, 1.0, 1.0], [10.0, 2.0, 1.0], [10.0, 1.0, 2.0], [10.0, 2.0, 2.0]] + phi: "tanh" + n_neurons: 1000 + n_neuron_types: 4 + n_frames: 100000 + delta_t: 0.01 + dpos_init: 0 + boundary: "no" + start_frame: -1000 + +graph_model: + signal_model_name: "PDE_N2" + particle_model_name: "" + mesh_model_name: "" + prediction: "first_derivative" + + input_size: 1 + output_size: 1 + hidden_dim: 64 + n_layers: 3 + + input_size_update: 3 + n_layers_update: 3 + hidden_dim_update: 64 + + aggr_type: "add" + embedding_dim: 2 + update_type: "none" + +plotting: + colormap: "tab10" + arrow_length: 1 + xlim: [-4, 4] + ylim: [-8, 8] + label_style: "greek" + mlp0_xlim: [-5, 5] + mlp0_ylim: [-8, 8] + mlp1_xlim: [-5, 5] + mlp1_ylim: [-1.1, 1.1] + +training: + n_epochs: 3 + n_runs: 1 + device: "auto" + + training_single_type: True + + batch_size: 8 + small_init_batch_size: False + + seed: 16 + + data_augmentation_loop: 50 + + sparsity: "replace_embedding_function" + sparsity_freq: 4 + cluster_method: "distance_plot" + cluster_distance_threshold: 0.1 + fix_cluster_embedding: True + + learning_rate_W_start: 1.0E-3 + learning_rate_start: 5.0E-4 + learning_rate_embedding_start: 7.50E-4 + + coeff_lin_phi_zero: 1.0 diff --git a/config/signal/signal_fig_supp_7_1.yaml b/config/signal/signal_fig_supp_7_1.yaml new file mode 100644 index 00000000..f7811b6b --- /dev/null +++ b/config/signal/signal_fig_supp_7_1.yaml @@ -0,0 +1,68 @@ +description: "Figure supp 7 - 1000 neurons, 4 types, no noise, no external inputs, 50,000 frames" +dataset: "signal_fig_supp_7_1" + +simulation: + connectivity_type: "Lorentz" + connectivity_init: [0, 0.1] + # params: [g, s, c] + # du = -c * u + s * torch.tanh(u) + g * msg + params: + [[10.0, 1.0, 1.0], [10.0, 2.0, 1.0], [10.0, 1.0, 2.0], [10.0, 2.0, 2.0]] + phi: "tanh" + n_neurons: 1000 + n_neuron_types: 4 + n_frames: 50000 + delta_t: 0.01 + dpos_init: 0 + boundary: "no" + start_frame: -1000 + +graph_model: + signal_model_name: "PDE_N2" + particle_model_name: "" + mesh_model_name: "" + prediction: "first_derivative" + + input_size: 1 + output_size: 1 + hidden_dim: 64 + n_layers: 3 + + input_size_update: 3 + n_layers_update: 3 + hidden_dim_update: 64 + + aggr_type: "add" + embedding_dim: 2 + update_type: "none" + +plotting: + colormap: "tab10" + arrow_length: 1 + xlim: [-5, 5] + ylim: [-8, 8] + label_style: "greek" + +training: + n_epochs: 10 + n_runs: 1 + device: "auto" + + batch_size: 8 + small_init_batch_size: False + + seed: 16 + + data_augmentation_loop: 100 + + sparsity: "replace_embedding_function" + sparsity_freq: 4 + cluster_method: "distance_plot" + cluster_distance_threshold: 0.1 + fix_cluster_embedding: True + + learning_rate_W_start: 1.0E-3 + learning_rate_start: 5.0E-4 + learning_rate_embedding_start: 7.50E-4 + + coeff_lin_phi_zero: 1.0 diff --git a/config/signal/signal_fig_supp_7_2.yaml b/config/signal/signal_fig_supp_7_2.yaml new file mode 100644 index 00000000..aae27df8 --- /dev/null +++ b/config/signal/signal_fig_supp_7_2.yaml @@ -0,0 +1,68 @@ +description: "Figure supp 7 - 1000 neurons, 4 types, no noise, no external inputs, 40,000 frames" +dataset: "signal_fig_supp_7_2" + +simulation: + connectivity_type: "Lorentz" + connectivity_init: [0, 0.1] + # params: [g, s, c] + # du = -c * u + s * torch.tanh(u) + g * msg + params: + [[10.0, 1.0, 1.0], [10.0, 2.0, 1.0], [10.0, 1.0, 2.0], [10.0, 2.0, 2.0]] + phi: "tanh" + n_neurons: 1000 + n_neuron_types: 4 + n_frames: 40000 + delta_t: 0.01 + dpos_init: 0 + boundary: "no" + start_frame: -1000 + +graph_model: + signal_model_name: "PDE_N2" + particle_model_name: "" + mesh_model_name: "" + prediction: "first_derivative" + + input_size: 1 + output_size: 1 + hidden_dim: 64 + n_layers: 3 + + input_size_update: 3 + n_layers_update: 3 + hidden_dim_update: 64 + + aggr_type: "add" + embedding_dim: 2 + update_type: "none" + +plotting: + colormap: "tab10" + arrow_length: 1 + xlim: [-5, 5] + ylim: [-8, 8] + label_style: "greek" + +training: + n_epochs: 10 + n_runs: 1 + device: "auto" + + batch_size: 8 + small_init_batch_size: False + + seed: 16 + + data_augmentation_loop: 125 + + sparsity: "replace_embedding_function" + sparsity_freq: 4 + cluster_method: "distance_plot" + cluster_distance_threshold: 0.1 + fix_cluster_embedding: True + + learning_rate_W_start: 1.0E-3 + learning_rate_start: 5.0E-4 + learning_rate_embedding_start: 7.50E-4 + + coeff_lin_phi_zero: 1.0 diff --git a/config/signal/signal_fig_supp_7_3.yaml b/config/signal/signal_fig_supp_7_3.yaml new file mode 100644 index 00000000..e1505d11 --- /dev/null +++ b/config/signal/signal_fig_supp_7_3.yaml @@ -0,0 +1,68 @@ +description: "Figure supp 7 - 1000 neurons, 4 types, no noise, no external inputs, 30,000 frames" +dataset: "signal_fig_supp_7_3" + +simulation: + connectivity_type: "Lorentz" + connectivity_init: [0, 0.1] + # params: [g, s, c] + # du = -c * u + s * torch.tanh(u) + g * msg + params: + [[10.0, 1.0, 1.0], [10.0, 2.0, 1.0], [10.0, 1.0, 2.0], [10.0, 2.0, 2.0]] + phi: "tanh" + n_neurons: 1000 + n_neuron_types: 4 + n_frames: 30000 + delta_t: 0.01 + dpos_init: 0 + boundary: "no" + start_frame: -1000 + +graph_model: + signal_model_name: "PDE_N2" + particle_model_name: "" + mesh_model_name: "" + prediction: "first_derivative" + + input_size: 1 + output_size: 1 + hidden_dim: 64 + n_layers: 3 + + input_size_update: 3 + n_layers_update: 3 + hidden_dim_update: 64 + + aggr_type: "add" + embedding_dim: 2 + update_type: "none" + +plotting: + colormap: "tab10" + arrow_length: 1 + xlim: [-5, 5] + ylim: [-8, 8] + label_style: "greek" + +training: + n_epochs: 10 + n_runs: 1 + device: "auto" + + batch_size: 8 + small_init_batch_size: False + + seed: 16 + + data_augmentation_loop: 168 + + sparsity: "replace_embedding_function" + sparsity_freq: 4 + cluster_method: "distance_plot" + cluster_distance_threshold: 0.1 + fix_cluster_embedding: True + + learning_rate_W_start: 1.0E-3 + learning_rate_start: 5.0E-4 + learning_rate_embedding_start: 7.50E-4 + + coeff_lin_phi_zero: 1.0 diff --git a/config/signal/signal_fig_supp_7_4.yaml b/config/signal/signal_fig_supp_7_4.yaml new file mode 100644 index 00000000..13a38bdf --- /dev/null +++ b/config/signal/signal_fig_supp_7_4.yaml @@ -0,0 +1,68 @@ +description: "Figure supp 7 - 1000 neurons, 4 types, no noise, no external inputs, 20,000 frames" +dataset: "signal_fig_supp_7_4" + +simulation: + connectivity_type: "Lorentz" + connectivity_init: [0, 0.1] + # params: [g, s, c] + # du = -c * u + s * torch.tanh(u) + g * msg + params: + [[10.0, 1.0, 1.0], [10.0, 2.0, 1.0], [10.0, 1.0, 2.0], [10.0, 2.0, 2.0]] + phi: "tanh" + n_neurons: 1000 + n_neuron_types: 4 + n_frames: 20000 + delta_t: 0.01 + dpos_init: 0 + boundary: "no" + start_frame: -1000 + +graph_model: + signal_model_name: "PDE_N2" + particle_model_name: "" + mesh_model_name: "" + prediction: "first_derivative" + + input_size: 1 + output_size: 1 + hidden_dim: 64 + n_layers: 3 + + input_size_update: 3 + n_layers_update: 3 + hidden_dim_update: 64 + + aggr_type: "add" + embedding_dim: 2 + update_type: "none" + +plotting: + colormap: "tab10" + arrow_length: 1 + xlim: [-5, 5] + ylim: [-8, 8] + label_style: "greek" + +training: + n_epochs: 10 + n_runs: 1 + device: "auto" + + batch_size: 8 + small_init_batch_size: False + + seed: 16 + + data_augmentation_loop: 250 + + sparsity: "replace_embedding_function" + sparsity_freq: 4 + cluster_method: "distance_plot" + cluster_distance_threshold: 0.1 + fix_cluster_embedding: True + + learning_rate_W_start: 1.0E-3 + learning_rate_start: 5.0E-4 + learning_rate_embedding_start: 7.50E-4 + + coeff_lin_phi_zero: 1.0 diff --git a/config/signal/signal_fig_supp_7_5.yaml b/config/signal/signal_fig_supp_7_5.yaml new file mode 100644 index 00000000..c3ee3ab6 --- /dev/null +++ b/config/signal/signal_fig_supp_7_5.yaml @@ -0,0 +1,68 @@ +description: "Figure supp 7 - 1000 neurons, 4 types, no noise, no external inputs, 10,000 frames" +dataset: "signal_fig_supp_7_5" + +simulation: + connectivity_type: "Lorentz" + connectivity_init: [0, 0.1] + # params: [g, s, c] + # du = -c * u + s * torch.tanh(u) + g * msg + params: + [[10.0, 1.0, 1.0], [10.0, 2.0, 1.0], [10.0, 1.0, 2.0], [10.0, 2.0, 2.0]] + phi: "tanh" + n_neurons: 1000 + n_neuron_types: 4 + n_frames: 10000 + delta_t: 0.01 + dpos_init: 0 + boundary: "no" + start_frame: -1000 + +graph_model: + signal_model_name: "PDE_N2" + particle_model_name: "" + mesh_model_name: "" + prediction: "first_derivative" + + input_size: 1 + output_size: 1 + hidden_dim: 64 + n_layers: 3 + + input_size_update: 3 + n_layers_update: 3 + hidden_dim_update: 64 + + aggr_type: "add" + embedding_dim: 2 + update_type: "none" + +plotting: + colormap: "tab10" + arrow_length: 1 + xlim: [-5, 5] + ylim: [-8, 8] + label_style: "greek" + +training: + n_epochs: 10 + n_runs: 1 + device: "auto" + + batch_size: 8 + small_init_batch_size: False + + seed: 16 + + data_augmentation_loop: 500 + + sparsity: "replace_embedding_function" + sparsity_freq: 4 + cluster_method: "distance_plot" + cluster_distance_threshold: 0.1 + fix_cluster_embedding: True + + learning_rate_W_start: 1.0E-3 + learning_rate_start: 5.0E-4 + learning_rate_embedding_start: 7.50E-4 + + coeff_lin_phi_zero: 1.0 diff --git a/config/signal/signal_fig_supp_8 copy.yaml b/config/signal/signal_fig_supp_8 copy.yaml new file mode 100755 index 00000000..845f2cdc --- /dev/null +++ b/config/signal/signal_fig_supp_8 copy.yaml @@ -0,0 +1,76 @@ +description: "test sparsity 5% synaptic network 100.000 points" +dataset: "signal_fig_supp_8" + +simulation: + connectivity_type: "Lorentz" + connectivity_init: [0, 0.1] + connectivity_filling_factor: 0.05 + # params: [g, s, c] + # du = -c * u + s * torch.tanh(u) + g * msg + params: + [[10.0, 1.0, 1.0], [10.0, 2.0, 1.0], [10.0, 1.0, 2.0], [10.0, 2.0, 2.0]] + phi: "tanh" + tau: 1.0 + min_radius: 1.0 + max_radius: 1.0E6 + n_neurons: 1000 + n_neuron_types: 4 + n_frames: 100000 + delta_t: 0.01 + dpos_init: 0 + boundary: "no" + start_frame: 0 # -1000 + +graph_model: + signal_model_name: "PDE_N2" + particle_model_name: "" + mesh_model_name: "" + prediction: "first_derivative" + input_size: 1 + output_size: 1 + hidden_dim: 64 + n_layers: 3 + input_size_update: 3 + n_layers_update: 3 + hidden_dim_update: 64 + aggr_type: "add" + embedding_dim: 2 + update_type: "none" + +plotting: + colormap: "tab10" + arrow_length: 1 + xlim: [-5, 5] + ylim: [-8, 8] + +training: + n_epochs: 10 + n_runs: 1 + device: "auto" + + batch_size: 1 + small_init_batch_size: False + + seed: 16 + + data_augmentation_loop: 50 + + sparsity: "replace_embedding_function" + sparsity_freq: 4 + cluster_method: "distance_plot" + cluster_distance_threshold: 0.1 + fix_cluster_embedding: True + + learning_rate_W_start: 1.0E-4 + learning_rate_start: 1.0E-4 + learning_rate_embedding_start: 1.0E-4 + + # Two-phase training (like ParticleGraph) + n_epochs_init: 2 # epochs in phase 1 + first_coeff_L1: 0 # phase 1: no L1 + coeff_W_L1: 1.0E-5 # phase 2: target L1 + + # Regularization coefficients + coeff_edge_diff: 100 # Monotonicity constraint (phase 1 only, disabled in phase 2) + coeff_update_diff: 0 # Monotonicity constraint on update function + coeff_lin_phi_zero: 1.0 # Penalize phi output at zero diff --git a/config/signal/signal_fig_supp_8_1.yaml b/config/signal/signal_fig_supp_8_1.yaml new file mode 100755 index 00000000..99c10089 --- /dev/null +++ b/config/signal/signal_fig_supp_8_1.yaml @@ -0,0 +1,76 @@ +description: "test sparsity 50% synaptic network 100.000 points" +dataset: "signal_fig_supp_8_1" + +simulation: + connectivity_type: "Lorentz" + connectivity_init: [0, 0.1] + connectivity_filling_factor: 0.5 + # params: [g, s, c] + # du = -c * u + s * torch.tanh(u) + g * msg + params: + [[10.0, 1.0, 1.0], [10.0, 2.0, 1.0], [10.0, 1.0, 2.0], [10.0, 2.0, 2.0]] + phi: "tanh" + tau: 1.0 + min_radius: 1.0 + max_radius: 1.0E6 + n_neurons: 1000 + n_neuron_types: 4 + n_frames: 100000 + delta_t: 0.01 + dpos_init: 0 + boundary: "no" + start_frame: 0 # -1000 + +graph_model: + signal_model_name: "PDE_N2" + particle_model_name: "" + mesh_model_name: "" + prediction: "first_derivative" + input_size: 1 + output_size: 1 + hidden_dim: 64 + n_layers: 3 + input_size_update: 3 + n_layers_update: 3 + hidden_dim_update: 64 + aggr_type: "add" + embedding_dim: 2 + update_type: "none" + +plotting: + colormap: "tab10" + arrow_length: 1 + xlim: [-5, 5] + ylim: [-8, 8] + +training: + n_epochs: 10 + n_runs: 1 + device: "auto" + + batch_size: 1 + small_init_batch_size: False + + seed: 16 + + data_augmentation_loop: 50 + + sparsity: "replace_embedding_function" + sparsity_freq: 4 + cluster_method: "distance_plot" + cluster_distance_threshold: 0.1 + fix_cluster_embedding: True + + learning_rate_W_start: 1.0E-4 + learning_rate_start: 1.0E-4 + learning_rate_embedding_start: 1.0E-4 + + # Two-phase training (like ParticleGraph) + n_epochs_init: 2 # epochs in phase 1 + first_coeff_L1: 0 # phase 1: no L1 + coeff_W_L1: 1.0E-5 # phase 2: target L1 + + # Regularization coefficients + coeff_edge_diff: 100 # Monotonicity constraint (phase 1 only, disabled in phase 2) + coeff_update_diff: 0 # Monotonicity constraint on update function + coeff_lin_phi_zero: 1.0 # Penalize phi output at zero diff --git a/config/signal/signal_fig_supp_8_2.yaml b/config/signal/signal_fig_supp_8_2.yaml new file mode 100755 index 00000000..8cbabd21 --- /dev/null +++ b/config/signal/signal_fig_supp_8_2.yaml @@ -0,0 +1,76 @@ +description: "test sparsity 20% synaptic network 100.000 points" +dataset: "signal_fig_supp_8_2" + +simulation: + connectivity_type: "Lorentz" + connectivity_init: [0, 0.1] + connectivity_filling_factor: 0.2 + # params: [g, s, c] + # du = -c * u + s * torch.tanh(u) + g * msg + params: + [[10.0, 1.0, 1.0], [10.0, 2.0, 1.0], [10.0, 1.0, 2.0], [10.0, 2.0, 2.0]] + phi: "tanh" + tau: 1.0 + min_radius: 1.0 + max_radius: 1.0E6 + n_neurons: 1000 + n_neuron_types: 4 + n_frames: 100000 + delta_t: 0.01 + dpos_init: 0 + boundary: "no" + start_frame: 0 # -1000 + +graph_model: + signal_model_name: "PDE_N2" + particle_model_name: "" + mesh_model_name: "" + prediction: "first_derivative" + input_size: 1 + output_size: 1 + hidden_dim: 64 + n_layers: 3 + input_size_update: 3 + n_layers_update: 3 + hidden_dim_update: 64 + aggr_type: "add" + embedding_dim: 2 + update_type: "none" + +plotting: + colormap: "tab10" + arrow_length: 1 + xlim: [-5, 5] + ylim: [-8, 8] + +training: + n_epochs: 10 + n_runs: 1 + device: "auto" + + batch_size: 1 + small_init_batch_size: False + + seed: 16 + + data_augmentation_loop: 50 + + sparsity: "replace_embedding_function" + sparsity_freq: 4 + cluster_method: "distance_plot" + cluster_distance_threshold: 0.1 + fix_cluster_embedding: True + + learning_rate_W_start: 1.0E-4 + learning_rate_start: 1.0E-4 + learning_rate_embedding_start: 1.0E-4 + + # Two-phase training (like ParticleGraph) + n_epochs_init: 2 # epochs in phase 1 + first_coeff_L1: 0 # phase 1: no L1 + coeff_W_L1: 1.0E-5 # phase 2: target L1 + + # Regularization coefficients + coeff_edge_diff: 100 # Monotonicity constraint (phase 1 only, disabled in phase 2) + coeff_update_diff: 0 # Monotonicity constraint on update function + coeff_lin_phi_zero: 1.0 # Penalize phi output at zero diff --git a/config/signal/signal_fig_supp_8_3.yaml b/config/signal/signal_fig_supp_8_3.yaml new file mode 100755 index 00000000..5f53974e --- /dev/null +++ b/config/signal/signal_fig_supp_8_3.yaml @@ -0,0 +1,76 @@ +description: "test sparsity 20% synaptic network 100.000 points" +dataset: "signal_fig_supp_8_3" + +simulation: + connectivity_type: "Lorentz" + connectivity_init: [0, 0.1] + connectivity_filling_factor: 0.1 + # params: [g, s, c] + # du = -c * u + s * torch.tanh(u) + g * msg + params: + [[10.0, 1.0, 1.0], [10.0, 2.0, 1.0], [10.0, 1.0, 2.0], [10.0, 2.0, 2.0]] + phi: "tanh" + tau: 1.0 + min_radius: 1.0 + max_radius: 1.0E6 + n_neurons: 1000 + n_neuron_types: 4 + n_frames: 100000 + delta_t: 0.01 + dpos_init: 0 + boundary: "no" + start_frame: 0 # -1000 + +graph_model: + signal_model_name: "PDE_N2" + particle_model_name: "" + mesh_model_name: "" + prediction: "first_derivative" + input_size: 1 + output_size: 1 + hidden_dim: 64 + n_layers: 3 + input_size_update: 3 + n_layers_update: 3 + hidden_dim_update: 64 + aggr_type: "add" + embedding_dim: 2 + update_type: "none" + +plotting: + colormap: "tab10" + arrow_length: 1 + xlim: [-5, 5] + ylim: [-8, 8] + +training: + n_epochs: 10 + n_runs: 1 + device: "auto" + + batch_size: 1 + small_init_batch_size: False + + seed: 16 + + data_augmentation_loop: 50 + + sparsity: "replace_embedding_function" + sparsity_freq: 4 + cluster_method: "distance_plot" + cluster_distance_threshold: 0.1 + fix_cluster_embedding: True + + learning_rate_W_start: 1.0E-4 + learning_rate_start: 1.0E-4 + learning_rate_embedding_start: 1.0E-4 + + # Two-phase training (like ParticleGraph) + n_epochs_init: 2 # epochs in phase 1 + first_coeff_L1: 0 # phase 1: no L1 + coeff_W_L1: 1.0E-5 # phase 2: target L1 + + # Regularization coefficients + coeff_edge_diff: 100 # Monotonicity constraint (phase 1 only, disabled in phase 2) + coeff_update_diff: 0 # Monotonicity constraint on update function + coeff_lin_phi_zero: 1.0 # Penalize phi output at zero diff --git a/config/signal/signal_landscape.yaml b/config/signal/signal_landscape.yaml index 0246dfbc..36173c3e 100644 --- a/config/signal/signal_landscape.yaml +++ b/config/signal/signal_landscape.yaml @@ -53,7 +53,7 @@ plotting: training: n_epochs: 10 n_runs: 1 - device: "auto" + device: "cuda:0" batch_size: 8 small_init_batch_size: False diff --git a/config/signal/signal_landscape_Claude.yaml b/config/signal/signal_landscape_Claude.yaml index 9485339e..b01bd45e 100644 --- a/config/signal/signal_landscape_Claude.yaml +++ b/config/signal/signal_landscape_Claude.yaml @@ -4,49 +4,49 @@ simulation: connectivity_type: chaotic connectivity_rank: 20 connectivity_init: - - 0 - - 0.1 + - 0 + - 0.1 connectivity_filling_factor: 0.2 Dale_law: false Dale_law_factor: 0.5 noise_model_level: 0.5 params: - - - 1.0 - - 0.0 - - 7.0 - - 0.0 - - 1.0 - - 0.0 - - - 1.0 - - 0.0 - - 7.0 - - 1.0 - - 1.0 - - 0.0 - - - 2.0 - - 0.0 - - 7.0 - - 1.0 - - 1.0 - - 0.0 - - - 2.0 - - 0.0 - - 7.0 - - 2.0 - - 1.0 - - 0.0 + - - 1.0 + - 0.0 + - 7.0 + - 0.0 + - 1.0 + - 0.0 + - - 1.0 + - 0.0 + - 7.0 + - 1.0 + - 1.0 + - 0.0 + - - 2.0 + - 0.0 + - 7.0 + - 1.0 + - 1.0 + - 0.0 + - - 2.0 + - 0.0 + - 7.0 + - 2.0 + - 1.0 + - 0.0 phi: tanh n_neurons: 100 n_neuron_types: 1 n_frames: 10000 delta_t: 0.01 dpos_init: 0 - boundary: 'no' + boundary: "no" start_frame: -100 graph_model: signal_model_name: PDE_N4 - particle_model_name: '' - mesh_model_name: '' + particle_model_name: "" + mesh_model_name: "" prediction: first_derivative input_size: 3 output_size: 1 @@ -66,15 +66,15 @@ plotting: colormap: tab10 arrow_length: 1 xlim: - - -5 - - 5 + - -5 + - 5 ylim: - - -2 - - 2 + - -2 + - 2 training: n_epochs: 1 n_runs: 1 - device: auto + device: "cuda:0" batch_size: 8 small_init_batch_size: false seed: 24 From 12a4239352429982c316564ad572099f0991438d Mon Sep 17 00:00:00 2001 From: Cedric Allier Date: Tue, 27 Jan 2026 06:55:17 -0800 Subject: [PATCH 09/12] Remove edge_index.pt and mask.pt saving, recreate edges from connectivity - data_generate_synaptic: only save connectivity.pt (remove edge_index.pt, mask.pt) - data_test_signal: recreate edge_index instead of loading from file - data_train_signal: backup only connectivity.pt to log folder Co-Authored-By: Claude Opus 4.5 --- .../generators/graph_data_generator.py | 4 ++-- src/NeuralGraph/models/graph_trainer.py | 19 +++++++++++++++++-- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/NeuralGraph/generators/graph_data_generator.py b/src/NeuralGraph/generators/graph_data_generator.py index 2b1db94a..b8a58325 100644 --- a/src/NeuralGraph/generators/graph_data_generator.py +++ b/src/NeuralGraph/generators/graph_data_generator.py @@ -230,6 +230,7 @@ def data_generate_fly_voltage(config, visualize=True, run_vizualized=0, style="c torch.random.fork_rng(devices=device) if simulation_config.seed != 42: torch.random.manual_seed(simulation_config.seed) + np.random.seed(simulation_config.seed) # Ensure numpy random state is also seeded for reproducibility dataset_name = config.dataset n_neurons = simulation_config.n_neurons @@ -1092,6 +1093,7 @@ def data_generate_synaptic( torch.random.fork_rng(devices=device) torch.random.manual_seed(simulation_config.seed) + np.random.seed(simulation_config.seed) # Ensure numpy random state is also seeded for reproducibility print( "generating data ..." @@ -1271,8 +1273,6 @@ def data_generate_synaptic( Dale_law=simulation_config.Dale_law, Dale_law_factor=simulation_config.Dale_law_factor, ) - torch.save(edge_index, f"./graphs_data/{dataset_name}/edge_index.pt") - torch.save(mask, f"./graphs_data/{dataset_name}/mask.pt") torch.save(connectivity, f"./graphs_data/{dataset_name}/connectivity.pt") # Plot eigenvalue spectrum and connectivity matrix diff --git a/src/NeuralGraph/models/graph_trainer.py b/src/NeuralGraph/models/graph_trainer.py index cc6c4e71..838d8e85 100644 --- a/src/NeuralGraph/models/graph_trainer.py +++ b/src/NeuralGraph/models/graph_trainer.py @@ -1,6 +1,7 @@ import os import time import glob +import shutil import warnings import logging @@ -204,6 +205,15 @@ def data_train_signal(config, erase, best_model, style, device, log_file=None): np.random.seed(config.training.seed) log_dir, logger = create_log_dir(config, erase) + + # Backup connectivity.pt to log folder for safety + data_folder = f'graphs_data/{dataset_name}' + src_path = os.path.join(data_folder, 'connectivity.pt') + if os.path.exists(src_path): + dst_path = os.path.join(log_dir, 'connectivity.pt') + shutil.copy(src_path, dst_path) + print(f'backed up connectivity.pt to {log_dir}') + print('loading data...') x_list = [] @@ -2834,7 +2844,11 @@ def data_test_signal(config=None, config_file=None, visualize=False, style='colo adj_t = adj_t.t() edge_index = adj_t.nonzero().t().contiguous() else: - edge_index = torch.load(f'./graphs_data/{dataset_name}/edge_index.pt', map_location=device) + # Create fully connected edges (all pairs except self-loops) + i_indices = torch.arange(n_neurons, device=device).repeat_interleave(n_neurons) + j_indices = torch.arange(n_neurons, device=device).repeat(n_neurons) + mask_edges = i_indices != j_indices + edge_index = torch.stack([i_indices[mask_edges], j_indices[mask_edges]], dim=0) edge_index_generated = edge_index.clone().detach() @@ -3382,7 +3396,8 @@ def data_test_signal(config=None, config_file=None, visualize=False, style='colo elif 'CElegans' in dataset_name: n = [20, 30, 40, 50, 60, 70, 80, 90, 100, 110] else: - n = [20, 30, 100, 150, 260, 270, 520, 620, 720, 780] + # Generate 10 evenly spaced indices within valid range + n = np.linspace(0, n_neurons - 1, 10, dtype=int).tolist() neuron_gt_list_ = torch.cat(neuron_gt_list, 0) neuron_pred_list_ = torch.cat(neuron_pred_list, 0) From 23ebc52d45b430e022126667bfde851f7f0e8925 Mon Sep 17 00:00:00 2001 From: Cedric Allier Date: Tue, 27 Jan 2026 07:30:46 -0800 Subject: [PATCH 10/12] Remove redundant model_p saving - params come from config model.p contains per-neuron-type parameters that are already available in config.simulation.params, so saving them to disk is unnecessary. This makes NeuralGraph consistent with neural-gnn. Co-Authored-By: Claude Opus 4.5 --- src/NeuralGraph/generators/graph_data_generator.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/NeuralGraph/generators/graph_data_generator.py b/src/NeuralGraph/generators/graph_data_generator.py index b8a58325..465de704 100644 --- a/src/NeuralGraph/generators/graph_data_generator.py +++ b/src/NeuralGraph/generators/graph_data_generator.py @@ -1472,7 +1472,6 @@ def data_generate_synaptic( if measurement_noise_level > 0: np.save(f"graphs_data/{dataset_name}/raw_x_list_{run}.npy", x_list) np.save(f"graphs_data/{dataset_name}/raw_y_list_{run}.npy", y_list) - torch.save(model.p, f"graphs_data/{dataset_name}/model_p.pt") for k in range(x_list.shape[0]): x_list[k, :, 3] = x_list[k, :, 3] + np.random.normal( 0, measurement_noise_level, x_list.shape[1] @@ -1499,7 +1498,6 @@ def data_generate_synaptic( inv_particle_dropout_mask, ) - torch.save(model.p, f"graphs_data/{dataset_name}/model_p_{run}.pt") print("data saved ...") From c3dbeda771d44aade79220711194f49909c3b2f8 Mon Sep 17 00:00:00 2001 From: Cedric Allier Date: Tue, 27 Jan 2026 10:29:13 -0800 Subject: [PATCH 11/12] Fix infinite loop in compute_ucb_scores with circular parent detection Added cycle detection to prevent infinite loop when traversing parent chain in UCB score computation. Warns if circular parent references are found. Co-Authored-By: Claude Opus 4.5 --- src/NeuralGraph/models/exploration_tree.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/NeuralGraph/models/exploration_tree.py b/src/NeuralGraph/models/exploration_tree.py index cf55f4a3..2643d16d 100644 --- a/src/NeuralGraph/models/exploration_tree.py +++ b/src/NeuralGraph/models/exploration_tree.py @@ -908,9 +908,13 @@ def compute_ucb_scores(analysis_path, ucb_path, c=1.0, current_log_path=None, cu # Backpropagate: for each node, increment all ancestors for node_id in sorted_node_ids: parent_id = nodes[node_id]['parent'] - while parent_id is not None and parent_id in nodes: + visited = set() # Protect against circular parent references + while parent_id is not None and parent_id in nodes and parent_id not in visited: + visited.add(parent_id) visits[parent_id] += 1 parent_id = nodes[parent_id]['parent'] + if parent_id in visited: + print(f"Warning: circular parent reference detected at node {node_id}") # Compute UCB for each node # Google PUCT formula: UCB(u) = RankScore(u) + c * sqrt(N_total) / (1 + V(u)) From 8c5946c26a03636d56f53b25004c2c02c530cde6 Mon Sep 17 00:00:00 2001 From: cedric Date: Tue, 27 Jan 2026 14:47:17 -0500 Subject: [PATCH 12/12] landscape training --- GNN_LLM.py | 4 ++-- config/signal/signal_landscape_Claude.yaml | 8 ++++---- src/NeuralGraph/generators/tmp.py | 1 + 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/GNN_LLM.py b/GNN_LLM.py index 337292d6..cde50a79 100755 --- a/GNN_LLM.py +++ b/GNN_LLM.py @@ -65,7 +65,7 @@ # resume support: start_iteration parameter (default 1) - start_iteration = 81 + start_iteration = 89 n_iterations = task_params.get('iterations', 5) @@ -669,7 +669,7 @@ 'claude', '-p', claude_prompt, '--output-format', 'text', - '--max-turns', '100', + '--max-turns', '500', '--allowedTools', 'Read', 'Edit', 'Write' ] diff --git a/config/signal/signal_landscape_Claude.yaml b/config/signal/signal_landscape_Claude.yaml index b01bd45e..e474a9e0 100644 --- a/config/signal/signal_landscape_Claude.yaml +++ b/config/signal/signal_landscape_Claude.yaml @@ -6,10 +6,10 @@ simulation: connectivity_init: - 0 - 0.1 - connectivity_filling_factor: 0.2 + connectivity_filling_factor: 0.9 Dale_law: false Dale_law_factor: 0.5 - noise_model_level: 0.5 + noise_model_level: 0 params: - - 1.0 - 0.0 @@ -84,10 +84,10 @@ training: cluster_method: distance_plot cluster_distance_threshold: 0.1 fix_cluster_embedding: true - learning_rate_W_start: 5.0e-03 + learning_rate_W_start: 1.2e-02 learning_rate_start: 1.0e-03 learning_rate_embedding_start: 2.5e-04 - coeff_W_L1: 0 + coeff_W_L1: 1.0e-05 coeff_edge_diff: 100 low_rank_factorization: false low_rank: 20 diff --git a/src/NeuralGraph/generators/tmp.py b/src/NeuralGraph/generators/tmp.py index d721c1be..8ab21dee 100644 --- a/src/NeuralGraph/generators/tmp.py +++ b/src/NeuralGraph/generators/tmp.py @@ -149,6 +149,7 @@ def data_generate_fly_voltage(config, visualize=True, run_vizualized=0, style="c torch.random.fork_rng(devices=device) if simulation_config.seed != 42: torch.random.manual_seed(simulation_config.seed) + np.random.seed(simulation_config.seed) # Ensure numpy random state is also seeded for reproducibility print( f"generating data ... {model_config.particle_model_name} {model_config.mesh_model_name} seed: {simulation_config.seed}")