Files
2026-07-13 13:24:13 +08:00

264 lines
9.0 KiB
Python

import collections
import json
import os
import sys
from argparse import ArgumentParser
from concurrent.futures import ThreadPoolExecutor, as_completed
from datasets import load_dataset
from glob import glob
from tqdm import tqdm
sys.set_int_max_str_digits(0)
def worker(x):
item, orig_item = x
input_output = item["test_cases"]
inputs = input_output["inputs"]
num_test_cases = len(input_output["inputs"])
outputs_counter = [
collections.Counter() for _ in inputs
]
output_str2orig_pred = [
{} for _ in inputs
]
resp2outputs = [
{} for _ in range(len(item["outputs"]))
]
output2res = [
{} for _ in range(len(inputs))
]
assert len(item["full_res"]) == len(item["outputs"]) == len(item["pred"]), (len(item["full_res"]), len(item["outputs"]), len(item["pred"]))
for resp_id, (full_res, pg_outputs) in enumerate(zip(item["full_res"], item["outputs"])):
for case_j, (case_r, case_o) in enumerate(zip(full_res, pg_outputs)):
if case_j >= len(inputs):
break
if case_r != 0:
continue
# assert case_o # sometimes is could be `int` or `True` or `False`. We believe the `case_r` here.
# if not str(case_o):
# continue # some outputs could be empty string
if str(case_o) not in output_str2orig_pred[case_j]:
output_str2orig_pred[case_j][str(case_o)] = case_o
outputs_counter[case_j][str(case_o)] += 1
resp2outputs[resp_id][case_j] = str(case_o)
# assert case_j < len(orig_item["full_res"][resp_id]), (resp_id, case_j, orig_item["full_res"][resp_id])
if len(orig_item["full_res"][resp_id]) <= case_j:
assert all(x == -1 for x in orig_item["full_res"][resp_id])
output_res = -1
else:
output_res = orig_item["full_res"][resp_id][case_j]
output2res[case_j][str(case_o)] = output_res
# Get self-consistency
sc_res = True
sc_outputs = []
for case_j, output_cnt in enumerate(outputs_counter):
if len(output_cnt) == 0:
sc_res = False
sc_outputs.append(None)
continue
sc_o = output_cnt.most_common(1)[0][0]
sc_o_res = output2res[case_j][sc_o]
if not sc_o_res:
sc_res = False
sc_outputs.append(sc_o)
# Estimate the corresponding frequency rates
tot_freq = 0
for case_j, output_cnt in enumerate(outputs_counter):
if len(output_cnt) == 0:
continue
max_freq = output_cnt.most_common(1)[0][1]
tot_freq += max_freq
tot_freq /= num_test_cases
# Confirm if there is some program solution meets all self-consistency outputs
sc_match_res = []
for resp_id, (full_res, pg_outputs) in enumerate(zip(item["full_res"], item["outputs"])):
resp_match_res = True
for case_j, (case_r, case_o) in enumerate(zip(full_res, pg_outputs)):
if case_j >= len(inputs):
# resp_match_res = False
break
if case_r != 0:
resp_match_res = False
break
# if not str(case_o):
# resp_match_res = False
# break
if sc_outputs[case_j] is None:
resp_match_res = False
break
if str(case_o) != sc_outputs[case_j]:
resp_match_res = False
if orig_item["full_res"][resp_id][case_j] and output2res[case_j][sc_outputs[case_j]]:
print(f"warning", str(case_o), sc_outputs[case_j], orig_item["test_cases"]["outputs"][case_j])
break
sc_match_res.append(resp_match_res)
if sc_res and any(
sc_match_res): # If the self-consistency determined group is correct and there is one program match all predictions with self-consistency, then it is a correct solution
prog_sc_res = True
else:
prog_sc_res = False
return {
"sc_res": sc_res,
"sc_outputs": sc_outputs,
"tot_freq": tot_freq,
"prog_sc_res": prog_sc_res,
"sc_match_res": sc_match_res,
"res": orig_item["res"][0],
"id": item["id"]
}
def load_files(file_path):
data = []
if os.path.exists(file_path):
if file_path.endswith(".json"):
data.extend(json.load(open(file_path)))
else:
data.extend([json.loads(line) for line in open(file_path).readlines()])
else:
for file in glob(file_path):
print(file)
if file.endswith(".json"):
data.extend(json.load(open(file)))
else:
data.extend([json.loads(line) for line in open(file).readlines()])
return data
def merge_key(item, value):
assert isinstance(item, list)
if isinstance(value, list):
item = item + value
else:
item.append(value)
return item
def merge_seed_sampled_data(data):
id2data = {}
for item in data:
if item["id"] not in id2data:
id2data[item["id"]] = item
continue
tmp = id2data[item["id"]]
if isinstance(tmp["response"], str):
tmp["response"] = [tmp["response"]]
if not isinstance(tmp["res"], list):
tmp["res"] = [tmp["res"]]
if not isinstance(tmp["pred"], list):
tmp["pred"] = [tmp["pred"]]
if not isinstance(tmp["full_res"], list):
tmp["full_res"] = [tmp["full_res"]]
if "outputs" in tmp and not isinstance(tmp["outputs"], list):
tmp["outputs"] = [tmp["outputs"]]
tmp["response"] = merge_key(tmp["response"], item["response"])
tmp["res"] = merge_key(tmp["res"], item["res"])
tmp["pred"] = merge_key(tmp["pred"], item["pred"])
tmp["full_res"] = merge_key(tmp["full_res"], item["full_res"])
if "outputs" in tmp:
tmp["outputs"] = merge_key(tmp["outputs"], item["outputs"])
assert isinstance(tmp["pred"], list), tmp["pred"]
id2data[item["id"]] = tmp
return list(id2data.values())
def main():
parser = ArgumentParser()
parser.add_argument("--completion_file", type=str)
parser.add_argument("--exec_file", type=str)
parser.add_argument("--output_file", type=str)
parser.add_argument("--num_workers", type=int, default=8)
parser.add_argument("--split", default="test")
args = parser.parse_args()
if not os.path.exists(f'apps_difficulty_{args.split}.json'):
_dataset = load_dataset("codeparrot/apps", split=args.split).to_list()
problem_id2difficulty = {item["problem_id"]: item["difficulty"] for item in _dataset}
all_difficulties = collections.Counter(problem_id2difficulty.values())
json.dump(problem_id2difficulty, open(f'apps_difficulty_{args.split}.json', "w"), ensure_ascii=False)
json.dump(all_difficulties, open(f'apps_difficulty_{args.split}_all.json', "w"), ensure_ascii=False)
else:
problem_id2difficulty = json.load(open(f'apps_difficulty_{args.split}.json'))
all_difficulties = json.load(open(f'apps_difficulty_{args.split}_all.json'))
problem_id2difficulty = {int(k): v for k, v in problem_id2difficulty.items()}
completions = load_files(args.completion_file)
completions = merge_seed_sampled_data(completions)
execs = load_files(args.exec_file)
execs = merge_seed_sampled_data(execs)
print(len(completions), len(execs))
id2completion = {item["id"]: item for item in completions}
id2exec = {item["id"]: item for item in execs}
print(len(id2completion), len(id2exec))
commons = set(id2completion.keys()) & set(id2exec.keys())
print(f"Found {len(commons)} common items.")
inputs = []
for _id in commons:
inputs.append((id2exec[_id], id2completion[_id]))
pbar = tqdm(inputs)
outputs = []
with ThreadPoolExecutor(max_workers=args.num_workers) as executor:
futures = []
_annotate = worker
for _input in pbar:
future = executor.submit(_annotate, _input)
futures.append(future)
pbar.update()
for future in tqdm(as_completed(futures), total=len(futures), desc="Collecting results"):
result = future.result()
result["difficulty"] = problem_id2difficulty[result["id"]]
outputs.append(result)
json.dump(outputs, open(args.output_file, "w"))
sc = 0
prog_sc = 0
first_res = 0
num_prog = 0
for item in outputs:
if item["sc_res"]:
sc += 1
if item["prog_sc_res"]:
prog_sc += 1
if item["res"]:
first_res += 1
num_prog += len(item["sc_match_res"])
print(f"Self-consistency: {sc}/{len(completions)} = {sc / len(completions)}")
print(f"Program self-consistency: {prog_sc}/{len(completions)} = {prog_sc / len(completions)}")
print(f"First res: {first_res}/{len(completions)} = {first_res / len(completions)}")
print(f"Programs: {num_prog}/{len(completions)} = {num_prog / len(completions)}")
if __name__ == "__main__":
main()