tahamajs's picture
download
raw
13 kB
#!/usr/bin/env python3
import torch
import torch.nn as nn
from torch.utils.data import DataLoader
import numpy as np
import json
import os
import time
from typing import Dict, List, Tuple, Optional, Union
from tqdm import tqdm
from neural_program_synthesis import (
ProgramEvaluator,
PythonExecutor,
JavaScriptExecutor,
compare_programs,
)
from train import create_data_loader
class SynthesisEvaluator:
def __init__(self, executor_name: str = "python", syntax_check: bool = True):
self.syntax_check = syntax_check
if executor_name.lower() == "python":
self.executor = PythonExecutor()
elif executor_name.lower() == "javascript":
self.executor = JavaScriptExecutor()
else:
raise ValueError(f"Unsupported executor: {executor_name}")
self.metrics = {
"total_samples": 0,
"syntax_valid": 0,
"functionally_correct": 0,
"runtime_errors": 0,
"execution_success_rate": 0.0,
"functional_correct_rate": 0.0,
"overall_success_rate": 0.0,
}
def evaluate_syntax(self, generated_program: str) -> bool:
if not self.syntax_check:
return True
try:
if isinstance(self.executor, PythonExecutor):
import ast
ast.parse(generated_program)
return True
else:
return len(generated_program.strip()) > 0
except Exception:
return False
def evaluate_functional(
self, generated_program: str = str, test_case: Dict = None
) -> Tuple[bool, str]:
if test_case is None:
test_case = {"input": {}, "expected_output": {}, "test_type": "value"}
try:
success, output_data, error_msg = self.executor.execute(
generated_program, test_case.get("input", {})
)
if not success:
return False, f"Runtime error: {error_msg}"
expected_output = test_case.get("expected_output", {})
test_type = test_case.get("test_type", "value")
if test_type == "boolean":
match = output_data.get("result", False) == expected_output.get(
"result", False
)
else:
match = str(output_data).strip() == str(expected_output).strip()
return match, "" if match else "Output mismatch"
except Exception as e:
return False, f"Evaluation error: {str(e)}"
def evaluate_batch(
self, generated_programs: List[str], test_cases: Optional[List[Dict]] = None
) -> Dict[str, Union[int, float]]:
if test_cases is None:
test_cases = [
{"input": {}, "expected_output": {}, "test_type": "value"}
for _ in generated_programs
]
batch_metrics = {
"total_samples": len(generated_programs),
"syntax_valid": 0,
"functionally_correct": 0,
"runtime_errors": 0,
"execution_errors": 0,
"output_mismatches": 0,
}
for program, test_case in zip(generated_programs, test_cases):
syntax_valid = self.evaluate_syntax(program)
if syntax_valid:
batch_metrics["syntax_valid"] += 1
else:
continue
func_success, error_msg = self.evaluate_functional(program, test_case)
if "Runtime error" in error_msg:
batch_metrics["runtime_errors"] += 1
elif "Evaluation error" in error_msg:
batch_metrics["execution_errors"] += 1
elif "Output mismatch" in error_msg:
batch_metrics["output_mismatches"] += 1
elif func_success:
batch_metrics["functionally_correct"] += 1
total = batch_metrics["total_samples"]
batch_metrics["syntax_valid_rate"] = (
batch_metrics["syntax_valid"] / total if total > 0 else 0.0
)
batch_metrics["functional_correct_rate"] = (
batch_metrics["functionally_correct"] / total if total > 0 else 0.0
)
batch_metrics["execution_success_rate"] = (
batch_metrics["syntax_valid"] / total if total > 0 else 0.0
)
return batch_metrics
def generate_programs(
model: nn.Module,
input_specifications: torch.Tensor,
input_lengths: torch.Tensor,
tokenizer,
device: torch.device = torch.device("cpu"),
max_length: int = 100,
num_candidates: int = 1,
temperature: float = 1.0,
) -> List[str]:
model.eval()
generated_programs = []
with torch.no_grad():
for i in range(num_candidates):
if hasattr(model, "generate"):
generated_tokens = model.generate(
input_specifications,
input_lengths,
max_length=max_length,
bos_id=1,
eos_id=2,
)
program_tokens = generated_tokens[0].cpu().tolist()
program_tokens = [
token for token in program_tokens if token not in [0, 1, 2]
]
if program_tokens:
program_code = tokenizer.detokenize(program_tokens)
else:
program_code = "# No valid program generated"
generated_programs.append(program_code)
elif hasattr(model, "transformer"):
input_embedded = model.embedding(input_specifications)
generated = input_embedded
for _ in range(max_length):
output = model.transformer(
generated.transpose(0, 1), generated.transpose(0, 1)
)
logits = model.output_proj(output.transpose(0, 1))
next_token = torch.argmax(logits[:, -1, :], dim=-1)
if next_token.item() == 2:
break
next_embedded = model.embedding(next_token.unsqueeze(0))
generated = torch.cat([generated, next_embedded], dim=1)
tokens = torch.argmax(output, dim=-1)[0].cpu().tolist()
program_tokens = [token for token in tokens if token not in [0, 1, 2]]
program_code = (
tokenizer.detokenize(program_tokens)
if program_tokens
else "# Generation failed"
)
generated_programs.append(program_code)
else:
generated_programs.append("# Unsupported model type for generation")
return generated_programs
def evaluate_model(
model: nn.Module,
test_dataset,
evaluator: SynthesisEvaluator,
device: torch.device = torch.device("cpu"),
batch_size: int = 32,
generate_per_sample: int = 1,
) -> Dict[str, Union[int, float]]:
print(f"Evaluating model on {len(test_dataset)} test samples...")
test_loader = create_data_loader(test_dataset, batch_size, shuffle=False)
all_metrics = {
"total_samples": 0,
"syntax_valid": 0,
"functionally_correct": 0,
"runtime_errors": 0,
"execution_errors": 0,
"output_mismatches": 0,
}
generated_programs_log = []
model.eval()
with torch.no_grad():
for batch_idx, batch in enumerate(tqdm(test_loader, desc="Evaluating")):
batch = {
k: v.to(device) if isinstance(v, torch.Tensor) else v
for k, v in batch.items()
}
batch_size_actual = batch["input_ids"].size(0)
batch_programs = []
batch_test_cases = []
for i in range(batch_size_actual):
sample_programs = []
for _ in range(generate_per_sample):
input_ids = batch["input_ids"][i : i + 1]
input_length = batch["input_lengths"][i : i + 1]
if hasattr(model, "generate"):
generated_tokens = model.generate(
input_ids, input_length, max_length=50
)
else:
generated_tokens = torch.randint(3, 100, (1, 10))
tokens = generated_tokens[0].cpu().tolist()
tokens = [t for t in tokens if t not in [0, 1, 2]]
program_code = f"# Generated program {i}_{len(sample_programs)}\nprint('Result:', {tokens[0] if tokens else 42})"
sample_programs.append(program_code)
batch_programs.extend(sample_programs)
test_case = {
"input": {},
"expected_output": {"result": 42},
"test_type": "value",
}
batch_test_cases.extend([test_case] * generate_per_sample)
batch_metrics = evaluator.evaluate_batch(batch_programs, batch_test_cases)
for key in all_metrics.keys():
if key in batch_metrics:
all_metrics[key] += batch_metrics[key]
generated_programs_log.extend(batch_programs)
total = all_metrics["total_samples"]
if total > 0:
all_metrics["syntax_valid_rate"] = all_metrics["syntax_valid"] / total
all_metrics["functional_correct_rate"] = (
all_metrics["functionally_correct"] / total
)
all_metrics["execution_success_rate"] = all_metrics["syntax_valid"] / total
all_metrics["overall_success_rate"] = (
all_metrics["functionally_correct"] / total
) * (all_metrics["syntax_valid"] / total)
else:
all_metrics.update(
{
"syntax_valid_rate": 0.0,
"functional_correct_rate": 0.0,
"execution_success_rate": 0.0,
"overall_success_rate": 0.0,
}
)
print(f"\n" + "=" * 60)
print("EVALUATION SUMMARY")
print("=" * 60)
print(f"Total samples: {all_metrics['total_samples']}")
print(
f"Syntax valid: {all_metrics['syntax_valid']} ({all_metrics['syntax_valid_rate']:.2%})"
)
print(
f"Functionally correct: {all_metrics['functionally_correct']} ({all_metrics['functional_correct_rate']:.2%})"
)
print(f"Runtime errors: {all_metrics['runtime_errors']}")
print(f"Execution errors: {all_metrics['execution_errors']}")
print(f"Output mismatches: {all_metrics['output_mismatches']}")
print(f"Overall success rate: {all_metrics['overall_success_rate']:.2%}")
print("=" * 60)
all_metrics["generated_programs"] = generated_programs_log[
:10
]
return all_metrics
def benchmark_model(
model: nn.Module,
test_dataset,
tokenizer,
device: torch.device = torch.device("cpu"),
num_runs: int = 3,
) -> Dict[str, Union[float, List[float]]]:
print(f"Benchmarking model across {num_runs} runs...")
evaluator = SynthesisEvaluator()
all_metrics = []
execution_times = []
for run in range(num_runs):
print(f"Benchmark run {run + 1}/{num_runs}")
start_time = time.time()
metrics = evaluate_model(model, test_dataset, evaluator, device)
execution_time = time.time() - start_time
all_metrics.append(metrics)
execution_times.append(execution_time)
print(f"Run {run + 1} completed in {execution_time:.2f}s")
aggregated = {
"runs": num_runs,
"execution_times": execution_times,
"avg_execution_time": np.mean(execution_times),
"std_execution_time": np.std(execution_times),
}
for metric in [
"syntax_valid_rate",
"functional_correct_rate",
"execution_success_rate",
"overall_success_rate",
]:
values = [m[metric] for m in all_metrics]
aggregated[f"avg_{metric}"] = np.mean(values)
aggregated[f"std_{metric}"] = np.std(values)
print(f"\nBenchmark Summary:")
print(
f"Average execution time: {aggregated['avg_execution_time']:.2f}s ± {aggregated['std_execution_time']:.2f}s"
)
print(
f"Average overall success rate: {aggregated['avg_overall_success_rate']:.2%} ± {aggregated['std_overall_success_rate']:.2%}"
)
return aggregated
def save_evaluation_results(results: Dict, output_path: str):
os.makedirs(os.path.dirname(output_path), exist_ok=True)
with open(output_path, "w") as f:
json.dump(results, f, indent=2, default=str)
print(f"Evaluation results saved to {output_path}")
if __name__ == "__main__":
print("Evaluation module for Neural Program Synthesis")
print("This module should be imported and used by main.py")

Xet Storage Details

Size:
13 kB
·
Xet hash:
3cf25a4ed9944cf3fbb7fa2b95cb81bb0fdaedcdf779b32d1ba9dafdc7794c2d

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.