experiment_church_primitives.py 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584
  1. """
  2. 实验:用 LLM + Dataset 构造 Lambda 演算原语
  3. 验证核心命题:LLM 在特定数据集(prompt 示例)上可以学习到
  4. Church 编码的基本函数——后继、布尔值、逻辑运算、条件分支、有序对、递归。
  5. 使用方式:
  6. pip install anthropic
  7. export ANTHROPIC_API_KEY=your_key
  8. python experiment_church_primitives.py
  9. 每个实验构造一个 (LLM, Dataset) 对,测试其是否正确实现对应的 Lambda 原语。
  10. """
  11. import json
  12. import os
  13. from dataclasses import dataclass, field
  14. # ============================================================
  15. # 核心抽象:LDS (LLM-Dataset System)
  16. # ============================================================
  17. @dataclass
  18. class Dataset:
  19. """数据集 D:一组 (input, output) 示例对"""
  20. examples: list[tuple[str, str]]
  21. description: str = ""
  22. def to_prompt(self) -> str:
  23. lines = []
  24. if self.description:
  25. lines.append(self.description)
  26. lines.append("")
  27. for inp, out in self.examples:
  28. lines.append(f"Input: {inp}")
  29. lines.append(f"Output: {out}")
  30. lines.append("")
  31. return "\n".join(lines)
  32. @dataclass
  33. class LDS:
  34. """
  35. LLM-Dataset System: (M, D) 对
  36. 对应 Lambda 演算中的一个 Lambda 项。
  37. 调用 LDS(input) 对应 Lambda 演算中的函数应用 (f x)。
  38. """
  39. name: str
  40. dataset: Dataset
  41. system_prompt: str = ""
  42. model: str = "claude-sonnet-4-20250514"
  43. temperature: float = 0.0 # 确定性模式,对应精确 Lambda 演算
  44. def __call__(self, input_text: str) -> str:
  45. """函数应用:F_{M,D}(x),对应 β-规约"""
  46. prompt = self.dataset.to_prompt()
  47. prompt += f"Input: {input_text}\nOutput:"
  48. # 延迟导入,方便查看代码结构
  49. import anthropic
  50. client = anthropic.Anthropic()
  51. messages = [{"role": "user", "content": prompt}]
  52. system = self.system_prompt or (
  53. "You are a precise function executor. Given the examples, "
  54. "learn the pattern and apply it to the new input. "
  55. "Output ONLY the result, nothing else."
  56. )
  57. response = client.messages.create(
  58. model=self.model,
  59. max_tokens=256,
  60. temperature=self.temperature,
  61. system=system,
  62. messages=messages,
  63. )
  64. return response.content[0].text.strip()
  65. @dataclass
  66. class ExperimentResult:
  67. name: str
  68. tests: list[dict] = field(default_factory=list)
  69. def add(self, input_val: str, expected: str, actual: str):
  70. passed = actual.strip() == expected.strip()
  71. self.tests.append({
  72. "input": input_val,
  73. "expected": expected,
  74. "actual": actual,
  75. "passed": passed,
  76. })
  77. def summary(self) -> str:
  78. total = len(self.tests)
  79. passed = sum(1 for t in self.tests if t["passed"])
  80. lines = [f"\n{'='*60}", f"实验: {self.name}", f"通过: {passed}/{total}", f"{'='*60}"]
  81. for t in self.tests:
  82. status = "✓" if t["passed"] else "✗"
  83. lines.append(f" {status} input={t['input']!r} expected={t['expected']!r} actual={t['actual']!r}")
  84. return "\n".join(lines)
  85. # ============================================================
  86. # 实验 1:后继函数 SUCC ≡ λn.λf.λx. f(n f x)
  87. # ============================================================
  88. def build_succ() -> LDS:
  89. """构造后继函数的 LDS"""
  90. dataset = Dataset(
  91. description="Function: Given a number n, output n+1 (the successor).",
  92. examples=[
  93. ("0", "1"),
  94. ("1", "2"),
  95. ("2", "3"),
  96. ("5", "6"),
  97. ("9", "10"),
  98. ("13", "14"),
  99. ("99", "100"),
  100. ]
  101. )
  102. return LDS(name="SUCC", dataset=dataset)
  103. def test_succ(succ: LDS) -> ExperimentResult:
  104. result = ExperimentResult("SUCC (后继函数)")
  105. for n in [3, 4, 7, 10, 15, 42, 127, 255]:
  106. actual = succ(str(n))
  107. result.add(str(n), str(n + 1), actual)
  108. return result
  109. # ============================================================
  110. # 实验 2:布尔值 TRUE ≡ λa.λb.a / FALSE ≡ λa.λb.b
  111. # ============================================================
  112. def build_true() -> LDS:
  113. dataset = Dataset(
  114. description="Function: Given two options A and B, always select A (the first one).",
  115. examples=[
  116. ("A='apple', B='banana'", "apple"),
  117. ("A='red', B='blue'", "red"),
  118. ("A='1', B='0'", "1"),
  119. ("A='yes', B='no'", "yes"),
  120. ("A='cat', B='dog'", "cat"),
  121. ]
  122. )
  123. return LDS(name="TRUE", dataset=dataset)
  124. def build_false() -> LDS:
  125. dataset = Dataset(
  126. description="Function: Given two options A and B, always select B (the second one).",
  127. examples=[
  128. ("A='apple', B='banana'", "banana"),
  129. ("A='red', B='blue'", "blue"),
  130. ("A='1', B='0'", "0"),
  131. ("A='yes', B='no'", "no"),
  132. ("A='cat', B='dog'", "dog"),
  133. ]
  134. )
  135. return LDS(name="FALSE", dataset=dataset)
  136. def test_booleans(true_lds: LDS, false_lds: LDS) -> ExperimentResult:
  137. result = ExperimentResult("TRUE/FALSE (Church 布尔值)")
  138. test_pairs = [
  139. ("sun", "moon"),
  140. ("hello", "world"),
  141. ("42", "0"),
  142. ("alpha", "omega"),
  143. ]
  144. for a, b in test_pairs:
  145. inp = f"A='{a}', B='{b}'"
  146. actual_t = true_lds(inp)
  147. result.add(f"TRUE({inp})", a, actual_t)
  148. actual_f = false_lds(inp)
  149. result.add(f"FALSE({inp})", b, actual_f)
  150. return result
  151. # ============================================================
  152. # 实验 3:逻辑运算 AND / OR / NOT
  153. # ============================================================
  154. def build_and() -> LDS:
  155. dataset = Dataset(
  156. description="Function: Logical AND. Given two boolean values, output the result of AND.",
  157. examples=[
  158. ("TRUE, TRUE", "TRUE"),
  159. ("TRUE, FALSE", "FALSE"),
  160. ("FALSE, TRUE", "FALSE"),
  161. ("FALSE, FALSE", "FALSE"),
  162. ]
  163. )
  164. return LDS(name="AND", dataset=dataset)
  165. def build_or() -> LDS:
  166. dataset = Dataset(
  167. description="Function: Logical OR. Given two boolean values, output the result of OR.",
  168. examples=[
  169. ("TRUE, TRUE", "TRUE"),
  170. ("TRUE, FALSE", "TRUE"),
  171. ("FALSE, TRUE", "TRUE"),
  172. ("FALSE, FALSE", "FALSE"),
  173. ]
  174. )
  175. return LDS(name="OR", dataset=dataset)
  176. def build_not() -> LDS:
  177. dataset = Dataset(
  178. description="Function: Logical NOT. Given a boolean value, output its negation.",
  179. examples=[
  180. ("TRUE", "FALSE"),
  181. ("FALSE", "TRUE"),
  182. ]
  183. )
  184. return LDS(name="NOT", dataset=dataset)
  185. def test_logic(and_lds: LDS, or_lds: LDS, not_lds: LDS) -> ExperimentResult:
  186. result = ExperimentResult("AND/OR/NOT (逻辑运算)")
  187. # AND 测试(用新的组合输入)
  188. result.add("AND(TRUE, TRUE)", "TRUE", and_lds("TRUE, TRUE"))
  189. result.add("AND(TRUE, FALSE)", "FALSE", and_lds("TRUE, FALSE"))
  190. result.add("AND(FALSE, TRUE)", "FALSE", and_lds("FALSE, TRUE"))
  191. result.add("AND(FALSE, FALSE)", "FALSE", and_lds("FALSE, FALSE"))
  192. # OR 测试
  193. result.add("OR(TRUE, TRUE)", "TRUE", or_lds("TRUE, TRUE"))
  194. result.add("OR(TRUE, FALSE)", "TRUE", or_lds("TRUE, FALSE"))
  195. result.add("OR(FALSE, TRUE)", "TRUE", or_lds("FALSE, TRUE"))
  196. result.add("OR(FALSE, FALSE)", "FALSE", or_lds("FALSE, FALSE"))
  197. # NOT 测试
  198. result.add("NOT(TRUE)", "FALSE", not_lds("TRUE"))
  199. result.add("NOT(FALSE)", "TRUE", not_lds("FALSE"))
  200. return result
  201. # ============================================================
  202. # 实验 4:条件分支 IF ≡ λcond.λthen.λelse. cond then else
  203. # ============================================================
  204. def build_if() -> LDS:
  205. dataset = Dataset(
  206. description="Function: IF-THEN-ELSE. If condition is TRUE, output the THEN value. If FALSE, output the ELSE value.",
  207. examples=[
  208. ("condition=TRUE, then='yes', else='no'", "yes"),
  209. ("condition=FALSE, then='yes', else='no'", "no"),
  210. ("condition=TRUE, then='42', else='0'", "42"),
  211. ("condition=FALSE, then='42', else='0'", "0"),
  212. ("condition=TRUE, then='go', else='stop'", "go"),
  213. ("condition=FALSE, then='go', else='stop'", "stop"),
  214. ]
  215. )
  216. return LDS(name="IF", dataset=dataset)
  217. def test_if(if_lds: LDS) -> ExperimentResult:
  218. result = ExperimentResult("IF-THEN-ELSE (条件分支)")
  219. cases = [
  220. ("condition=TRUE, then='accept', else='reject'", "accept"),
  221. ("condition=FALSE, then='accept', else='reject'", "reject"),
  222. ("condition=TRUE, then='100', else='-1'", "100"),
  223. ("condition=FALSE, then='100', else='-1'", "-1"),
  224. ]
  225. for inp, expected in cases:
  226. actual = if_lds(inp)
  227. result.add(inp, expected, actual)
  228. return result
  229. # ============================================================
  230. # 实验 5:有序对 PAIR / FST / SND
  231. # ============================================================
  232. def build_pair() -> LDS:
  233. dataset = Dataset(
  234. description="Function: PAIR. Given two values a and b, construct the pair (a, b).",
  235. examples=[
  236. ("a=3, b=7", "(3, 7)"),
  237. ("a='hello', b='world'", "(hello, world)"),
  238. ("a=0, b=1", "(0, 1)"),
  239. ("a='x', b='y'", "(x, y)"),
  240. ]
  241. )
  242. return LDS(name="PAIR", dataset=dataset)
  243. def build_fst() -> LDS:
  244. dataset = Dataset(
  245. description="Function: FST. Given a pair, extract the first element.",
  246. examples=[
  247. ("(3, 7)", "3"),
  248. ("(hello, world)", "hello"),
  249. ("(0, 1)", "0"),
  250. ("(x, y)", "x"),
  251. ("(42, 99)", "42"),
  252. ]
  253. )
  254. return LDS(name="FST", dataset=dataset)
  255. def build_snd() -> LDS:
  256. dataset = Dataset(
  257. description="Function: SND. Given a pair, extract the second element.",
  258. examples=[
  259. ("(3, 7)", "7"),
  260. ("(hello, world)", "world"),
  261. ("(0, 1)", "1"),
  262. ("(x, y)", "y"),
  263. ("(42, 99)", "99"),
  264. ]
  265. )
  266. return LDS(name="SND", dataset=dataset)
  267. def test_pairs(pair_lds: LDS, fst_lds: LDS, snd_lds: LDS) -> ExperimentResult:
  268. result = ExperimentResult("PAIR/FST/SND (有序对)")
  269. # 构造对
  270. p = pair_lds("a=10, b=20")
  271. result.add("PAIR(10, 20)", "(10, 20)", p)
  272. # 解构
  273. result.add("FST((5, 8))", "5", fst_lds("(5, 8)"))
  274. result.add("SND((5, 8))", "8", snd_lds("(5, 8)"))
  275. # 组合测试:FST(PAIR(a, b)) = a
  276. pair_result = pair_lds("a='alpha', b='beta'")
  277. fst_result = fst_lds(pair_result)
  278. result.add("FST(PAIR(alpha, beta))", "alpha", fst_result)
  279. snd_result = snd_lds(pair_result)
  280. result.add("SND(PAIR(alpha, beta))", "beta", snd_result)
  281. return result
  282. # ============================================================
  283. # 实验 6:函数组合(高阶函数)
  284. # ============================================================
  285. def build_double() -> LDS:
  286. dataset = Dataset(
  287. description="Function: DOUBLE. Given a number n, output 2*n.",
  288. examples=[
  289. ("0", "0"),
  290. ("1", "2"),
  291. ("2", "4"),
  292. ("3", "6"),
  293. ("5", "10"),
  294. ("10", "20"),
  295. ]
  296. )
  297. return LDS(name="DOUBLE", dataset=dataset)
  298. def test_composition(succ: LDS, double: LDS) -> ExperimentResult:
  299. """测试函数组合:(DOUBLE ∘ SUCC)(n) = 2*(n+1)"""
  300. result = ExperimentResult("函数组合 (DOUBLE ∘ SUCC)")
  301. for n in [0, 1, 3, 5, 10]:
  302. # 先 SUCC 再 DOUBLE:对应 Lambda 中的 (double (succ n))
  303. intermediate = succ(str(n))
  304. final = double(intermediate)
  305. expected = str(2 * (n + 1))
  306. result.add(f"DOUBLE(SUCC({n}))", expected, final)
  307. return result
  308. # ============================================================
  309. # 实验 7:递归 —— Chain-of-Thought 作为 Y 组合子
  310. # ============================================================
  311. def build_factorial() -> LDS:
  312. """阶乘:通过 CoT 实现递归展开,对应 Y 组合子"""
  313. dataset = Dataset(
  314. description=(
  315. "Function: FACTORIAL. Compute n! step by step.\n"
  316. "Show each recursive step, then give the final answer on the last line as 'Result: <number>'."
  317. ),
  318. examples=[
  319. ("0", "0! = 1\nResult: 1"),
  320. ("1", "1! = 1 × 0! = 1 × 1 = 1\nResult: 1"),
  321. ("3", "3! = 3 × 2!\n2! = 2 × 1!\n1! = 1 × 0!\n0! = 1\nSo: 1 × 1 × 2 × 3 = 6\nResult: 6"),
  322. ("5", "5! = 5 × 4!\n4! = 4 × 3!\n3! = 3 × 2!\n2! = 2 × 1!\n1! = 1 × 0!\n0! = 1\nSo: 1 × 1 × 2 × 3 × 4 × 5 = 120\nResult: 120"),
  323. ]
  324. )
  325. return LDS(name="FACTORIAL", dataset=dataset)
  326. def test_factorial(fact: LDS) -> ExperimentResult:
  327. result = ExperimentResult("FACTORIAL (递归/Y 组合子)")
  328. import math
  329. for n in [2, 4, 6, 7]:
  330. actual_raw = fact(str(n))
  331. # 提取 "Result: xxx" 行
  332. lines = actual_raw.strip().split("\n")
  333. actual = ""
  334. for line in reversed(lines):
  335. if "Result:" in line:
  336. actual = line.split("Result:")[-1].strip()
  337. break
  338. if not actual:
  339. actual = lines[-1].strip()
  340. expected = str(math.factorial(n))
  341. result.add(f"{n}!", expected, actual)
  342. return result
  343. # ============================================================
  344. # 实验 8:Church 数的直接实现
  345. # ============================================================
  346. def build_church_apply() -> LDS:
  347. """Church 数:将函数 f 作用于 x 共 n 次"""
  348. dataset = Dataset(
  349. description=(
  350. "Function: CHURCH_APPLY. Given n, f, and x, apply f to x exactly n times.\n"
  351. "f(x) means apply f once. f(f(x)) means apply f twice. Etc."
  352. ),
  353. examples=[
  354. ("n=0, f='add1', x='0'", "0"),
  355. ("n=1, f='add1', x='0'", "1"),
  356. ("n=2, f='add1', x='0'", "2"),
  357. ("n=3, f='add1', x='0'", "3"),
  358. ("n=0, f='double', x='1'", "1"),
  359. ("n=1, f='double', x='1'", "2"),
  360. ("n=2, f='double', x='1'", "4"),
  361. ("n=3, f='double', x='1'", "8"),
  362. ("n=4, f='double', x='1'", "16"),
  363. ]
  364. )
  365. return LDS(name="CHURCH_APPLY", dataset=dataset)
  366. def test_church(church: LDS) -> ExperimentResult:
  367. result = ExperimentResult("CHURCH 数 (f^n(x))")
  368. # add1 测试
  369. for n in [4, 5, 7]:
  370. actual = church(f"n={n}, f='add1', x='0'")
  371. result.add(f"add1^{n}(0)", str(n), actual)
  372. # double 测试:double^n(1) = 2^n
  373. for n in [1, 2, 3, 5]:
  374. actual = church(f"n={n}, f='double', x='1'")
  375. result.add(f"double^{n}(1)", str(2**n), actual)
  376. return result
  377. # ============================================================
  378. # 主程序
  379. # ============================================================
  380. def run_all_experiments():
  381. """运行所有实验并汇总结果"""
  382. print("=" * 60)
  383. print("LDS (LLM-Dataset System) ≡ Lambda Calculus")
  384. print("实验验证:用 LLM + Dataset 构造 Church 编码原语")
  385. print("=" * 60)
  386. all_results: list[ExperimentResult] = []
  387. # 构建所有 LDS
  388. print("\n构建 LDS 系统...")
  389. succ = build_succ()
  390. true_lds = build_true()
  391. false_lds = build_false()
  392. and_lds = build_and()
  393. or_lds = build_or()
  394. not_lds = build_not()
  395. if_lds = build_if()
  396. pair_lds = build_pair()
  397. fst_lds = build_fst()
  398. snd_lds = build_snd()
  399. double = build_double()
  400. fact = build_factorial()
  401. church = build_church_apply()
  402. # 运行实验
  403. experiments = [
  404. ("1. 后继函数", lambda: test_succ(succ)),
  405. ("2. Church 布尔值", lambda: test_booleans(true_lds, false_lds)),
  406. ("3. 逻辑运算", lambda: test_logic(and_lds, or_lds, not_lds)),
  407. ("4. 条件分支", lambda: test_if(if_lds)),
  408. ("5. 有序对", lambda: test_pairs(pair_lds, fst_lds, snd_lds)),
  409. ("6. 函数组合", lambda: test_composition(succ, double)),
  410. ("7. 递归 (Y 组合子)", lambda: test_factorial(fact)),
  411. ("8. Church 数", lambda: test_church(church)),
  412. ]
  413. for name, run_fn in experiments:
  414. print(f"\n运行实验 {name}...")
  415. try:
  416. r = run_fn()
  417. all_results.append(r)
  418. print(r.summary())
  419. except Exception as e:
  420. print(f" 错误: {e}")
  421. # 总结
  422. total_tests = sum(len(r.tests) for r in all_results)
  423. total_passed = sum(sum(1 for t in r.tests if t["passed"]) for r in all_results)
  424. print("\n" + "=" * 60)
  425. print(f"总计: {total_passed}/{total_tests} 测试通过")
  426. print("=" * 60)
  427. # 保存结果到 JSON
  428. output = {
  429. "experiments": [
  430. {
  431. "name": r.name,
  432. "tests": r.tests,
  433. "passed": sum(1 for t in r.tests if t["passed"]),
  434. "total": len(r.tests),
  435. }
  436. for r in all_results
  437. ],
  438. "total_passed": total_passed,
  439. "total_tests": total_tests,
  440. }
  441. with open("experiment_results.json", "w", encoding="utf-8") as f:
  442. json.dump(output, f, ensure_ascii=False, indent=2)
  443. print("\n结果已保存到 experiment_results.json")
  444. def run_dry_run():
  445. """不调用 API 的演示模式,展示实验结构"""
  446. print("=" * 60)
  447. print("DRY RUN: 展示 LDS 构造(不调用 API)")
  448. print("=" * 60)
  449. primitives = [
  450. ("SUCC (后继)", build_succ()),
  451. ("TRUE (Church 真)", build_true()),
  452. ("FALSE (Church 假)", build_false()),
  453. ("AND (逻辑与)", build_and()),
  454. ("OR (逻辑或)", build_or()),
  455. ("NOT (逻辑非)", build_not()),
  456. ("IF (条件)", build_if()),
  457. ("PAIR (构造对)", build_pair()),
  458. ("FST (取第一)", build_fst()),
  459. ("SND (取第二)", build_snd()),
  460. ("DOUBLE (翻倍)", build_double()),
  461. ("FACTORIAL (阶乘/递归)", build_factorial()),
  462. ("CHURCH_APPLY (Church 数)", build_church_apply()),
  463. ]
  464. for name, lds in primitives:
  465. print(f"\n{'─'*40}")
  466. print(f"Lambda 原语: {name}")
  467. print(f"LDS 名称: {lds.name}")
  468. print(f"数据集大小: {len(lds.dataset.examples)} 个示例")
  469. print(f"数据集预览:")
  470. for inp, out in lds.dataset.examples[:3]:
  471. print(f" ({inp!r}) → {out!r}")
  472. if len(lds.dataset.examples) > 3:
  473. print(f" ... 共 {len(lds.dataset.examples)} 个示例")
  474. print(f"\n{'='*60}")
  475. print("共构造 13 个 LDS,覆盖 Lambda 演算全部基本原语。")
  476. print("运行 `python experiment_church_primitives.py --run` 执行完整实验。")
  477. if __name__ == "__main__":
  478. import sys
  479. if "--run" in sys.argv:
  480. run_all_experiments()
  481. else:
  482. run_dry_run()